Skip to content
Snippets Groups Projects
Commit aa2579a6 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Device component- IETF L2VPN Driver:

- Added DebugAPI client used to acquire remote controller topology info
- Extended skeleton and preliminary implemented driver methods
- Extended WIM connector with a method to get a specific service
parent faa68396
No related branches found
No related tags found
2 merge requests!142Release TeraFlowSDN 2.1,!71OFC'23 + IETF L2VPN Device Driver + Device Controllers + Multiple small improvements
......@@ -15,24 +15,30 @@
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)))
return False
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 = [
RESOURCE_ENDPOINTS,
......@@ -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
......@@ -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)
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))
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))
else:
# assume single-service retrieval
service = self.wim.get_connectivity_service()
results.append(process_connectivity_service('GetConfig', service))
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
results.extend(self.dac.get_devices_endpoints())
elif resource_key == RESOURCE_SERVICES:
# return all services through
reply = self.wim.get_all_active_connectivity_services()
results.extend(process_connectivity_services('GetConfig', reply.json()))
else:
# assume single-service retrieval
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)
......@@ -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')
vlan_id = find_key(resource, 'vlan_id')
conn_info = {}
if service_exists(self.wim, service_uuid):
exc = NotImplementedError('IETF L2VPN Service Update is still not supported')
results.append((resource[0], exc))
continue
result = self.wim.get_connectivity_service_status(
service_uuid, conn_info=conn_info)
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')
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
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
......
# 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
......@@ -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)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment