diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index 789ee7a78c6bcff3e62a6dd373bd58dbb2e7a960..65d05db904b65a22c202881fdc50bb0496269701 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -162,7 +162,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer return Empty() @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) - def SelectDevices(self, request : DeviceFilter, context : grpc.ServicerContext) -> DeviceList: + def SelectDevice(self, request : DeviceFilter, context : grpc.ServicerContext) -> DeviceList: return DeviceList(devices=device_select(self.db_engine, request)) @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) diff --git a/src/context/service/database/ConfigRule.py b/src/context/service/database/ConfigRule.py index 09723cc6f6b31e2496bf5ab475f50d0aa58f95c2..c5b259a2dfc2ba684f6881dfb2a9a79b3a36032a 100644 --- a/src/context/service/database/ConfigRule.py +++ b/src/context/service/database/ConfigRule.py @@ -21,7 +21,8 @@ from typing import Dict, List, Optional, Set from common.proto.context_pb2 import ConfigRule from common.tools.grpc.Tools import grpc_message_to_json_string from .models.enums.ConfigAction import ORM_ConfigActionEnum, grpc_to_enum__config_action -from .models.ConfigRuleModel import ConfigRuleKindEnum, ConfigRuleModel +from .models.ConfigRuleModel import ( + ConfigRuleKindEnum, DeviceConfigRuleModel, ServiceConfigRuleModel, SliceConfigRuleModel) from .uuids._Builder import get_uuid_from_string from .uuids.EndPoint import endpoint_get_uuid @@ -83,6 +84,16 @@ def upsert_config_rules( session : Session, config_rules : List[Dict], is_delete : bool = False, device_uuid : Optional[str] = None, service_uuid : Optional[str] = None, slice_uuid : Optional[str] = None, ) -> bool: + if device_uuid is not None and service_uuid is None and slice_uuid is None: + klass = DeviceConfigRuleModel + elif device_uuid is None and service_uuid is not None and slice_uuid is None: + klass = ServiceConfigRuleModel + elif device_uuid is None and service_uuid is None and slice_uuid is not None: + klass = SliceConfigRuleModel + else: + MSG = 'DataModel cannot be identified (device_uuid={:s}, service_uuid={:s}, slice_uuid={:s})' + raise Exception(MSG.format(str(device_uuid), str(service_uuid), str(slice_uuid))) + uuids_to_delete : Set[str] = set() uuids_to_upsert : Dict[str, int] = dict() rules_to_upsert : List[Dict] = list() @@ -108,11 +119,11 @@ def upsert_config_rules( delete_affected = False if len(uuids_to_delete) > 0: - 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 ) - stmt = stmt.where(ConfigRuleModel.configrule_uuid.in_(uuids_to_delete)) + stmt = delete(klass) + if device_uuid is not None: stmt = stmt.where(klass.device_uuid == device_uuid ) + if service_uuid is not None: stmt = stmt.where(klass.service_uuid == service_uuid) + if slice_uuid is not None: stmt = stmt.where(klass.slice_uuid == slice_uuid ) + stmt = stmt.where(klass.configrule_uuid.in_(uuids_to_delete)) #str_stmt = stmt.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True}) #LOGGER.warning('delete stmt={:s}'.format(str(str_stmt))) configrule_deletes = session.execute(stmt) @@ -121,9 +132,9 @@ def upsert_config_rules( upsert_affected = False if len(rules_to_upsert) > 0: - stmt = insert(ConfigRuleModel).values(rules_to_upsert) + stmt = insert(klass).values(rules_to_upsert) stmt = stmt.on_conflict_do_update( - index_elements=[ConfigRuleModel.configrule_uuid], + index_elements=[klass.configrule_uuid], set_=dict( position = stmt.excluded.position, action = stmt.excluded.action, @@ -131,7 +142,7 @@ def upsert_config_rules( updated_at = stmt.excluded.updated_at, ) ) - stmt = stmt.returning(ConfigRuleModel.created_at, ConfigRuleModel.updated_at) + stmt = stmt.returning(klass.created_at, klass.updated_at) #str_stmt = stmt.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True}) #LOGGER.warning('upsert stmt={:s}'.format(str(str_stmt))) configrule_updates = session.execute(stmt).fetchall() diff --git a/src/context/service/database/Constraint.py b/src/context/service/database/Constraint.py index 3a73f6589f9332aa4c84f8f296f2cb56db3048bf..592d7f4c545a222092ca95924afafa69d2798d7c 100644 --- a/src/context/service/database/Constraint.py +++ b/src/context/service/database/Constraint.py @@ -20,7 +20,7 @@ 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 .models.ConstraintModel import ConstraintKindEnum, ServiceConstraintModel, SliceConstraintModel from .uuids._Builder import get_uuid_from_string from .uuids.EndPoint import endpoint_get_uuid @@ -84,6 +84,14 @@ def upsert_constraints( session : Session, constraints : List[Dict], is_delete : bool = False, service_uuid : Optional[str] = None, slice_uuid : Optional[str] = None ) -> bool: + if service_uuid is not None and slice_uuid is None: + klass = ServiceConstraintModel + elif service_uuid is None and slice_uuid is not None: + klass = SliceConstraintModel + else: + MSG = 'DataModel cannot be identified (service_uuid={:s}, slice_uuid={:s})' + raise Exception(MSG.format(str(service_uuid), str(slice_uuid))) + uuids_to_upsert : Dict[str, int] = dict() rules_to_upsert : List[Dict] = list() for constraint in constraints: @@ -100,10 +108,10 @@ def upsert_constraints( # Delete all constraints not in uuids_to_upsert delete_affected = False if len(uuids_to_upsert) > 0: - 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 ) - stmt = stmt.where(ConstraintModel.constraint_uuid.not_in(set(uuids_to_upsert.keys()))) + stmt = delete(klass) + if service_uuid is not None: stmt = stmt.where(klass.service_uuid == service_uuid) + if slice_uuid is not None: stmt = stmt.where(klass.slice_uuid == slice_uuid ) + stmt = stmt.where(klass.constraint_uuid.not_in(set(uuids_to_upsert.keys()))) #str_stmt = stmt.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True}) #LOGGER.warning('delete stmt={:s}'.format(str(str_stmt))) constraint_deletes = session.execute(stmt) @@ -112,16 +120,16 @@ def upsert_constraints( upsert_affected = False if not is_delete and len(constraints) > 0: - stmt = insert(ConstraintModel).values(constraints) + stmt = insert(klass).values(constraints) stmt = stmt.on_conflict_do_update( - index_elements=[ConstraintModel.constraint_uuid], + index_elements=[klass.constraint_uuid], set_=dict( position = stmt.excluded.position, data = stmt.excluded.data, updated_at = stmt.excluded.updated_at, ) ) - stmt = stmt.returning(ConstraintModel.created_at, ConstraintModel.updated_at) + stmt = stmt.returning(klass.created_at, klass.updated_at) #str_stmt = stmt.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True}) #LOGGER.warning('upsert stmt={:s}'.format(str(str_stmt))) constraint_updates = session.execute(stmt).fetchall() diff --git a/src/context/service/database/models/ConfigRuleModel.py b/src/context/service/database/models/ConfigRuleModel.py index d7bb97cd0fec1037e98c8713b885b2d5141cae63..5d14b62a83b71d7a146f74e649435ed941dad6d3 100644 --- a/src/context/service/database/models/ConfigRuleModel.py +++ b/src/context/service/database/models/ConfigRuleModel.py @@ -24,13 +24,11 @@ class ConfigRuleKindEnum(enum.Enum): CUSTOM = 'custom' ACL = 'acl' -class ConfigRuleModel(_Base): - __tablename__ = 'configrule' +class DeviceConfigRuleModel(_Base): + __tablename__ = 'device_configrule' configrule_uuid = Column(UUID(as_uuid=False), primary_key=True) - device_uuid = Column(ForeignKey('device.device_uuid', ondelete='CASCADE'), nullable=True, index=True) - service_uuid = Column(ForeignKey('service.service_uuid', ondelete='CASCADE'), nullable=True, index=True) - slice_uuid = Column(ForeignKey('slice.slice_uuid', ondelete='CASCADE'), nullable=True, index=True) + device_uuid = Column(ForeignKey('device.device_uuid', ondelete='CASCADE'), nullable=False) #, index=True position = Column(Integer, nullable=False) kind = Column(Enum(ConfigRuleKindEnum), nullable=False) action = Column(Enum(ORM_ConfigActionEnum), nullable=False) @@ -41,7 +39,51 @@ class ConfigRuleModel(_Base): __table_args__ = ( CheckConstraint(position >= 0, name='check_position_value'), #UniqueConstraint('device_uuid', 'position', name='unique_per_device' ), + ) + + def dump(self) -> Dict: + return { + 'action': self.action.value, + self.kind.value: json.loads(self.data), + } + +class ServiceConfigRuleModel(_Base): + __tablename__ = 'service_configrule' + + configrule_uuid = Column(UUID(as_uuid=False), primary_key=True) + service_uuid = Column(ForeignKey('service.service_uuid', ondelete='CASCADE'), nullable=False) #, index=True + position = Column(Integer, nullable=False) + kind = Column(Enum(ConfigRuleKindEnum), nullable=False) + action = Column(Enum(ORM_ConfigActionEnum), nullable=False) + data = Column(String, nullable=False) + created_at = Column(DateTime, nullable=False) + updated_at = Column(DateTime, nullable=False) + + __table_args__ = ( + CheckConstraint(position >= 0, name='check_position_value'), #UniqueConstraint('service_uuid', 'position', name='unique_per_service'), + ) + + def dump(self) -> Dict: + return { + 'action': self.action.value, + self.kind.value: json.loads(self.data), + } + +class SliceConfigRuleModel(_Base): + __tablename__ = 'slice_configrule' + + configrule_uuid = Column(UUID(as_uuid=False), primary_key=True) + slice_uuid = Column(ForeignKey('slice.slice_uuid', ondelete='CASCADE'), nullable=False) #, index=True + position = Column(Integer, nullable=False) + kind = Column(Enum(ConfigRuleKindEnum), nullable=False) + action = Column(Enum(ORM_ConfigActionEnum), nullable=False) + data = Column(String, nullable=False) + created_at = Column(DateTime, nullable=False) + updated_at = Column(DateTime, nullable=False) + + __table_args__ = ( + CheckConstraint(position >= 0, name='check_position_value'), #UniqueConstraint('slice_uuid', 'position', name='unique_per_slice' ), ) diff --git a/src/context/service/database/models/ConstraintModel.py b/src/context/service/database/models/ConstraintModel.py index 2412080c1a2883e7bed85e6e22f389270b3f73bc..cbbe0b5d7280a6f14d645b66abd4df444abb41aa 100644 --- a/src/context/service/database/models/ConstraintModel.py +++ b/src/context/service/database/models/ConstraintModel.py @@ -31,12 +31,11 @@ class ConstraintKindEnum(enum.Enum): SLA_AVAILABILITY = 'sla_availability' SLA_ISOLATION = 'sla_isolation' -class ConstraintModel(_Base): - __tablename__ = 'constraint' +class ServiceConstraintModel(_Base): + __tablename__ = 'service_constraint' constraint_uuid = Column(UUID(as_uuid=False), primary_key=True) - service_uuid = Column(ForeignKey('service.service_uuid', ondelete='CASCADE'), nullable=True, index=True) - slice_uuid = Column(ForeignKey('slice.slice_uuid', ondelete='CASCADE'), nullable=True, index=True) + service_uuid = Column(ForeignKey('service.service_uuid', ondelete='CASCADE'), nullable=False) #, index=True position = Column(Integer, nullable=False) kind = Column(Enum(ConstraintKindEnum), nullable=False) data = Column(String, nullable=False) @@ -46,6 +45,24 @@ class ConstraintModel(_Base): __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)} + +class SliceConstraintModel(_Base): + __tablename__ = 'slice_constraint' + + constraint_uuid = Column(UUID(as_uuid=False), primary_key=True) + slice_uuid = Column(ForeignKey('slice.slice_uuid', ondelete='CASCADE'), nullable=False) #, index=True + position = Column(Integer, nullable=False) + kind = Column(Enum(ConstraintKindEnum), nullable=False) + data = Column(String, nullable=False) + created_at = Column(DateTime, nullable=False) + updated_at = Column(DateTime, nullable=False) + + __table_args__ = ( + CheckConstraint(position >= 0, name='check_position_value'), #UniqueConstraint('slice_uuid', 'position', name='unique_per_slice' ), ) diff --git a/src/context/service/database/models/DeviceModel.py b/src/context/service/database/models/DeviceModel.py index 24130841d2bafde3608f2fa1cbdd476d28acba46..beb500d601aa725c5c0d3c01633aebf31aa23e5b 100644 --- a/src/context/service/database/models/DeviceModel.py +++ b/src/context/service/database/models/DeviceModel.py @@ -33,7 +33,7 @@ class DeviceModel(_Base): updated_at = Column(DateTime, nullable=False) #topology_devices = relationship('TopologyDeviceModel', back_populates='device') - config_rules = relationship('ConfigRuleModel', passive_deletes=True) # lazy='joined', back_populates='device' + config_rules = relationship('DeviceConfigRuleModel', passive_deletes=True) # lazy='joined', back_populates='device' endpoints = relationship('EndPointModel', passive_deletes=True) # lazy='joined', back_populates='device' def dump_id(self) -> Dict: @@ -48,8 +48,8 @@ class DeviceModel(_Base): for config_rule in sorted(self.config_rules, key=operator.attrgetter('position')) ]} - #def dump_components(self) -> List[Dict]: - # return [] + def dump_components(self) -> List[Dict]: + return [] def dump(self, include_endpoints : bool = True, include_config_rules : bool = True, include_components : bool = True, @@ -63,5 +63,5 @@ class DeviceModel(_Base): } if include_endpoints: result['device_endpoints'] = self.dump_endpoints() if include_config_rules: result['device_config'] = self.dump_config_rules() - #if include_components: result['components'] = self.dump_components() + if include_components: result['component'] = self.dump_components() return result diff --git a/src/context/service/database/models/ServiceModel.py b/src/context/service/database/models/ServiceModel.py index ef6e1b06aaaa616ede6f9633e4e0d7fc0aabf336..2895a7ce9ddfeefd025a9397040a01423c9682a4 100644 --- a/src/context/service/database/models/ServiceModel.py +++ b/src/context/service/database/models/ServiceModel.py @@ -34,8 +34,8 @@ class ServiceModel(_Base): context = relationship('ContextModel', back_populates='services', lazy='selectin') service_endpoints = relationship('ServiceEndPointModel') # lazy='selectin', back_populates='service' - constraints = relationship('ConstraintModel', passive_deletes=True) # lazy='selectin', back_populates='service' - config_rules = relationship('ConfigRuleModel', passive_deletes=True) # lazy='selectin', back_populates='service' + constraints = relationship('ServiceConstraintModel', passive_deletes=True) # lazy='selectin', back_populates='service' + config_rules = relationship('ServiceConfigRuleModel', passive_deletes=True) # lazy='selectin', back_populates='service' def dump_id(self) -> Dict: return { diff --git a/src/context/service/database/models/SliceModel.py b/src/context/service/database/models/SliceModel.py index 423af244e186301cf3132eea3fc7cbea16bf9fe9..d3befa66b4c7ecba4a28fefa745a7c2214f37caf 100644 --- a/src/context/service/database/models/SliceModel.py +++ b/src/context/service/database/models/SliceModel.py @@ -37,8 +37,8 @@ class SliceModel(_Base): slice_services = relationship('SliceServiceModel') # lazy='selectin', back_populates='slice' slice_subslices = relationship( 'SliceSubSliceModel', primaryjoin='slice.c.slice_uuid == slice_subslice.c.slice_uuid') - constraints = relationship('ConstraintModel', passive_deletes=True) # lazy='selectin', back_populates='slice' - config_rules = relationship('ConfigRuleModel', passive_deletes=True) # lazy='selectin', back_populates='slice' + constraints = relationship('SliceConstraintModel', passive_deletes=True) # lazy='selectin', back_populates='slice' + config_rules = relationship('SliceConfigRuleModel', passive_deletes=True) # lazy='selectin', back_populates='slice' def dump_id(self) -> Dict: return { @@ -46,7 +46,6 @@ class SliceModel(_Base): 'slice_uuid': {'uuid': self.slice_uuid}, } - def dump_endpoint_ids(self) -> List[Dict]: return [ slice_endpoint.endpoint.dump_id() diff --git a/src/context/service/database/models/_Base.py b/src/context/service/database/models/_Base.py index a10de60eb8731132ec815de1ff897c06ac12b665..b87b9b06d6adc5825ab5dd84cf64347eb9c26f66 100644 --- a/src/context/service/database/models/_Base.py +++ b/src/context/service/database/models/_Base.py @@ -30,23 +30,23 @@ def create_performance_enhancers(db_engine : sqlalchemy.engine.Engine) -> None: return text(INDEX_STORING.format(index_name, table_name, str_index_fields, str_storing_fields)) statements = [ - index_storing('configrule_device_uuid_rec_idx', 'configrule', ['device_uuid'], [ - 'service_uuid', 'slice_uuid', 'position', 'kind', 'action', 'data', 'created_at', 'updated_at' + index_storing('device_configrule_device_uuid_rec_idx', 'device_configrule', ['device_uuid'], [ + 'position', 'kind', 'action', 'data', 'created_at', 'updated_at' ]), - index_storing('configrule_service_uuid_rec_idx', 'configrule', ['service_uuid'], [ - 'device_uuid', 'slice_uuid', 'position', 'kind', 'action', 'data', 'created_at', 'updated_at' + index_storing('service_configrule_service_uuid_rec_idx', 'service_configrule', ['service_uuid'], [ + 'position', 'kind', 'action', 'data', 'created_at', 'updated_at' ]), - index_storing('configrule_slice_uuid_rec_idx', 'configrule', ['slice_uuid'], [ - 'device_uuid', 'service_uuid', 'position', 'kind', 'action', 'data', 'created_at', 'updated_at' + index_storing('slice_configrule_slice_uuid_rec_idx', 'slice_configrule', ['slice_uuid'], [ + 'position', 'kind', 'action', 'data', 'created_at', 'updated_at' ]), index_storing('connection_service_uuid_rec_idx', 'connection', ['service_uuid'], [ 'settings', 'created_at', 'updated_at' ]), - index_storing('constraint_service_uuid_rec_idx', 'constraint', ['service_uuid'], [ - 'slice_uuid', 'position', 'kind', 'data', 'created_at', 'updated_at' + index_storing('service_constraint_service_uuid_rec_idx', 'service_constraint', ['service_uuid'], [ + 'position', 'kind', 'data', 'created_at', 'updated_at' ]), - index_storing('constraint_slice_uuid_rec_idx', 'constraint', ['slice_uuid'], [ - 'service_uuid', 'position', 'kind', 'data', 'created_at', 'updated_at' + index_storing('slice_constraint_slice_uuid_rec_idx', 'slice_constraint', ['slice_uuid'], [ + 'position', 'kind', 'data', 'created_at', 'updated_at' ]), index_storing('endpoint_device_uuid_rec_idx', 'endpoint', ['device_uuid'], [ 'topology_uuid', 'name', 'endpoint_type', 'kpi_sample_types', 'created_at', 'updated_at' @@ -57,7 +57,6 @@ def create_performance_enhancers(db_engine : sqlalchemy.engine.Engine) -> None: index_storing('slice_context_uuid_rec_idx', 'slice', ['context_uuid'], [ 'slice_name', 'slice_status', 'slice_owner_uuid', 'slice_owner_string', 'created_at', 'updated_at' ]), - index_storing('topology_context_uuid_rec_idx', 'topology', ['context_uuid'], [ 'topology_name', 'created_at', 'updated_at' ]),