| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # -*- coding: utf-8 -*- |
| 3 | |
| 4 | ## |
| 5 | # Copyright 2018 David García, University of the Basque Country |
| 6 | # Copyright 2018 University of the Basque Country |
| 7 | # This file is part of openmano |
| 8 | # All Rights Reserved. |
| 9 | # Contact information at http://i2t.ehu.eus |
| 10 | # |
| 11 | # # Licensed under the Apache License, Version 2.0 (the "License"); |
| 12 | # you may not use this file except in compliance with the License. |
| 13 | # You may obtain a copy of the License at |
| 14 | # |
| 15 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 16 | # |
| 17 | # Unless required by applicable law or agreed to in writing, software |
| 18 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| 20 | # implied. |
| 21 | # See the License for the specific language governing permissions and |
| 22 | # limitations under the License. |
| 23 | |
| sousaedu | 049cbb1 | 2022-01-05 11:39:35 +0000 | [diff] [blame] | 24 | from enum import Enum |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 25 | import json |
| 26 | import logging |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 27 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 28 | from osm_ro_plugin.sdnconn import SdnConnectorBase, SdnConnectorError |
| sousaedu | 049cbb1 | 2022-01-05 11:39:35 +0000 | [diff] [blame] | 29 | import requests |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 30 | |
| 31 | |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 32 | class SdnError(Enum): |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 33 | UNREACHABLE = "Unable to reach the WIM." |
| 34 | SERVICE_TYPE_ERROR = 'Unexpected service_type. Only "L2" is accepted.' |
| 35 | CONNECTION_POINTS_SIZE = "Unexpected number of connection points: 2 expected." |
| 36 | ENCAPSULATION_TYPE = ( |
| 37 | 'Unexpected service_endpoint_encapsulation_type. Only "dotq1" is accepted.' |
| 38 | ) |
| 39 | BANDWIDTH = "Unable to get the bandwidth." |
| 40 | STATUS = "Unable to get the status for the service." |
| 41 | DELETE = "Unable to delete service." |
| 42 | CLEAR_ALL = "Unable to clear all the services" |
| 43 | UNKNOWN_ACTION = "Unknown action invoked." |
| 44 | BACKUP = "Unable to get the backup parameter." |
| 45 | UNSUPPORTED_FEATURE = "Unsupported feature" |
| David García | 0ce24dc | 2019-01-10 23:31:03 +0100 | [diff] [blame] | 46 | UNAUTHORIZED = "Failed while authenticating" |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 47 | |
| 48 | |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 49 | class SdnAPIActions(Enum): |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 50 | CHECK_CONNECTIVITY = "CHECK_CONNECTIVITY" |
| 51 | CREATE_SERVICE = "CREATE_SERVICE" |
| 52 | DELETE_SERVICE = "DELETE_SERVICE" |
| 53 | CLEAR_ALL = "CLEAR_ALL" |
| 54 | SERVICE_STATUS = "SERVICE_STATUS" |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 55 | |
| 56 | |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 57 | class DynpacConnector(SdnConnectorBase): |
| sasiain | 1c7f6e4 | 2019-07-08 10:15:31 +0200 | [diff] [blame] | 58 | __supported_service_types = ["ELINE (L2)", "ELINE"] |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 59 | __supported_encapsulation_types = ["dot1q"] |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 60 | __WIM_LOGGER = "ro.sdn.dynpac" |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 61 | __ENCAPSULATION_TYPE_PARAM = "service_endpoint_encapsulation_type" |
| David García | 0ce24dc | 2019-01-10 23:31:03 +0100 | [diff] [blame] | 62 | __ENCAPSULATION_INFO_PARAM = "service_endpoint_encapsulation_info" |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 63 | __BACKUP_PARAM = "backup" |
| 64 | __BANDWIDTH_PARAM = "bandwidth" |
| 65 | __SERVICE_ENDPOINT_PARAM = "service_endpoint_id" |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 66 | __WAN_SERVICE_ENDPOINT_PARAM = "service_endpoint_id" |
| 67 | __WAN_MAPPING_INFO_PARAM = "service_mapping_info" |
| 68 | __SW_ID_PARAM = "switch_dpid" |
| 69 | __SW_PORT_PARAM = "switch_port" |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 70 | __VLAN_PARAM = "vlan" |
| 71 | |
| 72 | # Public functions exposed to the Resource Orchestrator |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 73 | def __init__(self, wim, wim_account, config=None, logger=None): |
| 74 | self.logger = logger or logging.getLogger(self.__WIM_LOGGER) |
| 75 | super().__init__(wim, wim_account, config, self.logger) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 76 | self.__wim = wim |
| 77 | self.__wim_account = wim_account |
| 78 | self.__config = config |
| 79 | self.__wim_url = self.__wim.get("wim_url") |
| 80 | self.__user = wim_account.get("user") |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 81 | self.__passwd = wim_account.get("password") |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 82 | self.logger.info("Initialized.") |
| 83 | |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 84 | def create_connectivity_service(self, service_type, connection_points, **kwargs): |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 85 | self.__check_service(service_type, connection_points, kwargs) |
| 86 | |
| 87 | body = self.__get_body(service_type, connection_points, kwargs) |
| 88 | |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 89 | headers = {"Content-type": "application/x-www-form-urlencoded"} |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 90 | endpoint = "{}/service/create".format(self.__wim_url) |
| 91 | |
| 92 | try: |
| 93 | response = requests.post(endpoint, data=body, headers=headers) |
| 94 | except requests.exceptions.RequestException as e: |
| 95 | self.__exception(e.message, http_code=503) |
| 96 | |
| 97 | if response.status_code != 200: |
| 98 | error = json.loads(response.content) |
| 99 | reason = "Reason: {}. ".format(error.get("code")) |
| 100 | description = "Description: {}.".format(error.get("description")) |
| 101 | exception = reason + description |
| 102 | self.__exception(exception, http_code=response.status_code) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 103 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 104 | uuid = response.content |
| 105 | self.logger.info("Service with uuid {} created.".format(uuid)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 106 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 107 | return (uuid, None) |
| 108 | |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 109 | def edit_connectivity_service( |
| 110 | self, service_uuid, conn_info, connection_points, **kwargs |
| 111 | ): |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 112 | self.__exception(SdnError.UNSUPPORTED_FEATURE, http_code=501) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 113 | |
| 114 | def get_connectivity_service_status(self, service_uuid): |
| 115 | endpoint = "{}/service/status/{}".format(self.__wim_url, service_uuid) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 116 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 117 | try: |
| 118 | response = requests.get(endpoint) |
| 119 | except requests.exceptions.RequestException as e: |
| 120 | self.__exception(e.message, http_code=503) |
| 121 | |
| 122 | if response.status_code != 200: |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 123 | self.__exception(SdnError.STATUS, http_code=response.status_code) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 124 | |
| 125 | self.logger.info( |
| 126 | "Status for service with uuid {}: {}".format(service_uuid, response.content) |
| 127 | ) |
| 128 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 129 | return response.content |
| 130 | |
| 131 | def delete_connectivity_service(self, service_uuid, conn_info): |
| 132 | endpoint = "{}/service/delete/{}".format(self.__wim_url, service_uuid) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 133 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 134 | try: |
| 135 | response = requests.delete(endpoint) |
| 136 | except requests.exceptions.RequestException as e: |
| 137 | self.__exception(e.message, http_code=503) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 138 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 139 | if response.status_code != 200: |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 140 | self.__exception(SdnError.DELETE, http_code=response.status_code) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 141 | |
| 142 | self.logger.info("Service with uuid: {} deleted".format(service_uuid)) |
| 143 | |
| 144 | def clear_all_connectivity_services(self): |
| 145 | endpoint = "{}/service/clearAll".format(self.__wim_url) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 146 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 147 | try: |
| 148 | response = requests.delete(endpoint) |
| 149 | http_code = response.status_code |
| 150 | except requests.exceptions.RequestException as e: |
| 151 | self.__exception(e.message, http_code=503) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 152 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 153 | if http_code != 200: |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 154 | self.__exception(SdnError.CLEAR_ALL, http_code=http_code) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 155 | |
| 156 | self.logger.info("{} services deleted".format(response.content)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 157 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 158 | return "{} services deleted".format(response.content) |
| 159 | |
| 160 | def check_connectivity(self): |
| 161 | endpoint = "{}/checkConnectivity".format(self.__wim_url) |
| David García | 0ce24dc | 2019-01-10 23:31:03 +0100 | [diff] [blame] | 162 | |
| 163 | try: |
| 164 | response = requests.get(endpoint) |
| 165 | http_code = response.status_code |
| 166 | except requests.exceptions.RequestException as e: |
| 167 | self.__exception(e.message, http_code=503) |
| 168 | |
| 169 | if http_code != 200: |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 170 | self.__exception(SdnError.UNREACHABLE, http_code=http_code) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 171 | |
| David García | 0ce24dc | 2019-01-10 23:31:03 +0100 | [diff] [blame] | 172 | self.logger.info("Connectivity checked") |
| 173 | |
| 174 | def check_credentials(self): |
| 175 | endpoint = "{}/checkCredentials".format(self.__wim_url) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 176 | auth = (self.__user, self.__passwd) |
| 177 | |
| 178 | try: |
| 179 | response = requests.get(endpoint, auth=auth) |
| 180 | http_code = response.status_code |
| 181 | except requests.exceptions.RequestException as e: |
| 182 | self.__exception(e.message, http_code=503) |
| 183 | |
| 184 | if http_code != 200: |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 185 | self.__exception(SdnError.UNAUTHORIZED, http_code=http_code) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 186 | |
| David García | 0ce24dc | 2019-01-10 23:31:03 +0100 | [diff] [blame] | 187 | self.logger.info("Credentials checked") |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 188 | |
| 189 | # Private functions |
| 190 | def __exception(self, x, **kwargs): |
| 191 | http_code = kwargs.get("http_code") |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 192 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 193 | if hasattr(x, "value"): |
| 194 | error = x.value |
| 195 | else: |
| 196 | error = x |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 197 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 198 | self.logger.error(error) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 199 | |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 200 | raise SdnConnectorError(error, http_code=http_code) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 201 | |
| 202 | def __check_service(self, service_type, connection_points, kwargs): |
| 203 | if service_type not in self.__supported_service_types: |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 204 | self.__exception(SdnError.SERVICE_TYPE_ERROR, http_code=400) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 205 | |
| 206 | if len(connection_points) != 2: |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 207 | self.__exception(SdnError.CONNECTION_POINTS_SIZE, http_code=400) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 208 | |
| 209 | for connection_point in connection_points: |
| 210 | enc_type = connection_point.get(self.__ENCAPSULATION_TYPE_PARAM) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 211 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 212 | if enc_type not in self.__supported_encapsulation_types: |
| tierno | ed3e4d4 | 2019-10-21 15:31:27 +0000 | [diff] [blame] | 213 | self.__exception(SdnError.ENCAPSULATION_TYPE, http_code=400) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 214 | |
| sasiain | 1c7f6e4 | 2019-07-08 10:15:31 +0200 | [diff] [blame] | 215 | # Commented out for as long as parameter isn't implemented |
| 216 | # bandwidth = kwargs.get(self.__BANDWIDTH_PARAM) |
| 217 | # if not isinstance(bandwidth, int): |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 218 | # self.__exception(SdnError.BANDWIDTH, http_code=400) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 219 | |
| sasiain | 1c7f6e4 | 2019-07-08 10:15:31 +0200 | [diff] [blame] | 220 | # Commented out for as long as parameter isn't implemented |
| 221 | # backup = kwargs.get(self.__BACKUP_PARAM) |
| 222 | # if not isinstance(backup, bool): |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 223 | # self.__exception(SdnError.BACKUP, http_code=400) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 224 | |
| 225 | def __get_body(self, service_type, connection_points, kwargs): |
| sasiain | 1c7f6e4 | 2019-07-08 10:15:31 +0200 | [diff] [blame] | 226 | port_mapping = self.__config.get("service_endpoint_mapping") |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 227 | selected_ports = [] |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 228 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 229 | for connection_point in connection_points: |
| 230 | endpoint_id = connection_point.get(self.__SERVICE_ENDPOINT_PARAM) |
| Gulsum Atici | 2f99505 | 2022-11-23 16:06:40 +0300 | [diff] [blame] | 231 | port = list( |
| 232 | filter( |
| 233 | lambda x: x.get(self.__WAN_SERVICE_ENDPOINT_PARAM) == endpoint_id, |
| 234 | port_mapping, |
| 235 | ) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 236 | )[0] |
| sasiain | 1c7f6e4 | 2019-07-08 10:15:31 +0200 | [diff] [blame] | 237 | port_info = port.get(self.__WAN_MAPPING_INFO_PARAM) |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 238 | selected_ports.append(port_info) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 239 | |
| sasiain | 1c7f6e4 | 2019-07-08 10:15:31 +0200 | [diff] [blame] | 240 | if service_type == "ELINE (L2)" or service_type == "ELINE": |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 241 | service_type = "L2" |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 242 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 243 | body = { |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 244 | "connection_points": [ |
| 245 | { |
| 246 | "wan_switch_dpid": selected_ports[0].get(self.__SW_ID_PARAM), |
| 247 | "wan_switch_port": selected_ports[0].get(self.__SW_PORT_PARAM), |
| 248 | "wan_vlan": connection_points[0] |
| 249 | .get(self.__ENCAPSULATION_INFO_PARAM) |
| 250 | .get(self.__VLAN_PARAM), |
| 251 | }, |
| 252 | { |
| 253 | "wan_switch_dpid": selected_ports[1].get(self.__SW_ID_PARAM), |
| 254 | "wan_switch_port": selected_ports[1].get(self.__SW_PORT_PARAM), |
| 255 | "wan_vlan": connection_points[1] |
| 256 | .get(self.__ENCAPSULATION_INFO_PARAM) |
| 257 | .get(self.__VLAN_PARAM), |
| 258 | }, |
| 259 | ], |
| sasiain | 1c7f6e4 | 2019-07-08 10:15:31 +0200 | [diff] [blame] | 260 | "bandwidth": 100, # Hardcoded for as long as parameter isn't implemented |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 261 | "service_type": service_type, |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 262 | "backup": False, # Hardcoded for as long as parameter isn't implemented |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 263 | } |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 264 | |
| David García | 00e29dd | 2018-12-10 09:43:50 +0100 | [diff] [blame] | 265 | return "body={}".format(json.dumps(body)) |