Bug 780 fixed - port_mapping reading error
[osm/RO.git] / osm_ro / wim / wimconn_ietfl2vpn.py
1 # -*- coding: utf-8 -*-
2 ##
3 # Copyright 2018 Telefonica
4 # All Rights Reserved.
5 #
6 # Contributors: Oscar Gonzalez de Dios, Manuel Lopez Bravo, Guillermo Pajares Martin
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16 # implied.
17 # See the License for the specific language governing permissions and
18 # limitations under the License.
19 #
20 # This work has been performed in the context of the Metro-Haul project -
21 # funded by the European Commission under Grant number 761727 through the
22 # Horizon 2020 program.
23 ##
24 """The WIM connector is responsible for establishing wide area network
25 connectivity.
26
27 This WIM connector implements the standard IETF RFC 8466 "A YANG Data
28 Model for Layer 2 Virtual Private Network (L2VPN) Service Delivery"
29
30 It receives the endpoints and the necessary details to request
31 the Layer 2 service.
32 """
33 import requests
34 import uuid
35 import logging
36 from .wimconn import WimConnector, WimConnectorError
37 """CHeck layer where we move it"""
38
39
40 class WimconnectorIETFL2VPN(WimConnector):
41
42 def __init__(self, wim, wim_account, config=None, logger=None):
43 """IETF L2VPM WIM connector
44
45 Arguments: (To be completed)
46 wim (dict): WIM record, as stored in the database
47 wim_account (dict): WIM account record, as stored in the database
48 """
49 self.logger = logging.getLogger('openmano.wimconn.ietfl2vpn')
50 super(WimconnectorIETFL2VPN, self).__init__(wim, wim_account, config, logger)
51 self.headers = {'Content-Type': 'application/json'}
52 self.mappings = {m['wan_service_endpoint_id']: m
53 for m in self.service_endpoint_mapping}
54 self.user = wim_account.get("user")
55 self.passwd = wim_account.get("passwd")
56 if self.user and self.passwd is not None:
57 self.auth = (self.user, self.passwd)
58 else:
59 self.auth = None
60 self.logger.info("IETFL2VPN Connector Initialized.")
61
62 def check_credentials(self):
63 endpoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(self.wim["wim_url"])
64 try:
65 response = requests.get(endpoint, auth=self.auth)
66 http_code = response.status_code
67 except requests.exceptions.RequestException as e:
68 raise WimConnectorError(e.message, http_code=503)
69
70 if http_code != 200:
71 raise WimConnectorError("Failed while authenticating", http_code=http_code)
72 self.logger.info("Credentials checked")
73
74 def get_connectivity_service_status(self, service_uuid, conn_info=None):
75 """Monitor the status of the connectivity service stablished
76
77 Arguments:
78 service_uuid: Connectivity service unique identifier
79
80 Returns:
81 Examples::
82 {'wim_status': 'ACTIVE'}
83 {'wim_status': 'INACTIVE'}
84 {'wim_status': 'DOWN'}
85 {'wim_status': 'ERROR'}
86 """
87 try:
88 self.logger.info("Sending get connectivity service stuatus")
89 servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format(
90 self.wim["wim_url"], service_uuid)
91 response = requests.get(servicepoint, auth=self.auth)
92 if response.status_code != requests.codes.ok:
93 raise WimConnectorError("Unable to obtain connectivity servcice status", http_code=response.status_code)
94 service_status = {'wim_status': 'ACTIVE'}
95 return service_status
96 except requests.exceptions.ConnectionError:
97 raise WimConnectorError("Request Timeout", http_code=408)
98
99 def search_mapp(self, connection_point):
100 id = connection_point['service_endpoint_id']
101 if id not in self.mappings:
102 raise WimConnectorError("Endpoint {} not located".format(str(id)))
103 else:
104 return self.mappings[id]
105
106 def create_connectivity_service(self, service_type, connection_points, **kwargs):
107 """Stablish WAN connectivity between the endpoints
108
109 Arguments:
110 service_type (str): ``ELINE`` (L2), ``ELAN`` (L2), ``ETREE`` (L2),
111 ``L3``.
112 connection_points (list): each point corresponds to
113 an entry point from the DC to the transport network. One
114 connection point serves to identify the specific access and
115 some other service parameters, such as encapsulation type.
116 Represented by a dict as follows::
117
118 {
119 "service_endpoint_id": ..., (str[uuid])
120 "service_endpoint_encapsulation_type": ...,
121 (enum: none, dot1q, ...)
122 "service_endpoint_encapsulation_info": {
123 ... (dict)
124 "vlan": ..., (int, present if encapsulation is dot1q)
125 "vni": ... (int, present if encapsulation is vxlan),
126 "peers": [(ipv4_1), (ipv4_2)]
127 (present if encapsulation is vxlan)
128 }
129 }
130
131 The service endpoint ID should be previously informed to the WIM
132 engine in the RO when the WIM port mapping is registered.
133
134 Keyword Arguments:
135 bandwidth (int): value in kilobytes
136 latency (int): value in milliseconds
137
138 Other QoS might be passed as keyword arguments.
139
140 Returns:
141 tuple: ``(service_id, conn_info)`` containing:
142 - *service_uuid* (str): UUID of the established connectivity
143 service
144 - *conn_info* (dict or None): Information to be stored at the
145 database (or ``None``). This information will be provided to
146 the :meth:`~.edit_connectivity_service` and :obj:`~.delete`.
147 **MUST** be JSON/YAML-serializable (plain data structures).
148
149 Raises:
150 WimConnectorException: In case of error.
151 """
152 if service_type == "ELINE":
153 if len(connection_points) > 2:
154 raise WimConnectorError('Connections between more than 2 endpoints are not supported')
155 if len(connection_points) < 2:
156 raise WimConnectorError('Connections must be of at least 2 endpoints')
157 """ First step, create the vpn service """
158 uuid_l2vpn = str(uuid.uuid4())
159 vpn_service = {}
160 vpn_service["vpn-id"] = uuid_l2vpn
161 vpn_service["vpn-scv-type"] = "vpws"
162 vpn_service["svc-topo"] = "any-to-any"
163 vpn_service["customer-name"] = "osm"
164 vpn_service_list = []
165 vpn_service_list.append(vpn_service)
166 vpn_service_l = {"vpn-service": vpn_service_list}
167 response_service_creation = None
168 conn_info = []
169 self.logger.info("Sending vpn-service :{}".format(vpn_service_l))
170 try:
171 endpoint_service_creation = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(
172 self.wim["wim_url"])
173 response_service_creation = requests.post(endpoint_service_creation, headers=self.headers,
174 json=vpn_service_l, auth=self.auth)
175 except requests.exceptions.ConnectionError:
176 raise WimConnectorError("Request to create service Timeout", http_code=408)
177 if response_service_creation.status_code == 409:
178 raise WimConnectorError("Service already exists", http_code=response_service_creation.status_code)
179 elif response_service_creation.status_code != requests.codes.created:
180 raise WimConnectorError("Request to create service not accepted",
181 http_code=response_service_creation.status_code)
182 """ Second step, create the connections and vpn attachments """
183 for connection_point in connection_points:
184 connection_point_wan_info = self.search_mapp(connection_point)
185 site_network_access = {}
186 connection = {}
187 if connection_point["service_endpoint_encapsulation_type"] != "none":
188 if connection_point["service_endpoint_encapsulation_type"] == "dot1q":
189 """ The connection is a VLAN """
190 connection["encapsulation-type"] = "dot1q-vlan-tagged"
191 tagged = {}
192 tagged_interf = {}
193 service_endpoint_encapsulation_info = connection_point["service_endpoint_encapsulation_info"]
194 if service_endpoint_encapsulation_info["vlan"] is None:
195 raise WimConnectorError("VLAN must be provided")
196 tagged_interf["cvlan-id"] = service_endpoint_encapsulation_info["vlan"]
197 tagged["dot1q-vlan-tagged"] = tagged_interf
198 connection["tagged-interface"] = tagged
199 else:
200 raise NotImplementedError("Encapsulation type not implemented")
201 site_network_access["connection"] = connection
202 self.logger.info("Sending connection:{}".format(connection))
203 vpn_attach = {}
204 vpn_attach["vpn-id"] = uuid_l2vpn
205 vpn_attach["site-role"] = vpn_service["svc-topo"]+"-role"
206 site_network_access["vpn-attachment"] = vpn_attach
207 self.logger.info("Sending vpn-attachement :{}".format(vpn_attach))
208 uuid_sna = str(uuid.uuid4())
209 site_network_access["network-access-id"] = uuid_sna
210 site_network_accesses = {}
211 site_network_access_list = []
212 site_network_access_list.append(site_network_access)
213 site_network_accesses["site-network-access"] = site_network_access_list
214 conn_info_d = {}
215 conn_info_d["site"] = connection_point_wan_info["wan_service_mapping_info"]["site-id"]
216 conn_info_d["site-network-access-id"] = site_network_access["network-access-id"]
217 conn_info_d["mapping"] = None
218 conn_info.append(conn_info_d)
219 try:
220 endpoint_site_network_access_creation = \
221 "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/sites/site={}/site-network-accesses/".format(
222 self.wim["wim_url"], connection_point_wan_info["wan_service_mapping_info"]["site-id"])
223 response_endpoint_site_network_access_creation = requests.post(
224 endpoint_site_network_access_creation,
225 headers=self.headers,
226 json=site_network_accesses,
227 auth=self.auth)
228
229 if response_endpoint_site_network_access_creation.status_code == 409:
230 self.delete_connectivity_service(vpn_service["vpn-id"])
231 raise WimConnectorError("Site_Network_Access with ID '{}' already exists".format(
232 site_network_access["network-access-id"]),
233 http_code=response_endpoint_site_network_access_creation.status_code)
234
235 elif response_endpoint_site_network_access_creation.status_code == 400:
236 self.delete_connectivity_service(vpn_service["vpn-id"])
237 raise WimConnectorError("Site {} does not exist".format(
238 connection_point_wan_info["wan_service_mapping_info"]["site-id"]),
239 http_code=response_endpoint_site_network_access_creation.status_code)
240
241 elif response_endpoint_site_network_access_creation.status_code != requests.codes.created and \
242 response_endpoint_site_network_access_creation.status_code != requests.codes.no_content:
243 self.delete_connectivity_service(vpn_service["vpn-id"])
244 raise WimConnectorError("Request no accepted",
245 http_code=response_endpoint_site_network_access_creation.status_code)
246
247 except requests.exceptions.ConnectionError:
248 self.delete_connectivity_service(vpn_service["vpn-id"])
249 raise WimConnectorError("Request Timeout", http_code=408)
250 return uuid_l2vpn, conn_info
251
252 else:
253 raise NotImplementedError
254
255 def delete_connectivity_service(self, service_uuid, conn_info=None):
256 """Disconnect multi-site endpoints previously connected
257
258 This method should receive as the first argument the UUID generated by
259 the ``create_connectivity_service``
260 """
261 try:
262 self.logger.info("Sending delete")
263 servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format(
264 self.wim["wim_url"], service_uuid)
265 response = requests.delete(servicepoint, auth=self.auth)
266 if response.status_code != requests.codes.no_content:
267 raise WimConnectorError("Error in the request", http_code=response.status_code)
268 except requests.exceptions.ConnectionError:
269 raise WimConnectorError("Request Timeout", http_code=408)
270
271 def edit_connectivity_service(self, service_uuid, conn_info=None,
272 connection_points=None, **kwargs):
273 """Change an existing connectivity service, see
274 ``create_connectivity_service``"""
275
276 # sites = {"sites": {}}
277 # site_list = []
278 vpn_service = {}
279 vpn_service["svc-topo"] = "any-to-any"
280 counter = 0
281 for connection_point in connection_points:
282 site_network_access = {}
283 connection_point_wan_info = self.search_mapp(connection_point)
284 params_site = {}
285 params_site["site-id"] = connection_point_wan_info["wan_service_mapping_info"]["site-id"]
286 params_site["site-vpn-flavor"] = "site-vpn-flavor-single"
287 device_site = {}
288 device_site["device-id"] = connection_point_wan_info["device-id"]
289 params_site["devices"] = device_site
290 # network_access = {}
291 connection = {}
292 if connection_point["service_endpoint_encapsulation_type"] != "none":
293 if connection_point["service_endpoint_encapsulation_type"] == "dot1q":
294 """ The connection is a VLAN """
295 connection["encapsulation-type"] = "dot1q-vlan-tagged"
296 tagged = {}
297 tagged_interf = {}
298 service_endpoint_encapsulation_info = connection_point["service_endpoint_encapsulation_info"]
299 if service_endpoint_encapsulation_info["vlan"] is None:
300 raise WimConnectorError("VLAN must be provided")
301 tagged_interf["cvlan-id"] = service_endpoint_encapsulation_info["vlan"]
302 tagged["dot1q-vlan-tagged"] = tagged_interf
303 connection["tagged-interface"] = tagged
304 else:
305 raise NotImplementedError("Encapsulation type not implemented")
306 site_network_access["connection"] = connection
307 vpn_attach = {}
308 vpn_attach["vpn-id"] = service_uuid
309 vpn_attach["site-role"] = vpn_service["svc-topo"]+"-role"
310 site_network_access["vpn-attachment"] = vpn_attach
311 uuid_sna = conn_info[counter]["site-network-access-id"]
312 site_network_access["network-access-id"] = uuid_sna
313 site_network_accesses = {}
314 site_network_access_list = []
315 site_network_access_list.append(site_network_access)
316 site_network_accesses["site-network-access"] = site_network_access_list
317 try:
318 endpoint_site_network_access_edit = \
319 "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/sites/site={}/site-network-accesses/".format(
320 self.wim["wim_url"], connection_point_wan_info["wan_service_mapping_info"]["site-id"])
321 response_endpoint_site_network_access_creation = requests.put(endpoint_site_network_access_edit,
322 headers=self.headers,
323 json=site_network_accesses,
324 auth=self.auth)
325 if response_endpoint_site_network_access_creation.status_code == 400:
326 raise WimConnectorError("Service does not exist",
327 http_code=response_endpoint_site_network_access_creation.status_code)
328 elif response_endpoint_site_network_access_creation.status_code != 201 and \
329 response_endpoint_site_network_access_creation.status_code != 204:
330 raise WimConnectorError("Request no accepted",
331 http_code=response_endpoint_site_network_access_creation.status_code)
332 except requests.exceptions.ConnectionError:
333 raise WimConnectorError("Request Timeout", http_code=408)
334 counter += 1
335 return None
336
337 def clear_all_connectivity_services(self):
338 """Delete all WAN Links corresponding to a WIM"""
339 try:
340 self.logger.info("Sending clear all connectivity services")
341 servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(self.wim["wim_url"])
342 response = requests.delete(servicepoint, auth=self.auth)
343 if response.status_code != requests.codes.no_content:
344 raise WimConnectorError("Unable to clear all connectivity services", http_code=response.status_code)
345 except requests.exceptions.ConnectionError:
346 raise WimConnectorError("Request Timeout", http_code=408)
347
348 def get_all_active_connectivity_services(self):
349 """Provide information about all active connections provisioned by a
350 WIM
351 """
352 try:
353 self.logger.info("Sending get all connectivity services")
354 servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(self.wim["wim_url"])
355 response = requests.get(servicepoint, auth=self.auth)
356 if response.status_code != requests.codes.ok:
357 raise WimConnectorError("Unable to get all connectivity services", http_code=response.status_code)
358 return response
359 except requests.exceptions.ConnectionError:
360 raise WimConnectorError("Request Timeout", http_code=408)