From: David García Date: Mon, 10 Dec 2018 08:43:50 +0000 (+0100) Subject: Add Dynpac WIM Connector X-Git-Tag: v5.0.3~4^2~5 X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FRO.git;a=commitdiff_plain;h=00e29ddd2bc186460698cb2431f6751daa24eca8 Add Dynpac WIM Connector A file osm_ro.wim.wimconn_dynpac added, which defines a the DynpacConnector class Change-Id: I72fbd5387cdf3c09bc69cdde10713e224b7876ed Signed-off-by: David García --- diff --git a/openmano b/openmano index 15776561..13a93da4 100755 --- a/openmano +++ b/openmano @@ -2254,7 +2254,7 @@ if __name__=="__main__": wim_create_parser.add_argument("url", action="store", help="url for the wim") wim_create_parser.add_argument("--type", action="store", - help="wim type: tapi, onos or odl (default)") + help="wim type: tapi, onos, dynpac or odl (default)") wim_create_parser.add_argument("--config", action="store", help="additional configuration in json/yaml format") wim_create_parser.add_argument("--description", action="store", diff --git a/osm_ro/wim/schemas.py b/osm_ro/wim/schemas.py index a040405b..f4be2166 100644 --- a/osm_ro/wim/schemas.py +++ b/osm_ro/wim/schemas.py @@ -39,14 +39,14 @@ from ..openmano_schemas import ( ) # WIM ------------------------------------------------------------------------- -wim_types = ["tapi", "onos", "odl"] +wim_types = ["tapi", "onos", "odl", "dynpac"] wim_schema_properties = { "name": name_schema, "description": description_schema, "type": { "type": "string", - "enum": ["tapi", "onos", "odl"] + "enum": ["tapi", "onos", "odl", "dynpac"] }, "wim_url": description_schema, "config": {"type": "object"} diff --git a/osm_ro/wim/wim_thread.py b/osm_ro/wim/wim_thread.py index a13d6a25..fa64fbba 100644 --- a/osm_ro/wim/wim_thread.py +++ b/osm_ro/wim/wim_thread.py @@ -51,7 +51,7 @@ from time import time, sleep from six import reraise from six.moves import queue -from . import wan_link_actions, wimconn_odl # wimconn_tapi +from . import wan_link_actions, wimconn_odl, wimconn_dynpac # wimconn_tapi from ..utils import ensure, partition, pipe from .actions import IGNORE, PENDING, REFRESH from .errors import ( @@ -71,6 +71,7 @@ CONNECTORS = { "odl": wimconn_odl.OdlConnector, # "tapi": wimconn_tapi # Add extra connectors here + "dynpac": wimconn_dynpac.DynpacConnector } diff --git a/osm_ro/wim/wimconn_dynpac.py b/osm_ro/wim/wimconn_dynpac.py new file mode 100644 index 00000000..c85c7a1e --- /dev/null +++ b/osm_ro/wim/wimconn_dynpac.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +## +# Copyright 2018 David García, University of the Basque Country +# Copyright 2018 University of the Basque Country +# This file is part of openmano +# All Rights Reserved. +# Contact information at http://i2t.ehu.eus +# +# # 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 requests +import json +import logging +from enum import Enum + +from wimconn import WimConnector, WimConnectorError + + +class WimError(Enum): + UNREACHABLE = 'Unable to reach the WIM.', + SERVICE_TYPE_ERROR = 'Unexpected service_type. Only "L2" is accepted.', + CONNECTION_POINTS_SIZE = \ + 'Unexpected number of connection points: 2 expected.', + ENCAPSULATION_TYPE = \ + 'Unexpected service_endpoint_encapsulation_type. \ + Only "dotq1" is accepted.', + BANDWIDTH = 'Unable to get the bandwidth.', + STATUS = 'Unable to get the status for the service.', + DELETE = 'Unable to delete service.', + CLEAR_ALL = 'Unable to clear all the services', + UNKNOWN_ACTION = 'Unknown action invoked.', + BACKUP = 'Unable to get the backup parameter.', + UNSUPPORTED_FEATURE = "Unsupported feature" + + +class WimAPIActions(Enum): + CHECK_CONNECTIVITY = "CHECK_CONNECTIVITY", + CREATE_SERVICE = "CREATE_SERVICE", + DELETE_SERVICE = "DELETE_SERVICE", + CLEAR_ALL = "CLEAR_ALL", + SERVICE_STATUS = "SERVICE_STATUS", + + +class DynpacConnector(WimConnector): + __supported_service_types = ["ELINE (L2)"] + __supported_encapsulation_types = ["dot1q"] + __WIM_LOGGER = 'openmano.wimconn.dynpac' + __ENCAPSULATION_TYPE_PARAM = "service_endpoint_encapsulation_type" + __BACKUP_PARAM = "backup" + __BANDWIDTH_PARAM = "bandwidth" + __SERVICE_ENDPOINT_PARAM = "service_endpoint_id" + __WAN_SERVICE_ENDPOINT_PARAM = "wan_service_endpoint_id" + __WAN_MAPPING_INFO_PARAM = "wan_service_mapping_info" + __SW_ID_PARAM = "wan_switch_dpid" + __SW_PORT_PARAM = "wan_switch_port" + __VLAN_PARAM = "vlan" + + # Public functions exposed to the Resource Orchestrator + def __init__(self, wim, wim_account, config): + self.logger = logging.getLogger(self.__WIM_LOGGER) + self.__wim = wim + self.__wim_account = wim_account + self.__config = config + self.__wim_url = self.__wim.get("wim_url") + self.__user = wim_account.get("user") + self.__passwd = wim_account.get("passwd") + self.logger.info("Initialized.") + + def create_connectivity_service(self, + service_type, + connection_points, + **kwargs): + self.__check_service(service_type, connection_points, kwargs) + + body = self.__get_body(service_type, connection_points, kwargs) + + headers = {'Content-type': 'application/x-www-form-urlencoded'} + endpoint = "{}/service/create".format(self.__wim_url) + + try: + response = requests.post(endpoint, data=body, headers=headers) + except requests.exceptions.RequestException as e: + self.__exception(e.message, http_code=503) + + if response.status_code != 200: + error = json.loads(response.content) + reason = "Reason: {}. ".format(error.get("code")) + description = "Description: {}.".format(error.get("description")) + exception = reason + description + self.__exception(exception, http_code=response.status_code) + uuid = response.content + self.logger.info("Service with uuid {} created.".format(uuid)) + return (uuid, None) + + def edit_connectivity_service(self, service_uuid, + conn_info, connection_points, + **kwargs): + self.__exception(WimError.UNSUPPORTED_FEATURE, http_code=501) + + def get_connectivity_service_status(self, service_uuid): + endpoint = "{}/service/status/{}".format(self.__wim_url, service_uuid) + try: + response = requests.get(endpoint) + except requests.exceptions.RequestException as e: + self.__exception(e.message, http_code=503) + + if response.status_code != 200: + self.__exception(WimError.STATUS, http_code=response.status_code) + self.logger.info("Status for service with uuid {}: {}" + .format(service_uuid, response.content)) + return response.content + + def delete_connectivity_service(self, service_uuid, conn_info): + endpoint = "{}/service/delete/{}".format(self.__wim_url, service_uuid) + try: + response = requests.delete(endpoint) + except requests.exceptions.RequestException as e: + self.__exception(e.message, http_code=503) + if response.status_code != 200: + self.__exception(WimError.DELETE, http_code=response.status_code) + + self.logger.info("Service with uuid: {} deleted".format(service_uuid)) + + def clear_all_connectivity_services(self): + endpoint = "{}/service/clearAll".format(self.__wim_url) + try: + response = requests.delete(endpoint) + http_code = response.status_code + except requests.exceptions.RequestException as e: + self.__exception(e.message, http_code=503) + if http_code != 200: + self.__exception(WimError.CLEAR_ALL, http_code=http_code) + + self.logger.info("{} services deleted".format(response.content)) + return "{} services deleted".format(response.content) + + def check_connectivity(self): + endpoint = "{}/checkConnectivity".format(self.__wim_url) + auth = (self.__user, self.__passwd) + + try: + response = requests.get(endpoint, auth=auth) + http_code = response.status_code + except requests.exceptions.RequestException as e: + self.__exception(e.message, http_code=503) + + if http_code != 200: + self.__exception(WimError.UNREACHABLE, http_code=http_code) + self.logger.info("Connectivity checked") + + # Private functions + def __exception(self, x, **kwargs): + http_code = kwargs.get("http_code") + if hasattr(x, "value"): + error = x.value + else: + error = x + self.logger.error(error) + raise WimConnectorError(error, http_code=http_code) + + def __check_service(self, service_type, connection_points, kwargs): + if service_type not in self.__supported_service_types: + self.__exception(WimError.SERVICE_TYPE_ERROR, http_code=400) + + if len(connection_points) != 2: + self.__exception(WimError.CONNECTION_POINTS_SIZE, http_code=400) + + for connection_point in connection_points: + enc_type = connection_point.get(self.__ENCAPSULATION_TYPE_PARAM) + if enc_type not in self.__supported_encapsulation_types: + self.__exception(WimError.ENCAPSULATION_TYPE, http_code=400) + + bandwidth = kwargs.get(self.__BANDWIDTH_PARAM) + if not isinstance(bandwidth, int): + self.__exception(WimError.BANDWIDTH, http_code=400) + + backup = kwargs.get(self.__BACKUP_PARAM) + if not isinstance(backup, bool): + self.__exception(WimError.BACKUP, http_code=400) + + def __get_body(self, service_type, connection_points, kwargs): + port_mapping = self.__config.get("port_mapping") + selected_ports = [] + for connection_point in connection_points: + endpoint_id = connection_point.get(self.__SERVICE_ENDPOINT_PARAM) + port = filter(lambda x: x.get(self.__WAN_SERVICE_ENDPOINT_PARAM) + == endpoint_id, port_mapping)[0] + wsmpi_json = port.get(self.__WAN_MAPPING_INFO_PARAM) + port_info = json.loads(wsmpi_json) + selected_ports.append(port_info) + if service_type == "ELINE (L2)": + service_type = "L2" + body = { + "connection_points": [{ + "wan_switch_dpid": selected_ports[0].get(self.__SW_ID_PARAM), + "wan_switch_port": selected_ports[0].get(self.__SW_PORT_PARAM), + "wan_vlan": connection_points[0].get(self.__VLAN_PARAM) + }, { + "wan_switch_dpid": selected_ports[1].get(self.__SW_ID_PARAM), + "wan_switch_port": selected_ports[1].get(self.__SW_PORT_PARAM), + "wan_vlan": connection_points[1].get(self.__VLAN_PARAM) + }], + "bandwidth": kwargs.get(self.__BANDWIDTH_PARAM), + "service_type": service_type, + "backup": kwargs.get(self.__BACKUP_PARAM) + } + return "body={}".format(json.dumps(body))