Commit 0895f598 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Merge branch 'feat/device-p4' into 'develop'

feat: basic P4 device driver

See merge request teraflow-h2020/controller!63
parents 59398a94 5ac25d5f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -13,3 +13,4 @@ pytz
redis
requests
xmltodict
p4runtime==1.3.0
+1 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ class DeviceTypeFilterFieldEnum(Enum):
    OPTICAL_LINE_SYSTEM = 'optical-line-system'
    PACKET_ROUTER       = 'packet-router'
    PACKET_SWITCH       = 'packet-switch'
    P4_SWITCH           = 'p4-switch'

class FilterFieldEnum(Enum):
    DEVICE_TYPE   = 'device_type'
+7 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ from ..driver_api.FilterFields import FilterFieldEnum, DeviceTypeFilterFieldEnum
from .emulated.EmulatedDriver import EmulatedDriver
from .openconfig.OpenConfigDriver import OpenConfigDriver
from .transport_api.TransportApiDriver import TransportApiDriver
from .p4.p4_driver import P4Driver

DRIVERS = [
    (EmulatedDriver, [
@@ -22,4 +23,10 @@ DRIVERS = [
            FilterFieldEnum.DRIVER     : ORM_DeviceDriverEnum.TRANSPORT_API,
        }
    ]),
    (P4Driver, [
        {
            FilterFieldEnum.DEVICE_TYPE: DeviceTypeFilterFieldEnum.P4_SWITCH,
            FilterFieldEnum.DRIVER     : ORM_DeviceDriverEnum.P4,
        }
    ]),
]
+0 −0

Empty file added.

+246 −0
Original line number Diff line number Diff line
"""
P4 driver plugin for the TeraFlow SDN controller.
"""

import logging
import threading
from typing import Any, Iterator, List, Optional, Tuple, Union
from .p4_util import P4RuntimeClient,\
    P4_ATTR_DEV_ID, P4_ATTR_DEV_NAME, P4_ATTR_DEV_VENDOR,\
    P4_ATTR_DEV_HW_VER, P4_ATTR_DEV_SW_VER, P4_ATTR_DEV_PIPECONF,\
    P4_VAL_DEF_VENDOR, P4_VAL_DEF_HW_VER, P4_VAL_DEF_SW_VER, P4_VAL_DEF_PIPECONF

try:
    from _Driver import _Driver
except ImportError:
    from device.service.driver_api._Driver import _Driver

LOGGER = logging.getLogger(__name__)


