Skip to content
Snippets Groups Projects
ConfigRule.py 7.01 KiB
Newer Older
  • Learn to ignore specific revisions
  • Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    # Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    #
    # 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.
    
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    import datetime, json, logging
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    from sqlalchemy import delete
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    #from sqlalchemy.dialects import postgresql
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    from sqlalchemy.dialects.postgresql import insert
    from sqlalchemy.orm import Session
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    from typing import Dict, List, Optional, Set
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    from common.proto.context_pb2 import ConfigRule
    from common.tools.grpc.Tools import grpc_message_to_json_string
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    from .models.enums.ConfigAction import ORM_ConfigActionEnum, grpc_to_enum__config_action
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    from .models.ConfigRuleModel import ConfigRuleKindEnum, ConfigRuleModel
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    from .uuids._Builder import get_uuid_from_string
    from .uuids.EndPoint import endpoint_get_uuid
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    LOGGER = logging.getLogger(__name__)
    
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    def compose_config_rules_data(
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
        config_rules : List[ConfigRule], now : datetime.datetime,
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
        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):
            str_kind = config_rule.WhichOneof('config_rule')
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            kind = ConfigRuleKindEnum._member_map_.get(str_kind.upper()) # pylint: disable=no-member
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            dict_config_rule = {
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
                'position'  : position,
                'kind'      : kind,
                'action'    : grpc_to_enum__config_action(config_rule.action),
                'data'      : grpc_message_to_json_string(getattr(config_rule, str_kind, {})),
                'created_at': now,
                'updated_at': now,
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            }
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            parent_kind,parent_uuid = '',None
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            if device_uuid is not None:
                dict_config_rule['device_uuid'] = device_uuid
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
                parent_kind,parent_uuid = 'device',device_uuid
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            elif service_uuid is not None:
                dict_config_rule['service_uuid'] = service_uuid
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
                parent_kind,parent_uuid = 'service',service_uuid
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            elif slice_uuid is not None:
                dict_config_rule['slice_uuid'] = slice_uuid
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
                parent_kind,parent_uuid = 'slice',slice_uuid
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            else:
                MSG = 'Parent for ConfigRule({:s}) cannot be identified '+\
                      '(device_uuid={:s}, service_uuid={:s}, slice_uuid={:s})'
                str_config_rule = grpc_message_to_json_string(config_rule)
                raise Exception(MSG.format(str_config_rule, str(device_uuid), str(service_uuid), str(slice_uuid)))
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            configrule_name = None
            if kind == ConfigRuleKindEnum.CUSTOM:
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
                configrule_name = '{:s}:{:s}:{:s}'.format(parent_kind, kind.value, config_rule.custom.resource_key)
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            elif kind == ConfigRuleKindEnum.ACL:
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
                _, _, endpoint_uuid = endpoint_get_uuid(config_rule.acl.endpoint_id, allow_random=False)
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
                rule_set_name = config_rule.acl.rule_set.name
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
                configrule_name = '{:s}:{:s}:{:s}:{:s}'.format(parent_kind, kind.value, endpoint_uuid, rule_set_name)
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            else:
                MSG = 'Name for ConfigRule({:s}) cannot be inferred '+\
                      '(device_uuid={:s}, service_uuid={:s}, slice_uuid={:s})'
                str_config_rule = grpc_message_to_json_string(config_rule)
                raise Exception(MSG.format(str_config_rule, str(device_uuid), str(service_uuid), str(slice_uuid)))
    
            configrule_uuid = get_uuid_from_string(configrule_name, prefix_for_name=parent_uuid)
            dict_config_rule['configrule_uuid'] = configrule_uuid
    
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            dict_config_rules.append(dict_config_rule)
        return dict_config_rules
    
    def upsert_config_rules(
        session : Session, config_rules : List[Dict],
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
        device_uuid : Optional[str] = None, service_uuid : Optional[str] = None, slice_uuid : Optional[str] = None,
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    ) -> bool:
        uuids_to_delete : Set[str] = set()
        uuids_to_upsert : Dict[str, int] = dict()
        rules_to_upsert : List[Dict] = list()
        for config_rule in config_rules:
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            configrule_uuid = config_rule['configrule_uuid']
            configrule_action = config_rule['action']
            if configrule_action == ORM_ConfigActionEnum.SET:
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
                position = uuids_to_upsert.get(configrule_uuid)
                if position is None:
                    # if not added, add it
                    rules_to_upsert.append(config_rule)
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
                    uuids_to_upsert[configrule_uuid] = len(rules_to_upsert) - 1
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
                else:
                    # if already added, update occurrence
                    rules_to_upsert[position] = config_rule
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            elif configrule_action == ORM_ConfigActionEnum.DELETE:
                uuids_to_delete.add(configrule_uuid)
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            else:
                MSG = 'Action for ConfigRule({:s}) is not supported '+\
                      '(device_uuid={:s}, service_uuid={:s}, slice_uuid={:s})'
                str_config_rule = json.dumps(config_rule)
                raise Exception(MSG.format(str_config_rule, str(device_uuid), str(service_uuid), str(slice_uuid)))
    
        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))
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            #str_stmt = stmt.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True})
            #LOGGER.warning('delete stmt={:s}'.format(str(str_stmt)))
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            configrule_deletes = session.execute(stmt)
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            #LOGGER.warning('configrule_deletes.rowcount={:s}'.format(str(configrule_deletes.rowcount)))
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            delete_affected = int(configrule_deletes.rowcount) > 0
    
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
        upsert_affected = False
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
        if len(rules_to_upsert) > 0:
            stmt = insert(ConfigRuleModel).values(rules_to_upsert)
            stmt = stmt.on_conflict_do_update(
                index_elements=[ConfigRuleModel.configrule_uuid],
                set_=dict(
                    position   = stmt.excluded.position,
                    action     = stmt.excluded.action,
                    data       = stmt.excluded.data,
                    updated_at = stmt.excluded.updated_at,
                )
            )
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            stmt = stmt.returning(ConfigRuleModel.created_at, ConfigRuleModel.updated_at)
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            #str_stmt = stmt.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True})
            #LOGGER.warning('upsert stmt={:s}'.format(str(str_stmt)))
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            configrule_updates = session.execute(stmt).fetchall()
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
            upsert_affected = any([(updated_at > created_at) for created_at,updated_at in configrule_updates])
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
        return delete_affected or upsert_affected