Commit 568149a1 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Service component:

- Removed deprecated Database Model within Service
- Corrected constructor signature of ServiceHandler interface
- Removed unneeded file in L3NMEmulated Service Handler
- Removed unused imports from TAPI Service Handler
parent 92dfd5b4
Loading
Loading
Loading
Loading
+0 −122
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.

import functools, logging, operator
from enum import Enum
from typing import Dict, List, Tuple, Union
from common.orm.Database import Database
from common.orm.HighLevel import get_object, get_or_create_object, update_or_create_object
from common.orm.backend.Tools import key_to_str
from common.orm.fields.EnumeratedField import EnumeratedField
from common.orm.fields.ForeignKeyField import ForeignKeyField
from common.orm.fields.IntegerField import IntegerField
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.fields.StringField import StringField
from common.orm.model.Model import Model
from common.proto.context_pb2 import ConfigActionEnum
from common.tools.grpc.Tools import grpc_message_to_json_string
from .Tools import fast_hasher, grpc_to_enum, remove_dict_key

LOGGER = logging.getLogger(__name__)

class ORM_ConfigActionEnum(Enum):
    UNDEFINED = ConfigActionEnum.CONFIGACTION_UNDEFINED
    SET       = ConfigActionEnum.CONFIGACTION_SET
    DELETE    = ConfigActionEnum.CONFIGACTION_DELETE

grpc_to_enum__config_action = functools.partial(
    grpc_to_enum, ConfigActionEnum, ORM_ConfigActionEnum)

class ConfigModel(Model): # pylint: disable=abstract-method
    pk = PrimaryKeyField()

    def dump(self) -> List[Dict]:
        db_config_rule_pks = self.references(ConfigRuleModel)
        config_rules = [ConfigRuleModel(self.database, pk).dump(include_position=True) for pk,_ in db_config_rule_pks]
        config_rules = sorted(config_rules, key=operator.itemgetter('position'))
        return [remove_dict_key(config_rule, 'position') for config_rule in config_rules]

class ConfigRuleModel(Model): # pylint: disable=abstract-method
    pk = PrimaryKeyField()
    config_fk = ForeignKeyField(ConfigModel)
    position = IntegerField(min_value=0, required=True)
    action = EnumeratedField(ORM_ConfigActionEnum, required=True)
    key = StringField(required=True, allow_empty=False)
    value = StringField(required=False, allow_empty=True)

    def dump(self, include_position=True) -> Dict: # pylint: disable=arguments-differ
        result = {
            'action': self.action.value,
            'custom': {
                'resource_key': self.key,
                'resource_value': self.value,
            },
        }
        if include_position: result['position'] = self.position
        return result

def delete_all_config_rules(database : Database, db_parent_pk : str, config_name : str) -> None:
    str_config_key = key_to_str([db_parent_pk, config_name], separator=':')
    db_config : ConfigModel = get_object(database, ConfigModel, str_config_key, raise_if_not_found=False)
    if db_config is None: return
    db_config_rule_pks = db_config.references(ConfigRuleModel)
    for pk,_ in db_config_rule_pks: ConfigRuleModel(database, pk).delete()

def grpc_config_rules_to_raw(grpc_config_rules) -> List[Tuple[ORM_ConfigActionEnum, str, str]]:
    def translate(grpc_config_rule):
        action = grpc_to_enum__config_action(grpc_config_rule.action)
        config_rule_type = str(grpc_config_rule.WhichOneof('config_rule'))
        if config_rule_type != 'custom':
            raise NotImplementedError('ConfigRule of type {:s} is not implemented: {:s}'.format(
                config_rule_type, grpc_message_to_json_string(grpc_config_rule)))
        return action, grpc_config_rule.custom.resource_key, grpc_config_rule.custom.resource_value
    return [translate(grpc_config_rule) for grpc_config_rule in grpc_config_rules]

def get_config_rules(
    database : Database, db_parent_pk : str, config_name : str
    ) -> List[Tuple[ORM_ConfigActionEnum, str, str]]:

    str_config_key = key_to_str([db_parent_pk, config_name], separator=':')
    db_config = get_object(database, ConfigModel, str_config_key, raise_if_not_found=False)
    return [] if db_config is None else [
        # pylint: disable=no-member, protected-access
        (ORM_ConfigActionEnum._value2member_map_.get(config_rule['action']),
            config_rule['custom']['resource_key'], config_rule['custom']['resource_value'])
        for config_rule in db_config.dump()
        if 'custom' in config_rule
    ]

