Skip to content
Snippets Groups Projects
Commit 568149a1 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Service component:

- Removed deprecated Database Model within Service
- Corrected constructor signature of ServiceHandler interface
- Removed unneeded file in L3NMEmulated Service Handler
- Removed unused imports from TAPI Service Handler
parent 92dfd5b4
No related branches found
No related tags found
1 merge request!54Release 2.0.0
Showing
with 11 additions and 1005 deletions
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import functools, logging, operator
from enum import Enum
from typing import Dict, List, Tuple, Union
from common.orm.Database import Database
from common.orm.HighLevel import get_object, get_or_create_object, update_or_create_object
from common.orm.backend.Tools import key_to_str
from common.orm.fields.EnumeratedField import EnumeratedField
from common.orm.fields.ForeignKeyField import ForeignKeyField
from common.orm.fields.IntegerField import IntegerField
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.fields.StringField import StringField
from common.orm.model.Model import Model
from common.proto.context_pb2 import ConfigActionEnum
from common.tools.grpc.Tools import grpc_message_to_json_string
from .Tools import fast_hasher, grpc_to_enum, remove_dict_key
LOGGER = logging.getLogger(__name__)
class ORM_ConfigActionEnum(Enum):
UNDEFINED = ConfigActionEnum.CONFIGACTION_UNDEFINED
SET = ConfigActionEnum.CONFIGACTION_SET
DELETE = ConfigActionEnum.CONFIGACTION_DELETE
grpc_to_enum__config_action = functools.partial(
grpc_to_enum, ConfigActionEnum, ORM_ConfigActionEnum)
class ConfigModel(Model): # pylint: disable=abstract-method
pk = PrimaryKeyField()
def dump(self) -> List[Dict]:
db_config_rule_pks = self.references(ConfigRuleModel)
config_rules = [ConfigRuleModel(self.database, pk).dump(include_position=True) for pk,_ in db_config_rule_pks]
config_rules = sorted(config_rules, key=operator.itemgetter('position'))
return [remove_dict_key(config_rule, 'position') for config_rule in config_rules]
class ConfigRuleModel(Model): # pylint: disable=abstract-method
pk = PrimaryKeyField()
config_fk = ForeignKeyField(ConfigModel)
position = IntegerField(min_value=0, required=True)
action = EnumeratedField(ORM_ConfigActionEnum, required=True)
key = StringField(required=True, allow_empty=False)
value = StringField(required=False, allow_empty=True)
def dump(self, include_position=True) -> Dict: # pylint: disable=arguments-differ
result = {
'action': self.action.value,
'custom': {
'resource_key': self.key,
'resource_value': self.value,
},
}
if include_position: result['position'] = self.position
return result
def delete_all_config_rules(database : Database, db_parent_pk : str, config_name : str) -> None:
str_config_key = key_to_str([db_parent_pk, config_name], separator=':')
db_config : ConfigModel = get_object(database, ConfigModel, str_config_key, raise_if_not_found=False)
if db_config is None: return
db_config_rule_pks = db_config.references(ConfigRuleModel)
for pk,_ in db_config_rule_pks: ConfigRuleModel(database, pk).delete()
def grpc_config_rules_to_raw(grpc_config_rules) -> List[Tuple[ORM_ConfigActionEnum, str, str]]:
def translate(grpc_config_rule):
action = grpc_to_enum__config_action(grpc_config_rule.action)
config_rule_type = str(grpc_config_rule.WhichOneof('config_rule'))
if config_rule_type != 'custom':
raise NotImplementedError('ConfigRule of type {:s} is not implemented: {:s}'.format(
config_rule_type, grpc_message_to_json_string(grpc_config_rule)))
return action, grpc_config_rule.custom.resource_key, grpc_config_rule.custom.resource_value
return [translate(grpc_config_rule) for grpc_config_rule in grpc_config_rules]
def get_config_rules(
database : Database, db_parent_pk : str, config_name : str
) -> List[Tuple[ORM_ConfigActionEnum, str, str]]:
str_config_key = key_to_str([db_parent_pk, config_name], separator=':')
db_config = get_object(database, ConfigModel, str_config_key, raise_if_not_found=False)
return [] if db_config is None else [
# pylint: disable=no-member, protected-access
(ORM_ConfigActionEnum._value2member_map_.get(config_rule['action']),
config_rule['custom']['resource_key'], config_rule['custom']['resource_value'])
for config_rule in db_config.dump()
if 'custom' in config_rule
]
def update_config(
database : Database, db_parent_pk : str, config_name : str,
raw_config_rules : List[Tuple[ORM_ConfigActionEnum, str, str]]
) -> List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]]:
str_config_key = key_to_str([db_parent_pk, config_name], separator=':')
result : Tuple[ConfigModel, bool] = get_or_create_object(database, ConfigModel, str_config_key)
db_config, created = result
db_objects : List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]] = [(db_config, created)]
for position,(action, resource_key, resource_value) in enumerate(raw_config_rules):
str_rule_key_hash = fast_hasher(resource_key)
str_config_rule_key = key_to_str([db_config.pk, str_rule_key_hash], separator=':')
result : Tuple[ConfigRuleModel, bool] = update_or_create_object(
database, ConfigRuleModel, str_config_rule_key, {
'config_fk': db_config, 'position': position, 'action': action, 'key': resource_key,
'value': resource_value,
})
db_config_rule, updated = result
db_objects.append((db_config_rule, updated))
return db_objects
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging, operator
from typing import Dict, List, Tuple, Union
from common.orm.Database import Database
from common.orm.HighLevel import get_object, get_or_create_object, update_or_create_object
from common.orm.backend.Tools import key_to_str
from common.orm.fields.ForeignKeyField import ForeignKeyField
from common.orm.fields.IntegerField import IntegerField
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.fields.StringField import StringField
from common.orm.model.Model import Model
from service.service.database.Tools import fast_hasher, remove_dict_key
LOGGER = logging.getLogger(__name__)
class ConstraintsModel(Model): # pylint: disable=abstract-method
pk = PrimaryKeyField()
def dump(self) -> List[Dict]:
db_constraint_pks = self.references(ConstraintModel)
constraints = [ConstraintModel(self.database, pk).dump(include_position=True) for pk,_ in db_constraint_pks]
constraints = sorted(constraints, key=operator.itemgetter('position'))
return [remove_dict_key(constraint, 'position') for constraint in constraints]
class ConstraintModel(Model): # pylint: disable=abstract-method
pk = PrimaryKeyField()
constraints_fk = ForeignKeyField(ConstraintsModel)
position = IntegerField(min_value=0, required=True)
constraint_type = StringField(required=True, allow_empty=False)
constraint_value = StringField(required=True, allow_empty=False)
def dump(self, include_position=True) -> Dict: # pylint: disable=arguments-differ
result = {
'custom': {
'constraint_type': self.constraint_type,
'constraint_value': self.constraint_value,
},
}
if include_position: result['position'] = self.position
return result
def delete_all_constraints(database : Database, db_parent_pk : str, constraints_name : str) -> None:
str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':')
db_constraints : ConstraintsModel = get_object(
database, ConstraintsModel, str_constraints_key, raise_if_not_found=False)
if db_constraints is None: return
db_constraint_pks = db_constraints.references(ConstraintModel)
for pk,_ in db_constraint_pks: ConstraintModel(database, pk).delete()
def grpc_constraints_to_raw(grpc_constraints) -> List[Tuple[str, str]]:
return [
(grpc_constraint.custom.constraint_type, grpc_constraint.custom.constraint_value)
for grpc_constraint in grpc_constraints
if grpc_constraint.WhichOneof('constraint') == 'custom'
]
def get_constraints(database : Database, db_parent_pk : str, constraints_name : str) -> List[Tuple[str, str]]:
str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':')
db_constraints : ConstraintsModel = get_object(
database, ConstraintsModel, str_constraints_key, raise_if_not_found=False)
return [] if db_constraints is None else [
(constraint['custom']['constraint_type'], constraint['custom']['constraint_value'])
for constraint in db_constraints.dump()
if 'custom' in constraint
]
def update_constraints(
database : Database, db_parent_pk : str, constraints_name : str, raw_constraints : List[Tuple[str, str]]
) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]:
str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':')
result : Tuple[ConstraintsModel, bool] = get_or_create_object(database, ConstraintsModel, str_constraints_key)
db_constraints, created = result
db_objects : List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]] = [(db_constraints, created)]
for position,(constraint_type, constraint_value) in enumerate(raw_constraints):
str_constraint_key_hash = fast_hasher(constraint_type)
str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':')
result : Tuple[ConstraintModel, bool] = update_or_create_object(
database, ConstraintModel, str_constraint_key, {
'constraints_fk': db_constraints, 'position': position, 'constraint_type': constraint_type,
'constraint_value': constraint_value})
db_constraints_rule, updated = result
db_objects.append((db_constraints_rule, updated))
return db_objects
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from typing import Dict #, List
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.fields.StringField import StringField
from common.orm.model.Model import Model
LOGGER = logging.getLogger(__name__)
class ContextModel(Model):
pk = PrimaryKeyField()
context_uuid = StringField(required=True, allow_empty=False)
def dump_id(self) -> Dict:
return {'context_uuid': {'uuid': self.context_uuid}}
# def dump_service_ids(self) -> List[Dict]:
# from .ServiceModel import ServiceModel # pylint: disable=import-outside-toplevel
# db_service_pks = self.references(ServiceModel)
# return [ServiceModel(self.database, pk).dump_id() for pk,_ in db_service_pks]
#
# def dump_topology_ids(self) -> List[Dict]:
# from .TopologyModel import TopologyModel # pylint: disable=import-outside-toplevel
# db_topology_pks = self.references(TopologyModel)
# return [TopologyModel(self.database, pk).dump_id() for pk,_ in db_topology_pks]
#
# def dump(self, include_services=True, include_topologies=True) -> Dict: # pylint: disable=arguments-differ
# result = {'context_id': self.dump_id()}
# if include_services: result['service_ids'] = self.dump_service_ids()
# if include_topologies: result['topology_ids'] = self.dump_topology_ids()
# return result
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import grpc
from typing import Tuple
from common.orm.Database import Database
from common.orm.HighLevel import get_or_create_object, update_or_create_object
from common.orm.backend.Tools import key_to_str
from common.proto.context_pb2 import Device, DeviceId
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException
from context.client.ContextClient import ContextClient
from .ConfigModel import delete_all_config_rules, grpc_config_rules_to_raw, update_config
from .ContextModel import ContextModel
from .DeviceModel import DeviceModel, grpc_to_enum__device_operational_status, set_drivers
from .EndPointModel import EndPointModel
from .TopologyModel import TopologyModel
def update_device_in_local_database(database : Database, device : Device) -> Tuple[DeviceModel, bool]:
device_uuid = device.device_id.device_uuid.uuid
for i,endpoint in enumerate(device.device_endpoints):
endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid
if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid
if device_uuid != endpoint_device_uuid:
raise InvalidArgumentException(
'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid,
['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)])
config_rules = grpc_config_rules_to_raw(device.device_config.config_rules)
delete_all_config_rules(database, device_uuid, 'running')
running_config_result = update_config(database, device_uuid, 'running', config_rules)
result : Tuple[DeviceModel, bool] = update_or_create_object(database, DeviceModel, device_uuid, {
'device_uuid' : device_uuid,
'device_type' : device.device_type,
'device_operational_status': grpc_to_enum__device_operational_status(device.device_operational_status),
'device_config_fk' : running_config_result[0][0],
})
db_device, updated = result
set_drivers(database, db_device, device.device_drivers)
for i,endpoint in enumerate(device.device_endpoints):
endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid
endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid
if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid
str_endpoint_key = key_to_str([device_uuid, endpoint_uuid])
endpoint_attributes = {
'device_fk' : db_device,
'endpoint_uuid': endpoint_uuid,
'endpoint_type': endpoint.endpoint_type,
}
endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid
endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid
if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
result : Tuple[ContextModel, bool] = get_or_create_object(
database, ContextModel, endpoint_topology_context_uuid, defaults={
'context_uuid': endpoint_topology_context_uuid,
})
db_context, _ = result
str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
result : Tuple[TopologyModel, bool] = get_or_create_object(
database, TopologyModel, str_topology_key, defaults={
'context_fk': db_context,
'topology_uuid': endpoint_topology_uuid,
})
db_topology, _ = result
str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
endpoint_attributes['topology_fk'] = db_topology
result : Tuple[EndPointModel, bool] = update_or_create_object(
database, EndPointModel, str_endpoint_key, endpoint_attributes)
_, db_endpoint_updated = result
updated = updated or db_endpoint_updated
return db_device, updated
def sync_device_from_context(
device_uuid : str, context_client : ContextClient, database : Database
) -> Tuple[DeviceModel, bool]:
try:
device : Device = context_client.GetDevice(DeviceId(device_uuid={'uuid': device_uuid}))
except grpc.RpcError as e:
if e.code() != grpc.StatusCode.NOT_FOUND: raise # pylint: disable=no-member
return None
return update_device_in_local_database(database, device)
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import grpc
from typing import Tuple
from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
from common.orm.Database import Database
from common.orm.HighLevel import get_object, get_or_create_object, update_or_create_object
from common.orm.backend.Tools import key_to_str
from common.proto.context_pb2 import Service, ServiceId
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException
from context.client.ContextClient import ContextClient
from .ConfigModel import delete_all_config_rules, grpc_config_rules_to_raw, update_config
from .ConstraintModel import delete_all_constraints, grpc_constraints_to_raw, update_constraints
from .ContextModel import ContextModel
from .EndPointModel import EndPointModel
from .RelationModels import ServiceEndPointModel
from .ServiceModel import ServiceModel, grpc_to_enum__service_status, grpc_to_enum__service_type
from .TopologyModel import TopologyModel
def update_service_in_local_database(database : Database, service : Service) -> Tuple[ServiceModel, bool]:
service_uuid = service.service_id.service_uuid.uuid
service_context_uuid = service.service_id.context_id.context_uuid.uuid
if len(service_context_uuid) == 0: service_context_uuid = DEFAULT_CONTEXT_UUID
topology_uuids = {}
for i,endpoint_id in enumerate(service.service_endpoint_ids):
endpoint_uuid = endpoint_id.endpoint_uuid.uuid
endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid
endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid
endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
if len(endpoint_device_uuid) == 0:
raise InvalidArgumentException(
'request.service_endpoint_ids[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid,
['not set'])
if len(endpoint_topology_context_uuid) == 0: endpoint_topology_context_uuid = service_context_uuid
if service_context_uuid != endpoint_topology_context_uuid:
raise InvalidArgumentException(
'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i),
endpoint_device_uuid,
['should be == {:s}({:s})'.format('service_id.context_id.context_uuid.uuid', service_context_uuid)])
topology_uuids.setdefault(endpoint_topology_uuid, set()).add(
'request.service_endpoint_ids[{:d}].device_id.device_uuid.uuid'.format(i))
if len(topology_uuids) > 1:
raise InvalidArgumentException(
'request.service_endpoint_ids', '...',
['Multiple different topology_uuid values specified: {:s}'.format(str(topology_uuids))])
if len(topology_uuids) == 1:
topology_uuid = topology_uuids.popitem()[0]
else:
topology_uuid = DEFAULT_TOPOLOGY_UUID
result : Tuple[ContextModel, bool] = get_or_create_object(
database, ContextModel, service_context_uuid, defaults={'context_uuid': service_context_uuid})
db_context, _ = result
str_topology_key = None
if len(topology_uuid) > 0:
str_topology_key = key_to_str([service_context_uuid, topology_uuid])
result : Tuple[TopologyModel, bool] = get_or_create_object(
database, TopologyModel, str_topology_key, defaults={
'context_fk': db_context, 'topology_uuid': topology_uuid})
#db_topology, _ = result
str_service_key = key_to_str([service_context_uuid, service_uuid])
config_rules = grpc_config_rules_to_raw(service.service_config.config_rules)
delete_all_config_rules(database, str_service_key, 'running')
running_config_result = update_config(database, str_service_key, 'running', config_rules)
constraints = grpc_constraints_to_raw(service.service_constraints)
delete_all_constraints(database, str_service_key, 'running')
running_constraints_result = update_constraints(database, str_service_key, 'running', constraints)
result : Tuple[ContextModel, bool] = get_or_create_object(
database, ContextModel, service_context_uuid, defaults={
'context_uuid': service_context_uuid,
})
db_context, _ = result
result : Tuple[ServiceModel, bool] = update_or_create_object(database, ServiceModel, str_service_key, {
'context_fk' : db_context,
'service_uuid' : service_uuid,
'service_type' : grpc_to_enum__service_type(service.service_type),
'service_status' : grpc_to_enum__service_status(service.service_status.service_status),
'service_constraints_fk': running_constraints_result[0][0],
'service_config_fk' : running_config_result[0][0],
})
db_service, updated = result
for i,endpoint_id in enumerate(service.service_endpoint_ids):
endpoint_uuid = endpoint_id.endpoint_uuid.uuid
endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid
str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid])
if str_topology_key is not None:
str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
db_endpoint : EndPointModel = get_object(database, EndPointModel, str_endpoint_key)
str_service_endpoint_key = key_to_str([str_service_key, str_endpoint_key], separator='--')
result : Tuple[ServiceEndPointModel, bool] = get_or_create_object(
database, ServiceEndPointModel, str_service_endpoint_key, {
'service_fk': db_service, 'endpoint_fk': db_endpoint})
_, service_endpoint_created = result
updated = updated or service_endpoint_created
return db_service, updated
def sync_service_from_context(
context_uuid : str, service_uuid : str, context_client : ContextClient, database : Database
) -> Tuple[ServiceModel, bool]:
try:
service : Service = context_client.GetService(ServiceId(
context_id={'context_uuid': {'uuid': context_uuid}},
service_uuid={'uuid': service_uuid}))
except grpc.RpcError as e:
if e.code() != grpc.StatusCode.NOT_FOUND: raise # pylint: disable=no-member
return None
return update_service_in_local_database(database, service)
def sync_service_to_context(db_service : ServiceModel, context_client : ContextClient) -> None:
if db_service is None: return
context_client.SetService(Service(**db_service.dump(
include_endpoint_ids=True, include_constraints=True, include_config_rules=True)))
def delete_service_from_context(db_service : ServiceModel, context_client : ContextClient) -> None:
if db_service is None: return
context_client.RemoveService(ServiceId(**db_service.dump_id()))
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import functools, logging
from enum import Enum
from typing import Dict, List
from common.orm.Database import Database
from common.orm.backend.Tools import key_to_str
from common.orm.fields.EnumeratedField import EnumeratedField
from common.orm.fields.ForeignKeyField import ForeignKeyField
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.fields.StringField import StringField
from common.orm.model.Model import Model
from common.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum
from .ConfigModel import ConfigModel
from .Tools import grpc_to_enum
LOGGER = logging.getLogger(__name__)
class ORM_DeviceDriverEnum(Enum):
UNDEFINED = DeviceDriverEnum.DEVICEDRIVER_UNDEFINED
OPENCONFIG = DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG
TRANSPORT_API = DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API
P4 = DeviceDriverEnum.DEVICEDRIVER_P4
IETF_NETWORK_TOPOLOGY = DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY
ONF_TR_352 = DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352
grpc_to_enum__device_driver = functools.partial(
grpc_to_enum, DeviceDriverEnum, ORM_DeviceDriverEnum)
class ORM_DeviceOperationalStatusEnum(Enum):
UNDEFINED = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_UNDEFINED
DISABLED = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED
ENABLED = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED
grpc_to_enum__device_operational_status = functools.partial(
grpc_to_enum, DeviceOperationalStatusEnum, ORM_DeviceOperationalStatusEnum)
class DeviceModel(Model):
pk = PrimaryKeyField()
device_uuid = StringField(required=True, allow_empty=False)
device_type = StringField()
device_config_fk = ForeignKeyField(ConfigModel)
device_operational_status = EnumeratedField(ORM_DeviceOperationalStatusEnum, required=True)
def dump_id(self) -> Dict:
return {'device_uuid': {'uuid': self.device_uuid}}
def dump_config(self) -> Dict:
return ConfigModel(self.database, self.device_config_fk).dump()
def dump_drivers(self) -> List[int]:
db_driver_pks = self.references(DriverModel)
return [DriverModel(self.database, pk).dump() for pk,_ in db_driver_pks]
def dump_endpoints(self) -> List[Dict]:
from .EndPointModel import EndPointModel # pylint: disable=import-outside-toplevel
db_endpoints_pks = self.references(EndPointModel)
return [EndPointModel(self.database, pk).dump() for pk,_ in db_endpoints_pks]
def dump( # pylint: disable=arguments-differ
self, include_config_rules=True, include_drivers=True, include_endpoints=True
) -> Dict:
result = {
'device_id': self.dump_id(),
'device_type': self.device_type,
'device_operational_status': self.device_operational_status.value,
}
if include_config_rules: result.setdefault('device_config', {})['config_rules'] = self.dump_config()
if include_drivers: result['device_drivers'] = self.dump_drivers()
if include_endpoints: result['device_endpoints'] = self.dump_endpoints()
return result
class DriverModel(Model): # pylint: disable=abstract-method
pk = PrimaryKeyField()
device_fk = ForeignKeyField(DeviceModel)
driver = EnumeratedField(ORM_DeviceDriverEnum, required=True)
def dump(self) -> Dict:
return self.driver.value
def set_drivers(database : Database, db_device : DeviceModel, grpc_device_drivers):
db_device_pk = db_device.pk
for driver in grpc_device_drivers:
orm_driver = grpc_to_enum__device_driver(driver)
str_device_driver_key = key_to_str([db_device_pk, orm_driver.name])
db_device_driver = DriverModel(database, str_device_driver_key)
db_device_driver.device_fk = db_device
db_device_driver.driver = orm_driver
db_device_driver.save()
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from typing import Dict, List, Optional, Tuple
from common.orm.fields.ForeignKeyField import ForeignKeyField
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.fields.StringField import StringField
from common.orm.model.Model import Model
from common.proto.context_pb2 import EndPointId
from .DeviceModel import DeviceModel
from .TopologyModel import TopologyModel
LOGGER = logging.getLogger(__name__)
class EndPointModel(Model):
pk = PrimaryKeyField()
topology_fk = ForeignKeyField(TopologyModel, required=False)
device_fk = ForeignKeyField(DeviceModel)
endpoint_uuid = StringField(required=True, allow_empty=False)
endpoint_type = StringField()
def dump_id(self) -> Dict:
device_id = DeviceModel(self.database, self.device_fk).dump_id()
result = {
'device_id': device_id,
'endpoint_uuid': {'uuid': self.endpoint_uuid},
}
if self.topology_fk is not None:
result['topology_id'] = TopologyModel(self.database, self.topology_fk).dump_id()
return result
def dump(self) -> Dict:
return {
'endpoint_id': self.dump_id(),
'endpoint_type': self.endpoint_type,
}
def grpc_endpointids_to_raw(grpc_endpointids : List[EndPointId]) -> List[Tuple[str, str, Optional[str]]]:
def translate(grpc_endpointid : EndPointId) -> Tuple[str, str, Optional[str]]:
device_uuid = grpc_endpointid.device_id.device_uuid.uuid
endpoint_uuid = grpc_endpointid.endpoint_uuid.uuid
topology_uuid = grpc_endpointid.topology_id.topology_uuid.uuid
if len(topology_uuid) == 0: topology_uuid = None
return device_uuid, endpoint_uuid, topology_uuid
return [translate(grpc_endpointid) for grpc_endpointid in grpc_endpointids]
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging, operator
from typing import Dict, List
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.fields.StringField import StringField
from common.orm.model.Model import Model
from common.orm.HighLevel import get_related_objects
LOGGER = logging.getLogger(__name__)
class LinkModel(Model):
pk = PrimaryKeyField()
link_uuid = StringField(required=True, allow_empty=False)
def dump_id(self) -> Dict:
return {'link_uuid': {'uuid': self.link_uuid}}
def dump_endpoint_ids(self) -> List[Dict]:
from .RelationModels import LinkEndPointModel # pylint: disable=import-outside-toplevel
db_endpoints = get_related_objects(self, LinkEndPointModel, 'endpoint_fk')
return [db_endpoint.dump_id() for db_endpoint in sorted(db_endpoints, key=operator.attrgetter('pk'))]
def dump(self) -> Dict:
return {
'link_id': self.dump_id(),
'link_endpoint_ids': self.dump_endpoint_ids(),
}
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from common.orm.fields.ForeignKeyField import ForeignKeyField
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.model.Model import Model
from .DeviceModel import DeviceModel
from .EndPointModel import EndPointModel
from .LinkModel import LinkModel
from .ServiceModel import ServiceModel
from .TopologyModel import TopologyModel
LOGGER = logging.getLogger(__name__)
class LinkEndPointModel(Model): # pylint: disable=abstract-method
pk = PrimaryKeyField()
link_fk = ForeignKeyField(LinkModel)
endpoint_fk = ForeignKeyField(EndPointModel)
class ServiceEndPointModel(Model): # pylint: disable=abstract-method
pk = PrimaryKeyField()
service_fk = ForeignKeyField(ServiceModel)
endpoint_fk = ForeignKeyField(EndPointModel)
class TopologyDeviceModel(Model): # pylint: disable=abstract-method
pk = PrimaryKeyField()
topology_fk = ForeignKeyField(TopologyModel)
device_fk = ForeignKeyField(DeviceModel)
class TopologyLinkModel(Model): # pylint: disable=abstract-method
pk = PrimaryKeyField()
topology_fk = ForeignKeyField(TopologyModel)
link_fk = ForeignKeyField(LinkModel)
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import functools, logging, operator
from enum import Enum
from typing import Dict, List
from common.orm.fields.EnumeratedField import EnumeratedField
from common.orm.fields.ForeignKeyField import ForeignKeyField
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.fields.StringField import StringField
from common.orm.model.Model import Model
from common.orm.HighLevel import get_related_objects
from common.proto.context_pb2 import ServiceStatusEnum, ServiceTypeEnum
from .ConfigModel import ConfigModel
from .ConstraintModel import ConstraintsModel
from .ContextModel import ContextModel
from .Tools import grpc_to_enum
LOGGER = logging.getLogger(__name__)
class ORM_ServiceTypeEnum(Enum):
UNKNOWN = ServiceTypeEnum.SERVICETYPE_UNKNOWN
L3NM = ServiceTypeEnum.SERVICETYPE_L3NM
L2NM = ServiceTypeEnum.SERVICETYPE_L2NM
TAPI_CONNECTIVITY_SERVICE = ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE
grpc_to_enum__service_type = functools.partial(
grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum)
class ORM_ServiceStatusEnum(Enum):
UNDEFINED = ServiceStatusEnum.SERVICESTATUS_UNDEFINED
PLANNED = ServiceStatusEnum.SERVICESTATUS_PLANNED
ACTIVE = ServiceStatusEnum.SERVICESTATUS_ACTIVE
PENDING_REMOVAL = ServiceStatusEnum.SERVICESTATUS_PENDING_REMOVAL
grpc_to_enum__service_status = functools.partial(
grpc_to_enum, ServiceStatusEnum, ORM_ServiceStatusEnum)
class ServiceModel(Model):
pk = PrimaryKeyField()
context_fk = ForeignKeyField(ContextModel)
service_uuid = StringField(required=True, allow_empty=False)
service_type = EnumeratedField(ORM_ServiceTypeEnum, required=True)
service_constraints_fk = ForeignKeyField(ConstraintsModel)
service_status = EnumeratedField(ORM_ServiceStatusEnum, required=True)
service_config_fk = ForeignKeyField(ConfigModel)
def dump_id(self) -> Dict:
context_id = ContextModel(self.database, self.context_fk).dump_id()
return {
'context_id': context_id,
'service_uuid': {'uuid': self.service_uuid},
}
def dump_endpoint_ids(self) -> List[Dict]:
from .RelationModels import ServiceEndPointModel # pylint: disable=import-outside-toplevel
db_endpoints = get_related_objects(self, ServiceEndPointModel, 'endpoint_fk')
return [db_endpoint.dump_id() for db_endpoint in sorted(db_endpoints, key=operator.attrgetter('pk'))]
def dump_constraints(self) -> List[Dict]:
return ConstraintsModel(self.database, self.service_constraints_fk).dump()
def dump_config(self) -> Dict:
return ConfigModel(self.database, self.service_config_fk).dump()
def dump( # pylint: disable=arguments-differ
self, include_endpoint_ids=True, include_constraints=True, include_config_rules=True
) -> Dict:
result = {
'service_id': self.dump_id(),
'service_type': self.service_type.value,
'service_status': {'service_status': self.service_status.value},
}
if include_endpoint_ids: result['service_endpoint_ids'] = self.dump_endpoint_ids()
if include_constraints: result['service_constraints'] = self.dump_constraints()
if include_config_rules: result.setdefault('service_config', {})['config_rules'] = self.dump_config()
return result
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import hashlib, re
from enum import Enum
from typing import Dict, List, Tuple, Union
# Convenient helper function to remove dictionary items in dict/list/set comprehensions.
def remove_dict_key(dictionary : Dict, key : str):
dictionary.pop(key, None)
return dictionary
# Enumeration classes are redundant with gRPC classes, but gRPC does not provide a programmatical method to retrieve
# the values it expects from strings containing the desired value symbol or its integer value, so a kind of mapping is
# required. Besides, ORM Models expect Enum classes in EnumeratedFields; we create specific and conveniently defined
# Enum classes to serve both purposes.
def grpc_to_enum(grpc_enum_class, orm_enum_class : Enum, grpc_enum_value):
grpc_enum_name = grpc_enum_class.Name(grpc_enum_value)
grpc_enum_prefix = orm_enum_class.__name__.upper()
grpc_enum_prefix = re.sub(r'^ORM_(.+)$', r'\1', grpc_enum_prefix)
grpc_enum_prefix = re.sub(r'^(.+)ENUM$', r'\1', grpc_enum_prefix)
grpc_enum_prefix = grpc_enum_prefix + '_'
orm_enum_name = grpc_enum_name.replace(grpc_enum_prefix, '')
orm_enum_value = orm_enum_class._member_map_.get(orm_enum_name) # pylint: disable=protected-access
return orm_enum_value
# For some models, it is convenient to produce a string hash for fast comparisons of existence or modification. Method
# fast_hasher computes configurable length (between 1 and 64 byte) hashes and retrieves them in hex representation.
FASTHASHER_ITEM_ACCEPTED_FORMAT = 'Union[bytes, str]'
FASTHASHER_DATA_ACCEPTED_FORMAT = 'Union[{fmt:s}, List[{fmt:s}], Tuple[{fmt:s}]]'.format(
fmt=FASTHASHER_ITEM_ACCEPTED_FORMAT)
def fast_hasher(data : Union[bytes, str, List[Union[bytes, str]], Tuple[Union[bytes, str]]], digest_size : int = 8):
hasher = hashlib.blake2b(digest_size=digest_size)
# Do not accept sets, dicts, or other unordered dats tructures since their order is arbitrary thus producing
# different hashes depending on the order. Consider adding support for sets or dicts with previous sorting of
# items by their key.
if isinstance(data, bytes):
data = [data]
elif isinstance(data, str):
data = [data.encode('UTF-8')]
elif isinstance(data, (list, tuple)):
pass
else:
msg = 'data({:s}) must be {:s}, found {:s}'
raise TypeError(msg.format(str(data), FASTHASHER_DATA_ACCEPTED_FORMAT, str(type(data))))
for i,item in enumerate(data):
if isinstance(item, str):
item = item.encode('UTF-8')
elif isinstance(item, bytes):
pass
else:
msg = 'data[{:d}]({:s}) must be {:s}, found {:s}'
raise TypeError(msg.format(i, str(item), FASTHASHER_ITEM_ACCEPTED_FORMAT, str(type(item))))
hasher.update(item)
return hasher.hexdigest()
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging, operator
from typing import Dict, List
from common.orm.fields.ForeignKeyField import ForeignKeyField
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.fields.StringField import StringField
from common.orm.model.Model import Model
from common.orm.HighLevel import get_related_objects
from .ContextModel import ContextModel
LOGGER = logging.getLogger(__name__)
class TopologyModel(Model):
pk = PrimaryKeyField()
context_fk = ForeignKeyField(ContextModel)
topology_uuid = StringField(required=True, allow_empty=False)
def dump_id(self) -> Dict:
context_id = ContextModel(self.database, self.context_fk).dump_id()
return {
'context_id': context_id,
'topology_uuid': {'uuid': self.topology_uuid},
}
def dump_device_ids(self) -> List[Dict]:
from .RelationModels import TopologyDeviceModel # pylint: disable=import-outside-toplevel
db_devices = get_related_objects(self, TopologyDeviceModel, 'device_fk')
return [db_device.dump_id() for db_device in sorted(db_devices, key=operator.attrgetter('pk'))]
def dump_link_ids(self) -> List[Dict]:
from .RelationModels import TopologyLinkModel # pylint: disable=import-outside-toplevel
db_links = get_related_objects(self, TopologyLinkModel, 'link_fk')
return [db_link.dump_id() for db_link in sorted(db_links, key=operator.attrgetter('pk'))]
def dump( # pylint: disable=arguments-differ
self, include_devices=True, include_links=True
) -> Dict:
result = {'topology_id': self.dump_id()}
if include_devices: result['device_ids'] = self.dump_device_ids()
if include_links: result['link_ids'] = self.dump_link_ids()
return result
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# In-Memory database with a simplified representation of Context Database focused on the Service model.
# Used as an internal data cache, for message validation, and message formatting purposes.
......@@ -13,30 +13,22 @@
# limitations under the License.
from typing import Any, List, Optional, Tuple, Union
from common.orm.Database import Database
from context.client.ContextClient import ContextClient
from device.client.DeviceClient import DeviceClient
from service.service.database.ServiceModel import ServiceModel
from common.proto.context_pb2 import Service
from service.task_scheduler.TaskExecutor import TaskExecutor
class _ServiceHandler:
def __init__(self,
db_service: ServiceModel,
database: Database,
context_client: ContextClient,
device_client: DeviceClient,
service: Service,
task_executor : TaskExecutor,
**settings) -> None:
""" Initialize Driver.
Parameters:
db_service
The service instance from the local in-memory database.
database
The instance of the local in-memory database.
context_client
An instance of context client to be used to retrieve
information from the service and the devices.
device_client
An instance of device client to be used to configure
the devices.
service
The service instance (gRPC message) to be managed.
task_executor
An instance of Task Executor providing access to the
service handlers factory, the context and device clients,
and an internal cache of already-loaded gRPC entities.
**settings
Extra settings required by the service handler.
"""
......
......@@ -14,12 +14,10 @@
import anytree, json, logging
from typing import Any, Dict, List, Optional, Tuple, Union
from common.orm.HighLevel import get_object
from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, Device, DeviceId, Service
from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service
from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
from common.tools.object_factory.Device import json_device_id
from common.type_checkers.Checkers import chk_type
from service.service.database.DeviceModel import DeviceModel
from service.service.service_handler_api._ServiceHandler import _ServiceHandler
from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value
from service.service.task_scheduler.TaskExecutor import TaskExecutor
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment