Loading src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py +55 −43 Original line number Diff line number Diff line Loading @@ -15,23 +15,29 @@ import logging, threading from typing import Any, Iterator, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method 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.drivers.ietf_l2vpn.TfsDebugApiClient import TfsDebugApiClient from .Tools import connection_point, find_key, wim_mapping from .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN LOGGER = logging.getLogger(__name__) def process_endpoint(method : str, endpoint : Any) -> Any: LOGGER.warning('[{:s}][process_endpoint] endpoint={:s}'.format(str(method), str(endpoint))) return endpoint def process_connectivity_services(method : str, services : Any) -> Any: LOGGER.warning('[{:s}][process_connectivity_services] services={:s}'.format(str(method), str(services))) return services def process_connectivity_service(method : str, service : Any) -> Any: LOGGER.warning('[{:s}][process_connectivity_service] service={:s}'.format(str(method), str(service))) return service def service_exists(param : Any) -> bool: LOGGER.warning('[service_exists] param={:s}'.format(str(param))) def service_exists(wim : WimconnectorIETFL2VPN, service_uuid : str) -> bool: try: wim.get_connectivity_service_status(service_uuid) return True except: # pylint: disable=bare-except return False ALL_RESOURCE_KEYS = [ Loading @@ -55,6 +61,7 @@ class IetfL2VpnDriver(_Driver): wim_account = {'user': username, 'password': password} # Mapping updated dynamically with each request config = {'mapping_not_needed': False, 'service_endpoint_mapping': []} self.dac = TfsDebugApiClient(address, int(port), scheme=scheme, username=username, password=password) self.wim = WimconnectorIETFL2VPN(wim, wim_account, config=config) self.conn_info = {} # internal database emulating OSM storage provided to WIM Connectors Loading Loading @@ -88,20 +95,22 @@ class IetfL2VpnDriver(_Driver): if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS for i, resource_key in enumerate(resource_keys): str_resource_name = 'resource_key[#{:d}]'.format(i) try: chk_string(str_resource_name, resource_key, allow_empty=False) if resource_key == RESOURCE_ENDPOINTS: # return endpoints through debug-api and list-devices method endpoints = self.debug_api.get_endpoints() for endpoint in endpoints: results.append(process_endpoint('GetConfig', endpoint)) results.extend(self.dac.get_devices_endpoints()) elif resource_key == RESOURCE_SERVICES: # return all services through services = self.wim.get_all_active_connectivity_services() for service in services: results.append(process_connectivity_service('GetConfig', service)) reply = self.wim.get_all_active_connectivity_services() results.extend(process_connectivity_services('GetConfig', reply.json())) else: # assume single-service retrieval service = self.wim.get_connectivity_service() results.append(process_connectivity_service('GetConfig', service)) reply = self.wim.get_connectivity_service(resource_key) results.append(process_connectivity_service('GetConfig', reply.json())) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unhandled error processing resource_key({:s})'.format(str(resource_key))) results.append((resource_key, e)) return results @metered_subclass_method(METRICS_POOL) Loading @@ -114,32 +123,35 @@ class IetfL2VpnDriver(_Driver): LOGGER.info('resource = {:s}'.format(str(resource))) service_uuid = find_key(resource, 'uuid') a_endpoint = find_key(resource, 'a_endpoint') z_endpoint = find_key(resource, 'z_endpoint') #capacity_value = find_key(resource, 'capacity_value') #capacity_unit = find_key(resource, 'capacity_unit') #layer_protocol_name = find_key(resource, 'layer_protocol_name') #layer_protocol_qualifier = find_key(resource, 'layer_protocol_qualifier') #direction = find_key(resource, 'direction') encapsulation_type = find_key(resource, 'encapsulation_type') if service_exists(self.wim, service_uuid): exc = NotImplementedError('IETF L2VPN Service Update is still not supported') results.append((resource[0], exc)) continue src_device_uuid = find_key(resource, 'src_device_uuid') src_endpoint_uuid = find_key(resource, 'src_endpoint_uuid') dst_device_uuid = find_key(resource, 'dst_device_uuid') dst_endpoint_uuid = find_key(resource, 'dst_endpoint_uuid') encap_type = find_key(resource, 'encapsulation_type') vlan_id = find_key(resource, 'vlan_id') conn_info = {} src_endpoint_id = json_endpoint_id(json_device_id(src_device_uuid), src_endpoint_uuid) src_service_endpoint_id, src_mapping = wim_mapping('1', src_endpoint_id) self.wim.mappings[src_service_endpoint_id] = src_mapping result = self.wim.get_connectivity_service_status( service_uuid, conn_info=conn_info) dst_endpoint_id = json_endpoint_id(json_device_id(dst_device_uuid), dst_endpoint_uuid) dst_service_endpoint_id, dst_mapping = wim_mapping('2', dst_endpoint_id) self.wim.mappings[dst_service_endpoint_id] = dst_mapping connection_points = [ connection_point(src_service_endpoint_id, encap_type, vlan_id), connection_point(dst_service_endpoint_id, encap_type, vlan_id), ] result = self.wim.create_connectivity_service(SERVICE_TYPE, connection_points) LOGGER.info('[SetConfig] CREATE result={:s}'.format(str(result))) connection_points = [] for endpoint_id in [a_endpoint, z_endpoint]: site_id = str(endpoint_id) self.wim.mappings[endpoint_id] = wim_mapping(site_id, endpoint_id) connection_points.append(connection_point(endpoint_id, encapsulation_type, vlan_id)) if service_exists(result): result = self.wim.create_connectivity_service( SERVICE_TYPE, connection_points) else: self.wim.edit_connectivity_service( service_uuid, conn_info=conn_info, connection_points=connection_points) results.extend(process_connectivity_service('SetConfig', None)) return results Loading src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py 0 → 100644 +92 −0 Original line number Diff line number Diff line # Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) # # 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. import logging, requests from requests.auth import HTTPBasicAuth from typing import Dict, List, Optional GET_DEVICES_URL = '{:s}://{:s}:{:d}/restconf/debug-api/devices' TIMEOUT = 30 HTTP_OK_CODES = { 200, # OK 201, # Created 202, # Accepted 204, # No Content } MAPPING_STATUS = { 'DEVICEOPERATIONALSTATUS_UNDEFINED': 0, 'DEVICEOPERATIONALSTATUS_DISABLED' : 1, 'DEVICEOPERATIONALSTATUS_ENABLED' : 2, } MAPPING_DRIVER = { 'DEVICEDRIVER_UNDEFINED' : 0, 'DEVICEDRIVER_OPENCONFIG' : 1, 'DEVICEDRIVER_TRANSPORT_API' : 2, 'DEVICEDRIVER_P4' : 3, 'DEVICEDRIVER_IETF_NETWORK_TOPOLOGY': 4, 'DEVICEDRIVER_ONF_TR_352' : 5, 'DEVICEDRIVER_XR' : 6, 'DEVICEDRIVER_IETF_L2VPN' : 7, } MSG_ERROR = 'Could not retrieve devices in remote TeraFlowSDN instance({:s}). status_code={:s} reply={:s}' LOGGER = logging.getLogger(__name__) class TfsDebugApiClient: def __init__( 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._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) if reply.status_code not in HTTP_OK_CODES: msg = MSG_ERROR.format(str(self._url), str(reply.status_code), str(reply)) LOGGER.error(msg) raise Exception(msg) result = list() for json_device in reply.json()['devices']: device_uuid : str = json_device['device_id']['device_uuid']['uuid'] device_type : str = json_device['device_type'] if not device_type.startswith('emu-'): device_type = 'emu-' + device_type device_status = json_device['device_operational_status'] device_url = '/devices/device[{:s}]'.format(device_uuid) device_data = { 'uuid': json_device['device_id']['device_uuid']['uuid'], 'name': json_device['name'], 'type': device_type, 'status': MAPPING_STATUS[device_status], 'drivers': [MAPPING_DRIVER[driver] for driver in json_device['device_drivers']], } result.append((device_url, device_data)) for json_endpoint in json_device['device_endpoints']: endpoint_uuid = json_endpoint['endpoint_id']['endpoint_uuid']['uuid'] endpoint_url = '/endpoints/endpoint[{:s}]'.format(endpoint_uuid) endpoint_data = { 'device_uuid': device_uuid, 'uuid': endpoint_uuid, 'name': json_endpoint['name'], 'type': json_endpoint['endpoint_type'], } result.append((endpoint_url, endpoint_data)) return result src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py +25 −0 Original line number Diff line number Diff line Loading @@ -541,3 +541,28 @@ class WimconnectorIETFL2VPN(SdnConnectorBase): return response except requests.exceptions.ConnectionError: raise SdnConnectorError("Request Timeout", http_code=408) def get_connectivity_service(self, service_uuid, conn_info=None): """Provide information about a specific connection provisioned by a WIM. This method should receive as the first argument the UUID generated by the ``create_connectivity_service`` """ try: self.logger.info("Sending get connectivity service") servicepoint = ( "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format( self.wim["wim_url"], service_uuid ) ) response = requests.get(servicepoint, auth=self.auth) if response.status_code != requests.codes.ok: raise SdnConnectorError( "Unable to get connectivity service {:s}".format(str(service_uuid)), http_code=response.status_code, ) return response except requests.exceptions.ConnectionError: raise SdnConnectorError("Request Timeout", http_code=408) Loading
src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py +55 −43 Original line number Diff line number Diff line Loading @@ -15,23 +15,29 @@ import logging, threading from typing import Any, Iterator, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method 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.drivers.ietf_l2vpn.TfsDebugApiClient import TfsDebugApiClient from .Tools import connection_point, find_key, wim_mapping from .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN LOGGER = logging.getLogger(__name__) def process_endpoint(method : str, endpoint : Any) -> Any: LOGGER.warning('[{:s}][process_endpoint] endpoint={:s}'.format(str(method), str(endpoint))) return endpoint def process_connectivity_services(method : str, services : Any) -> Any: LOGGER.warning('[{:s}][process_connectivity_services] services={:s}'.format(str(method), str(services))) return services def process_connectivity_service(method : str, service : Any) -> Any: LOGGER.warning('[{:s}][process_connectivity_service] service={:s}'.format(str(method), str(service))) return service def service_exists(param : Any) -> bool: LOGGER.warning('[service_exists] param={:s}'.format(str(param))) def service_exists(wim : WimconnectorIETFL2VPN, service_uuid : str) -> bool: try: wim.get_connectivity_service_status(service_uuid) return True except: # pylint: disable=bare-except return False ALL_RESOURCE_KEYS = [ Loading @@ -55,6 +61,7 @@ class IetfL2VpnDriver(_Driver): wim_account = {'user': username, 'password': password} # Mapping updated dynamically with each request config = {'mapping_not_needed': False, 'service_endpoint_mapping': []} self.dac = TfsDebugApiClient(address, int(port), scheme=scheme, username=username, password=password) self.wim = WimconnectorIETFL2VPN(wim, wim_account, config=config) self.conn_info = {} # internal database emulating OSM storage provided to WIM Connectors Loading Loading @@ -88,20 +95,22 @@ class IetfL2VpnDriver(_Driver): if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS for i, resource_key in enumerate(resource_keys): str_resource_name = 'resource_key[#{:d}]'.format(i) try: chk_string(str_resource_name, resource_key, allow_empty=False) if resource_key == RESOURCE_ENDPOINTS: # return endpoints through debug-api and list-devices method endpoints = self.debug_api.get_endpoints() for endpoint in endpoints: results.append(process_endpoint('GetConfig', endpoint)) results.extend(self.dac.get_devices_endpoints()) elif resource_key == RESOURCE_SERVICES: # return all services through services = self.wim.get_all_active_connectivity_services() for service in services: results.append(process_connectivity_service('GetConfig', service)) reply = self.wim.get_all_active_connectivity_services() results.extend(process_connectivity_services('GetConfig', reply.json())) else: # assume single-service retrieval service = self.wim.get_connectivity_service() results.append(process_connectivity_service('GetConfig', service)) reply = self.wim.get_connectivity_service(resource_key) results.append(process_connectivity_service('GetConfig', reply.json())) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unhandled error processing resource_key({:s})'.format(str(resource_key))) results.append((resource_key, e)) return results @metered_subclass_method(METRICS_POOL) Loading @@ -114,32 +123,35 @@ class IetfL2VpnDriver(_Driver): LOGGER.info('resource = {:s}'.format(str(resource))) service_uuid = find_key(resource, 'uuid') a_endpoint = find_key(resource, 'a_endpoint') z_endpoint = find_key(resource, 'z_endpoint') #capacity_value = find_key(resource, 'capacity_value') #capacity_unit = find_key(resource, 'capacity_unit') #layer_protocol_name = find_key(resource, 'layer_protocol_name') #layer_protocol_qualifier = find_key(resource, 'layer_protocol_qualifier') #direction = find_key(resource, 'direction') encapsulation_type = find_key(resource, 'encapsulation_type') if service_exists(self.wim, service_uuid): exc = NotImplementedError('IETF L2VPN Service Update is still not supported') results.append((resource[0], exc)) continue src_device_uuid = find_key(resource, 'src_device_uuid') src_endpoint_uuid = find_key(resource, 'src_endpoint_uuid') dst_device_uuid = find_key(resource, 'dst_device_uuid') dst_endpoint_uuid = find_key(resource, 'dst_endpoint_uuid') encap_type = find_key(resource, 'encapsulation_type') vlan_id = find_key(resource, 'vlan_id') conn_info = {} src_endpoint_id = json_endpoint_id(json_device_id(src_device_uuid), src_endpoint_uuid) src_service_endpoint_id, src_mapping = wim_mapping('1', src_endpoint_id) self.wim.mappings[src_service_endpoint_id] = src_mapping result = self.wim.get_connectivity_service_status( service_uuid, conn_info=conn_info) dst_endpoint_id = json_endpoint_id(json_device_id(dst_device_uuid), dst_endpoint_uuid) dst_service_endpoint_id, dst_mapping = wim_mapping('2', dst_endpoint_id) self.wim.mappings[dst_service_endpoint_id] = dst_mapping connection_points = [ connection_point(src_service_endpoint_id, encap_type, vlan_id), connection_point(dst_service_endpoint_id, encap_type, vlan_id), ] result = self.wim.create_connectivity_service(SERVICE_TYPE, connection_points) LOGGER.info('[SetConfig] CREATE result={:s}'.format(str(result))) connection_points = [] for endpoint_id in [a_endpoint, z_endpoint]: site_id = str(endpoint_id) self.wim.mappings[endpoint_id] = wim_mapping(site_id, endpoint_id) connection_points.append(connection_point(endpoint_id, encapsulation_type, vlan_id)) if service_exists(result): result = self.wim.create_connectivity_service( SERVICE_TYPE, connection_points) else: self.wim.edit_connectivity_service( service_uuid, conn_info=conn_info, connection_points=connection_points) results.extend(process_connectivity_service('SetConfig', None)) return results Loading
src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py 0 → 100644 +92 −0 Original line number Diff line number Diff line # Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) # # 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. import logging, requests from requests.auth import HTTPBasicAuth from typing import Dict, List, Optional GET_DEVICES_URL = '{:s}://{:s}:{:d}/restconf/debug-api/devices' TIMEOUT = 30 HTTP_OK_CODES = { 200, # OK 201, # Created 202, # Accepted 204, # No Content } MAPPING_STATUS = { 'DEVICEOPERATIONALSTATUS_UNDEFINED': 0, 'DEVICEOPERATIONALSTATUS_DISABLED' : 1, 'DEVICEOPERATIONALSTATUS_ENABLED' : 2, } MAPPING_DRIVER = { 'DEVICEDRIVER_UNDEFINED' : 0, 'DEVICEDRIVER_OPENCONFIG' : 1, 'DEVICEDRIVER_TRANSPORT_API' : 2, 'DEVICEDRIVER_P4' : 3, 'DEVICEDRIVER_IETF_NETWORK_TOPOLOGY': 4, 'DEVICEDRIVER_ONF_TR_352' : 5, 'DEVICEDRIVER_XR' : 6, 'DEVICEDRIVER_IETF_L2VPN' : 7, } MSG_ERROR = 'Could not retrieve devices in remote TeraFlowSDN instance({:s}). status_code={:s} reply={:s}' LOGGER = logging.getLogger(__name__) class TfsDebugApiClient: def __init__( 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._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) if reply.status_code not in HTTP_OK_CODES: msg = MSG_ERROR.format(str(self._url), str(reply.status_code), str(reply)) LOGGER.error(msg) raise Exception(msg) result = list() for json_device in reply.json()['devices']: device_uuid : str = json_device['device_id']['device_uuid']['uuid'] device_type : str = json_device['device_type'] if not device_type.startswith('emu-'): device_type = 'emu-' + device_type device_status = json_device['device_operational_status'] device_url = '/devices/device[{:s}]'.format(device_uuid) device_data = { 'uuid': json_device['device_id']['device_uuid']['uuid'], 'name': json_device['name'], 'type': device_type, 'status': MAPPING_STATUS[device_status], 'drivers': [MAPPING_DRIVER[driver] for driver in json_device['device_drivers']], } result.append((device_url, device_data)) for json_endpoint in json_device['device_endpoints']: endpoint_uuid = json_endpoint['endpoint_id']['endpoint_uuid']['uuid'] endpoint_url = '/endpoints/endpoint[{:s}]'.format(endpoint_uuid) endpoint_data = { 'device_uuid': device_uuid, 'uuid': endpoint_uuid, 'name': json_endpoint['name'], 'type': json_endpoint['endpoint_type'], } result.append((endpoint_url, endpoint_data)) return result
src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py +25 −0 Original line number Diff line number Diff line Loading @@ -541,3 +541,28 @@ class WimconnectorIETFL2VPN(SdnConnectorBase): return response except requests.exceptions.ConnectionError: raise SdnConnectorError("Request Timeout", http_code=408) def get_connectivity_service(self, service_uuid, conn_info=None): """Provide information about a specific connection provisioned by a WIM. This method should receive as the first argument the UUID generated by the ``create_connectivity_service`` """ try: self.logger.info("Sending get connectivity service") servicepoint = ( "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format( self.wim["wim_url"], service_uuid ) ) response = requests.get(servicepoint, auth=self.auth) if response.status_code != requests.codes.ok: raise SdnConnectorError( "Unable to get connectivity service {:s}".format(str(service_uuid)), http_code=response.status_code, ) return response except requests.exceptions.ConnectionError: raise SdnConnectorError("Request Timeout", http_code=408)