def update_config(
    database : Database, db_parent_pk : str, config_name : str,
    raw_config_rules : List[Tuple[ORM_ConfigActionEnum, str, str]]
) -> List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]]:

    str_config_key = key_to_str([db_parent_pk, config_name], separator=':')
    result : Tuple[ConfigModel, bool] = get_or_create_object(database, ConfigModel, str_config_key)
    db_config, created = result

    db_objects : List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]] = [(db_config, created)]

    for position,(action, resource_key, resource_value) in enumerate(raw_config_rules):
        str_rule_key_hash = fast_hasher(resource_key)
        str_config_rule_key = key_to_str([db_config.pk, str_rule_key_hash], separator=':')
        result : Tuple[ConfigRuleModel, bool] = update_or_create_object(
            database, ConfigRuleModel, str_config_rule_key, {
                'config_fk': db_config, 'position': position, 'action': action, 'key': resource_key,
                'value': resource_value,
            })
        db_config_rule, updated = result
        db_objects.append((db_config_rule, updated))

    return db_objects
+0 −100
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.

import logging, operator
from typing import Dict, List, Tuple, Union
from common.orm.Database import Database
from common.orm.HighLevel import get_object, get_or_create_object, update_or_create_object
from common.orm.backend.Tools import key_to_str
from common.orm.fields.ForeignKeyField import ForeignKeyField
from common.orm.fields.IntegerField import IntegerField
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.fields.StringField import StringField
from common.orm.model.Model import Model
from service.service.database.Tools import fast_hasher, remove_dict_key

LOGGER = logging.getLogger(__name__)

class ConstraintsModel(Model): # pylint: disable=abstract-method
    pk = PrimaryKeyField()

    def dump(self) -> List[Dict]:
        db_constraint_pks = self.references(ConstraintModel)
        constraints = [ConstraintModel(self.database, pk).dump(include_position=True) for pk,_ in db_constraint_pks]
        constraints = sorted(constraints, key=operator.itemgetter('position'))
        return [remove_dict_key(constraint, 'position') for constraint in constraints]

class ConstraintModel(Model): # pylint: disable=abstract-method
    pk = PrimaryKeyField()
    constraints_fk = ForeignKeyField(ConstraintsModel)
    position = IntegerField(min_value=0, required=True)
    constraint_type = StringField(required=True, allow_empty=False)
    constraint_value = StringField(required=True, allow_empty=False)

    def dump(self, include_position=True) -> Dict: # pylint: disable=arguments-differ
        result = {
            'custom': {
                'constraint_type': self.constraint_type,
                'constraint_value': self.constraint_value,
            },
        }
        if include_position: result['position'] = self.position
        return result

def delete_all_constraints(database : Database, db_parent_pk : str, constraints_name : str) -> None:
    str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':')
    db_constraints : ConstraintsModel = get_object(
        database, ConstraintsModel, str_constraints_key, raise_if_not_found=False)
    if db_constraints is None: return
    db_constraint_pks = db_constraints.references(ConstraintModel)
    for pk,_ in db_constraint_pks: ConstraintModel(database, pk).delete()

def grpc_constraints_to_raw(grpc_constraints) -> List[Tuple[str, str]]:
    return [
        (grpc_constraint.custom.constraint_type, grpc_constraint.custom.constraint_value)
        for grpc_constraint in grpc_constraints
        if grpc_constraint.WhichOneof('constraint') == 'custom'
    ]

def get_constraints(database : Database, db_parent_pk : str, constraints_name : str) -> List[Tuple[str, str]]:
    str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':')
    db_constraints : ConstraintsModel = get_object(
        database, ConstraintsModel, str_constraints_key, raise_if_not_found=False)
    return [] if db_constraints is None else [
        (constraint['custom']['constraint_type'], constraint['custom']['constraint_value'])
        for constraint in db_constraints.dump()
        if 'custom' in constraint
    ]

def update_constraints(
    database : Database, db_parent_pk : str, constraints_name : str, raw_constraints : List[Tuple[str, str]]
) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]:

    str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':')
    result : Tuple[ConstraintsModel, bool] = get_or_create_object(database, ConstraintsModel, str_constraints_key)
    db_constraints, created = result

    db_objects : List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]] = [(db_constraints, created)]

    for position,(constraint_type, constraint_value) in enumerate(raw_constraints):
        str_constraint_key_hash = fast_hasher(constraint_type)
        str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':')
        result : Tuple[ConstraintModel, bool] = update_or_create_object(
            database, ConstraintModel, str_constraint_key, {
                'constraints_fk': db_constraints, 'position': position, 'constraint_type': constraint_type,
                'constraint_value': constraint_value})
        db_constraints_rule, updated = result
        db_objects.append((db_constraints_rule, updated))

    return db_objects
+0 −44
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.

