blob: 7a84f2226f15f7de72ecf5bfef0189f97625df5a [file] [log] [blame]
David García00e29dd2018-12-10 09:43:50 +01001#!/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
24import requests
25import json
26import logging
27from enum import Enum
28
tierno72774862020-05-04 11:44:15 +000029from osm_ro_plugin.sdnconn import SdnConnectorBase, SdnConnectorError
David García00e29dd2018-12-10 09:43:50 +010030
31
tiernoed3e4d42019-10-21 15:31:27 +000032class SdnError(Enum):
sousaedu80135b92021-02-17 15:05:18 +010033 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ía0ce24dc2019-01-10 23:31:03 +010046 UNAUTHORIZED = "Failed while authenticating"
David García00e29dd2018-12-10 09:43:50 +010047
48
tiernoed3e4d42019-10-21 15:31:27 +000049class SdnAPIActions(Enum):
sousaedu80135b92021-02-17 15:05:18 +010050 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ía00e29dd2018-12-10 09:43:50 +010055
56
tiernoed3e4d42019-10-21 15:31:27 +000057class DynpacConnector(SdnConnectorBase):
sasiain1c7f6e42019-07-08 10:15:31 +020058 __supported_service_types = ["ELINE (L2)", "ELINE"]
David García00e29dd2018-12-10 09:43:50 +010059 __supported_encapsulation_types = ["dot1q"]
sousaedu80135b92021-02-17 15:05:18 +010060 __WIM_LOGGER = "ro.sdn.dynpac"
David García00e29dd2018-12-10 09:43:50 +010061 __ENCAPSULATION_TYPE_PARAM = "service_endpoint_encapsulation_type"
David García0ce24dc2019-01-10 23:31:03 +010062 __ENCAPSULATION_INFO_PARAM = "service_endpoint_encapsulation_info"
David García00e29dd2018-12-10 09:43:50 +010063 __BACKUP_PARAM = "backup"
64 __BANDWIDTH_PARAM = "bandwidth"
65 __SERVICE_ENDPOINT_PARAM = "service_endpoint_id"
tiernoed3e4d42019-10-21 15:31:27 +000066 __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ía00e29dd2018-12-10 09:43:50 +010070 __VLAN_PARAM = "vlan"
71
72 # Public functions exposed to the Resource Orchestrator
tiernoed3e4d42019-10-21 15:31:27 +000073 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ía00e29dd2018-12-10 09:43:50 +010076 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")
tiernoed3e4d42019-10-21 15:31:27 +000081 self.__passwd = wim_account.get("password")
David García00e29dd2018-12-10 09:43:50 +010082 self.logger.info("Initialized.")
83
tiernoed3e4d42019-10-21 15:31:27 +000084 def create_connectivity_service(self, service_type, connection_points, **kwargs):
David García00e29dd2018-12-10 09:43:50 +010085 self.__check_service(service_type, connection_points, kwargs)
86
87 body = self.__get_body(service_type, connection_points, kwargs)
88
sousaedu80135b92021-02-17 15:05:18 +010089 headers = {"Content-type": "application/x-www-form-urlencoded"}
David García00e29dd2018-12-10 09:43:50 +010090 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)
sousaedu80135b92021-02-17 15:05:18 +0100103
David García00e29dd2018-12-10 09:43:50 +0100104 uuid = response.content
105 self.logger.info("Service with uuid {} created.".format(uuid))
sousaedu80135b92021-02-17 15:05:18 +0100106
David García00e29dd2018-12-10 09:43:50 +0100107 return (uuid, None)
108
sousaedu80135b92021-02-17 15:05:18 +0100109 def edit_connectivity_service(
110 self, service_uuid, conn_info, connection_points, **kwargs
111 ):
tiernoed3e4d42019-10-21 15:31:27 +0000112 self.__exception(SdnError.UNSUPPORTED_FEATURE, http_code=501)
David García00e29dd2018-12-10 09:43:50 +0100113
114 def get_connectivity_service_status(self, service_uuid):
115 endpoint = "{}/service/status/{}".format(self.__wim_url, service_uuid)
sousaedu80135b92021-02-17 15:05:18 +0100116
David García00e29dd2018-12-10 09:43:50 +0100117 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:
tiernoed3e4d42019-10-21 15:31:27 +0000123 self.__exception(SdnError.STATUS, http_code=response.status_code)
sousaedu80135b92021-02-17 15:05:18 +0100124
125 self.logger.info(
126 "Status for service with uuid {}: {}".format(service_uuid, response.content)
127 )
128
David García00e29dd2018-12-10 09:43:50 +0100129 return response.content
130
131 def delete_connectivity_service(self, service_uuid, conn_info):
132 endpoint = "{}/service/delete/{}".format(self.__wim_url, service_uuid)
sousaedu80135b92021-02-17 15:05:18 +0100133
David García00e29dd2018-12-10 09:43:50 +0100134 try:
135 response = requests.delete(endpoint)
136 except requests.exceptions.RequestException as e:
137 self.__exception(e.message, http_code=503)
sousaedu80135b92021-02-17 15:05:18 +0100138
David García00e29dd2018-12-10 09:43:50 +0100139 if response.status_code != 200:
tiernoed3e4d42019-10-21 15:31:27 +0000140 self.__exception(SdnError.DELETE, http_code=response.status_code)
David García00e29dd2018-12-10 09:43:50 +0100141
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)
sousaedu80135b92021-02-17 15:05:18 +0100146
David García00e29dd2018-12-10 09:43:50 +0100147 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)
sousaedu80135b92021-02-17 15:05:18 +0100152
David García00e29dd2018-12-10 09:43:50 +0100153 if http_code != 200:
tiernoed3e4d42019-10-21 15:31:27 +0000154 self.__exception(SdnError.CLEAR_ALL, http_code=http_code)
David García00e29dd2018-12-10 09:43:50 +0100155
156 self.logger.info("{} services deleted".format(response.content))
sousaedu80135b92021-02-17 15:05:18 +0100157
David García00e29dd2018-12-10 09:43:50 +0100158 return "{} services deleted".format(response.content)
159
160 def check_connectivity(self):
161 endpoint = "{}/checkConnectivity".format(self.__wim_url)
David García0ce24dc2019-01-10 23:31:03 +0100162
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:
tiernoed3e4d42019-10-21 15:31:27 +0000170 self.__exception(SdnError.UNREACHABLE, http_code=http_code)
sousaedu80135b92021-02-17 15:05:18 +0100171
David García0ce24dc2019-01-10 23:31:03 +0100172 self.logger.info("Connectivity checked")
173
174 def check_credentials(self):
175 endpoint = "{}/checkCredentials".format(self.__wim_url)
David García00e29dd2018-12-10 09:43:50 +0100176 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:
tiernoed3e4d42019-10-21 15:31:27 +0000185 self.__exception(SdnError.UNAUTHORIZED, http_code=http_code)
sousaedu80135b92021-02-17 15:05:18 +0100186
David García0ce24dc2019-01-10 23:31:03 +0100187 self.logger.info("Credentials checked")
David García00e29dd2018-12-10 09:43:50 +0100188
189 # Private functions
190 def __exception(self, x, **kwargs):
191 http_code = kwargs.get("http_code")
sousaedu80135b92021-02-17 15:05:18 +0100192
David García00e29dd2018-12-10 09:43:50 +0100193 if hasattr(x, "value"):
194 error = x.value
195 else:
196 error = x
sousaedu80135b92021-02-17 15:05:18 +0100197
David García00e29dd2018-12-10 09:43:50 +0100198 self.logger.error(error)
sousaedu80135b92021-02-17 15:05:18 +0100199
tiernoed3e4d42019-10-21 15:31:27 +0000200 raise SdnConnectorError(error, http_code=http_code)
David García00e29dd2018-12-10 09:43:50 +0100201
202 def __check_service(self, service_type, connection_points, kwargs):
203 if service_type not in self.__supported_service_types:
tiernoed3e4d42019-10-21 15:31:27 +0000204 self.__exception(SdnError.SERVICE_TYPE_ERROR, http_code=400)
David García00e29dd2018-12-10 09:43:50 +0100205
206 if len(connection_points) != 2:
tiernoed3e4d42019-10-21 15:31:27 +0000207 self.__exception(SdnError.CONNECTION_POINTS_SIZE, http_code=400)
David García00e29dd2018-12-10 09:43:50 +0100208
209 for connection_point in connection_points:
210 enc_type = connection_point.get(self.__ENCAPSULATION_TYPE_PARAM)
sousaedu80135b92021-02-17 15:05:18 +0100211
David García00e29dd2018-12-10 09:43:50 +0100212 if enc_type not in self.__supported_encapsulation_types:
tiernoed3e4d42019-10-21 15:31:27 +0000213 self.__exception(SdnError.ENCAPSULATION_TYPE, http_code=400)
David García00e29dd2018-12-10 09:43:50 +0100214
sasiain1c7f6e42019-07-08 10:15:31 +0200215 # Commented out for as long as parameter isn't implemented
216 # bandwidth = kwargs.get(self.__BANDWIDTH_PARAM)
217 # if not isinstance(bandwidth, int):
sousaedu80135b92021-02-17 15:05:18 +0100218 # self.__exception(SdnError.BANDWIDTH, http_code=400)
David García00e29dd2018-12-10 09:43:50 +0100219
sasiain1c7f6e42019-07-08 10:15:31 +0200220 # Commented out for as long as parameter isn't implemented
221 # backup = kwargs.get(self.__BACKUP_PARAM)
222 # if not isinstance(backup, bool):
sousaedu80135b92021-02-17 15:05:18 +0100223 # self.__exception(SdnError.BACKUP, http_code=400)
David García00e29dd2018-12-10 09:43:50 +0100224
225 def __get_body(self, service_type, connection_points, kwargs):
sasiain1c7f6e42019-07-08 10:15:31 +0200226 port_mapping = self.__config.get("service_endpoint_mapping")
David García00e29dd2018-12-10 09:43:50 +0100227 selected_ports = []
sousaedu80135b92021-02-17 15:05:18 +0100228
David García00e29dd2018-12-10 09:43:50 +0100229 for connection_point in connection_points:
230 endpoint_id = connection_point.get(self.__SERVICE_ENDPOINT_PARAM)
sousaedu80135b92021-02-17 15:05:18 +0100231 port = filter(
232 lambda x: x.get(self.__WAN_SERVICE_ENDPOINT_PARAM) == endpoint_id,
233 port_mapping,
234 )[0]
sasiain1c7f6e42019-07-08 10:15:31 +0200235 port_info = port.get(self.__WAN_MAPPING_INFO_PARAM)
David García00e29dd2018-12-10 09:43:50 +0100236 selected_ports.append(port_info)
sousaedu80135b92021-02-17 15:05:18 +0100237
sasiain1c7f6e42019-07-08 10:15:31 +0200238 if service_type == "ELINE (L2)" or service_type == "ELINE":
David García00e29dd2018-12-10 09:43:50 +0100239 service_type = "L2"
sousaedu80135b92021-02-17 15:05:18 +0100240
David García00e29dd2018-12-10 09:43:50 +0100241 body = {
sousaedu80135b92021-02-17 15:05:18 +0100242 "connection_points": [
243 {
244 "wan_switch_dpid": selected_ports[0].get(self.__SW_ID_PARAM),
245 "wan_switch_port": selected_ports[0].get(self.__SW_PORT_PARAM),
246 "wan_vlan": connection_points[0]
247 .get(self.__ENCAPSULATION_INFO_PARAM)
248 .get(self.__VLAN_PARAM),
249 },
250 {
251 "wan_switch_dpid": selected_ports[1].get(self.__SW_ID_PARAM),
252 "wan_switch_port": selected_ports[1].get(self.__SW_PORT_PARAM),
253 "wan_vlan": connection_points[1]
254 .get(self.__ENCAPSULATION_INFO_PARAM)
255 .get(self.__VLAN_PARAM),
256 },
257 ],
sasiain1c7f6e42019-07-08 10:15:31 +0200258 "bandwidth": 100, # Hardcoded for as long as parameter isn't implemented
David García00e29dd2018-12-10 09:43:50 +0100259 "service_type": service_type,
sousaedu80135b92021-02-17 15:05:18 +0100260 "backup": False, # Hardcoded for as long as parameter isn't implemented
David García00e29dd2018-12-10 09:43:50 +0100261 }
sousaedu80135b92021-02-17 15:05:18 +0100262
David García00e29dd2018-12-10 09:43:50 +0100263 return "body={}".format(json.dumps(body))