diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index 6914e05a01b8382968b656ce818b91f10ffd7329..edb5095b99f393a040e976c3841d82349a0d9450 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -38,7 +38,7 @@ from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered from .database.Context import context_delete, context_get, context_list_ids, context_list_objs, context_set from .database.Device import device_delete, device_get, device_list_ids, device_list_objs, device_set from .database.Link import link_delete, link_get, link_list_ids, link_list_objs, link_set -#from .database.Service import service_delete, service_get, service_list_ids, service_list_objs, service_set +from .database.Service import service_delete, service_get, service_list_ids, service_list_objs, service_set from .database.Topology import topology_delete, topology_get, topology_list_ids, topology_list_objs, topology_set #from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string #from context.service.Database import Database @@ -231,31 +231,31 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # ----- Service ---------------------------------------------------------------------------------------------------- -# @safe_and_metered_rpc_method(METRICS, LOGGER) -# def ListServiceIds(self, request : ContextId, context : grpc.ServicerContext) -> ServiceIdList: -# return service_list_ids(self.db_engine, request) + @safe_and_metered_rpc_method(METRICS, LOGGER) + def ListServiceIds(self, request : ContextId, context : grpc.ServicerContext) -> ServiceIdList: + return service_list_ids(self.db_engine, request) -# @safe_and_metered_rpc_method(METRICS, LOGGER) -# def ListServices(self, request : ContextId, context : grpc.ServicerContext) -> ServiceList: -# return service_list_objs(self.db_engine, request) + @safe_and_metered_rpc_method(METRICS, LOGGER) + def ListServices(self, request : ContextId, context : grpc.ServicerContext) -> ServiceList: + return service_list_objs(self.db_engine, request) -# @safe_and_metered_rpc_method(METRICS, LOGGER) -# def GetService(self, request : ServiceId, context : grpc.ServicerContext) -> Service: -# return service_get(self.db_engine, request) + @safe_and_metered_rpc_method(METRICS, LOGGER) + def GetService(self, request : ServiceId, context : grpc.ServicerContext) -> Service: + return service_get(self.db_engine, request) -# @safe_and_metered_rpc_method(METRICS, LOGGER) -# def SetService(self, request : Service, context : grpc.ServicerContext) -> ServiceId: -# service_id,updated = service_set(self.db_engine, request) -# #event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE -# #notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': service_id}) -# return service_id + @safe_and_metered_rpc_method(METRICS, LOGGER) + def SetService(self, request : Service, context : grpc.ServicerContext) -> ServiceId: + service_id,updated = service_set(self.db_engine, request) + #event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE + #notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': service_id}) + return service_id -# @safe_and_metered_rpc_method(METRICS, LOGGER) -# def RemoveService(self, request : ServiceId, context : grpc.ServicerContext) -> Empty: -# deleted = service_delete(self.db_engine, request) -# #if deleted: -# # notify_event(self.messagebroker, TOPIC_SERVICE, EventTypeEnum.EVENTTYPE_REMOVE, {'service_id': request}) -# return Empty() + @safe_and_metered_rpc_method(METRICS, LOGGER) + def RemoveService(self, request : ServiceId, context : grpc.ServicerContext) -> Empty: + deleted = service_delete(self.db_engine, request) + #if deleted: + # notify_event(self.messagebroker, TOPIC_SERVICE, EventTypeEnum.EVENTTYPE_REMOVE, {'service_id': request}) + return Empty() @safe_and_metered_rpc_method(METRICS, LOGGER) def GetServiceEvents(self, request : Empty, context : grpc.ServicerContext) -> Iterator[ServiceEvent]: diff --git a/src/context/service/database/ConfigRule.py b/src/context/service/database/ConfigRule.py new file mode 100644 index 0000000000000000000000000000000000000000..af1dd1ec5689f53bd9180e504f3c3acb57f49532 --- /dev/null +++ b/src/context/service/database/ConfigRule.py @@ -0,0 +1,185 @@ +# 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. + +from sqlalchemy import delete +from sqlalchemy.dialects.postgresql import insert +from sqlalchemy.orm import Session +from typing import Dict, List, Optional +from common.proto.context_pb2 import ConfigRule +from common.tools.grpc.Tools import grpc_message_to_json_string +from .models.enums.ConfigAction import grpc_to_enum__config_action +from .models.ConfigRuleModel import ConfigRuleKindEnum, ConfigRuleModel +from .uuids._Builder import get_uuid_random + +def compose_config_rules_data( + config_rules : List[ConfigRule], + device_uuid : Optional[str] = None, service_uuid : Optional[str] = None, slice_uuid : Optional[str] = None +) -> List[Dict]: + dict_config_rules : List[Dict] = list() + for position,config_rule in enumerate(config_rules): + configrule_uuid = get_uuid_random() + str_kind = config_rule.WhichOneof('config_rule') + dict_config_rule = { + 'configrule_uuid': configrule_uuid, + 'position' : position, + 'kind' : ConfigRuleKindEnum._member_map_.get(str_kind.upper()), # pylint: disable=no-member + 'action' : grpc_to_enum__config_action(config_rule.action), + 'data' : grpc_message_to_json_string(getattr(config_rule, str_kind, {})), + } + if device_uuid is not None: dict_config_rule['device_uuid' ] = device_uuid + if service_uuid is not None: dict_config_rule['service_uuid'] = service_uuid + if slice_uuid is not None: dict_config_rule['slice_uuid' ] = slice_uuid + dict_config_rules.append(dict_config_rule) + return dict_config_rules + +def upsert_config_rules( + session : Session, config_rules : List[Dict], + device_uuid : Optional[str] = None, service_uuid : Optional[str] = None, slice_uuid : Optional[str] = None +) -> None: + stmt = delete(ConfigRuleModel) + if device_uuid is not None: stmt = stmt.where(ConfigRuleModel.device_uuid == device_uuid ) + if service_uuid is not None: stmt = stmt.where(ConfigRuleModel.service_uuid == service_uuid) + if slice_uuid is not None: stmt = stmt.where(ConfigRuleModel.slice_uuid == slice_uuid ) + session.execute(stmt) + session.execute(insert(ConfigRuleModel).values(config_rules)) + + +#Union_SpecificConfigRule = Union[ +# ConfigRuleCustomModel, ConfigRuleAclModel +#] +# +#def set_config_rule( +# database : Database, db_config : ConfigModel, position : int, resource_key : str, resource_value : str, +#): # -> Tuple[ConfigRuleModel, bool]: +# +# str_rule_key_hash = fast_hasher(resource_key) +# str_config_rule_key = key_to_str([db_config.config_uuid, str_rule_key_hash], separator=':') +# +# data = {'config_fk': db_config, 'position': position, 'action': ORM_ConfigActionEnum.SET, 'key': resource_key, +# 'value': resource_value} +# to_add = ConfigRuleModel(**data) +# +# result = database.create_or_update(to_add) +# return result +#Tuple_ConfigRuleSpecs = Tuple[Type, str, Dict, ConfigRuleKindEnum] +# +#def parse_config_rule_custom(database : Database, grpc_config_rule) -> Tuple_ConfigRuleSpecs: +# config_rule_class = ConfigRuleCustomModel +# str_config_rule_id = grpc_config_rule.custom.resource_key +# config_rule_data = { +# 'key' : grpc_config_rule.custom.resource_key, +# 'value': grpc_config_rule.custom.resource_value, +# } +# return config_rule_class, str_config_rule_id, config_rule_data, ConfigRuleKindEnum.CUSTOM +# +#def parse_config_rule_acl(database : Database, grpc_config_rule) -> Tuple_ConfigRuleSpecs: +# config_rule_class = ConfigRuleAclModel +# grpc_endpoint_id = grpc_config_rule.acl.endpoint_id +# grpc_rule_set = grpc_config_rule.acl.rule_set +# device_uuid = grpc_endpoint_id.device_id.device_uuid.uuid +# endpoint_uuid = grpc_endpoint_id.endpoint_uuid.uuid +# str_endpoint_key = '/'.join([device_uuid, endpoint_uuid]) +# #str_endpoint_key, db_endpoint = get_endpoint(database, grpc_endpoint_id) +# str_config_rule_id = ':'.join([str_endpoint_key, grpc_rule_set.name]) +# config_rule_data = { +# #'endpoint_fk': db_endpoint, +# 'endpoint_id': grpc_message_to_json_string(grpc_endpoint_id), +# 'acl_data': grpc_message_to_json_string(grpc_rule_set), +# } +# return config_rule_class, str_config_rule_id, config_rule_data, ConfigRuleKindEnum.ACL +# +#CONFIGRULE_PARSERS = { +# 'custom': parse_config_rule_custom, +# 'acl' : parse_config_rule_acl, +#} +# +#Union_ConfigRuleModel = Union[ +# ConfigRuleCustomModel, ConfigRuleAclModel, +#] +# +#def set_config_rule( +# database : Database, db_config : ConfigModel, grpc_config_rule : ConfigRule, position : int +#) -> Tuple[Union_ConfigRuleModel, bool]: +# grpc_config_rule_kind = str(grpc_config_rule.WhichOneof('config_rule')) +# parser = CONFIGRULE_PARSERS.get(grpc_config_rule_kind) +# if parser is None: +# raise NotImplementedError('ConfigRule of kind {:s} is not implemented: {:s}'.format( +# grpc_config_rule_kind, grpc_message_to_json_string(grpc_config_rule))) +# +# # create specific ConfigRule +# config_rule_class, str_config_rule_id, config_rule_data, config_rule_kind = parser(database, grpc_config_rule) +# str_config_rule_key_hash = fast_hasher(':'.join([config_rule_kind.value, str_config_rule_id])) +# str_config_rule_key = key_to_str([db_config.pk, str_config_rule_key_hash], separator=':') +# result : Tuple[Union_ConfigRuleModel, bool] = update_or_create_object( +# database, config_rule_class, str_config_rule_key, config_rule_data) +# db_specific_config_rule, updated = result +# +# # create generic ConfigRule +# config_rule_fk_field_name = 'config_rule_{:s}_fk'.format(config_rule_kind.value) +# config_rule_data = { +# 'config_fk': db_config, 'kind': config_rule_kind, 'position': position, +# 'action': ORM_ConfigActionEnum.SET, +# config_rule_fk_field_name: db_specific_config_rule +# } +# result : Tuple[ConfigRuleModel, bool] = update_or_create_object( +# database, ConfigRuleModel, str_config_rule_key, config_rule_data) +# db_config_rule, updated = result +# +# return db_config_rule, updated +# +#def delete_config_rule( +# database : Database, db_config : ConfigModel, grpc_config_rule : ConfigRule +#) -> None: +# grpc_config_rule_kind = str(grpc_config_rule.WhichOneof('config_rule')) +# parser = CONFIGRULE_PARSERS.get(grpc_config_rule_kind) +# if parser is None: +# raise NotImplementedError('ConfigRule of kind {:s} is not implemented: {:s}'.format( +# grpc_config_rule_kind, grpc_message_to_json_string(grpc_config_rule))) +# +# # delete generic config rules; self deletes specific config rule +# _, str_config_rule_id, _, config_rule_kind = parser(database, grpc_config_rule) +# str_config_rule_key_hash = fast_hasher(':'.join([config_rule_kind.value, str_config_rule_id])) +# str_config_rule_key = key_to_str([db_config.pk, str_config_rule_key_hash], separator=':') +# db_config_rule : Optional[ConfigRuleModel] = get_object( +# database, ConfigRuleModel, str_config_rule_key, raise_if_not_found=False) +# if db_config_rule is None: return +# db_config_rule.delete() +# +#def update_config( +# database : Database, db_parent_pk : str, config_name : str, grpc_config_rules +#) -> List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]]: +# +# str_config_key = key_to_str([config_name, db_parent_pk], separator=':') +# result : Tuple[ConfigModel, bool] = get_or_create_object(database, ConfigModel, str_config_key) +# db_config, created = result +# +# db_objects = [(db_config, created)] +# +# for position,grpc_config_rule in enumerate(grpc_config_rules): +# action = grpc_to_enum__config_action(grpc_config_rule.action) +# +# if action == ORM_ConfigActionEnum.SET: +# result : Tuple[ConfigRuleModel, bool] = set_config_rule( +# database, db_config, grpc_config_rule, position) +# db_config_rule, updated = result +# db_objects.append((db_config_rule, updated)) +# elif action == ORM_ConfigActionEnum.DELETE: +# delete_config_rule(database, db_config, grpc_config_rule) +# else: +# msg = 'Unsupported Action({:s}) for ConfigRule({:s})' +# str_action = str(ConfigActionEnum.Name(action)) +# str_config_rule = grpc_message_to_json_string(grpc_config_rule) +# raise AttributeError(msg.format(str_action, str_config_rule)) +# +# return db_objects diff --git a/src/context/service/database/Constraint.py b/src/context/service/database/Constraint.py new file mode 100644 index 0000000000000000000000000000000000000000..5c94d13c07d247f53d59f6a6da73bef1370754e3 --- /dev/null +++ b/src/context/service/database/Constraint.py @@ -0,0 +1,110 @@ +# 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. + +from sqlalchemy import delete +from sqlalchemy.dialects.postgresql import insert +from sqlalchemy.orm import Session +from typing import Dict, List, Optional +from common.proto.context_pb2 import Constraint +from common.tools.grpc.Tools import grpc_message_to_json_string +from .models.ConstraintModel import ConstraintKindEnum, ConstraintModel +from .uuids._Builder import get_uuid_random + +def compose_constraints_data( + constraints : List[Constraint], + service_uuid : Optional[str] = None, slice_uuid : Optional[str] = None +) -> List[Dict]: + dict_constraints : List[Dict] = list() + for position,constraint in enumerate(constraints): + str_kind = constraint.WhichOneof('constraint') + dict_constraint = { + 'constraint_uuid': get_uuid_random(), + 'position' : position, + 'kind' : ConstraintKindEnum._member_map_.get(str_kind.upper()), # pylint: disable=no-member + 'data' : grpc_message_to_json_string(getattr(constraint, str_kind, {})), + } + if service_uuid is not None: dict_constraint['service_uuid'] = service_uuid + if slice_uuid is not None: dict_constraint['slice_uuid' ] = slice_uuid + dict_constraints.append(dict_constraint) + return dict_constraints + +def upsert_constraints( + session : Session, constraints : List[Dict], + service_uuid : Optional[str] = None, slice_uuid : Optional[str] = None +) -> None: + stmt = delete(ConstraintModel) + if service_uuid is not None: stmt = stmt.where(ConstraintModel.service_uuid == service_uuid) + if slice_uuid is not None: stmt = stmt.where(ConstraintModel.slice_uuid == slice_uuid ) + session.execute(stmt) + session.execute(insert(ConstraintModel).values(constraints)) + +# def set_constraint(self, db_constraints: ConstraintsModel, grpc_constraint: Constraint, position: int +# ) -> Tuple[Union_ConstraintModel, bool]: +# with self.session() as session: +# +# grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint')) +# +# parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind) +# if parser is None: +# raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format( +# grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint))) +# +# # create specific constraint +# constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(grpc_constraint) +# str_constraint_id = str(uuid.uuid4()) +# LOGGER.info('str_constraint_id: {}'.format(str_constraint_id)) +# # str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) +# # str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') +# +# # result : Tuple[Union_ConstraintModel, bool] = update_or_create_object( +# # database, constraint_class, str_constraint_key, constraint_data) +# constraint_data[constraint_class.main_pk_name()] = str_constraint_id +# db_new_constraint = constraint_class(**constraint_data) +# result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint) +# db_specific_constraint, updated = result +# +# # create generic constraint +# # constraint_fk_field_name = 'constraint_uuid'.format(constraint_kind.value) +# constraint_data = { +# 'constraints_uuid': db_constraints.constraints_uuid, 'position': position, 'kind': constraint_kind +# } +# +# db_new_constraint = ConstraintModel(**constraint_data) +# result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint) +# db_constraint, updated = result +# +# return db_constraint, updated +# +# def set_constraints(self, service_uuid: str, constraints_name : str, grpc_constraints +# ) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]: +# with self.session() as session: +# # 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) +# result = session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none() +# created = None +# if result: +# created = True +# session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none() +# db_constraints = ConstraintsModel(constraints_uuid=service_uuid) +# session.add(db_constraints) +# +# db_objects = [(db_constraints, created)] +# +# for position,grpc_constraint in enumerate(grpc_constraints): +# result : Tuple[ConstraintModel, bool] = self.set_constraint( +# db_constraints, grpc_constraint, position) +# db_constraint, updated = result +# db_objects.append((db_constraint, updated)) +# +# return db_objects diff --git a/src/context/service/database/Device.py b/src/context/service/database/Device.py index a0e0a53e5c766c48cfdc6b8fde2c432bf4d634db..7607a2349fd109134f1680161a36c3ccea926262 100644 --- a/src/context/service/database/Device.py +++ b/src/context/service/database/Device.py @@ -12,25 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -from sqlalchemy import delete from sqlalchemy.dialects.postgresql import insert from sqlalchemy.engine import Engine from sqlalchemy.orm import Session, sessionmaker from sqlalchemy_cockroachdb import run_transaction -from typing import Dict, List, Optional, Set, Tuple +from typing import Dict, List, Optional, Set from common.proto.context_pb2 import Device, DeviceId, DeviceIdList, DeviceList from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException, NotFoundException from common.tools.object_factory.Device import json_device_id -from common.tools.grpc.Tools import grpc_message_to_json_string -from .models.ConfigRuleModel import ConfigRuleKindEnum, ConfigRuleModel +from context.service.database.ConfigRule import compose_config_rules_data, upsert_config_rules from .models.DeviceModel import DeviceModel from .models.EndPointModel import EndPointModel from .models.RelationModels import TopologyDeviceModel -from .models.enums.ConfigAction import grpc_to_enum__config_action from .models.enums.DeviceDriver import grpc_to_enum__device_driver from .models.enums.DeviceOperationalStatus import grpc_to_enum__device_operational_status from .models.enums.KpiSampleType import grpc_to_enum__kpi_sample_type -from .uuids._Builder import get_uuid_random from .uuids.Device import device_get_uuid from .uuids.EndPoint import endpoint_get_uuid @@ -108,18 +104,7 @@ def device_set(db_engine : Engine, request : Device) -> bool: }) topology_uuids.add(endpoint_topology_uuid) - config_rules : List[Dict] = list() - for position,config_rule in enumerate(request.device_config.config_rules): - configrule_uuid = get_uuid_random() - str_kind = config_rule.WhichOneof('config_rule') - config_rules.append({ - 'configrule_uuid': configrule_uuid, - 'device_uuid' : device_uuid, - 'position' : position, - 'kind' : ConfigRuleKindEnum._member_map_.get(str_kind.upper()), # pylint: disable=no-member - 'action' : grpc_to_enum__config_action(config_rule.action), - 'data' : grpc_message_to_json_string(getattr(config_rule, str_kind, {})), - }) + config_rules = compose_config_rules_data(request.device_config.config_rules, device_uuid=device_uuid) device_data = [{ 'device_uuid' : device_uuid, @@ -157,8 +142,7 @@ def device_set(db_engine : Engine, request : Device) -> bool: index_elements=[TopologyDeviceModel.topology_uuid, TopologyDeviceModel.device_uuid] )) - session.execute(delete(ConfigRuleModel).where(ConfigRuleModel.device_uuid == device_uuid)) - session.execute(insert(ConfigRuleModel).values(config_rules)) + upsert_config_rules(session, config_rules, device_uuid=device_uuid) run_transaction(sessionmaker(bind=db_engine), callback) updated = False # TODO: improve and check if created/updated @@ -167,143 +151,6 @@ def device_set(db_engine : Engine, request : Device) -> bool: def device_delete(db_engine : Engine, request : DeviceId) -> bool: device_uuid = device_get_uuid(request, allow_random=False) def callback(session : Session) -> bool: - #session.query(TopologyDeviceModel).filter_by(device_uuid=device_uuid).delete() num_deleted = session.query(DeviceModel).filter_by(device_uuid=device_uuid).delete() - #db_device = session.query(DeviceModel).filter_by(device_uuid=device_uuid).one_or_none() - #session.query(ConfigRuleModel).filter_by(config_uuid=db_device.device_config_uuid).delete() - #session.query(ConfigModel).filter_by(config_uuid=db_device.device_config_uuid).delete() - #session.query(DeviceModel).filter_by(device_uuid=device_uuid).delete() return num_deleted > 0 return run_transaction(sessionmaker(bind=db_engine), callback) - - - - -#Union_SpecificConfigRule = Union[ -# ConfigRuleCustomModel, ConfigRuleAclModel -#] -# -#def set_config_rule( -# database : Database, db_config : ConfigModel, position : int, resource_key : str, resource_value : str, -#): # -> Tuple[ConfigRuleModel, bool]: -# -# str_rule_key_hash = fast_hasher(resource_key) -# str_config_rule_key = key_to_str([db_config.config_uuid, str_rule_key_hash], separator=':') -# -# data = {'config_fk': db_config, 'position': position, 'action': ORM_ConfigActionEnum.SET, 'key': resource_key, -# 'value': resource_value} -# to_add = ConfigRuleModel(**data) -# -# result = database.create_or_update(to_add) -# return result -#Tuple_ConfigRuleSpecs = Tuple[Type, str, Dict, ConfigRuleKindEnum] -# -#def parse_config_rule_custom(database : Database, grpc_config_rule) -> Tuple_ConfigRuleSpecs: -# config_rule_class = ConfigRuleCustomModel -# str_config_rule_id = grpc_config_rule.custom.resource_key -# config_rule_data = { -# 'key' : grpc_config_rule.custom.resource_key, -# 'value': grpc_config_rule.custom.resource_value, -# } -# return config_rule_class, str_config_rule_id, config_rule_data, ConfigRuleKindEnum.CUSTOM -# -#def parse_config_rule_acl(database : Database, grpc_config_rule) -> Tuple_ConfigRuleSpecs: -# config_rule_class = ConfigRuleAclModel -# grpc_endpoint_id = grpc_config_rule.acl.endpoint_id -# grpc_rule_set = grpc_config_rule.acl.rule_set -# device_uuid = grpc_endpoint_id.device_id.device_uuid.uuid -# endpoint_uuid = grpc_endpoint_id.endpoint_uuid.uuid -# str_endpoint_key = '/'.join([device_uuid, endpoint_uuid]) -# #str_endpoint_key, db_endpoint = get_endpoint(database, grpc_endpoint_id) -# str_config_rule_id = ':'.join([str_endpoint_key, grpc_rule_set.name]) -# config_rule_data = { -# #'endpoint_fk': db_endpoint, -# 'endpoint_id': grpc_message_to_json_string(grpc_endpoint_id), -# 'acl_data': grpc_message_to_json_string(grpc_rule_set), -# } -# return config_rule_class, str_config_rule_id, config_rule_data, ConfigRuleKindEnum.ACL -# -#CONFIGRULE_PARSERS = { -# 'custom': parse_config_rule_custom, -# 'acl' : parse_config_rule_acl, -#} -# -#Union_ConfigRuleModel = Union[ -# ConfigRuleCustomModel, ConfigRuleAclModel, -#] -# -#def set_config_rule( -# database : Database, db_config : ConfigModel, grpc_config_rule : ConfigRule, position : int -#) -> Tuple[Union_ConfigRuleModel, bool]: -# grpc_config_rule_kind = str(grpc_config_rule.WhichOneof('config_rule')) -# parser = CONFIGRULE_PARSERS.get(grpc_config_rule_kind) -# if parser is None: -# raise NotImplementedError('ConfigRule of kind {:s} is not implemented: {:s}'.format( -# grpc_config_rule_kind, grpc_message_to_json_string(grpc_config_rule))) -# -# # create specific ConfigRule -# config_rule_class, str_config_rule_id, config_rule_data, config_rule_kind = parser(database, grpc_config_rule) -# str_config_rule_key_hash = fast_hasher(':'.join([config_rule_kind.value, str_config_rule_id])) -# str_config_rule_key = key_to_str([db_config.pk, str_config_rule_key_hash], separator=':') -# result : Tuple[Union_ConfigRuleModel, bool] = update_or_create_object( -# database, config_rule_class, str_config_rule_key, config_rule_data) -# db_specific_config_rule, updated = result -# -# # create generic ConfigRule -# config_rule_fk_field_name = 'config_rule_{:s}_fk'.format(config_rule_kind.value) -# config_rule_data = { -# 'config_fk': db_config, 'kind': config_rule_kind, 'position': position, -# 'action': ORM_ConfigActionEnum.SET, -# config_rule_fk_field_name: db_specific_config_rule -# } -# result : Tuple[ConfigRuleModel, bool] = update_or_create_object( -# database, ConfigRuleModel, str_config_rule_key, config_rule_data) -# db_config_rule, updated = result -# -# return db_config_rule, updated -# -#def delete_config_rule( -# database : Database, db_config : ConfigModel, grpc_config_rule : ConfigRule -#) -> None: -# grpc_config_rule_kind = str(grpc_config_rule.WhichOneof('config_rule')) -# parser = CONFIGRULE_PARSERS.get(grpc_config_rule_kind) -# if parser is None: -# raise NotImplementedError('ConfigRule of kind {:s} is not implemented: {:s}'.format( -# grpc_config_rule_kind, grpc_message_to_json_string(grpc_config_rule))) -# -# # delete generic config rules; self deletes specific config rule -# _, str_config_rule_id, _, config_rule_kind = parser(database, grpc_config_rule) -# str_config_rule_key_hash = fast_hasher(':'.join([config_rule_kind.value, str_config_rule_id])) -# str_config_rule_key = key_to_str([db_config.pk, str_config_rule_key_hash], separator=':') -# db_config_rule : Optional[ConfigRuleModel] = get_object( -# database, ConfigRuleModel, str_config_rule_key, raise_if_not_found=False) -# if db_config_rule is None: return -# db_config_rule.delete() -# -#def update_config( -# database : Database, db_parent_pk : str, config_name : str, grpc_config_rules -#) -> List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]]: -# -# str_config_key = key_to_str([config_name, db_parent_pk], separator=':') -# result : Tuple[ConfigModel, bool] = get_or_create_object(database, ConfigModel, str_config_key) -# db_config, created = result -# -# db_objects = [(db_config, created)] -# -# for position,grpc_config_rule in enumerate(grpc_config_rules): -# action = grpc_to_enum__config_action(grpc_config_rule.action) -# -# if action == ORM_ConfigActionEnum.SET: -# result : Tuple[ConfigRuleModel, bool] = set_config_rule( -# database, db_config, grpc_config_rule, position) -# db_config_rule, updated = result -# db_objects.append((db_config_rule, updated)) -# elif action == ORM_ConfigActionEnum.DELETE: -# delete_config_rule(database, db_config, grpc_config_rule) -# else: -# msg = 'Unsupported Action({:s}) for ConfigRule({:s})' -# str_action = str(ConfigActionEnum.Name(action)) -# str_config_rule = grpc_message_to_json_string(grpc_config_rule) -# raise AttributeError(msg.format(str_action, str_config_rule)) -# -# return db_objects diff --git a/src/context/service/database/Link.py b/src/context/service/database/Link.py index 93f90b3ea0aeba5b97ba8a360be1f289eb8f0d0a..9f11cad2376b0d36ba5db5addff43097d1de26c3 100644 --- a/src/context/service/database/Link.py +++ b/src/context/service/database/Link.py @@ -108,10 +108,6 @@ def link_set(db_engine : Engine, request : Link) -> bool: def link_delete(db_engine : Engine, request : LinkId) -> bool: link_uuid = link_get_uuid(request, allow_random=False) def callback(session : Session) -> bool: - #session.query(TopologyLinkModel).filter_by(link_uuid=link_uuid).delete() - #session.query(LinkEndPointModel).filter_by(link_uuid=link_uuid).delete() num_deleted = session.query(LinkModel).filter_by(link_uuid=link_uuid).delete() - #db_link = session.query(LinkModel).filter_by(link_uuid=link_uuid).one_or_none() - #session.query(LinkModel).filter_by(link_uuid=link_uuid).delete() return num_deleted > 0 return run_transaction(sessionmaker(bind=db_engine), callback) diff --git a/src/context/service/database/Service.py b/src/context/service/database/Service.py index 3b6b4cc267c8087a2d1b2bc2c1e856b8ee22fbd7..7e3d9d044b1991c145528897bc28c09889ae1862 100644 --- a/src/context/service/database/Service.py +++ b/src/context/service/database/Service.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import time from sqlalchemy.dialects.postgresql import insert from sqlalchemy.engine import Engine from sqlalchemy.orm import Session, sessionmaker @@ -20,10 +19,20 @@ from sqlalchemy_cockroachdb import run_transaction from typing import Dict, List, Optional from common.proto.context_pb2 import ContextId, Service, ServiceId, ServiceIdList, ServiceList from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException, NotFoundException +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Service import json_service_id +from context.service.database.ConfigRule import compose_config_rules_data, upsert_config_rules +from context.service.database.Constraint import compose_constraints_data, upsert_constraints +from .models.enums.ServiceStatus import grpc_to_enum__service_status +from .models.enums.ServiceType import grpc_to_enum__service_type +from .models.RelationModels import ServiceEndPointModel from .models.ServiceModel import ServiceModel +from .uuids.Context import context_get_uuid +from .uuids.EndPoint import endpoint_get_uuid +from .uuids.Service import service_get_uuid def service_list_ids(db_engine : Engine, request : ContextId) -> ServiceIdList: - context_uuid = request.context_uuid.uuid + context_uuid = context_get_uuid(request, allow_random=False) def callback(session : Session) -> List[Dict]: obj_list : List[ServiceModel] = session.query(ServiceModel).filter_by(context_uuid=context_uuid).all() #.options(selectinload(ContextModel.service)).filter_by(context_uuid=context_uuid).one_or_none() @@ -31,7 +40,7 @@ def service_list_ids(db_engine : Engine, request : ContextId) -> ServiceIdList: return ServiceIdList(service_ids=run_transaction(sessionmaker(bind=db_engine), callback)) def service_list_objs(db_engine : Engine, request : ContextId) -> ServiceList: - context_uuid = request.context_uuid.uuid + context_uuid = context_get_uuid(request, allow_random=False) def callback(session : Session) -> List[Dict]: obj_list : List[ServiceModel] = session.query(ServiceModel).filter_by(context_uuid=context_uuid).all() #.options(selectinload(ContextModel.service)).filter_by(context_uuid=context_uuid).one_or_none() @@ -39,225 +48,87 @@ def service_list_objs(db_engine : Engine, request : ContextId) -> ServiceList: return ServiceList(services=run_transaction(sessionmaker(bind=db_engine), callback)) def service_get(db_engine : Engine, request : ServiceId) -> Service: - context_uuid = request.context_id.context_uuid.uuid - service_uuid = request.service_uuid.uuid - + _,service_uuid = service_get_uuid(request, allow_random=False) def callback(session : Session) -> Optional[Dict]: obj : Optional[ServiceModel] = session.query(ServiceModel)\ - .filter_by(context_uuid=context_uuid, service_uuid=service_uuid).one_or_none() + .filter_by(service_uuid=service_uuid).one_or_none() return None if obj is None else obj.dump() obj = run_transaction(sessionmaker(bind=db_engine), callback) if obj is None: - obj_uuid = '{:s}/{:s}'.format(context_uuid, service_uuid) - raise NotFoundException('Service', obj_uuid) + context_uuid = context_get_uuid(request.context_id, allow_random=False) + raw_service_uuid = '{:s}/{:s}'.format(request.context_id.context_uuid.uuid, request.service_uuid.uuid) + raise NotFoundException('Service', raw_service_uuid, extra_details=[ + 'context_uuid generated was: {:s}'.format(context_uuid), + 'service_uuid generated was: {:s}'.format(service_uuid), + ]) return Service(**obj) def service_set(db_engine : Engine, request : Service) -> bool: - context_uuid = request.service_id.context_id.context_uuid.uuid - service_uuid = request.service_id.service_uuid.uuid - service_name = request.name + raw_context_uuid = request.service_id.context_id.context_uuid.uuid + raw_service_uuid = request.service_id.service_uuid.uuid + raw_service_name = request.name + service_name = raw_service_uuid if len(raw_service_name) == 0 else raw_service_name + context_uuid,service_uuid = service_get_uuid(request.service_id, service_name=service_name, allow_random=True) + + service_type = grpc_to_enum__service_type(request.service_type) + service_status = grpc_to_enum__service_status(request.service_status.service_status) + service_endpoints_data : List[Dict] = list() for i,endpoint_id in enumerate(request.service_endpoint_ids): endpoint_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - if len(endpoint_context_uuid) > 0 and context_uuid != endpoint_context_uuid: + if len(endpoint_context_uuid) == 0: endpoint_context_uuid = context_uuid + if endpoint_context_uuid not in {raw_context_uuid, context_uuid}: raise InvalidArgumentException( 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), endpoint_context_uuid, - ['should be == {:s}({:s})'.format('request.service_id.context_id.context_uuid.uuid', context_uuid)]) + ['should be == request.service_id.context_id.context_uuid.uuid({:s})'.format(raw_context_uuid)]) + + _, _, endpoint_uuid = endpoint_get_uuid(endpoint_id, allow_random=False) + service_endpoints_data.append({ + 'service_uuid' : service_uuid, + 'endpoint_uuid': endpoint_uuid, + }) + constraints = compose_constraints_data(request.service_constraints, service_uuid=service_uuid) + config_rules = compose_config_rules_data(request.service_config.config_rules, service_uuid=service_uuid) + + service_data = [{ + 'context_uuid' : context_uuid, + 'service_uuid' : service_uuid, + 'service_name' : service_name, + 'service_type' : service_type, + 'service_status': service_status, + }] def callback(session : Session) -> None: - service_data = [{ - 'context_uuid' : context_uuid, - 'service_uuid': service_uuid, - 'service_name': service_name, - 'created_at' : time.time(), - }] stmt = insert(ServiceModel).values(service_data) stmt = stmt.on_conflict_do_update( - index_elements=[ServiceModel.context_uuid, ServiceModel.service_uuid], - set_=dict(service_name = stmt.excluded.service_name) + index_elements=[ServiceModel.service_uuid], + set_=dict( + service_name = stmt.excluded.service_name, + service_type = stmt.excluded.service_type, + service_status = stmt.excluded.service_status, + ) ) session.execute(stmt) - run_transaction(sessionmaker(bind=db_engine), callback) - return False # TODO: improve and check if created/updated - + stmt = insert(ServiceEndPointModel).values(service_endpoints_data) + stmt = stmt.on_conflict_do_nothing( + index_elements=[ServiceEndPointModel.service_uuid, ServiceEndPointModel.endpoint_uuid] + ) + session.execute(stmt) -# # db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) -# db_context = session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none() -# # str_service_key = key_to_str([context_uuid, service_uuid]) -# constraints_result = self.set_constraints(service_uuid, 'constraints', request.service_constraints) -# db_constraints = constraints_result[0][0] -# -# config_rules = grpc_config_rules_to_raw(request.service_config.config_rules) -# running_config_result = update_config(self.database, str_service_key, 'running', config_rules) -# db_running_config = running_config_result[0][0] -# -# result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, { -# 'context_fk' : db_context, -# 'service_uuid' : service_uuid, -# 'service_type' : grpc_to_enum__service_type(request.service_type), -# 'service_constraints_fk': db_constraints, -# 'service_status' : grpc_to_enum__service_status(request.service_status.service_status), -# 'service_config_fk' : db_running_config, -# }) -# db_service, updated = result -# -# for i,endpoint_id in enumerate(request.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 -# -# str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) -# if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: -# str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) -# str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') -# -# db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) -# -# str_service_endpoint_key = key_to_str([service_uuid, str_endpoint_key], separator='--') -# result : Tuple[ServiceEndPointModel, bool] = get_or_create_object( -# self.database, ServiceEndPointModel, str_service_endpoint_key, { -# 'service_fk': db_service, 'endpoint_fk': db_endpoint}) -# #db_service_endpoint, service_endpoint_created = result -# -# event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE -# dict_service_id = db_service.dump_id() -# notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) -# return ServiceId(**dict_service_id) -# context_uuid = request.service_id.context_id.context_uuid.uuid -# db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) -# -# for i,endpoint_id in enumerate(request.service_endpoint_ids): -# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid -# if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: -# raise InvalidArgumentException( -# 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), -# endpoint_topology_context_uuid, -# ['should be == {:s}({:s})'.format( -# 'request.service_id.context_id.context_uuid.uuid', context_uuid)]) -# -# service_uuid = request.service_id.service_uuid.uuid -# str_service_key = key_to_str([context_uuid, service_uuid]) -# -# constraints_result = set_constraints( -# self.database, str_service_key, 'service', request.service_constraints) -# db_constraints = constraints_result[0][0] -# -# running_config_rules = update_config( -# self.database, str_service_key, 'service', request.service_config.config_rules) -# db_running_config = running_config_rules[0][0] -# -# result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, { -# 'context_fk' : db_context, -# 'service_uuid' : service_uuid, -# 'service_type' : grpc_to_enum__service_type(request.service_type), -# 'service_constraints_fk': db_constraints, -# 'service_status' : grpc_to_enum__service_status(request.service_status.service_status), -# 'service_config_fk' : db_running_config, -# }) -# db_service, updated = result -# -# for i,endpoint_id in enumerate(request.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 -# -# str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) -# if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: -# str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) -# str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') -# -# db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) -# -# str_service_endpoint_key = key_to_str([service_uuid, str_endpoint_key], separator='--') -# result : Tuple[ServiceEndPointModel, bool] = get_or_create_object( -# self.database, ServiceEndPointModel, str_service_endpoint_key, { -# 'service_fk': db_service, 'endpoint_fk': db_endpoint}) -# #db_service_endpoint, service_endpoint_created = result -# -# event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE -# dict_service_id = db_service.dump_id() -# notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) -# return ServiceId(**dict_service_id) + upsert_constraints(session, constraints, service_uuid=service_uuid) + upsert_config_rules(session, config_rules, service_uuid=service_uuid) + run_transaction(sessionmaker(bind=db_engine), callback) + updated = False # TODO: improve and check if created/updated + return ServiceId(**json_service_id(service_uuid, json_context_id(context_uuid))),updated -# def set_constraint(self, db_constraints: ConstraintsModel, grpc_constraint: Constraint, position: int -# ) -> Tuple[Union_ConstraintModel, bool]: -# with self.session() as session: -# -# grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint')) -# -# parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind) -# if parser is None: -# raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format( -# grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint))) -# -# # create specific constraint -# constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(grpc_constraint) -# str_constraint_id = str(uuid.uuid4()) -# LOGGER.info('str_constraint_id: {}'.format(str_constraint_id)) -# # str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) -# # str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') -# -# # result : Tuple[Union_ConstraintModel, bool] = update_or_create_object( -# # database, constraint_class, str_constraint_key, constraint_data) -# constraint_data[constraint_class.main_pk_name()] = str_constraint_id -# db_new_constraint = constraint_class(**constraint_data) -# result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint) -# db_specific_constraint, updated = result -# -# # create generic constraint -# # constraint_fk_field_name = 'constraint_uuid'.format(constraint_kind.value) -# constraint_data = { -# 'constraints_uuid': db_constraints.constraints_uuid, 'position': position, 'kind': constraint_kind -# } -# -# db_new_constraint = ConstraintModel(**constraint_data) -# result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint) -# db_constraint, updated = result -# -# return db_constraint, updated -# -# def set_constraints(self, service_uuid: str, constraints_name : str, grpc_constraints -# ) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]: -# with self.session() as session: -# # 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) -# result = session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none() -# created = None -# if result: -# created = True -# session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none() -# db_constraints = ConstraintsModel(constraints_uuid=service_uuid) -# session.add(db_constraints) -# -# db_objects = [(db_constraints, created)] -# -# for position,grpc_constraint in enumerate(grpc_constraints): -# result : Tuple[ConstraintModel, bool] = self.set_constraint( -# db_constraints, grpc_constraint, position) -# db_constraint, updated = result -# db_objects.append((db_constraint, updated)) -# -# return db_objects def service_delete(db_engine : Engine, request : ServiceId) -> bool: - context_uuid = request.context_id.context_uuid.uuid - service_uuid = request.service_uuid.uuid + _,service_uuid = service_get_uuid(request, allow_random=False) def callback(session : Session) -> bool: - num_deleted = session.query(ServiceModel)\ - .filter_by(context_uuid=context_uuid, service_uuid=service_uuid).delete() + num_deleted = session.query(ServiceModel).filter_by(service_uuid=service_uuid).delete() return num_deleted > 0 return run_transaction(sessionmaker(bind=db_engine), callback) - - # def delete(self) -> None: - # from .RelationModels import ServiceEndPointModel - # for db_service_endpoint_pk,_ in self.references(ServiceEndPointModel): - # ServiceEndPointModel(self.database, db_service_endpoint_pk).delete() - # super().delete() - # ConfigModel(self.database, self.service_config_fk).delete() - # ConstraintsModel(self.database, self.service_constraints_fk).delete() diff --git a/src/context/service/database/Topology.py b/src/context/service/database/Topology.py index 25fa02f4b6a336d8b1e6c1489c09ed2be0a76006..ae8d0a8bd889a37bea82ff699ce75e521b6faf23 100644 --- a/src/context/service/database/Topology.py +++ b/src/context/service/database/Topology.py @@ -12,20 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging from sqlalchemy.dialects.postgresql import insert from sqlalchemy.engine import Engine from sqlalchemy.orm import Session, sessionmaker from sqlalchemy_cockroachdb import run_transaction -from typing import Dict, List, Optional, Set +from typing import Dict, List, Optional from common.proto.context_pb2 import ContextId, Topology, TopologyId, TopologyIdList, TopologyList -from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentsException, NotFoundException +from common.rpc_method_wrapper.ServiceExceptions import NotFoundException from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Topology import json_topology_id -#from .models.RelationModels import TopologyDeviceModel, TopologyLinkModel from .models.TopologyModel import TopologyModel from .uuids.Context import context_get_uuid from .uuids.Topology import topology_get_uuid +LOGGER = logging.getLogger(__name__) + def topology_list_ids(db_engine : Engine, request : ContextId) -> TopologyIdList: context_uuid = context_get_uuid(request, allow_random=False) def callback(session : Session) -> List[Dict]: @@ -63,21 +65,15 @@ def topology_set(db_engine : Engine, request : Topology) -> bool: if len(topology_name) == 0: topology_name = request.topology_id.topology_uuid.uuid context_uuid,topology_uuid = topology_get_uuid(request.topology_id, topology_name=topology_name, allow_random=True) - #device_uuids : Set[str] = set() - #devices_to_add : List[Dict] = list() - #for device_id in request.device_ids: - # device_uuid = device_id.device_uuid.uuid - # if device_uuid in device_uuids: continue - # devices_to_add.append({'topology_uuid': topology_uuid, 'device_uuid': device_uuid}) - # device_uuids.add(device_uuid) + # Ignore request.device_ids and request.link_ids. They are used for retrieving devices and links added into the + # topology. Explicit addition into the topology is done automatically when creating the devices and links, based + # on the topologies specified in the endpoints associated with the devices and links. + + if len(request.device_ids) > 0: # pragma: no cover + LOGGER.warning('Items in field "device_ids" ignored. This field is used for retrieval purposes only.') - #link_uuids : Set[str] = set() - #links_to_add : List[Dict] = list() - #for link_id in request.link_ids: - # link_uuid = link_id.link_uuid.uuid - # if link_uuid in link_uuids: continue - # links_to_add.append({'topology_uuid': topology_uuid, 'link_uuid': link_uuid}) - # link_uuids.add(link_uuid) + if len(request.link_ids) > 0: # pragma: no cover + LOGGER.warning('Items in field "link_ids" ignored. This field is used for retrieval purposes only.') topology_data = [{ 'context_uuid' : context_uuid, @@ -93,16 +89,6 @@ def topology_set(db_engine : Engine, request : Topology) -> bool: ) session.execute(stmt) - #if len(devices_to_add) > 0: - # session.execute(insert(TopologyDeviceModel).values(devices_to_add).on_conflict_do_nothing( - # index_elements=[TopologyDeviceModel.topology_uuid, TopologyDeviceModel.device_uuid] - # )) - - #if len(links_to_add) > 0: - # session.execute(insert(TopologyLinkModel).values(links_to_add).on_conflict_do_nothing( - # index_elements=[TopologyLinkModel.topology_uuid, TopologyLinkModel.link_uuid] - # )) - run_transaction(sessionmaker(bind=db_engine), callback) updated = False # TODO: improve and check if created/updated return TopologyId(**json_topology_id(topology_uuid, json_context_id(context_uuid))),updated @@ -110,7 +96,6 @@ def topology_set(db_engine : Engine, request : Topology) -> bool: def topology_delete(db_engine : Engine, request : TopologyId) -> bool: _,topology_uuid = topology_get_uuid(request, allow_random=False) def callback(session : Session) -> bool: - num_deleted = session.query(TopologyModel)\ - .filter_by(topology_uuid=topology_uuid).delete() + num_deleted = session.query(TopologyModel).filter_by(topology_uuid=topology_uuid).delete() return num_deleted > 0 return run_transaction(sessionmaker(bind=db_engine), callback) diff --git a/src/context/service/database/models/ConfigRuleModel.py b/src/context/service/database/models/ConfigRuleModel.py index 9d56344e8fa1a2983d757e58f2700d510970727f..11e151ef66e9f06649579fea96ff18bdc1f855d0 100644 --- a/src/context/service/database/models/ConfigRuleModel.py +++ b/src/context/service/database/models/ConfigRuleModel.py @@ -28,15 +28,17 @@ class ConfigRuleModel(_Base): __tablename__ = 'configrule' configrule_uuid = Column(UUID(as_uuid=False), primary_key=True) - device_uuid = Column(ForeignKey('device.device_uuid', ondelete='CASCADE')) + device_uuid = Column(ForeignKey('device.device_uuid', ondelete='CASCADE'), nullable=True) + service_uuid = Column(ForeignKey('service.service_uuid', ondelete='CASCADE'), nullable=True) position = Column(Integer, nullable=False) - kind = Column(Enum(ConfigRuleKindEnum)) - action = Column(Enum(ORM_ConfigActionEnum)) + kind = Column(Enum(ConfigRuleKindEnum), nullable=False) + action = Column(Enum(ORM_ConfigActionEnum), nullable=False) data = Column(String, nullable=False) __table_args__ = ( CheckConstraint(position >= 0, name='check_position_value'), #UniqueConstraint('device_uuid', 'position', name='unique_per_device'), + #UniqueConstraint('service_uuid', 'position', name='unique_per_service'), ) def dump(self) -> Dict: diff --git a/src/context/service/database/models/ConstraintModel.py b/src/context/service/database/models/ConstraintModel.py index d616c3a7fe86adfccd37faa39fef737a4e21dfeb..118ae950556d6e7377053aa56c276cfcc653226a 100644 --- a/src/context/service/database/models/ConstraintModel.py +++ b/src/context/service/database/models/ConstraintModel.py @@ -12,144 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging, operator -from typing import Dict, List, Optional, Tuple, Type, Union -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 Constraint -from common.tools.grpc.Tools import grpc_message_to_json_string -from .EndPointModel import EndPointModel -from .Tools import fast_hasher -from sqlalchemy import Column, ForeignKey, String, Float, CheckConstraint, Integer, Boolean, Enum +import enum, json +from sqlalchemy import CheckConstraint, Column, Enum, ForeignKey, Integer, String from sqlalchemy.dialects.postgresql import UUID -from context.service.database.models._Base import Base -import enum +from typing import Dict +from ._Base import _Base -LOGGER = logging.getLogger(__name__) - -def remove_dict_key(dictionary : Dict, key : str): - dictionary.pop(key, None) - return dictionary - -class ConstraintsModel(Base): # pylint: disable=abstract-method - __tablename__ = 'Constraints' - constraints_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) - - @staticmethod - def main_pk_name(): - return 'constraints_uuid' - - - def dump(self, constraints) -> List[Dict]: - constraints = sorted(constraints, key=operator.itemgetter('position')) - return [remove_dict_key(constraint, 'position') for constraint in constraints] - - -class ConstraintCustomModel(Base): # pylint: disable=abstract-method - __tablename__ = 'ConstraintCustom' - constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) - constraint_type = Column(String, nullable=False) - constraint_value = Column(String, nullable=False) - - @staticmethod - def main_pk_name(): - return 'constraint_uuid' - - - def dump(self) -> Dict: # pylint: disable=arguments-differ - return {'custom': {'constraint_type': self.constraint_type, 'constraint_value': self.constraint_value}} - - -Union_ConstraintEndpoint = Union[ - 'ConstraintEndpointLocationGpsPositionModel', 'ConstraintEndpointLocationRegionModel', - 'ConstraintEndpointPriorityModel' -] - -class ConstraintEndpointLocationRegionModel(Model): # pylint: disable=abstract-method - endpoint_fk = ForeignKeyField(EndPointModel) - region = StringField(required=True, allow_empty=False) - - def dump(self) -> Dict: # pylint: disable=arguments-differ - json_endpoint_id = EndPointModel(self.database, self.endpoint_fk).dump_id() - return {'endpoint_location': {'endpoint_id': json_endpoint_id, 'location': {'region': self.region}}} - -# def dump_endpoint_id(endpoint_constraint: Union_ConstraintEndpoint): -# db_endpoints_pks = list(endpoint_constraint.references(EndPointModel)) -# num_endpoints = len(db_endpoints_pks) -# if num_endpoints != 1: -# raise Exception('Wrong number({:d}) of associated Endpoints with constraint'.format(num_endpoints)) -# db_endpoint = EndPointModel(endpoint_constraint.database, db_endpoints_pks[0]) -# return db_endpoint.dump_id() - - -class ConstraintEndpointLocationRegionModel(Base): # pylint: disable=abstract-method - __tablename__ = 'ConstraintEndpointLocationRegion' - constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) - endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) - region = Column(String, nullable=False) - - @staticmethod - def main_pk_name(): - return 'constraint_uuid' - - def dump(self, endpoint) -> Dict: # pylint: disable=arguments-differ - return {'endpoint_location': {'endpoint_id': endpoint.dump_id(), 'region': self.region}} - - def dump(self) -> Dict: # pylint: disable=arguments-differ - gps_position = {'latitude': self.latitude, 'longitude': self.longitude} - json_endpoint_id = EndPointModel(self.database, self.endpoint_fk).dump_id() - return {'endpoint_location': {'endpoint_id': json_endpoint_id, 'location': {'gps_position': gps_position}}} - -class ConstraintEndpointPriorityModel(Model): # pylint: disable=abstract-method - endpoint_fk = ForeignKeyField(EndPointModel) - priority = IntegerField(required=True, min_value=0) - - def dump(self) -> Dict: # pylint: disable=arguments-differ - json_endpoint_id = EndPointModel(self.database, self.endpoint_fk).dump_id() - return {'endpoint_priority': {'endpoint_id': json_endpoint_id, 'priority': self.priority}} - -class ConstraintEndpointLocationGpsPositionModel(Base): # pylint: disable=abstract-method - __tablename__ = 'ConstraintEndpointLocationGpsPosition' - constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) - endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) - latitude = Column(Float, CheckConstraint('latitude > -90.0 AND latitude < 90.0'), nullable=False) - longitude = Column(Float, CheckConstraint('longitude > -90.0 AND longitude < 90.0'), nullable=False) - - def dump(self, endpoint) -> Dict: # pylint: disable=arguments-differ - gps_position = {'latitude': self.latitude, 'longitude': self.longitude} - return {'endpoint_location': {'endpoint_id': endpoint.dump_id(), 'gps_position': gps_position}} - - -class ConstraintEndpointPriorityModel(Base): # pylint: disable=abstract-method - __tablename__ = 'ConstraintEndpointPriority' - constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) - endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) - # endpoint_fk = ForeignKeyField(EndPointModel) - # priority = FloatField(required=True) - priority = Column(Float, nullable=False) - @staticmethod - def main_pk_name(): - return 'constraint_uuid' - - def dump(self, endpoint) -> Dict: # pylint: disable=arguments-differ - return {'endpoint_priority': {'endpoint_id': endpoint.dump_id(), 'priority': self.priority}} - - -class ConstraintSlaAvailabilityModel(Base): # pylint: disable=abstract-method - __tablename__ = 'ConstraintSlaAvailability' - constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) - # num_disjoint_paths = IntegerField(required=True, min_value=1) - num_disjoint_paths = Column(Integer, CheckConstraint('num_disjoint_paths > 1'), nullable=False) - # all_active = BooleanField(required=True) - all_active = Column(Boolean, nullable=False) - @staticmethod - def main_pk_name(): - return 'constraint_uuid' - - def dump(self) -> Dict: # pylint: disable=arguments-differ - return {'sla_availability': {'num_disjoint_paths': self.num_disjoint_paths, 'all_active': self.all_active}} - -# enum values should match name of field in ConstraintModel +# Enum values should match name of field in ConstraintModel class ConstraintKindEnum(enum.Enum): CUSTOM = 'custom' ENDPOINT_LOCATION_REGION = 'ep_loc_region' @@ -157,215 +26,370 @@ class ConstraintKindEnum(enum.Enum): ENDPOINT_PRIORITY = 'ep_priority' SLA_AVAILABILITY = 'sla_avail' -Union_SpecificConstraint = Union[ - ConstraintCustomModel, ConstraintEndpointLocationRegionModel, ConstraintEndpointLocationGpsPositionModel, - ConstraintEndpointPriorityModel, ConstraintSlaAvailabilityModel, -] - -class ConstraintModel(Base): # pylint: disable=abstract-method - __tablename__ = 'Constraint' - # pk = PrimaryKeyField() - # constraints_fk = ForeignKeyField(ConstraintsModel) - constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) - constraints_uuid = Column(UUID(as_uuid=False), ForeignKey("Constraints.constraints_uuid"), primary_key=True) - # kind = EnumeratedField(ConstraintKindEnum) - kind = Column(Enum(ConstraintKindEnum, create_constraint=False, native_enum=False)) - # position = IntegerField(min_value=0, required=True) - position = Column(Integer, CheckConstraint('position >= 0'), nullable=False) - # constraint_custom_fk = ForeignKeyField(ConstraintCustomModel, required=False) - constraint_custom = Column(UUID(as_uuid=False), ForeignKey("ConstraintCustom.constraint_uuid")) - # constraint_ep_loc_region_fk = ForeignKeyField(ConstraintEndpointLocationRegionModel, required=False) - constraint_ep_loc_region = Column(UUID(as_uuid=False), ForeignKey("ConstraintEndpointLocationRegion.constraint_uuid")) - # constraint_ep_loc_gpspos_fk = ForeignKeyField(ConstraintEndpointLocationGpsPositionModel, required=False) - constraint_ep_loc_gpspos = Column(UUID(as_uuid=False), ForeignKey("ConstraintEndpointLocationGpsPosition.constraint_uuid")) - # constraint_ep_priority_fk = ForeignKeyField(ConstraintEndpointPriorityModel, required=False) - constraint_ep_priority = Column(UUID(as_uuid=False), ForeignKey("ConstraintEndpointPriority.constraint_uuid"),) - # constraint_sla_avail_fk = ForeignKeyField(ConstraintSlaAvailabilityModel, required=False) - constraint_sla_avail = Column(UUID(as_uuid=False), ForeignKey("ConstraintSlaAvailability.constraint_uuid")) - - @staticmethod - def main_pk_name(): - return 'constraint_uuid' - - # def delete(self) -> None: - # field_name = 'constraint_{:s}_fk'.format(str(self.kind.value)) - # specific_fk_value : Optional[ForeignKeyField] = getattr(self, field_name, None) - # if specific_fk_value is None: - # raise Exception('Unable to find constraint key for field_name({:s})'.format(field_name)) - # specific_fk_class = getattr(ConstraintModel, field_name, None) - # foreign_model_class : Model = specific_fk_class.foreign_model - # super().delete() - # get_object(self.database, foreign_model_class, str(specific_fk_value)).delete() - - def dump(self, include_position=True) -> Dict: # pylint: disable=arguments-differ - field_name = 'constraint_{:s}'.format(str(self.kind.value)) - specific_fk_value = getattr(self, field_name, None) - if specific_fk_value is None: - raise Exception('Unable to find constraint key for field_name({:s})'.format(field_name)) - specific_fk_class = getattr(ConstraintModel, field_name, None) - foreign_model_class: Base = specific_fk_class.foreign_model - constraint: Union_SpecificConstraint = get_object(self.database, foreign_model_class, str(specific_fk_value)) - result = constraint.dump() - if include_position: - result['position'] = self.position - return result - -Tuple_ConstraintSpecs = Tuple[Type, str, Dict, ConstraintKindEnum] - -def parse_constraint_custom(grpc_constraint) -> Tuple_ConstraintSpecs: - constraint_class = ConstraintCustomModel - str_constraint_id = grpc_constraint.custom.constraint_type - constraint_data = { - 'constraint_type' : grpc_constraint.custom.constraint_type, - 'constraint_value': grpc_constraint.custom.constraint_value, - } - return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.CUSTOM - -def parse_constraint_endpoint_location(db_endpoint, grpc_constraint) -> Tuple_ConstraintSpecs: - grpc_endpoint_id = grpc_constraint.endpoint_location.endpoint_id - # str_endpoint_key, db_endpoint = get_endpoint(database, grpc_endpoint_id) - - str_constraint_id = db_endpoint.endpoint_uuid - constraint_data = {'endpoint_fk': db_endpoint} - - grpc_location = grpc_constraint.endpoint_location.location - location_kind = str(grpc_location.WhichOneof('location')) - if location_kind == 'region': - constraint_class = ConstraintEndpointLocationRegionModel - constraint_data.update({'region': grpc_location.region}) - return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.ENDPOINT_LOCATION_REGION - elif location_kind == 'gps_position': - constraint_class = ConstraintEndpointLocationGpsPositionModel - gps_position = grpc_location.gps_position - constraint_data.update({'latitude': gps_position.latitude, 'longitude': gps_position.longitude}) - return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.ENDPOINT_LOCATION_GPSPOSITION - else: - MSG = 'Location kind {:s} in Constraint of kind endpoint_location is not implemented: {:s}' - raise NotImplementedError(MSG.format(location_kind, grpc_message_to_json_string(grpc_constraint))) - -def parse_constraint_endpoint_priority(db_endpoint, grpc_constraint) -> Tuple_ConstraintSpecs: - grpc_endpoint_id = grpc_constraint.endpoint_priority.endpoint_id - # str_endpoint_key, db_endpoint = get_endpoint(database, grpc_endpoint_id) - - constraint_class = ConstraintEndpointPriorityModel - str_constraint_id = db_endpoint.endpoint_uuid - priority = grpc_constraint.endpoint_priority.priority - constraint_data = {'endpoint_fk': db_endpoint, 'priority': priority} - - return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.ENDPOINT_PRIORITY - -def parse_constraint_sla_availability(grpc_constraint) -> Tuple_ConstraintSpecs: - constraint_class = ConstraintSlaAvailabilityModel - str_constraint_id = '' - constraint_data = { - 'num_disjoint_paths' : grpc_constraint.sla_availability.num_disjoint_paths, - 'all_active': grpc_constraint.sla_availability.all_active, - } - return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.SLA_AVAILABILITY - -CONSTRAINT_PARSERS = { - 'custom' : parse_constraint_custom, - 'endpoint_location' : parse_constraint_endpoint_location, - 'endpoint_priority' : parse_constraint_endpoint_priority, - 'sla_availability' : parse_constraint_sla_availability, -} - -Union_ConstraintModel = Union[ - ConstraintCustomModel, ConstraintEndpointLocationGpsPositionModel, ConstraintEndpointLocationRegionModel, - ConstraintEndpointPriorityModel, ConstraintSlaAvailabilityModel -] - -# def set_constraint( -# db_constraints : ConstraintsModel, grpc_constraint : Constraint, position : int -# ) -> Tuple[Union_ConstraintModel, bool]: -# grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint')) -# -# parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind) -# if parser is None: -# raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format( -# grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint))) -# -# # create specific constraint -# constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(database, grpc_constraint) -# str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) -# str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') -# result : Tuple[Union_ConstraintModel, bool] = update_or_create_object( -# database, constraint_class, str_constraint_key, constraint_data) -# db_specific_constraint, updated = result -# -# # create generic constraint -# constraint_fk_field_name = 'constraint_{:s}_fk'.format(constraint_kind.value) -# constraint_data = { -# 'constraints_fk': db_constraints, 'position': position, 'kind': constraint_kind, -# constraint_fk_field_name: db_specific_constraint -# } -# result : Tuple[ConstraintModel, bool] = update_or_create_object( -# database, ConstraintModel, str_constraint_key, constraint_data) -# db_constraint, updated = result -# -# return db_constraint, updated -# -# def set_constraints( -# database : Database, db_parent_pk : str, constraints_name : str, grpc_constraints -# ) -> 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 = [(db_constraints, created)] -# -# for position,grpc_constraint in enumerate(grpc_constraints): -# result : Tuple[ConstraintModel, bool] = set_constraint( -# database, db_constraints, grpc_constraint, position) -# db_constraint, updated = result -# db_objects.append((db_constraint, updated)) -# -# return db_objects -def set_constraint( - database : Database, db_constraints : ConstraintsModel, grpc_constraint : Constraint, position : int -) -> Tuple[Union_ConstraintModel, bool]: - grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint')) - - parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind) - if parser is None: - raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format( - grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint))) - - # create specific constraint - constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(database, grpc_constraint) - str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) - str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') - result : Tuple[Union_ConstraintModel, bool] = update_or_create_object( - database, constraint_class, str_constraint_key, constraint_data) - db_specific_constraint, updated = result - - # create generic constraint - constraint_fk_field_name = 'constraint_{:s}_fk'.format(constraint_kind.value) - constraint_data = { - 'constraints_fk': db_constraints, 'position': position, 'kind': constraint_kind, - constraint_fk_field_name: db_specific_constraint - } - result : Tuple[ConstraintModel, bool] = update_or_create_object( - database, ConstraintModel, str_constraint_key, constraint_data) - db_constraint, updated = result - - return db_constraint, updated - -def set_constraints( - database : Database, db_parent_pk : str, constraints_name : str, grpc_constraints -) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]: - - str_constraints_key = key_to_str([constraints_name, db_parent_pk], separator=':') - result : Tuple[ConstraintsModel, bool] = get_or_create_object(database, ConstraintsModel, str_constraints_key) - db_constraints, created = result - - db_objects = [(db_constraints, created)] - - for position,grpc_constraint in enumerate(grpc_constraints): - result : Tuple[ConstraintModel, bool] = set_constraint( - database, db_constraints, grpc_constraint, position) - db_constraint, updated = result - db_objects.append((db_constraint, updated)) - - return db_objects +class ConstraintModel(_Base): + __tablename__ = 'constraint' + + constraint_uuid = Column(UUID(as_uuid=False), primary_key=True) + service_uuid = Column(ForeignKey('service.service_uuid', ondelete='CASCADE'), nullable=False) + position = Column(Integer, nullable=False) + kind = Column(Enum(ConstraintKindEnum), nullable=False) + data = Column(String, nullable=False) + + __table_args__ = ( + CheckConstraint(position >= 0, name='check_position_value'), + #UniqueConstraint('service_uuid', 'position', name='unique_per_service'), + ) + + def dump(self) -> Dict: + return {self.kind.value: json.loads(self.data)} + + +#import logging, operator +#from typing import Dict, List, Optional, Tuple, Type, Union +#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 Constraint +#from common.tools.grpc.Tools import grpc_message_to_json_string +#from .EndPointModel import EndPointModel +#from .Tools import fast_hasher +#from sqlalchemy import Column, ForeignKey, String, Float, CheckConstraint, Integer, Boolean, Enum +#from sqlalchemy.dialects.postgresql import UUID +#from context.service.database.models._Base import Base +#import enum +# +#LOGGER = logging.getLogger(__name__) +# +#def remove_dict_key(dictionary : Dict, key : str): +# dictionary.pop(key, None) +# return dictionary +# +#class ConstraintsModel(Base): # pylint: disable=abstract-method +# __tablename__ = 'Constraints' +# constraints_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) +# +# @staticmethod +# def main_pk_name(): +# return 'constraints_uuid' +# +# +# def dump(self, constraints) -> List[Dict]: +# constraints = sorted(constraints, key=operator.itemgetter('position')) +# return [remove_dict_key(constraint, 'position') for constraint in constraints] +# +# +#class ConstraintCustomModel(Base): # pylint: disable=abstract-method +# __tablename__ = 'ConstraintCustom' +# constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) +# constraint_type = Column(String, nullable=False) +# constraint_value = Column(String, nullable=False) +# +# @staticmethod +# def main_pk_name(): +# return 'constraint_uuid' +# +# +# def dump(self) -> Dict: # pylint: disable=arguments-differ +# return {'custom': {'constraint_type': self.constraint_type, 'constraint_value': self.constraint_value}} +# +# +#Union_ConstraintEndpoint = Union[ +# 'ConstraintEndpointLocationGpsPositionModel', 'ConstraintEndpointLocationRegionModel', +# 'ConstraintEndpointPriorityModel' +#] +# +#class ConstraintEndpointLocationRegionModel(Model): # pylint: disable=abstract-method +# endpoint_fk = ForeignKeyField(EndPointModel) +# region = StringField(required=True, allow_empty=False) +# +# def dump(self) -> Dict: # pylint: disable=arguments-differ +# json_endpoint_id = EndPointModel(self.database, self.endpoint_fk).dump_id() +# return {'endpoint_location': {'endpoint_id': json_endpoint_id, 'location': {'region': self.region}}} +# +## def dump_endpoint_id(endpoint_constraint: Union_ConstraintEndpoint): +## db_endpoints_pks = list(endpoint_constraint.references(EndPointModel)) +## num_endpoints = len(db_endpoints_pks) +## if num_endpoints != 1: +## raise Exception('Wrong number({:d}) of associated Endpoints with constraint'.format(num_endpoints)) +## db_endpoint = EndPointModel(endpoint_constraint.database, db_endpoints_pks[0]) +## return db_endpoint.dump_id() +# +# +#class ConstraintEndpointLocationRegionModel(Base): # pylint: disable=abstract-method +# __tablename__ = 'ConstraintEndpointLocationRegion' +# constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) +# endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) +# region = Column(String, nullable=False) +# +# @staticmethod +# def main_pk_name(): +# return 'constraint_uuid' +# +# def dump(self, endpoint) -> Dict: # pylint: disable=arguments-differ +# return {'endpoint_location': {'endpoint_id': endpoint.dump_id(), 'region': self.region}} +# +# def dump(self) -> Dict: # pylint: disable=arguments-differ +# gps_position = {'latitude': self.latitude, 'longitude': self.longitude} +# json_endpoint_id = EndPointModel(self.database, self.endpoint_fk).dump_id() +# return {'endpoint_location': {'endpoint_id': json_endpoint_id, 'location': {'gps_position': gps_position}}} +# +#class ConstraintEndpointPriorityModel(Model): # pylint: disable=abstract-method +# endpoint_fk = ForeignKeyField(EndPointModel) +# priority = IntegerField(required=True, min_value=0) +# +# def dump(self) -> Dict: # pylint: disable=arguments-differ +# json_endpoint_id = EndPointModel(self.database, self.endpoint_fk).dump_id() +# return {'endpoint_priority': {'endpoint_id': json_endpoint_id, 'priority': self.priority}} +# +#class ConstraintEndpointLocationGpsPositionModel(Base): # pylint: disable=abstract-method +# __tablename__ = 'ConstraintEndpointLocationGpsPosition' +# constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) +# endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) +# latitude = Column(Float, CheckConstraint('latitude > -90.0 AND latitude < 90.0'), nullable=False) +# longitude = Column(Float, CheckConstraint('longitude > -90.0 AND longitude < 90.0'), nullable=False) +# +# def dump(self, endpoint) -> Dict: # pylint: disable=arguments-differ +# gps_position = {'latitude': self.latitude, 'longitude': self.longitude} +# return {'endpoint_location': {'endpoint_id': endpoint.dump_id(), 'gps_position': gps_position}} +# +# +#class ConstraintEndpointPriorityModel(Base): # pylint: disable=abstract-method +# __tablename__ = 'ConstraintEndpointPriority' +# constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) +# endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) +# # endpoint_fk = ForeignKeyField(EndPointModel) +# # priority = FloatField(required=True) +# priority = Column(Float, nullable=False) +# @staticmethod +# def main_pk_name(): +# return 'constraint_uuid' +# +# def dump(self, endpoint) -> Dict: # pylint: disable=arguments-differ +# return {'endpoint_priority': {'endpoint_id': endpoint.dump_id(), 'priority': self.priority}} +# +# +#class ConstraintSlaAvailabilityModel(Base): # pylint: disable=abstract-method +# __tablename__ = 'ConstraintSlaAvailability' +# constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) +# # num_disjoint_paths = IntegerField(required=True, min_value=1) +# num_disjoint_paths = Column(Integer, CheckConstraint('num_disjoint_paths > 1'), nullable=False) +# # all_active = BooleanField(required=True) +# all_active = Column(Boolean, nullable=False) +# @staticmethod +# def main_pk_name(): +# return 'constraint_uuid' +# +# def dump(self) -> Dict: # pylint: disable=arguments-differ +# return {'sla_availability': {'num_disjoint_paths': self.num_disjoint_paths, 'all_active': self.all_active}} +# +#Union_SpecificConstraint = Union[ +# ConstraintCustomModel, ConstraintEndpointLocationRegionModel, ConstraintEndpointLocationGpsPositionModel, +# ConstraintEndpointPriorityModel, ConstraintSlaAvailabilityModel, +#] +# +#class ConstraintModel(Base): # pylint: disable=abstract-method +# __tablename__ = 'Constraint' +# # pk = PrimaryKeyField() +# # constraints_fk = ForeignKeyField(ConstraintsModel) +# constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) +# constraints_uuid = Column(UUID(as_uuid=False), ForeignKey("Constraints.constraints_uuid"), primary_key=True) +# # kind = EnumeratedField(ConstraintKindEnum) +# kind = Column(Enum(ConstraintKindEnum, create_constraint=False, native_enum=False)) +# # position = IntegerField(min_value=0, required=True) +# position = Column(Integer, CheckConstraint('position >= 0'), nullable=False) +# # constraint_custom_fk = ForeignKeyField(ConstraintCustomModel, required=False) +# constraint_custom = Column(UUID(as_uuid=False), ForeignKey("ConstraintCustom.constraint_uuid")) +# # constraint_ep_loc_region_fk = ForeignKeyField(ConstraintEndpointLocationRegionModel, required=False) +# constraint_ep_loc_region = Column(UUID(as_uuid=False), ForeignKey("ConstraintEndpointLocationRegion.constraint_uuid")) +# # constraint_ep_loc_gpspos_fk = ForeignKeyField(ConstraintEndpointLocationGpsPositionModel, required=False) +# constraint_ep_loc_gpspos = Column(UUID(as_uuid=False), ForeignKey("ConstraintEndpointLocationGpsPosition.constraint_uuid")) +# # constraint_ep_priority_fk = ForeignKeyField(ConstraintEndpointPriorityModel, required=False) +# constraint_ep_priority = Column(UUID(as_uuid=False), ForeignKey("ConstraintEndpointPriority.constraint_uuid"),) +# # constraint_sla_avail_fk = ForeignKeyField(ConstraintSlaAvailabilityModel, required=False) +# constraint_sla_avail = Column(UUID(as_uuid=False), ForeignKey("ConstraintSlaAvailability.constraint_uuid")) +# +# @staticmethod +# def main_pk_name(): +# return 'constraint_uuid' +# +# # def delete(self) -> None: +# # field_name = 'constraint_{:s}_fk'.format(str(self.kind.value)) +# # specific_fk_value : Optional[ForeignKeyField] = getattr(self, field_name, None) +# # if specific_fk_value is None: +# # raise Exception('Unable to find constraint key for field_name({:s})'.format(field_name)) +# # specific_fk_class = getattr(ConstraintModel, field_name, None) +# # foreign_model_class : Model = specific_fk_class.foreign_model +# # super().delete() +# # get_object(self.database, foreign_model_class, str(specific_fk_value)).delete() +# +# def dump(self, include_position=True) -> Dict: # pylint: disable=arguments-differ +# field_name = 'constraint_{:s}'.format(str(self.kind.value)) +# specific_fk_value = getattr(self, field_name, None) +# if specific_fk_value is None: +# raise Exception('Unable to find constraint key for field_name({:s})'.format(field_name)) +# specific_fk_class = getattr(ConstraintModel, field_name, None) +# foreign_model_class: Base = specific_fk_class.foreign_model +# constraint: Union_SpecificConstraint = get_object(self.database, foreign_model_class, str(specific_fk_value)) +# result = constraint.dump() +# if include_position: +# result['position'] = self.position +# return result +# +#Tuple_ConstraintSpecs = Tuple[Type, str, Dict, ConstraintKindEnum] +# +#def parse_constraint_custom(grpc_constraint) -> Tuple_ConstraintSpecs: +# constraint_class = ConstraintCustomModel +# str_constraint_id = grpc_constraint.custom.constraint_type +# constraint_data = { +# 'constraint_type' : grpc_constraint.custom.constraint_type, +# 'constraint_value': grpc_constraint.custom.constraint_value, +# } +# return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.CUSTOM +# +#def parse_constraint_endpoint_location(db_endpoint, grpc_constraint) -> Tuple_ConstraintSpecs: +# grpc_endpoint_id = grpc_constraint.endpoint_location.endpoint_id +# # str_endpoint_key, db_endpoint = get_endpoint(database, grpc_endpoint_id) +# +# str_constraint_id = db_endpoint.endpoint_uuid +# constraint_data = {'endpoint_fk': db_endpoint} +# +# grpc_location = grpc_constraint.endpoint_location.location +# location_kind = str(grpc_location.WhichOneof('location')) +# if location_kind == 'region': +# constraint_class = ConstraintEndpointLocationRegionModel +# constraint_data.update({'region': grpc_location.region}) +# return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.ENDPOINT_LOCATION_REGION +# elif location_kind == 'gps_position': +# constraint_class = ConstraintEndpointLocationGpsPositionModel +# gps_position = grpc_location.gps_position +# constraint_data.update({'latitude': gps_position.latitude, 'longitude': gps_position.longitude}) +# return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.ENDPOINT_LOCATION_GPSPOSITION +# else: +# MSG = 'Location kind {:s} in Constraint of kind endpoint_location is not implemented: {:s}' +# raise NotImplementedError(MSG.format(location_kind, grpc_message_to_json_string(grpc_constraint))) +# +#def parse_constraint_endpoint_priority(db_endpoint, grpc_constraint) -> Tuple_ConstraintSpecs: +# grpc_endpoint_id = grpc_constraint.endpoint_priority.endpoint_id +# # str_endpoint_key, db_endpoint = get_endpoint(database, grpc_endpoint_id) +# +# constraint_class = ConstraintEndpointPriorityModel +# str_constraint_id = db_endpoint.endpoint_uuid +# priority = grpc_constraint.endpoint_priority.priority +# constraint_data = {'endpoint_fk': db_endpoint, 'priority': priority} +# +# return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.ENDPOINT_PRIORITY +# +#def parse_constraint_sla_availability(grpc_constraint) -> Tuple_ConstraintSpecs: +# constraint_class = ConstraintSlaAvailabilityModel +# str_constraint_id = '' +# constraint_data = { +# 'num_disjoint_paths' : grpc_constraint.sla_availability.num_disjoint_paths, +# 'all_active': grpc_constraint.sla_availability.all_active, +# } +# return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.SLA_AVAILABILITY +# +#CONSTRAINT_PARSERS = { +# 'custom' : parse_constraint_custom, +# 'endpoint_location' : parse_constraint_endpoint_location, +# 'endpoint_priority' : parse_constraint_endpoint_priority, +# 'sla_availability' : parse_constraint_sla_availability, +#} +# +#Union_ConstraintModel = Union[ +# ConstraintCustomModel, ConstraintEndpointLocationGpsPositionModel, ConstraintEndpointLocationRegionModel, +# ConstraintEndpointPriorityModel, ConstraintSlaAvailabilityModel +#] +# +## def set_constraint( +## db_constraints : ConstraintsModel, grpc_constraint : Constraint, position : int +## ) -> Tuple[Union_ConstraintModel, bool]: +## grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint')) +## +## parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind) +## if parser is None: +## raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format( +## grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint))) +## +## # create specific constraint +## constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(database, grpc_constraint) +## str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) +## str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') +## result : Tuple[Union_ConstraintModel, bool] = update_or_create_object( +## database, constraint_class, str_constraint_key, constraint_data) +## db_specific_constraint, updated = result +## +## # create generic constraint +## constraint_fk_field_name = 'constraint_{:s}_fk'.format(constraint_kind.value) +## constraint_data = { +## 'constraints_fk': db_constraints, 'position': position, 'kind': constraint_kind, +## constraint_fk_field_name: db_specific_constraint +## } +## result : Tuple[ConstraintModel, bool] = update_or_create_object( +## database, ConstraintModel, str_constraint_key, constraint_data) +## db_constraint, updated = result +## +## return db_constraint, updated +## +## def set_constraints( +## database : Database, db_parent_pk : str, constraints_name : str, grpc_constraints +## ) -> 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 = [(db_constraints, created)] +## +## for position,grpc_constraint in enumerate(grpc_constraints): +## result : Tuple[ConstraintModel, bool] = set_constraint( +## database, db_constraints, grpc_constraint, position) +## db_constraint, updated = result +## db_objects.append((db_constraint, updated)) +## +## return db_objects +#def set_constraint( +# database : Database, db_constraints : ConstraintsModel, grpc_constraint : Constraint, position : int +#) -> Tuple[Union_ConstraintModel, bool]: +# grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint')) +# +# parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind) +# if parser is None: +# raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format( +# grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint))) +# +# # create specific constraint +# constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(database, grpc_constraint) +# str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) +# str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') +# result : Tuple[Union_ConstraintModel, bool] = update_or_create_object( +# database, constraint_class, str_constraint_key, constraint_data) +# db_specific_constraint, updated = result +# +# # create generic constraint +# constraint_fk_field_name = 'constraint_{:s}_fk'.format(constraint_kind.value) +# constraint_data = { +# 'constraints_fk': db_constraints, 'position': position, 'kind': constraint_kind, +# constraint_fk_field_name: db_specific_constraint +# } +# result : Tuple[ConstraintModel, bool] = update_or_create_object( +# database, ConstraintModel, str_constraint_key, constraint_data) +# db_constraint, updated = result +# +# return db_constraint, updated +# +#def set_constraints( +# database : Database, db_parent_pk : str, constraints_name : str, grpc_constraints +#) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]: +# +# str_constraints_key = key_to_str([constraints_name, db_parent_pk], separator=':') +# result : Tuple[ConstraintsModel, bool] = get_or_create_object(database, ConstraintsModel, str_constraints_key) +# db_constraints, created = result +# +# db_objects = [(db_constraints, created)] +# +# for position,grpc_constraint in enumerate(grpc_constraints): +# result : Tuple[ConstraintModel, bool] = set_constraint( +# database, db_constraints, grpc_constraint, position) +# db_constraint, updated = result +# db_objects.append((db_constraint, updated)) +# +# return db_objects diff --git a/src/context/service/database/models/ContextModel.py b/src/context/service/database/models/ContextModel.py index 84039dea9a5a9c52376b08d8af499b711d509f1b..1a282e8bd4c9ac0e74b1257f035997393a04347c 100644 --- a/src/context/service/database/models/ContextModel.py +++ b/src/context/service/database/models/ContextModel.py @@ -25,7 +25,7 @@ class ContextModel(_Base): context_name = Column(String, nullable=False) topologies = relationship('TopologyModel', back_populates='context') - #services = relationship('ServiceModel', back_populates='context') + services = relationship('ServiceModel', back_populates='context') #slices = relationship('SliceModel', back_populates='context') def dump_id(self) -> Dict: @@ -36,6 +36,6 @@ class ContextModel(_Base): 'context_id' : self.dump_id(), 'name' : self.context_name, 'topology_ids': [obj.dump_id() for obj in self.topologies], - #'service_ids' : [obj.dump_id() for obj in self.services ], - #'slice_ids' : [obj.dump_id() for obj in self.slices ], + 'service_ids' : [obj.dump_id() for obj in self.services ], + #'slice_ids' : [obj.dump_id() for obj in self.slices ], } diff --git a/src/context/service/database/models/DeviceModel.py b/src/context/service/database/models/DeviceModel.py index 50db8e7bb7a93131e3a4ef635a887eb62316022f..74fa70cf8c75ffab11a363df39211a941b7f2c3f 100644 --- a/src/context/service/database/models/DeviceModel.py +++ b/src/context/service/database/models/DeviceModel.py @@ -14,8 +14,8 @@ import operator from typing import Dict -from sqlalchemy import Column, String, Enum -from sqlalchemy.dialects.postgresql import UUID, ARRAY +from sqlalchemy import Column, Enum, String +from sqlalchemy.dialects.postgresql import ARRAY, UUID from sqlalchemy.orm import relationship from .enums.DeviceDriver import ORM_DeviceDriverEnum from .enums.DeviceOperationalStatus import ORM_DeviceOperationalStatusEnum @@ -27,7 +27,7 @@ class DeviceModel(_Base): device_uuid = Column(UUID(as_uuid=False), primary_key=True) device_name = Column(String, nullable=False) device_type = Column(String, nullable=False) - device_operational_status = Column(Enum(ORM_DeviceOperationalStatusEnum)) + device_operational_status = Column(Enum(ORM_DeviceOperationalStatusEnum), nullable=False) device_drivers = Column(ARRAY(Enum(ORM_DeviceDriverEnum), dimensions=1)) #topology_devices = relationship('TopologyDeviceModel', back_populates='device') diff --git a/src/context/service/database/models/EndPointModel.py b/src/context/service/database/models/EndPointModel.py index f9d5f7658a041f3670fe3256e9c3a863f4682d5b..b69b4978b99c722f6af57fd4095dde1c4fb6df0a 100644 --- a/src/context/service/database/models/EndPointModel.py +++ b/src/context/service/database/models/EndPointModel.py @@ -23,10 +23,10 @@ class EndPointModel(_Base): __tablename__ = 'endpoint' endpoint_uuid = Column(UUID(as_uuid=False), primary_key=True) - device_uuid = Column(UUID(as_uuid=False), ForeignKey('device.device_uuid', ondelete='CASCADE' )) - topology_uuid = Column(UUID(as_uuid=False), ForeignKey('topology.topology_uuid', ondelete='RESTRICT')) - name = Column(String) - endpoint_type = Column(String) + device_uuid = Column(ForeignKey('device.device_uuid', ondelete='CASCADE' ), nullable=False) + topology_uuid = Column(ForeignKey('topology.topology_uuid', ondelete='RESTRICT'), nullable=False) + name = Column(String, nullable=False) + endpoint_type = Column(String, nullable=False) kpi_sample_types = Column(ARRAY(Enum(ORM_KpiSampleTypeEnum), dimensions=1)) device = relationship('DeviceModel', back_populates='endpoints') diff --git a/src/context/service/database/models/LinkModel.py b/src/context/service/database/models/LinkModel.py index 053dc0122797c99a956053d40af00e51559cbda5..fd4f80c166998e726d16ef0f7075a0f186a372c1 100644 --- a/src/context/service/database/models/LinkModel.py +++ b/src/context/service/database/models/LinkModel.py @@ -25,7 +25,7 @@ class LinkModel(_Base): link_name = Column(String, nullable=False) #topology_links = relationship('TopologyLinkModel', back_populates='link') - link_endpoints = relationship('LinkEndPointModel', back_populates='link') #, lazy='joined') + link_endpoints = relationship('LinkEndPointModel') # lazy='joined', back_populates='link' def dump_id(self) -> Dict: return {'link_uuid': {'uuid': self.link_uuid}} diff --git a/src/context/service/database/models/RelationModels.py b/src/context/service/database/models/RelationModels.py index 89e8e05e082058f28f15421912a9bfbfcd294959..a57d85eb3a598b3909600cba9e36d8e07ef9a74f 100644 --- a/src/context/service/database/models/RelationModels.py +++ b/src/context/service/database/models/RelationModels.py @@ -31,33 +31,14 @@ class LinkEndPointModel(_Base): link = relationship('LinkModel', back_populates='link_endpoints', lazy='joined') endpoint = relationship('EndPointModel', lazy='joined') # back_populates='link_endpoints' -#class ServiceEndPointModel(_Base): -# __tablename__ = 'service_endpoint' -# -# context_uuid = Column(UUID(as_uuid=False), primary_key=True) -# service_uuid = Column(UUID(as_uuid=False), primary_key=True) -# topology_uuid = Column(UUID(as_uuid=False), primary_key=True) -# device_uuid = Column(UUID(as_uuid=False), primary_key=True) -# endpoint_uuid = Column(UUID(as_uuid=False), primary_key=True) -# -# service = relationship('ServiceModel', back_populates='service_endpoints', lazy='joined') -# endpoint = relationship('EndPointModel', back_populates='service_endpoints', lazy='joined') -# writer = relationship( -# "Writer", -# primaryjoin="and_(Writer.id == foreign(Article.writer_id), " -# "Writer.magazine_id == Article.magazine_id)", -# ) -# -# __table_args__ = ( -# ForeignKeyConstraint( -# ['context_uuid', 'service_uuid'], -# ['service.context_uuid', 'service.service_uuid'], -# ondelete='CASCADE'), -# ForeignKeyConstraint( -# ['context_uuid', 'topology_uuid', 'device_uuid', 'endpoint_uuid'], -# ['endpoint.context_uuid', 'endpoint.topology_uuid', 'endpoint.device_uuid', 'endpoint.endpoint_uuid'], -# ondelete='CASCADE'), -# ) +class ServiceEndPointModel(_Base): + __tablename__ = 'service_endpoint' + + service_uuid = Column(ForeignKey('service.service_uuid', ondelete='CASCADE' ), primary_key=True) + endpoint_uuid = Column(ForeignKey('endpoint.endpoint_uuid', ondelete='RESTRICT'), primary_key=True) + + service = relationship('ServiceModel', back_populates='service_endpoints', lazy='joined') + endpoint = relationship('EndPointModel', lazy='joined') # back_populates='service_endpoints' # class SliceEndPointModel(Model): # pk = PrimaryKeyField() diff --git a/src/context/service/database/models/ServiceModel.py b/src/context/service/database/models/ServiceModel.py index ea4e895264d3702b174e85c22b35229c6e95687d..b080438448a3fd89b359d5b412cdbdcd7aa0e042 100644 --- a/src/context/service/database/models/ServiceModel.py +++ b/src/context/service/database/models/ServiceModel.py @@ -13,8 +13,8 @@ # limitations under the License. import operator -from sqlalchemy import Column, Enum, Float, ForeignKey, String from typing import Dict +from sqlalchemy import Column, Enum, ForeignKey, String from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship from .enums.ServiceStatus import ORM_ServiceStatusEnum @@ -24,17 +24,16 @@ from ._Base import _Base class ServiceModel(_Base): __tablename__ = 'service' - context_uuid = Column(UUID(as_uuid=False), ForeignKey('context.context_uuid'), primary_key=True) service_uuid = Column(UUID(as_uuid=False), primary_key=True) + context_uuid = Column(ForeignKey('context.context_uuid'), nullable=False) service_name = Column(String, nullable=False) - service_type = Column(Enum(ORM_ServiceTypeEnum)) - service_status = Column(Enum(ORM_ServiceStatusEnum)) - created_at = Column(Float) + service_type = Column(Enum(ORM_ServiceTypeEnum), nullable=False) + service_status = Column(Enum(ORM_ServiceStatusEnum), nullable=False) context = relationship('ContextModel', back_populates='services') - service_endpoints = relationship('ServiceEndPointModel', back_populates='service') #, lazy='joined') - #constraints = relationship('ConstraintModel', passive_deletes=True, back_populates='service', lazy='joined') - config_rules = relationship('ConfigRuleModel', passive_deletes=True, back_populates='service', lazy='joined') + service_endpoints = relationship('ServiceEndPointModel') # lazy='joined', back_populates='service' + constraints = relationship('ConstraintModel', passive_deletes=True) # lazy='joined', back_populates='service' + config_rules = relationship('ConfigRuleModel', passive_deletes=True) # lazy='joined', back_populates='service' def dump_id(self) -> Dict: return { @@ -53,8 +52,8 @@ class ServiceModel(_Base): for service_endpoint in self.service_endpoints ], 'service_constraints' : [ - #constraint.dump() - #for constraint in sorted(self.constraints, key=operator.attrgetter('position')) + constraint.dump() + for constraint in sorted(self.constraints, key=operator.attrgetter('position')) ], 'service_config' : {'config_rules': [ config_rule.dump() diff --git a/src/context/service/database/models/TopologyModel.py b/src/context/service/database/models/TopologyModel.py index e0119beadf631cfca0297d011e03448ce93b0efe..8c59bf58a01d64306ad0e6138ed7d561a1aae8c5 100644 --- a/src/context/service/database/models/TopologyModel.py +++ b/src/context/service/database/models/TopologyModel.py @@ -22,7 +22,7 @@ class TopologyModel(_Base): __tablename__ = 'topology' topology_uuid = Column(UUID(as_uuid=False), primary_key=True) - context_uuid = Column(UUID(as_uuid=False), ForeignKey('context.context_uuid')) + context_uuid = Column(ForeignKey('context.context_uuid'), nullable=False) topology_name = Column(String, nullable=False) context = relationship('ContextModel', back_populates='topologies') diff --git a/src/context/service/database/uuids/Service.py b/src/context/service/database/uuids/Service.py new file mode 100644 index 0000000000000000000000000000000000000000..56a5d12a0965b0ac26cd9027ad39ecb3546a426b --- /dev/null +++ b/src/context/service/database/uuids/Service.py @@ -0,0 +1,37 @@ +# 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. + +from typing import Tuple +from common.proto.context_pb2 import ServiceId +from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentsException +from ._Builder import get_uuid_from_string, get_uuid_random +from .Context import context_get_uuid + +def service_get_uuid( + service_id : ServiceId, service_name : str = '', allow_random : bool = False +) -> Tuple[str, str]: + context_uuid = context_get_uuid(service_id.context_id, allow_random=False) + raw_service_uuid = service_id.service_uuid.uuid + + if len(raw_service_uuid) > 0: + return context_uuid, get_uuid_from_string(raw_service_uuid, prefix_for_name=context_uuid) + if len(service_name) > 0: + return context_uuid, get_uuid_from_string(service_name, prefix_for_name=context_uuid) + if allow_random: + return context_uuid, get_uuid_random() + + raise InvalidArgumentsException([ + ('service_id.service_uuid.uuid', raw_service_uuid), + ('name', service_name), + ], extra_details=['At least one is required to produce a Service UUID']) diff --git a/src/context/tests/_test_service.py b/src/context/tests/test_service.py similarity index 58% rename from src/context/tests/_test_service.py rename to src/context/tests/test_service.py index 8bd6570de876cd71b09c55849090f4f84a68e282..ca81bbfa33d2a8e61b56201833acba0e7af29b8e 100644 --- a/src/context/tests/_test_service.py +++ b/src/context/tests/test_service.py @@ -13,108 +13,105 @@ # limitations under the License. import copy, grpc, pytest -from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID from common.proto.context_pb2 import ( Context, ContextId, Device, DeviceId, Service, ServiceId, ServiceStatusEnum, ServiceTypeEnum, Topology, TopologyId) from context.client.ContextClient import ContextClient +from context.service.database.uuids.Service import service_get_uuid #from context.client.EventsCollector import EventsCollector from .Objects import ( - CONTEXT, CONTEXT_ID, DEVICE_R1, DEVICE_R1_ID, DEVICE_R1_UUID, DEVICE_R2, DEVICE_R2_ID, DEVICE_R2_UUID, - SERVICE_R1_R2, SERVICE_R1_R2_ID, SERVICE_R1_R2_UUID, TOPOLOGY, TOPOLOGY_ID) + CONTEXT, CONTEXT_ID, CONTEXT_NAME, DEVICE_R1, DEVICE_R1_ID, SERVICE_R1_R2_NAME, DEVICE_R2, DEVICE_R2_ID, + SERVICE_R1_R2, SERVICE_R1_R2_ID, TOPOLOGY, TOPOLOGY_ID) -def grpc_service(context_client_grpc : ContextClient) -> None: +@pytest.mark.depends(on=['context/tests/test_link.py::test_link']) +def test_service(context_client : ContextClient) -> None: # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- #events_collector = EventsCollector( - # context_client_grpc, log_events_received=True, + # context_client, log_events_received=True, # activate_context_collector = False, activate_topology_collector = False, activate_device_collector = False, # activate_link_collector = False, activate_service_collector = True, activate_slice_collector = False, # activate_connection_collector = False) #events_collector.start() # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- - response = context_client_grpc.SetContext(Context(**CONTEXT)) - assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID - - response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) - assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID - - response = context_client_grpc.SetDevice(Device(**DEVICE_R1)) - assert response.device_uuid.uuid == DEVICE_R1_UUID - - response = context_client_grpc.SetDevice(Device(**DEVICE_R2)) - assert response.device_uuid.uuid == DEVICE_R2_UUID + context_client.SetContext(Context(**CONTEXT)) + context_client.SetTopology(Topology(**TOPOLOGY)) + context_client.SetDevice(Device(**DEVICE_R1)) + context_client.SetDevice(Device(**DEVICE_R2)) # events = events_collector.get_events(block=True, count=4) # assert isinstance(events[0], ContextEvent) # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - # assert events[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[0].context_id.context_uuid.uuid == context_uuid # assert isinstance(events[1], TopologyEvent) # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - # assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - # assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # assert events[1].topology_id.context_id.context_uuid.uuid == context_uuid + # assert events[1].topology_id.topology_uuid.uuid == topology_uuid # assert isinstance(events[2], DeviceEvent) # assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - # assert events[2].device_id.device_uuid.uuid == DEVICE_R1_UUID + # assert events[2].device_id.device_uuid.uuid == device_r1_uuid # assert isinstance(events[3], DeviceEvent) # assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - # assert events[3].device_id.device_uuid.uuid == DEVICE_R2_UUID + # assert events[3].device_id.device_uuid.uuid == device_r2_uuid # ----- Get when the object does not exist ------------------------------------------------------------------------- + service_id = ServiceId(**SERVICE_R1_R2_ID) + context_uuid,service_uuid = service_get_uuid(service_id, allow_random=False) with pytest.raises(grpc.RpcError) as e: - context_client_grpc.GetService(ServiceId(**SERVICE_R1_R2_ID)) + context_client.GetService(service_id) assert e.value.code() == grpc.StatusCode.NOT_FOUND - assert e.value.details() == 'Service({:s}) not found'.format(SERVICE_R1_R2_UUID) + MSG = 'Service({:s}/{:s}) not found; context_uuid generated was: {:s}; service_uuid generated was: {:s}' + assert e.value.details() == MSG.format(CONTEXT_NAME, SERVICE_R1_R2_NAME, context_uuid, service_uuid) # ----- List when the object does not exist ------------------------------------------------------------------------ - response = context_client_grpc.GetContext(ContextId(**CONTEXT_ID)) + response = context_client.GetContext(ContextId(**CONTEXT_ID)) assert len(response.topology_ids) == 1 assert len(response.service_ids) == 0 assert len(response.slice_ids) == 0 - response = context_client_grpc.ListServiceIds(ContextId(**CONTEXT_ID)) + response = context_client.ListServiceIds(ContextId(**CONTEXT_ID)) assert len(response.service_ids) == 0 - response = context_client_grpc.ListServices(ContextId(**CONTEXT_ID)) + response = context_client.ListServices(ContextId(**CONTEXT_ID)) assert len(response.services) == 0 # ----- Create the object ------------------------------------------------------------------------------------------ with pytest.raises(grpc.RpcError) as e: + WRONG_UUID = 'ffffffff-ffff-ffff-ffff-ffffffffffff' WRONG_SERVICE = copy.deepcopy(SERVICE_R1_R2) - WRONG_SERVICE['service_endpoint_ids'][0]\ - ['topology_id']['context_id']['context_uuid']['uuid'] = 'ca1ea172-728f-441d-972c-feeae8c9bffc' - context_client_grpc.SetService(Service(**WRONG_SERVICE)) + WRONG_SERVICE['service_endpoint_ids'][0]['topology_id']['context_id']['context_uuid']['uuid'] = WRONG_UUID + context_client.SetService(Service(**WRONG_SERVICE)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'request.service_endpoint_ids[0].topology_id.context_id.context_uuid.uuid(ca1ea172-728f-441d-972c-feeae8c9bffc) is invalid; '\ - 'should be == request.service_id.context_id.context_uuid.uuid({:s})'.format(DEFAULT_CONTEXT_UUID) - assert e.value.details() == msg + MSG = 'request.service_endpoint_ids[0].topology_id.context_id.context_uuid.uuid({}) is invalid; '\ + 'should be == request.service_id.context_id.context_uuid.uuid({})' + raw_context_uuid = service_id.context_id.context_uuid.uuid # pylint: disable=no-member + assert e.value.details() == MSG.format(WRONG_UUID, raw_context_uuid) - response = context_client_grpc.SetService(Service(**SERVICE_R1_R2)) - assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.service_uuid.uuid == SERVICE_R1_R2_UUID + response = context_client.SetService(Service(**SERVICE_R1_R2)) + assert response.context_id.context_uuid.uuid == context_uuid + assert response.service_uuid.uuid == service_uuid # ----- Check create event ----------------------------------------------------------------------------------------- #event = events_collector.get_event(block=True) #assert isinstance(event, ServiceEvent) #assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE - #assert event.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - #assert event.service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + #assert event.service_id.context_id.context_uuid.uuid == context_uuid + #assert event.service_id.service_uuid.uuid == service_uuid # ----- Get when the object exists --------------------------------------------------------------------------------- - response = context_client_grpc.GetContext(ContextId(**CONTEXT_ID)) - assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.name == '' + response = context_client.GetContext(ContextId(**CONTEXT_ID)) + assert response.context_id.context_uuid.uuid == context_uuid + assert response.name == CONTEXT_NAME assert len(response.topology_ids) == 1 assert len(response.service_ids) == 1 - assert response.service_ids[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.service_ids[0].service_uuid.uuid == SERVICE_R1_R2_UUID + assert response.service_ids[0].context_id.context_uuid.uuid == context_uuid + assert response.service_ids[0].service_uuid.uuid == service_uuid assert len(response.slice_ids) == 0 - response = context_client_grpc.GetService(ServiceId(**SERVICE_R1_R2_ID)) - assert response.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.service_id.service_uuid.uuid == SERVICE_R1_R2_UUID - assert response.name == '' + response = context_client.GetService(ServiceId(**SERVICE_R1_R2_ID)) + assert response.service_id.context_id.context_uuid.uuid == context_uuid + assert response.service_id.service_uuid.uuid == service_uuid + assert response.name == SERVICE_R1_R2_NAME assert response.service_type == ServiceTypeEnum.SERVICETYPE_L3NM assert len(response.service_endpoint_ids) == 2 assert len(response.service_constraints) == 2 @@ -122,106 +119,108 @@ def grpc_service(context_client_grpc : ContextClient) -> None: assert len(response.service_config.config_rules) == 3 # ----- List when the object exists -------------------------------------------------------------------------------- - response = context_client_grpc.ListServiceIds(ContextId(**CONTEXT_ID)) + response = context_client.ListServiceIds(ContextId(**CONTEXT_ID)) assert len(response.service_ids) == 1 - assert response.service_ids[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.service_ids[0].service_uuid.uuid == SERVICE_R1_R2_UUID + assert response.service_ids[0].context_id.context_uuid.uuid == context_uuid + assert response.service_ids[0].service_uuid.uuid == service_uuid - response = context_client_grpc.ListServices(ContextId(**CONTEXT_ID)) + response = context_client.ListServices(ContextId(**CONTEXT_ID)) assert len(response.services) == 1 - assert response.services[0].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.services[0].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID - assert response.services[0].name == '' - assert response.service_type == ServiceTypeEnum.SERVICETYPE_L3NM - assert len(response.service_endpoint_ids) == 2 - assert len(response.service_constraints) == 2 - assert response.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_PLANNED - assert len(response.service_config.config_rules) == 3 + assert response.services[0].service_id.context_id.context_uuid.uuid == context_uuid + assert response.services[0].service_id.service_uuid.uuid == service_uuid + assert response.services[0].name == SERVICE_R1_R2_NAME + assert response.services[0].service_type == ServiceTypeEnum.SERVICETYPE_L3NM + assert len(response.services[0].service_endpoint_ids) == 2 + assert len(response.services[0].service_constraints) == 2 + assert response.services[0].service_status.service_status == ServiceStatusEnum.SERVICESTATUS_PLANNED + assert len(response.services[0].service_config.config_rules) == 3 # ----- Update the object ------------------------------------------------------------------------------------------ - new_service_name = 'svc:r1-r2' + new_service_name = 'new' SERVICE_UPDATED = copy.deepcopy(SERVICE_R1_R2) SERVICE_UPDATED['name'] = new_service_name - response = context_client_grpc.SetService(Service(**SERVICE_UPDATED)) - assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.service_uuid.uuid == SERVICE_R1_R2_UUID + SERVICE_UPDATED['service_status']['service_status'] = ServiceStatusEnum.SERVICESTATUS_ACTIVE + response = context_client.SetService(Service(**SERVICE_UPDATED)) + assert response.context_id.context_uuid.uuid == context_uuid + assert response.service_uuid.uuid == service_uuid # ----- Check update event ----------------------------------------------------------------------------------------- #event = events_collector.get_event(block=True) #assert isinstance(event, ServiceEvent) #assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE - #assert event.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - #assert event.service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + #assert event.service_id.context_id.context_uuid.uuid == context_uuid + #assert event.service_id.service_uuid.uuid == service_uuid # ----- Get when the object is modified ---------------------------------------------------------------------------- - response = context_client_grpc.GetService(ServiceId(**SERVICE_R1_R2_ID)) - assert response.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + response = context_client.GetService(ServiceId(**SERVICE_R1_R2_ID)) + assert response.service_id.context_id.context_uuid.uuid == context_uuid + assert response.service_id.service_uuid.uuid == service_uuid assert response.name == new_service_name assert response.service_type == ServiceTypeEnum.SERVICETYPE_L3NM assert len(response.service_endpoint_ids) == 2 assert len(response.service_constraints) == 2 - assert response.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_PLANNED + assert response.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE assert len(response.service_config.config_rules) == 3 # ----- List when the object is modified --------------------------------------------------------------------------- - response = context_client_grpc.ListServiceIds(ContextId(**CONTEXT_ID)) + response = context_client.ListServiceIds(ContextId(**CONTEXT_ID)) assert len(response.service_ids) == 1 - assert response.service_ids[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.service_ids[0].service_uuid.uuid == SERVICE_R1_R2_UUID + assert response.service_ids[0].context_id.context_uuid.uuid == context_uuid + assert response.service_ids[0].service_uuid.uuid == service_uuid - response = context_client_grpc.ListServices(ContextId(**CONTEXT_ID)) + response = context_client.ListServices(ContextId(**CONTEXT_ID)) assert len(response.services) == 1 - assert response.services[0].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.services[0].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + assert response.services[0].service_id.context_id.context_uuid.uuid == context_uuid + assert response.services[0].service_id.service_uuid.uuid == service_uuid assert response.services[0].name == new_service_name assert response.services[0].service_type == ServiceTypeEnum.SERVICETYPE_L3NM assert len(response.services[0].service_endpoint_ids) == 2 assert len(response.services[0].service_constraints) == 2 - assert response.services[0].service_status.service_status == ServiceStatusEnum.SERVICESTATUS_PLANNED + assert response.services[0].service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE assert len(response.services[0].service_config.config_rules) == 3 # ----- Remove the object ------------------------------------------------------------------------------------------ - context_client_grpc.RemoveService(ServiceId(**SERVICE_R1_R2_ID)) + context_client.RemoveService(ServiceId(**SERVICE_R1_R2_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- #event = events_collector.get_event(block=True) #assert isinstance(event, ServiceEvent) - #assert event.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - #assert event.service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + #assert event.event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + #assert event.service_id.context_id.context_uuid.uuid == context_uuid + #assert event.service_id.service_uuid.uuid == service_uuid # ----- List after deleting the object ----------------------------------------------------------------------------- - response = context_client_grpc.GetContext(ContextId(**CONTEXT_ID)) + response = context_client.GetContext(ContextId(**CONTEXT_ID)) assert len(response.topology_ids) == 1 assert len(response.service_ids) == 0 assert len(response.slice_ids) == 0 - response = context_client_grpc.ListServiceIds(ContextId(**CONTEXT_ID)) + response = context_client.ListServiceIds(ContextId(**CONTEXT_ID)) assert len(response.service_ids) == 0 - response = context_client_grpc.ListServices(ContextId(**CONTEXT_ID)) + response = context_client.ListServices(ContextId(**CONTEXT_ID)) assert len(response.services) == 0 # ----- Clean dependencies used in the test and capture related events --------------------------------------------- - context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R1_ID)) - context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R2_ID)) - context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID)) - context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) + context_client.RemoveDevice(DeviceId(**DEVICE_R1_ID)) + context_client.RemoveDevice(DeviceId(**DEVICE_R2_ID)) + context_client.RemoveTopology(TopologyId(**TOPOLOGY_ID)) + context_client.RemoveContext(ContextId(**CONTEXT_ID)) #events = events_collector.get_events(block=True, count=4) #assert isinstance(events[0], DeviceEvent) #assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - #assert events[0].device_id.device_uuid.uuid == DEVICE_R1_UUID + #assert events[0].device_id.device_uuid.uuid == device_r1_uuid #assert isinstance(events[1], DeviceEvent) #assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - #assert events[1].device_id.device_uuid.uuid == DEVICE_R2_UUID + #assert events[1].device_id.device_uuid.uuid == device_r2_uuid #assert isinstance(events[2], TopologyEvent) #assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - #assert events[2].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - #assert events[2].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + #assert events[2].topology_id.context_id.context_uuid.uuid == context_uuid + #assert events[2].topology_id.topology_uuid.uuid == topology_uuid #assert isinstance(events[3], ContextEvent) #assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - #assert events[3].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + #assert events[3].context_id.context_uuid.uuid == context_uuid # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- #events_collector.stop() diff --git a/src/context/tests/test_topology.py b/src/context/tests/test_topology.py index 51b224007d7a60f4db6b4f015360e197dea4ec7f..23e73edc83e7f1cb16370c4325f93be277d3298a 100644 --- a/src/context/tests/test_topology.py +++ b/src/context/tests/test_topology.py @@ -31,8 +31,7 @@ def test_topology(context_client : ContextClient) -> None: #events_collector.start() # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- - response = context_client.SetContext(Context(**CONTEXT)) - context_uuid = response.context_uuid.uuid + context_client.SetContext(Context(**CONTEXT)) # event = events_collector.get_event(block=True) # assert isinstance(event, ContextEvent) diff --git a/test-context.sh b/test-context.sh index 79a9d5653621db615030a09e5b362438bfae637e..47d81817bd80bd771fa647449a6c49bcfaee9870 100755 --- a/test-context.sh +++ b/test-context.sh @@ -41,11 +41,12 @@ export PYTHONPATH=/home/tfs/tfs-ctrl/src # Run unitary tests and analyze coverage of code at same time # helpful pytest flags: --log-level=INFO -o log_cli=true --verbose --maxfail=1 --durations=0 coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose --maxfail=1 \ - context/tests/test_hasher.py \ - context/tests/test_context.py \ + context/tests/test_hasher.py \ + context/tests/test_context.py \ context/tests/test_topology.py \ - context/tests/test_device.py \ - context/tests/test_link.py + context/tests/test_device.py \ + context/tests/test_link.py \ + context/tests/test_service.py echo echo "Coverage report:"