Commit 1e2973a6 authored by Georgios Katsikas's avatar Georgios Katsikas
Browse files

Merge branch 'pr-service-handler-p4' into 'develop'

feat (service): P4 service handler

See merge request !1
parents aefd6f2b 7a2a4850
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -61,3 +61,13 @@ def json_service_tapi_planned(
        service_uuid, ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, context_id=json_context_id(context_uuid),
        status=ServiceStatusEnum.SERVICESTATUS_PLANNED, endpoint_ids=endpoint_ids, constraints=constraints,
        config_rules=config_rules)

def json_service_p4_planned(
        service_uuid : str, endpoint_ids : List[Dict] = [], constraints : List[Dict] = [],
        config_rules : List[Dict] = [], context_uuid : str = DEFAULT_CONTEXT_UUID
    ):

    return json_service(
        service_uuid, ServiceTypeEnum.SERVICETYPE_L2NM, context_id=json_context_id(context_uuid),
        status=ServiceStatusEnum.SERVICESTATUS_PLANNED, endpoint_ids=endpoint_ids, constraints=constraints,
        config_rules=config_rules)
 No newline at end of file
+1 −1
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ SERVICE_TYPE_VALUES = {
    ServiceTypeEnum.SERVICETYPE_UNKNOWN,
    ServiceTypeEnum.SERVICETYPE_L3NM,
    ServiceTypeEnum.SERVICETYPE_L2NM,
    ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
    ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE
}

DEVICE_DRIVER_VALUES = {
+8 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ from ..service_handler_api.FilterFields import FilterFieldEnum
from .l2nm_emulated.L2NMEmulatedServiceHandler import L2NMEmulatedServiceHandler
from .l3nm_emulated.L3NMEmulatedServiceHandler import L3NMEmulatedServiceHandler
from .l3nm_openconfig.L3NMOpenConfigServiceHandler import L3NMOpenConfigServiceHandler
from .p4.p4_service_handler import P4ServiceHandler
from .tapi_tapi.TapiServiceHandler import TapiServiceHandler
from .microwave.MicrowaveServiceHandler import MicrowaveServiceHandler

@@ -51,4 +52,10 @@ SERVICE_HANDLERS = [
            FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY,
        }
    ]),
    (P4ServiceHandler, [
        {
            FilterFieldEnum.SERVICE_TYPE: ServiceTypeEnum.SERVICETYPE_L2NM,
            FilterFieldEnum.DEVICE_DRIVER: DeviceDriverEnum.DEVICEDRIVER_P4,
        }
    ]),
]
+13 −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.
+305 −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.

"""
P4 service handler for the TeraFlowSDN controller.
"""

import anytree, json, logging
from typing import Any, Dict, List, Optional, Tuple, Union
from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service
from common.tools.object_factory.ConfigRule import json_config_rule, json_config_rule_delete, json_config_rule_set
from common.tools.object_factory.Device import json_device_id
from common.type_checkers.Checkers import chk_type, chk_length
from service.service.service_handler_api._ServiceHandler import _ServiceHandler
from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value
from service.service.task_scheduler.TaskExecutor import TaskExecutor

LOGGER = logging.getLogger(__name__)

def create_rule_set(endpoint_a, endpoint_b):
    return json_config_rule_set(
        'table',
        {
            'table-name': 'IngressPipeImpl.l2_exact_table',
            'match-fields': [
                {
                    'match-field': 'standard_metadata.ingress_port',
                    'match-value': endpoint_a
                }
            ],
            'action-name': 'IngressPipeImpl.set_egress_port',
            'action-params': [
                {
                    'action-param': 'port',
                    'action-value': endpoint_b
                }
            ]
        }
)

def create_rule_del(endpoint_a, endpoint_b):
    return json_config_rule_delete(
        'table',
        {
            'table-name': 'IngressPipeImpl.l2_exact_table',
            'match-fields': [
                {
                    'match-field': 'standard_metadata.ingress_port',
                    'match-value': endpoint_a
                }
            ],
            'action-name': 'IngressPipeImpl.set_egress_port',
            'action-params': [
                {
                    'action-param': 'port',
                    'action-value': endpoint_b
                }
            ]
        }
)

