diff --git a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py index 9498dc84cc6991fd2295371842fa8508c961f1bc..c79dde99a4d3c48f2f27ff00451f50aa1af9bee2 100644 --- a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py +++ b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py @@ -19,6 +19,7 @@ from common.tools.object_factory.Device import json_device_id from common.tools.object_factory.EndPoint import json_endpoint_id from common.type_checkers.Checkers import chk_string, chk_type from device.service.driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_SERVICES +from device.service.driver_api.ImportTopologyEnum import ImportTopologyEnum, get_import_topology from device.service.drivers.ietf_l2vpn.TfsDebugApiClient import TfsDebugApiClient from .Tools import connection_point, wim_mapping from .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN @@ -59,6 +60,14 @@ class IetfL2VpnDriver(_Driver): self.wim = WimconnectorIETFL2VPN(wim, wim_account, config=config) self.conn_info = {} # internal database emulating OSM storage provided to WIM Connectors + # Options are: + # disabled --> just import endpoints as usual + # devices --> imports sub-devices but not links connecting them. + # (a remotely-controlled transport domain might exist between them) + # topology --> imports sub-devices and links connecting them. + # (not supported by XR driver) + self.__import_topology = get_import_topology(self.settings, default=ImportTopologyEnum.DEVICES) + def Connect(self) -> bool: with self.__lock: try: @@ -93,7 +102,7 @@ class IetfL2VpnDriver(_Driver): chk_string(str_resource_name, resource_key, allow_empty=False) if resource_key == RESOURCE_ENDPOINTS: # return endpoints through debug-api and list-devices method - results.extend(self.dac.get_devices_endpoints()) + results.extend(self.dac.get_devices_endpoints(self.__import_topology)) elif resource_key == RESOURCE_SERVICES: # return all services through reply = self.wim.get_all_active_connectivity_services() diff --git a/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py b/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py index 4bf40af030fda990f96efe0ff8ab2ce54f82c312..19adc31c30e27f141c7c4dac50b1dcc4c047b1cf 100644 --- a/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py +++ b/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py @@ -15,8 +15,10 @@ import logging, requests from requests.auth import HTTPBasicAuth from typing import Dict, List, Optional +from device.service.driver_api.ImportTopologyEnum import ImportTopologyEnum GET_DEVICES_URL = '{:s}://{:s}:{:d}/restconf/debug-api/devices' +GET_LINKS_URL = '{:s}://{:s}:{:d}/restconf/debug-api/links' TIMEOUT = 30 HTTP_OK_CODES = { @@ -41,6 +43,7 @@ MAPPING_DRIVER = { 'DEVICEDRIVER_ONF_TR_352' : 5, 'DEVICEDRIVER_XR' : 6, 'DEVICEDRIVER_IETF_L2VPN' : 7, + 'DEVICEDRIVER_GNMI_OPENCONFIG' : 8, } MSG_ERROR = 'Could not retrieve devices in remote TeraFlowSDN instance({:s}). status_code={:s} reply={:s}' @@ -52,16 +55,23 @@ class TfsDebugApiClient: self, address : str, port : int, scheme : str = 'http', username : Optional[str] = None, password : Optional[str] = None ) -> None: - self._url = GET_DEVICES_URL.format(scheme, address, port) + self._devices_url = GET_DEVICES_URL.format(scheme, address, port) + self._links_url = GET_LINKS_URL.format(scheme, address, port) self._auth = HTTPBasicAuth(username, password) if username is not None and password is not None else None - def get_devices_endpoints(self) -> List[Dict]: - reply = requests.get(self._url, timeout=TIMEOUT, verify=False, auth=self._auth) + def get_devices_endpoints(self, import_topology : ImportTopologyEnum = ImportTopologyEnum.DEVICES) -> List[Dict]: + LOGGER.debug('[get_devices_endpoints] begin') + LOGGER.debug('[get_devices_endpoints] import_topology={:s}'.format(str(import_topology))) + + reply = requests.get(self._devices_url, timeout=TIMEOUT, verify=False, auth=self._auth) if reply.status_code not in HTTP_OK_CODES: - msg = MSG_ERROR.format(str(self._url), str(reply.status_code), str(reply)) + msg = MSG_ERROR.format(str(self._devices_url), str(reply.status_code), str(reply)) LOGGER.error(msg) raise Exception(msg) + if import_topology == ImportTopologyEnum.DISABLED: + raise Exception('Unsupported import_topology mode: {:s}'.format(str(import_topology))) + result = list() for json_device in reply.json()['devices']: device_uuid : str = json_device['device_id']['device_uuid']['uuid'] @@ -89,4 +99,29 @@ class TfsDebugApiClient: } result.append((endpoint_url, endpoint_data)) + if import_topology == ImportTopologyEnum.DEVICES: + LOGGER.debug('[get_devices_endpoints] devices only; returning') + return result + + reply = requests.get(self._links_url, timeout=TIMEOUT, verify=False, auth=self._auth) + if reply.status_code not in HTTP_OK_CODES: + msg = MSG_ERROR.format(str(self._links_url), str(reply.status_code), str(reply)) + LOGGER.error(msg) + raise Exception(msg) + + for json_link in reply.json()['links']: + link_uuid : str = json_link['link_id']['link_uuid']['uuid'] + link_url = '/links/link[{:s}]'.format(link_uuid) + link_endpoint_ids = [ + (json_endpoint_id['device_id']['device_uuid']['uuid'], json_endpoint_id['endpoint_uuid']['uuid']) + for json_endpoint_id in json_link['link_endpoint_ids'] + ] + link_data = { + 'uuid': json_link['link_id']['link_uuid']['uuid'], + 'name': json_link['name'], + 'endpoints': link_endpoint_ids, + } + result.append((link_url, link_data)) + + LOGGER.debug('[get_devices_endpoints] topology; returning') return result