Commit 89fa7f98 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Context component:

- corrected ConfigRuleModel and methods
- corrected ConstraintModel and methods
- corrected ServiceModel and methods
- corrected ServiceEndPointModel
- added missing non-null constraints
- removed redundant column definition data
- removed unneeded lazy loading parameters
- added Service UUID generator
- implemented unitary test for Service entity
parent c48a5577
Loading
Loading
Loading
Loading
+22 −22
Original line number Diff line number Diff line
@@ -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]:
+185 −0
Original line number Diff line number Diff line
# 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
+110 −0
Original line number Diff line number Diff line
# 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
+4 −157

File changed.

Preview size limit exceeded, changes collapsed.

+0 −4
Original line number Diff line number Diff line
@@ -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)
Loading