Commit 32c83559 authored by Shayan Hajipour's avatar Shayan Hajipour
Browse files

Merge branch...

Merge branch 'feat/215-cttc-implement-l3-vpn-sbi-driver-compatible-with-ietf-l3-service-model' into camara-integration
parents 9af61078 bcf185c3
Loading
Loading
Loading
Loading
+232 −63
Original line number Diff line number Diff line
@@ -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),
                {
            else:
                raise Exception("Endpoint settings not found")
            ietf_slice_running_cr = get_custom_config_rule(
                service_config, RUNNING_RESOURCE_KEY
            )
            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_obj.name,
                "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_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,
                "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_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"],
                },
            )
                "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[:]
            controller.device_config.config_rules.append(
                ConfigRule(**json_config_rule)
            )
            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