class P4ServiceHandler(_ServiceHandler):
    def __init__(self,
                 service: Service,
                 task_executor : TaskExecutor,
                 **settings) -> None:
        """ Initialize Driver.
            Parameters:
                service
                    The service instance (gRPC message) to be managed.
                task_executor
                    An instance of Task Executor providing access to the
                    service handlers factory, the context and device clients,
                    and an internal cache of already-loaded gRPC entities.
                **settings
                    Extra settings required by the service handler.
        """
        self.__service = service
        self.__task_executor = task_executor # pylint: disable=unused-private-member

    def SetEndpoint(
        self, endpoints : List[Tuple[str, str, Optional[str]]],
        connection_uuid : Optional[str] = None
    ) -> List[Union[bool, Exception]]:
        """ Create/Update service endpoints form a list.
            Parameters:
                endpoints: List[Tuple[str, str, Optional[str]]]
                    List of tuples, each containing a device_uuid,
                    endpoint_uuid and, optionally, the topology_uuid
                    of the endpoint to be added.
                connection_uuid : Optional[str]
                    If specified, is the UUID of the connection this endpoint is associated to.
            Returns:
                results: List[Union[bool, Exception]]
                    List of results for endpoint changes requested.
                    Return values must be in the same order as the requested
                    endpoints. If an endpoint is properly added, True must be
                    returned; otherwise, the Exception that is raised during
                    the processing must be returned.
        """
        chk_type('endpoints', endpoints, list)
        if len(endpoints) == 0: return []

        service_uuid = self.__service.service_id.service_uuid.uuid

        history = {}
        
        results = []
        index = {}
        i = 0
        for endpoint in endpoints:        
            device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now
            if device_uuid in history:       
                try:
                    matched_endpoint_uuid = history.pop(device_uuid)
                    device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))

                    del device.device_config.config_rules[:]

                    # One way
                    rule = create_rule_set(matched_endpoint_uuid, endpoint_uuid) 
                    device.device_config.config_rules.append(ConfigRule(**rule))
                    # The other way
                    rule = create_rule_set(endpoint_uuid, matched_endpoint_uuid) 
                    device.device_config.config_rules.append(ConfigRule(**rule))

                    self.__task_executor.configure_device(device)
            
                    results.append(True)
                    results[index[device_uuid]] = True
                except Exception as e:
                    LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint)))
                    results.append(e)
            else:
                history[device_uuid] = endpoint_uuid
                index[device_uuid] = i
                results.append(False)
            i = i+1

        return results

    def DeleteEndpoint(
        self, endpoints : List[Tuple[str, str, Optional[str]]],
        connection_uuid : Optional[str] = None
    ) -> List[Union[bool, Exception]]:
        """ Delete service endpoints form a list.
            Parameters:
                endpoints: List[Tuple[str, str, Optional[str]]]
                    List of tuples, each containing a device_uuid,
                    endpoint_uuid, and the topology_uuid of the endpoint
                    to be removed.
                connection_uuid : Optional[str]
                    If specified, is the UUID of the connection this endpoint is associated to.
            Returns:
                results: List[Union[bool, Exception]]
                    List of results for endpoint deletions requested.
                    Return values must be in the same order as the requested
                    endpoints. If an endpoint is properly deleted, True must be
                    returned; otherwise, the Exception that is raised during
                    the processing must be returned.
        """
        chk_type('endpoints', endpoints, list)
        if len(endpoints) == 0: return []

        service_uuid = self.__service.service_id.service_uuid.uuid

        history = {}
        
        results = []
        index = {}
        i = 0
        for endpoint in endpoints:        
            device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now
            if device_uuid in history:       
                try:
                    matched_endpoint_uuid = history.pop(device_uuid)
                    device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))

                    del device.device_config.config_rules[:]

                    # One way
                    rule = create_rule_del(matched_endpoint_uuid, endpoint_uuid) 
                    device.device_config.config_rules.append(ConfigRule(**rule))
                    # The other way
                    rule = create_rule_del(endpoint_uuid, matched_endpoint_uuid) 
                    device.device_config.config_rules.append(ConfigRule(**rule))

                    self.__task_executor.configure_device(device)
            
                    results.append(True)
                    results[index[device_uuid]] = True
                except Exception as e:
                    LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint)))
                    results.append(e)
            else:
                history[device_uuid] = endpoint_uuid
                index[device_uuid] = i
                results.append(False)
            i = i+1

        return results

    def SetConstraint(self, constraints: List[Tuple[str, Any]]) \
            -> List[Union[bool, Exception]]:
        """ Create/Update service constraints.
            Parameters:
                constraints: List[Tuple[str, Any]]
                    List of tuples, each containing a constraint_type and the
                    new constraint_value to be set.
            Returns:
                results: List[Union[bool, Exception]]
                    List of results for constraint changes requested.
                    Return values must be in the same order as the requested
                    constraints. If a constraint is properly set, True must be
                    returned; otherwise, the Exception that is raised during
                    the processing must be returned.
        """
        chk_type('constraints', constraints, list)
        if len(constraints) == 0: return []

        msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.'
        LOGGER.warning(msg.format(str(constraints)))
        return [True for _ in range(len(constraints))]

    def DeleteConstraint(self, constraints: List[Tuple[str, Any]]) \
            -> List[Union[bool, Exception]]:
        """ Delete service constraints.
            Parameters:
                constraints: List[Tuple[str, Any]]
                    List of tuples, each containing a constraint_type pointing
                    to the constraint to be deleted, and a constraint_value
                    containing possible additionally required values to locate
                    the constraint to be removed.
            Returns:
                results: List[Union[bool, Exception]]
                    List of results for constraint deletions requested.
                    Return values must be in the same order as the requested
                    constraints. If a constraint is properly deleted, True must
                    be returned; otherwise, the Exception that is raised during
                    the processing must be returned.
        """
        chk_type('constraints', constraints, list)
        if len(constraints) == 0: return []

        msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.'
        LOGGER.warning(msg.format(str(constraints)))
        return [True for _ in range(len(constraints))]

    def SetConfig(self, resources: List[Tuple[str, Any]]) \
            -> List[Union[bool, Exception]]:
        """ Create/Update configuration for a list of service resources.
            Parameters:
                resources: List[Tuple[str, Any]]
                    List of tuples, each containing a resource_key pointing to
                    the resource to be modified, and a resource_value
                    containing the new value to be set.
            Returns:
                results: List[Union[bool, Exception]]
                    List of results for resource key changes requested.
                    Return values must be in the same order as the requested
                    resource keys. If a resource is properly set, True must be
                    returned; otherwise, the Exception that is raised during
                    the processing must be returned.
        """
        chk_type('resources', resources, list)
        if len(resources) == 0: return []

        msg = '[SetConfig] Method not implemented. Resources({:s}) are being ignored.'
        LOGGER.warning(msg.format(str(resources)))
        return [True for _ in range(len(resources))]

    def DeleteConfig(self, resources: List[Tuple[str, Any]]) \
            -> List[Union[bool, Exception]]:
        """ Delete configuration for a list of service resources.
            Parameters:
                resources: List[Tuple[str, Any]]
                    List of tuples, each containing a resource_key pointing to
                    the resource to be modified, and a resource_value containing
                    possible additionally required values to locate the value
                    to be removed.
            Returns:
                results: List[Union[bool, Exception]]
                    List of results for resource key deletions requested.
                    Return values must be in the same order as the requested
                    resource keys. If a resource is properly deleted, True must
                    be returned; otherwise, the Exception that is raised during
                    the processing must be returned.
        """
        chk_type('resources', resources, list)
        if len(resources) == 0: return []

        msg = '[SetConfig] Method not implemented. Resources({:s}) are being ignored.'
        LOGGER.warning(msg.format(str(resources)))
        return [True for _ in range(len(resources))]
 No newline at end of file
Loading