import logging
from typing import Dict #, List
from common.orm.fields.PrimaryKeyField import PrimaryKeyField
from common.orm.fields.StringField import StringField
from common.orm.model.Model import Model

LOGGER = logging.getLogger(__name__)

class ContextModel(Model):
    pk = PrimaryKeyField()
    context_uuid = StringField(required=True, allow_empty=False)

    def dump_id(self) -> Dict:
        return {'context_uuid': {'uuid': self.context_uuid}}

#    def dump_service_ids(self) -> List[Dict]:
#        from .ServiceModel import ServiceModel # pylint: disable=import-outside-toplevel
#        db_service_pks = self.references(ServiceModel)
#        return [ServiceModel(self.database, pk).dump_id() for pk,_ in db_service_pks]
#
#    def dump_topology_ids(self) -> List[Dict]:
#        from .TopologyModel import TopologyModel # pylint: disable=import-outside-toplevel
#        db_topology_pks = self.references(TopologyModel)
#        return [TopologyModel(self.database, pk).dump_id() for pk,_ in db_topology_pks]
#
#    def dump(self, include_services=True, include_topologies=True) -> Dict: # pylint: disable=arguments-differ
#        result = {'context_id': self.dump_id()}
#        if include_services: result['service_ids'] = self.dump_service_ids()
#        if include_topologies: result['topology_ids'] = self.dump_topology_ids()
#        return result
+0 −101
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.

import grpc
from typing import Tuple
from common.orm.Database import Database
from common.orm.HighLevel import get_or_create_object, update_or_create_object
from common.orm.backend.Tools import key_to_str
from common.proto.context_pb2 import Device, DeviceId
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException
from context.client.ContextClient import ContextClient
from .ConfigModel import delete_all_config_rules, grpc_config_rules_to_raw, update_config
from .ContextModel import ContextModel
from .DeviceModel import DeviceModel, grpc_to_enum__device_operational_status, set_drivers
from .EndPointModel import EndPointModel
from .TopologyModel import TopologyModel

def update_device_in_local_database(database : Database, device : Device) -> Tuple[DeviceModel, bool]:
    device_uuid = device.device_id.device_uuid.uuid

    for i,endpoint in enumerate(device.device_endpoints):
        endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid
        if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid
        if device_uuid != endpoint_device_uuid:
            raise InvalidArgumentException(
                'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid,
                ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)])

    config_rules = grpc_config_rules_to_raw(device.device_config.config_rules)
    delete_all_config_rules(database, device_uuid, 'running')
    running_config_result = update_config(database, device_uuid, 'running', config_rules)

    result : Tuple[DeviceModel, bool] = update_or_create_object(database, DeviceModel, device_uuid, {
        'device_uuid'              : device_uuid,
        'device_type'              : device.device_type,
        'device_operational_status': grpc_to_enum__device_operational_status(device.device_operational_status),
        'device_config_fk'         : running_config_result[0][0],
    })
    db_device, updated = result
    set_drivers(database, db_device, device.device_drivers)

    for i,endpoint in enumerate(device.device_endpoints):
        endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid
        endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid
        if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid

        str_endpoint_key = key_to_str([device_uuid, endpoint_uuid])
        endpoint_attributes = {
            'device_fk'    : db_device,
            'endpoint_uuid': endpoint_uuid,
            'endpoint_type': endpoint.endpoint_type,
        }

        endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid
        endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid
        if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
            result : Tuple[ContextModel, bool] = get_or_create_object(
                database, ContextModel, endpoint_topology_context_uuid, defaults={
                    'context_uuid': endpoint_topology_context_uuid,
                })
            db_context, _ = result

            str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
            result : Tuple[TopologyModel, bool] = get_or_create_object(
                database, TopologyModel, str_topology_key, defaults={
                    'context_fk': db_context,
                    'topology_uuid': endpoint_topology_uuid,
                })
            db_topology, _ = result

            str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
            endpoint_attributes['topology_fk'] = db_topology

        result : Tuple[EndPointModel, bool] = update_or_create_object(
            database, EndPointModel, str_endpoint_key, endpoint_attributes)
        _, db_endpoint_updated = result
        updated = updated or db_endpoint_updated

    return db_device, updated

def sync_device_from_context(
    device_uuid : str, context_client : ContextClient, database : Database
    ) -> Tuple[DeviceModel, bool]:

    try:
        device : Device = context_client.GetDevice(DeviceId(device_uuid={'uuid': device_uuid}))
    except grpc.RpcError as e:
        if e.code() != grpc.StatusCode.NOT_FOUND: raise # pylint: disable=no-member
        return None
    return update_device_in_local_database(database, device)
+0 −144

File deleted.

Preview size limit exceeded, changes collapsed.

Loading