diff --git a/src/service/service/service_handlers/l3nm_ietfl3vpn/L3NM_IETFL3VPN_ServiceHandler.py b/src/service/service/service_handlers/l3nm_ietfl3vpn/L3NM_IETFL3VPN_ServiceHandler.py index 5f18e4657681be0c050733e169a4084048a10525..4b4e9add563de81c6228b74a9494cb86326c9276 100644 --- a/src/service/service/service_handlers/l3nm_ietfl3vpn/L3NM_IETFL3VPN_ServiceHandler.py +++ b/src/service/service/service_handlers/l3nm_ietfl3vpn/L3NM_IETFL3VPN_ServiceHandler.py @@ -14,14 +14,20 @@ import json import logging -from typing import Any, List, Optional, Tuple, Union +from typing import Any, List, Optional, Tuple, TypedDict, Union +from deepdiff import DeepDiff + +from common.DeviceTypes import DeviceTypeEnum from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method -from common.proto.context_pb2 import ConfigRule, DeviceId, Service -from common.tools.object_factory.ConfigRule import ( - json_config_rule_delete, - json_config_rule_set, +from common.proto.context_pb2 import ( + ConfigRule, + Device, + DeviceId, + Service, + ServiceConfig, ) +from common.tools.object_factory.ConfigRule import json_config_rule_delete from common.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_type from service.service.service_handler_api._ServiceHandler import _ServiceHandler @@ -32,11 +38,81 @@ from service.service.service_handler_api.Tools import ( ) from service.service.task_scheduler.TaskExecutor import TaskExecutor +from .ConfigRules import setup_config_rules + +RUNNING_RESOURCE_KEY = "running_ietf_slice" +CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice" +MTU = 1500 + LOGGER = logging.getLogger(__name__) METRICS_POOL = MetricsPool("Service", "Handler", labels={"handler": "l3nm_ietf_l3vpn"}) +class LANPrefixesDict(TypedDict): + lan: str + lan_tag: str + + +class Ipv4Info(TypedDict): + src_ip: str + dst_ip: str + src_port: str + dst_port: str + vlan: str + + +def get_custom_config_rule( + service_config: ServiceConfig, resource_key: str +) -> Optional[ConfigRule]: + for cr in service_config.config_rules: + if ( + cr.WhichOneof("config_rule") == "custom" + and cr.custom.resource_key == resource_key + ): + return cr + + +def get_running_candidate_ietf_slice_data_diff(service_config: ServiceConfig) -> dict: + running_ietf_slice_cr = get_custom_config_rule(service_config, RUNNING_RESOURCE_KEY) + running_resource_value_dict = json.loads( + running_ietf_slice_cr.custom.resource_value + ) + candidate_ietf_slice_cr = get_custom_config_rule( + service_config, CANDIDATE_RESOURCE_KEY + ) + candidate_resource_value_dict = json.loads( + candidate_ietf_slice_cr.custom.resource_value + ) + return DeepDiff( + running_resource_value_dict, + candidate_resource_value_dict, + ) + + +def extract_match_criterion_ipv4_info( + match_criterion: dict, +) -> Ipv4Info: + for type_value in match_criterion["match-type"]: + if type_value["type"] == "ietf-network-slice-service:source-ip-prefix": + src_ip = type_value["value"][0].split("/")[0] + elif type_value["type"] == "ietf-network-slice-service:destination-ip-prefix": + dst_ip = type_value["value"][0].split("/")[0] + elif type_value["type"] == "ietf-network-slice-service:source-tcp-port": + src_port = type_value["value"][0] + elif type_value["type"] == "ietf-network-slice-service:destination-tcp-port": + dst_port = type_value["value"][0] + elif type_value["type"] == "ietf-network-slice-service:vlan": + vlan = type_value["value"][0] + return Ipv4Info( + src_ip=src_ip, + dst_ip=dst_ip, + src_port=src_port, + dst_port=dst_port, + vlan=vlan, + ) + + class L3NM_IETFL3VPN_ServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service: Service, task_executor: TaskExecutor, **settings @@ -45,6 +121,43 @@ class L3NM_IETFL3VPN_ServiceHandler(_ServiceHandler): self.__task_executor = task_executor self.__settings_handler = SettingsHandler(service.service_config, **settings) + def __find_IP_transport_edge_endpoints( + self, endpoints + ) -> Tuple[str, str, str, str, Device]: + for ep in endpoints: + device_uuid, endpoint_uuid = get_device_endpoint_uuids(ep) + device_obj = self.__task_executor.get_device( + DeviceId(**json_device_id(device_uuid)) + ) + device_controller = self.__task_executor.get_device_controller(device_obj) + if device_controller.device_type == DeviceTypeEnum.IP_SDN_CONTROLLER: + src_device_uuid, src_endpoint_uuid = device_uuid, endpoint_uuid + src_device_controller = device_controller + break + else: + raise Exception("No IP transport edge endpoints found") + for ep in endpoints[::-1]: + device_uuid, endpoint_uuid = get_device_endpoint_uuids(ep) + device_obj = self.__task_executor.get_device( + DeviceId(**json_device_id(device_uuid)) + ) + device_controller = self.__task_executor.get_device_controller(device_obj) + if device_controller.device_type == DeviceTypeEnum.IP_SDN_CONTROLLER: + dst_device_uuid, dst_endpoint_uuid = device_uuid, endpoint_uuid + dst_device_controller = device_controller + break + else: + raise Exception("No IP transport edge endpoints found") + if src_device_controller != dst_device_controller: + raise Exception("Different Src-Dst devices not supported by now") + return ( + src_device_uuid, + src_endpoint_uuid, + dst_device_uuid, + dst_endpoint_uuid, + src_device_controller, + ) + @metered_subclass_method(METRICS_POOL) def SetEndpoint( self, @@ -54,79 +167,136 @@ class L3NM_IETFL3VPN_ServiceHandler(_ServiceHandler): chk_type("endpoints", endpoints, list) if len(endpoints) < 2: return [] - - service_uuid = self.__service.service_id.service_uuid.uuid - results = [] + service_uuid = self.__service.service_id.service_uuid.uuid + service_config = self.__service.service_config try: - src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0]) - + ( + src_device_uuid, + src_endpoint_uuid, + dst_device_uuid, + dst_endpoint_uuid, + controller, + ) = self.__find_IP_transport_edge_endpoints(endpoints) + service_uuid = self.__service.service_id.service_uuid.uuid + service_config = self.__service.service_config src_device_obj = self.__task_executor.get_device( DeviceId(**json_device_id(src_device_uuid)) ) src_endpoint_obj = get_endpoint_matching(src_device_obj, src_endpoint_uuid) - for cr in src_device_obj.device_config.config_rules: - if cr.WhichOneof('config_rule') == 'custom' and cr.custom.resource_key == f'/endpoints/endpoint[{src_endpoint_obj.name}]': - src_endpoint_settings = json.loads(cr.custom.resource_value) - break - # src_endpoint_settings = self.__settings_handler.get_endpoint_settings( - # src_device_obj, src_endpoint_obj - # ).value - - src_controller = self.__task_executor.get_device_controller(src_device_obj) - if src_controller is None: src_controller = src_device_obj - - dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids( - endpoints[-1] - ) + src_device_name = src_device_obj.name dst_device_obj = self.__task_executor.get_device( DeviceId(**json_device_id(dst_device_uuid)) ) dst_endpoint_obj = get_endpoint_matching(dst_device_obj, dst_endpoint_uuid) - + dst_device_name = dst_device_obj.name + for cr in src_device_obj.device_config.config_rules: + if ( + cr.WhichOneof("config_rule") == "custom" + and cr.custom.resource_key + == f"/endpoints/endpoint[{src_endpoint_obj.name}]" + ): + src_endpoint_settings = json.loads(cr.custom.resource_value) + break + else: + raise Exception("Endpoint settings not found") for cr in dst_device_obj.device_config.config_rules: - if cr.WhichOneof('config_rule') == 'custom' and cr.custom.resource_key == f'/endpoints/endpoint[{dst_endpoint_obj.name}]': + if ( + cr.WhichOneof("config_rule") == "custom" + and cr.custom.resource_key + == f"/endpoints/endpoint[{dst_endpoint_obj.name}]" + ): dst_endpoint_settings = json.loads(cr.custom.resource_value) break - # dst_endpoint_settings = self.__settings_handler.get_endpoint_settings( - # dst_device_obj, dst_endpoint_obj - # ).value - dst_controller = self.__task_executor.get_device_controller(dst_device_obj) - if dst_controller is None: dst_controller = dst_device_obj - - if src_controller.device_id.device_uuid.uuid != dst_controller.device_id.device_uuid.uuid: - raise Exception('Different Src-Dst devices not supported by now') - controller = src_controller - - - json_config_rule = json_config_rule_set( - "/services/service[{:s}]".format(service_uuid), - { - "uuid": service_uuid, - "src_device_name": src_device_obj.name, - "src_endpoint_name": src_endpoint_obj.name, - "src_site_location": src_endpoint_settings["site_location"], - "src_ipv4_lan_prefixes": src_endpoint_settings["ipv4_lan_prefixes"], - "src_ce_address": src_endpoint_settings["ce-ip"], - "src_pe_address": src_endpoint_settings["address_ip"], - "src_ce_pe_network_prefix": src_endpoint_settings["address_prefix"], - "src_mtu": src_endpoint_settings["mtu"], - "dst_device_name": dst_device_obj.name, - "dst_endpoint_name": dst_endpoint_obj.name, - "dst_site_location": dst_endpoint_settings["site_location"], - "dst_ipv4_lan_prefixes": dst_endpoint_settings["ipv4_lan_prefixes"], - "dst_ce_address": dst_endpoint_settings["ce-ip"], - "dst_pe_address": dst_endpoint_settings["address_ip"], - "dst_ce_pe_network_prefix": dst_endpoint_settings["address_prefix"], - "dst_mtu": dst_endpoint_settings["mtu"], - }, + else: + raise Exception("Endpoint settings not found") + ietf_slice_running_cr = get_custom_config_rule( + service_config, RUNNING_RESOURCE_KEY ) - del controller.device_config.config_rules[:] - controller.device_config.config_rules.append( - ConfigRule(**json_config_rule) + ietf_slice_candidate_cr = get_custom_config_rule( + service_config, CANDIDATE_RESOURCE_KEY ) + if ( + ietf_slice_running_cr and ietf_slice_candidate_cr + ): # The request comes from the IETF Slice NBI + running_resource_value_dict = json.loads( + ietf_slice_running_cr.custom.resource_value + ) + candidate_resource_value_dict = json.loads( + ietf_slice_candidate_cr.custom.resource_value + ) + running_candidate_diff = get_running_candidate_ietf_slice_data_diff( + service_config + ) + slice_services = candidate_resource_value_dict[ + "network-slice-services" + ]["slice-service"] + slice_service = slice_services[0] + sdps = slice_service["sdps"]["sdp"] + connection_groups = slice_service["connection-groups"][ + "connection-group" + ] + connection_group = connection_groups[0] + connecitivity_constructs = connection_group["connectivity-construct"] + connecitivity_construct = connecitivity_constructs[0] + src_sdp_idx = connecitivity_construct["p2p-sender-sdp"] + dst_sdp_idx = connecitivity_construct["p2p-receiver-sdp"] + src_sdp = next(sdp for sdp in sdps if sdp["id"] == src_sdp_idx) + dst_sdp = next(sdp for sdp in sdps if sdp["id"] == dst_sdp_idx) + src_match_criterion = src_sdp["service-match-criteria"][ + "match-criterion" + ][0] + dst_match_criterion = dst_sdp["service-match-criteria"][ + "match-criterion" + ][0] + src_match_criterion_ipv4_info = extract_match_criterion_ipv4_info( + src_match_criterion + ) + dst_match_criterion_ipv4_info = extract_match_criterion_ipv4_info( + dst_match_criterion + ) + src_ipv4_lan_prefixes = [ + LANPrefixesDict( + lan=src_match_criterion_ipv4_info["dst_ip"], + lan_tag=src_match_criterion_ipv4_info["vlan"], + ) + ] + dst_ipv4_lan_prefixes = [ + LANPrefixesDict( + lan=dst_match_criterion_ipv4_info["dst_ip"], + lan_tag=dst_match_criterion_ipv4_info["vlan"], + ) + ] + src_ce_address = src_endpoint_settings["address_ip"] + src_pe_address = src_ce_address + src_ce_address_prefix = src_endpoint_settings["address_prefix"] + dst_ce_address = dst_endpoint_settings["address_ip"] + dst_pe_address = dst_ce_address + dst_ce_address_prefix = dst_endpoint_settings["address_prefix"] + resource_value_dict = { + "uuid": service_uuid, + "src_device_name": src_device_name, + "src_endpoint_name": src_endpoint_obj.name, + "src_site_location": src_endpoint_settings["site_location"], + "src_ipv4_lan_prefixes": src_ipv4_lan_prefixes, + "src_ce_address": src_ce_address, + "src_pe_address": src_pe_address, + "src_ce_pe_network_prefix": src_ce_address_prefix, + "src_mtu": MTU, + "dst_device_name": dst_device_name, + "dst_endpoint_name": dst_endpoint_obj.name, + "dst_site_location": dst_endpoint_settings["site_location"], + "dst_ipv4_lan_prefixes": dst_ipv4_lan_prefixes, + "dst_ce_address": dst_ce_address, + "dst_pe_address": dst_pe_address, + "dst_ce_pe_network_prefix": dst_ce_address_prefix, + "dst_mtu": MTU, + } + json_config_rules = setup_config_rules(service_uuid, resource_value_dict) + del controller.device_config.config_rules[:] + for jcr in json_config_rules: + controller.device_config.config_rules.append(ConfigRule(**jcr)) self.__task_executor.configure_device(controller) - results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception( "Unable to SetEndpoint for Service({:s})".format(str(service_uuid)) @@ -160,7 +330,6 @@ class L3NM_IETFL3VPN_ServiceHandler(_ServiceHandler): DeviceId(**json_device_id(dst_device_uuid)) ) dst_controller = self.__task_executor.get_device_controller(dst_device) - if ( src_controller.device_id.device_uuid.uuid != dst_controller.device_id.device_uuid.uuid