Add Dynpac WIM Connector 46/7046/1
authorDavid García <david.garciaco@ehu.eus>
Mon, 10 Dec 2018 08:43:50 +0000 (09:43 +0100)
committerDavid García <david.garciaco@ehu.eus>
Mon, 10 Dec 2018 08:44:05 +0000 (09:44 +0100)
A file osm_ro.wim.wimconn_dynpac added, which defines a the DynpacConnector class

Change-Id: I72fbd5387cdf3c09bc69cdde10713e224b7876ed
Signed-off-by: David García <david.garciaco@ehu.eus>
openmano
osm_ro/wim/schemas.py
osm_ro/wim/wim_thread.py
osm_ro/wim/wimconn_dynpac.py [new file with mode: 0644]

index 1577656..13a93da 100755 (executable)
--- 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",
index a040405..f4be216 100644 (file)
@@ -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"}
index a13d6a2..fa64fbb 100644 (file)
@@ -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 (file)
index 0000000..c85c7a1
--- /dev/null
@@ -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))