1 # -*- coding: utf-8 -*-
3 # Copyright 2018 Telefonica
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
11 # http://www.apache.org/licenses/LICENSE-2.0
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
17 # See the License for the specific language governing permissions and
18 # limitations under the License.
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.
24 """The WIM connector is responsible for establishing wide area network
27 This WIM connector implements the standard IETF RFC 8466 "A YANG Data
28 Model for Layer 2 Virtual Private Network (L2VPN) Service Delivery"
30 It receives the endpoints and the necessary details to request
38 from .wimconn
import WimConnector
, WimConnectorError
39 """CHeck layer where we move it"""
41 class WimconnectorIETFL2VPN(WimConnector
):
42 """IETF L2VPM WIM connector
44 Arguments: (To be completed)
45 wim (dict): WIM record, as stored in the database
46 wim_account (dict): WIM account record, as stored in the database
48 def __init__(self
, wim
, wim_account
, config
=None, logger
=None):
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
!= None and self
.passwd
!= None:
57 self
.auth
= (self
.user
, self
.passwd
)
60 self
.logger
.info("IETFL2VPN Connector Initialized.")
62 def check_credentials(self
):
63 endpoint
= "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(self
.wim
["wim_url"])
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)
71 raise WimConnectorError("Failed while authenticating", http_code
=http_code
)
72 self
.logger
.info("Credentials checked")
75 def get_connectivity_service_status(self
, service_uuid
, conn_info
=None):
76 """Monitor the status of the connectivity service stablished
79 service_uuid: Connectivity service unique identifier
83 {'wim_status': 'ACTIVE'}
84 {'wim_status': 'INACTIVE'}
85 {'wim_status': 'DOWN'}
86 {'wim_status': 'ERROR'}
89 self
.logger
.info("Sending get connectivity service stuatus")
90 servicepoint
="{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format(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'}
96 except requests
.exceptions
.ConnectionError
:
97 raise WimConnectorError("Request Timeout",http_code
=408)
100 def search_mapp(self
,connection_point
):
101 id = connection_point
['service_endpoint_id']
102 if id not in self
.mappings
:
103 raise WimConnectorError("Endpoint {} not located".format(str(id)))
105 return self
.mappings
[id]
108 def create_connectivity_service(self
, service_type
, connection_points
,
110 """Stablish WAN connectivity between the endpoints
113 service_type (str): ``ELINE`` (L2), ``ELAN`` (L2), ``ETREE`` (L2),
115 connection_points (list): each point corresponds to
116 an entry point from the DC to the transport network. One
117 connection point serves to identify the specific access and
118 some other service parameters, such as encapsulation type.
119 Represented by a dict as follows::
122 "service_endpoint_id": ..., (str[uuid])
123 "service_endpoint_encapsulation_type": ...,
124 (enum: none, dot1q, ...)
125 "service_endpoint_encapsulation_info": {
127 "vlan": ..., (int, present if encapsulation is dot1q)
128 "vni": ... (int, present if encapsulation is vxlan),
129 "peers": [(ipv4_1), (ipv4_2)]
130 (present if encapsulation is vxlan)
134 The service endpoint ID should be previously informed to the WIM
135 engine in the RO when the WIM port mapping is registered.
138 bandwidth (int): value in kilobytes
139 latency (int): value in milliseconds
141 Other QoS might be passed as keyword arguments.
144 tuple: ``(service_id, conn_info)`` containing:
145 - *service_uuid* (str): UUID of the established connectivity
147 - *conn_info* (dict or None): Information to be stored at the
148 database (or ``None``). This information will be provided to
149 the :meth:`~.edit_connectivity_service` and :obj:`~.delete`.
150 **MUST** be JSON/YAML-serializable (plain data structures).
153 WimConnectorException: In case of error.
155 if service_type
=="ELINE":
156 if len(connection_points
)>2:
157 raise WimConnectorError('Connections between more than '
158 '2 endpoints are not supported')
159 if len(connection_points
)<2:
160 raise WimConnectorError('Connections must be of at least '
162 """ First step, create the vpn service """
163 uuid_l2vpn
=str(uuid
.uuid4())
165 vpn_service
["vpn-id"]= uuid_l2vpn
166 vpn_service
["vpn-scv-type"]="vpws"
167 vpn_service
["svc-topo"]= "any-to-any"
168 vpn_service
["customer-name"]= "osm"
170 vpn_service_list
.append(vpn_service
)
171 vpn_service_l
={"vpn-service":vpn_service_list
}
172 response_service_creation
=None
174 self
.logger
.info("Sending vpn-service :{}".format(vpn_service_l
))
176 endpoint_service_creation
="{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(self
.wim
["wim_url"])
177 response_service_creation
=requests
.post(endpoint_service_creation
, headers
=self
.headers
, json
=vpn_service_l
, auth
=self
.auth
)
178 except requests
.exceptions
.ConnectionError
:
179 raise WimConnectorError("Request to create service Timeout",http_code
=408)
180 if response_service_creation
.status_code
== 409:
181 raise WimConnectorError("Service already exists",http_code
=response_service_creation
.status_code
)
182 elif response_service_creation
.status_code
!= requests
.codes
.created
:
183 raise WimConnectorError("Request to create service not accepted",http_code
=response_service_creation
.status_code
)
184 """ Second step, create the connections and vpn attachments """
185 for connection_point
in connection_points
:
186 connection_point_wan_info
=self
.search_mapp(connection_point
)
187 site_network_access
={}
189 if connection_point
["service_endpoint_encapsulation_type"]!="none":
190 if connection_point
["service_endpoint_encapsulation_type"]=="dot1q":
191 """ The connection is a VLAN """
192 connection
["encapsulation-type"]="dot1q-vlan-tagged"
195 service_endpoint_encapsulation_info
=connection_point
["service_endpoint_encapsulation_info"]
196 if service_endpoint_encapsulation_info
["vlan"]==None:
197 raise WimConnectorError("VLAN must be provided")
198 tagged_interf
["cvlan-id"]= service_endpoint_encapsulation_info
["vlan"]
199 tagged
["dot1q-vlan-tagged"]=tagged_interf
200 connection
["tagged-interface"]=tagged
202 raise NotImplementedError("Encapsulation type not implemented")
203 site_network_access
["connection"]=connection
204 self
.logger
.info("Sending connection:{}".format(connection
))
206 vpn_attach
["vpn-id"]=uuid_l2vpn
207 vpn_attach
["site-role"]=vpn_service
["svc-topo"]+"-role"
208 site_network_access
["vpn-attachment"]=vpn_attach
209 self
.logger
.info("Sending vpn-attachement :{}".format(vpn_attach
))
210 uuid_sna
=str(uuid
.uuid4())
211 site_network_access
["network-access-id"]=uuid_sna
212 site_network_accesses
={}
213 site_network_access_list
=[]
214 site_network_access_list
.append(site_network_access
)
215 site_network_accesses
["site-network-access"]=site_network_access_list
217 conn_info_d
["site"] = connection_point_wan_info
["site-id"]
218 conn_info_d
["site-network-access-id"] = site_network_access
["network-access-id"]
219 conn_info_d
["mapping"] = None
220 conn_info
.append(conn_info_d
)
222 endpoint_site_network_access_creation
="{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/sites/site={}/site-network-accesses/".format(self
.wim
["wim_url"],connection_point_wan_info
["site-id"])
223 response_endpoint_site_network_access_creation
= requests
.post(endpoint_site_network_access_creation
, headers
=self
.headers
, json
=site_network_accesses
, auth
=self
.auth
)
225 if response_endpoint_site_network_access_creation
.status_code
== 409:
226 self
.delete_connectivity_service(vpn_service
["vpn-id"])
227 raise WimConnectorError("Site_Network_Access with ID '{}' already exists".format(site_network_access
["network-access-id"]),http_code
=response_endpoint_site_network_access_creation
.status_code
)
229 elif response_endpoint_site_network_access_creation
.status_code
== 400:
230 self
.delete_connectivity_service(vpn_service
["vpn-id"])
231 raise WimConnectorError("Site {} does not exist".format(connection_point_wan_info
["site-id"]),http_code
=response_endpoint_site_network_access_creation
.status_code
)
233 elif response_endpoint_site_network_access_creation
.status_code
!= requests
.codes
.created
and response_endpoint_site_network_access_creation
.status_code
!= requests
.codes
.no_content
:
234 self
.delete_connectivity_service(vpn_service
["vpn-id"])
235 raise WimConnectorError("Request no accepted",http_code
=response_endpoint_site_network_access_creation
.status_code
)
237 except requests
.exceptions
.ConnectionError
:
238 self
.delete_connectivity_service(vpn_service
["vpn-id"])
239 raise WimConnectorError("Request Timeout",http_code
=408)
240 return uuid_l2vpn
, conn_info
243 raise NotImplementedError
247 def delete_connectivity_service(self
, service_uuid
, conn_info
=None):
248 """Disconnect multi-site endpoints previously connected
250 This method should receive as the first argument the UUID generated by
251 the ``create_connectivity_service``
254 self
.logger
.info("Sending delete")
255 servicepoint
="{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format(self
.wim
["wim_url"],service_uuid
)
256 response
=requests
.delete(servicepoint
, auth
=self
.auth
)
257 if response
.status_code
!=requests
.codes
.no_content
:
258 raise WimConnectorError("Error in the request",http_code
=response
.status_code
)
259 except requests
.exceptions
.ConnectionError
:
260 raise WimConnectorError("Request Timeout",http_code
=408)
264 def edit_connectivity_service(self
, service_uuid
, conn_info
=None,
265 connection_points
=None, **kwargs
):
266 """Change an existing connectivity service, see
267 ``create_connectivity_service``"""
272 vpn_service
["svc-topo"]= "any-to-any"
274 for connection_point
in connection_points
:
275 site_network_access
={}
276 connection_point_wan_info
=self
.search_mapp(connection_point
)
278 params_site
["site-id"]= connection_point_wan_info
["site-id"]
279 params_site
["site-vpn-flavor"]= "site-vpn-flavor-single"
281 device_site
["device-id"]=connection_point_wan_info
["device-id"]
282 params_site
["devices"]=device_site
285 if connection_point
["service_endpoint_encapsulation_type"]!="none":
286 if connection_point
["service_endpoint_encapsulation_type"]=="dot1q":
287 """ The connection is a VLAN """
288 connection
["encapsulation-type"]="dot1q-vlan-tagged"
291 service_endpoint_encapsulation_info
=connection_point
["service_endpoint_encapsulation_info"]
292 if service_endpoint_encapsulation_info
["vlan"]==None:
293 raise WimConnectorError("VLAN must be provided")
294 tagged_interf
["cvlan-id"]= service_endpoint_encapsulation_info
["vlan"]
295 tagged
["dot1q-vlan-tagged"]=tagged_interf
296 connection
["tagged-interface"]=tagged
298 raise NotImplementedError("Encapsulation type not implemented")
299 site_network_access
["connection"]=connection
301 vpn_attach
["vpn-id"]=service_uuid
302 vpn_attach
["site-role"]=vpn_service
["svc-topo"]+"-role"
303 site_network_access
["vpn-attachment"]=vpn_attach
304 uuid_sna
=conn_info
[counter
]["site-network-access-id"]
305 site_network_access
["network-access-id"]=uuid_sna
306 site_network_accesses
={}
307 site_network_access_list
=[]
308 site_network_access_list
.append(site_network_access
)
309 site_network_accesses
["site-network-access"]=site_network_access_list
311 endpoint_site_network_access_edit
="{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/sites/site={}/site-network-accesses/".format(self
.wim
["wim_url"],connection_point_wan_info
["site-id"]) #MODIF
312 response_endpoint_site_network_access_creation
= requests
.put(endpoint_site_network_access_edit
, headers
=self
.headers
, json
=site_network_accesses
, auth
=self
.auth
)
313 if response_endpoint_site_network_access_creation
.status_code
==400:
314 raise WimConnectorError("Service does not exist",http_code
=response_endpoint_site_network_access_creation
.status_code
)
315 elif response_endpoint_site_network_access_creation
.status_code
!=201 and response_endpoint_site_network_access_creation
.status_code
!=204:
316 raise WimConnectorError("Request no accepted",http_code
=response_endpoint_site_network_access_creation
.status_code
)
317 except requests
.exceptions
.ConnectionError
:
318 raise WimConnectorError("Request Timeout",http_code
=408)
322 def clear_all_connectivity_services(self
):
323 """Delete all WAN Links corresponding to a WIM"""
325 self
.logger
.info("Sending clear all connectivity services")
326 servicepoint
="{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(self
.wim
["wim_url"])
327 response
=requests
.delete(servicepoint
, auth
=self
.auth
)
328 if response
.status_code
!=requests
.codes
.no_content
:
329 raise WimConnectorError("Unable to clear all connectivity services",http_code
=response
.status_code
)
330 except requests
.exceptions
.ConnectionError
:
331 raise WimConnectorError("Request Timeout",http_code
=408)
333 def get_all_active_connectivity_services(self
):
334 """Provide information about all active connections provisioned by a
338 self
.logger
.info("Sending get all connectivity services")
339 servicepoint
="{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(self
.wim
["wim_url"])
340 response
=requests
.get(servicepoint
, auth
=self
.auth
)
341 if response
.status_code
!= requests
.codes
.ok
:
342 raise WimConnectorError("Unable to get all connectivity services",http_code
=response
.status_code
)
344 except requests
.exceptions
.ConnectionError
:
345 raise WimConnectorError("Request Timeout",http_code
=408)