| #!/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. |
| |
| from enum import Enum |
| import json |
| import logging |
| |
| from osm_ro_plugin.sdnconn import SdnConnectorBase, SdnConnectorError |
| import requests |
| |
| |
| class SdnError(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" |
| UNAUTHORIZED = "Failed while authenticating" |
| |
| |
| class SdnAPIActions(Enum): |
| CHECK_CONNECTIVITY = "CHECK_CONNECTIVITY" |
| CREATE_SERVICE = "CREATE_SERVICE" |
| DELETE_SERVICE = "DELETE_SERVICE" |
| CLEAR_ALL = "CLEAR_ALL" |
| SERVICE_STATUS = "SERVICE_STATUS" |
| |
| |
| class DynpacConnector(SdnConnectorBase): |
| __supported_service_types = ["ELINE (L2)", "ELINE"] |
| __supported_encapsulation_types = ["dot1q"] |
| __WIM_LOGGER = "ro.sdn.dynpac" |
| __ENCAPSULATION_TYPE_PARAM = "service_endpoint_encapsulation_type" |
| __ENCAPSULATION_INFO_PARAM = "service_endpoint_encapsulation_info" |
| __BACKUP_PARAM = "backup" |
| __BANDWIDTH_PARAM = "bandwidth" |
| __SERVICE_ENDPOINT_PARAM = "service_endpoint_id" |
| __WAN_SERVICE_ENDPOINT_PARAM = "service_endpoint_id" |
| __WAN_MAPPING_INFO_PARAM = "service_mapping_info" |
| __SW_ID_PARAM = "switch_dpid" |
| __SW_PORT_PARAM = "switch_port" |
| __VLAN_PARAM = "vlan" |
| |
| # Public functions exposed to the Resource Orchestrator |
| def __init__(self, wim, wim_account, config=None, logger=None): |
| self.logger = logger or logging.getLogger(self.__WIM_LOGGER) |
| super().__init__(wim, wim_account, config, self.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("password") |
| 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(SdnError.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(SdnError.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(SdnError.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(SdnError.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) |
| |
| try: |
| response = requests.get(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(SdnError.UNREACHABLE, http_code=http_code) |
| |
| self.logger.info("Connectivity checked") |
| |
| def check_credentials(self): |
| endpoint = "{}/checkCredentials".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(SdnError.UNAUTHORIZED, http_code=http_code) |
| |
| self.logger.info("Credentials 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 SdnConnectorError(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(SdnError.SERVICE_TYPE_ERROR, http_code=400) |
| |
| if len(connection_points) != 2: |
| self.__exception(SdnError.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(SdnError.ENCAPSULATION_TYPE, http_code=400) |
| |
| # Commented out for as long as parameter isn't implemented |
| # bandwidth = kwargs.get(self.__BANDWIDTH_PARAM) |
| # if not isinstance(bandwidth, int): |
| # self.__exception(SdnError.BANDWIDTH, http_code=400) |
| |
| # Commented out for as long as parameter isn't implemented |
| # backup = kwargs.get(self.__BACKUP_PARAM) |
| # if not isinstance(backup, bool): |
| # self.__exception(SdnError.BACKUP, http_code=400) |
| |
| def __get_body(self, service_type, connection_points, kwargs): |
| port_mapping = self.__config.get("service_endpoint_mapping") |
| selected_ports = [] |
| |
| for connection_point in connection_points: |
| endpoint_id = connection_point.get(self.__SERVICE_ENDPOINT_PARAM) |
| port = list( |
| filter( |
| lambda x: x.get(self.__WAN_SERVICE_ENDPOINT_PARAM) == endpoint_id, |
| port_mapping, |
| ) |
| )[0] |
| port_info = port.get(self.__WAN_MAPPING_INFO_PARAM) |
| selected_ports.append(port_info) |
| |
| if service_type == "ELINE (L2)" or service_type == "ELINE": |
| 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.__ENCAPSULATION_INFO_PARAM) |
| .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.__ENCAPSULATION_INFO_PARAM) |
| .get(self.__VLAN_PARAM), |
| }, |
| ], |
| "bandwidth": 100, # Hardcoded for as long as parameter isn't implemented |
| "service_type": service_type, |
| "backup": False, # Hardcoded for as long as parameter isn't implemented |
| } |
| |
| return "body={}".format(json.dumps(body)) |