class P4Driver(_Driver):
    """
    P4Driver class inherits the abstract _Driver class to support P4 devices.

    Attributes
    ----------
    address : str
        IP address of the P4Runtime server running on the P4 device
    port : int
        transport port number of the P4Runtime server running on the P4 device
    **settings : map
        id : int
            P4 device ID (Mandatory)
        name : str
            P4 device name (Optional)
        vendor : str
            P4 device vendor (Optional)
        hw_ver : str
            Hardware version of the P4 device (Optional)
        sw_ver : str
            Software version of the P4 device (Optional)
        pipeconf : str
            P4 device table configuration (Optional)
    """

    def __init__(self, address: str, port: int, **settings) -> None:
        # pylint: disable=super-init-not-called
        self.__client = None
        self.__address = address
        self.__port = int(port)
        self.__settings = settings

        try:
            self.__dev_id = self.__settings.get(P4_ATTR_DEV_ID)
        except Exception as ex:
            LOGGER.error('P4 device ID is a mandatory setting')
            raise Exception from ex

        if P4_ATTR_DEV_NAME in self.__settings:
            self.__dev_name = self.__settings.get(P4_ATTR_DEV_NAME)
        else:
            self.__dev_name = str(self.__dev_id)
            LOGGER.warning(
                'No device name is provided. Setting default name: %s',
                self.__dev_name)

        if P4_ATTR_DEV_VENDOR in self.__settings:
            self.__dev_vendor = self.__settings.get(P4_ATTR_DEV_VENDOR)
        else:
            self.__dev_vendor = P4_VAL_DEF_VENDOR
            LOGGER.warning(
                'No vendor is provided. Setting default vendor: %s',
                self.__dev_vendor)

        if P4_ATTR_DEV_HW_VER in self.__settings:
            self.__dev_hw_version = self.__settings.get(P4_ATTR_DEV_HW_VER)
        else:
            self.__dev_hw_version = P4_VAL_DEF_HW_VER
            LOGGER.warning(
                'No HW version is provided. Setting default HW version: %s',
                self.__dev_hw_version)

        if P4_ATTR_DEV_SW_VER in self.__settings:
            self.__dev_sw_version = self.__settings.get(P4_ATTR_DEV_SW_VER)
        else:
            self.__dev_sw_version = P4_VAL_DEF_SW_VER
            LOGGER.warning(
                'No SW version is provided. Setting default SW version: %s',
                self.__dev_sw_version)

        if P4_ATTR_DEV_PIPECONF in self.__settings:
            self.__dev_pipeconf = self.__settings.get(P4_ATTR_DEV_PIPECONF)
        else:
            self.__dev_pipeconf = P4_VAL_DEF_PIPECONF
            LOGGER.warning(
                'No P4 pipeconf is provided. Setting default P4 pipeconf: %s',
                self.__dev_pipeconf)

        self.__lock = threading.Lock()
        self.__started = threading.Event()
        self.__terminate = threading.Event()

        LOGGER.info('Initializing P4 device at %s:%d with settings:',
                    self.__address, self.__port)

        for key, value in settings.items():
            LOGGER.info('\t%8s = %s', key, value)

    def Connect(self) -> bool:
        """
        Establishes a connection between the P4 device driver and a P4 device.

        :return: boolean connection status.
        """
        LOGGER.info(
            'Connecting to P4 device %s:%d ...',
            self.__address, self.__port)

        with self.__lock:
            # Skip if already connected
            if self.__started.is_set():
                return True

            # Instantiate a gRPC channel with the P4 device
            grpc_address = f'{self.__address}:{self.__port}'
            election_id = (1, 0)
            self.__client = P4RuntimeClient(
                self.__dev_id, grpc_address, election_id)
            LOGGER.info('\tConnected!')
            self.__started.set()

            return True

    def Disconnect(self) -> bool:
        """
        Terminates the connection between the P4 device driver and a P4 device.

        :return: boolean disconnection status.
        """
        LOGGER.info(
            'Disconnecting from P4 device %s:%d ...',
            self.__address, self.__port)

        # If not started, assume it is already disconnected
        if not self.__started.is_set():
            return True

        # gRPC client must already be instantiated
        assert self.__client

        # Trigger termination of loops and processes
        self.__terminate.set()

        # Trigger connection tear down with the P4Runtime server
        self.__client.tear_down()
        self.__client = None

        LOGGER.info('\tDisconnected!')

        return True

    def GetInitialConfig(self) -> List[Tuple[str, Any]]:
        """
        Retrieves the initial configuration of a P4 device.

        :return: list of initial configuration items.
        """
        LOGGER.info('P4 GetInitialConfig()')
        return []

    def GetConfig(self, resource_keys : List[str] = [])\
            -> List[Tuple[str, Union[Any, None, Exception]]]:
        """
        Retrieves the current configuration of a P4 device.

        :param resource_keys: configuration parameters to retrieve.
        :return: list of values associated with the requested resource keys.
        """

        LOGGER.info('P4 GetConfig()')
        return []

    def SetConfig(self, resources : List[Tuple[str, Any]])\
            -> List[Union[bool, Exception]]:
        """
        Submits a new configuration to a P4 device.

        :param resources: configuration parameters to set.
        :return: list of results for resource key changes requested.
        """
        LOGGER.info('P4 SetConfig()')
        return []

    def DeleteConfig(self, resources : List[Tuple[str, Any]])\
            -> List[Union[bool, Exception]]:
        """
        Revokes P4 device configuration.

        :param resources: list of tuples with resource keys to be deleted.
        :return: list of results for resource key deletions requested.
        """
        LOGGER.info('P4 DeleteConfig()')
        return []

    def GetResource(self, endpoint_uuid : str) -> Optional[str]:
        """
        Retrieves a certain resource from a P4 device.

        :param endpoint_uuid: target endpoint UUID.
        :return: The path of the endpoint or None if not found.
        """
        LOGGER.info('P4 GetResource()')
        return ""

    def GetState(self, blocking=False) -> Iterator[Tuple[str, Any]]:
        """
        Retrieves the state of a P4 device.

        :param blocking: if non-blocking, the driver terminates the loop and
        returns.
        :return: sequences of state sample.
        """
        LOGGER.info('P4 GetState()')
        return []

    def SubscribeState(self, subscriptions : List[Tuple[str, float, float]])\
            -> List[Union[bool, Exception]]:
        """
        Subscribes to certain state information.

        :param subscriptions: list of tuples with resources to be subscribed.
        :return: list of results for resource subscriptions requested.
        """
        LOGGER.info('P4 SubscribeState()')
        return []

    def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]])\
            -> List[Union[bool, Exception]]:
        """
        Unsubscribes from certain state information.

        :param subscriptions: list of tuples with resources to be unsubscribed.
        :return: list of results for resource un-subscriptions requested.
        """
        LOGGER.info('P4 UnsubscribeState()')
        return []
Loading