Juniper Contrail SDN plugin
[osm/RO.git] / RO-SDN-juniper_contrail / osm_rosdn_juniper_contrail / sdn_assist_juniper_contrail.py
1 # -*- coding: utf-8 -*-
2
3 # Copyright 2020 ETSI OSM
4 #
5 # All Rights Reserved.
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License"); you may
8 # not use this file except in compliance with the License. You may obtain
9 # 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, WITHOUT
15 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 # License for the specific language governing permissions and limitations
17 # under the License.
18 #
19
20 import logging
21 import uuid
22 import copy
23 import json
24 import yaml
25
26 #import requests
27 #from requests.auth import HTTPBasicAuth
28 from osm_ro.wim.sdnconn import SdnConnectorBase, SdnConnectorError
29 from osm_rosdn_juniper_contrail.rest_lib import Http
30
31 from io import BytesIO
32 import pycurl
33
34
35 class JuniperContrail(SdnConnectorBase):
36 """
37 Juniper Contrail SDN plugin. The plugin interacts with Juniper Contrail Controller,
38 whose API details can be found in these links:
39
40 - https://github.com/tonyliu0592/contrail/wiki/API-Configuration-REST
41 - https://www.juniper.net/documentation/en_US/contrail19/information-products/pathway-pages/api-guide-1910/tutorial_with_rest.html
42 - https://github.com/tonyliu0592/contrail-toolbox/blob/master/sriov/sriov
43 """
44 _WIM_LOGGER = "openmano.sdnconn.junipercontrail"
45
46 def __init__(self, wim, wim_account, config=None, logger=None):
47 """
48
49 :param wim: (dict). Contains among others 'wim_url'
50 :param wim_account: (dict). Contains among others 'uuid' (internal id), 'name',
51 'sdn' (True if is intended for SDN-assist or False if intended for WIM), 'user', 'password'.
52 :param config: (dict or None): Particular information of plugin. These keys if present have a common meaning:
53 'mapping_not_needed': (bool) False by default or if missing, indicates that mapping is not needed.
54 'service_endpoint_mapping': (list) provides the internal endpoint mapping. The meaning is:
55 KEY meaning for WIM meaning for SDN assist
56 -------- -------- --------
57 device_id pop_switch_dpid compute_id
58 device_interface_id pop_switch_port compute_pci_address
59 service_endpoint_id wan_service_endpoint_id SDN_service_endpoint_id
60 service_mapping_info wan_service_mapping_info SDN_service_mapping_info
61 contains extra information if needed. Text in Yaml format
62 switch_dpid wan_switch_dpid SDN_switch_dpid
63 switch_port wan_switch_port SDN_switch_port
64 datacenter_id vim_account vim_account
65 id: (internal, do not use)
66 wim_id: (internal, do not use)
67 :param logger (logging.Logger): optional logger object. If none is passed 'openmano.sdn.sdnconn' is used.
68 """
69 self.logger = logger or logging.getLogger(self._WIM_LOGGER)
70 self.logger.debug('wim: {}, wim_account: {}, config: {}'.format(wim, wim_account, config))
71 super().__init__(wim, wim_account, config, logger)
72
73 self.user = wim_account.get("user")
74 self.password = wim_account.get("password")
75
76 url = wim.get("wim_url")
77 auth_url = None
78 overlay_url = None
79 self.project = None
80 self.domain = None
81 self.asn = None
82 self.fabric = None
83 self.vni_range = None
84 if config:
85 auth_url = config.get("auth_url")
86 overlay_url = config.get("overlay_url")
87 self.project = config.get("project")
88 self.domain = config.get("domain")
89 self.asn = config.get("asn")
90 self.fabric = config.get("fabric")
91 self.vni_range = config.get("vni_range")
92
93 if not url:
94 raise SdnConnectorError("'url' must be provided")
95 if not url.startswith("http"):
96 url = "http://" + url
97 if not url.endswith("/"):
98 url = url + "/"
99 self.url = url
100
101 if not self.project:
102 raise SdnConnectorError("'project' must be provided")
103 if not self.asn:
104 # TODO: Get ASN from controller config; otherwise raise ERROR for the moment
105 raise SdnConnectorError("'asn' was not provided and was not possible to obtain it")
106 if not self.fabric:
107 # TODO: Get FABRIC from controller config; otherwise raise ERROR for the moment
108 raise SdnConnectorError("'fabric' was not provided and was not possible to obtain it")
109 if not self.domain:
110 self.domain = 'default'
111 self.logger.info("No domain was provided. Using 'default'")
112 if not self.vni_range:
113 self.vni_range = '1000001-2000000'
114 self.logger.info("No vni_range was provided. Using '1000001-2000000'")
115
116 if overlay_url:
117 if not overlay_url.startswith("http"):
118 overlay_url = "http://" + overlay_url
119 if not overlay_url.endswith("/"):
120 overlay_url = overlay_url + "/"
121 self.overlay_url = overlay_url
122
123 if auth_url:
124 if not auth_url.startswith("http"):
125 auth_url = "http://" + auth_url
126 if not auth_url.endswith("/"):
127 auth_url = auth_url + "/"
128 self.auth_url = auth_url
129
130 # Init http lib
131 self.http = Http(self.logger)
132
133 # Init http headers for all requests
134 self.headers = {'Content-Type': 'application/json'}
135 self.http_header = ['{}: {}'.format(key, val)
136 for (key, val) in list(self.headers.items())]
137
138 auth_dict = {}
139 auth_dict['auth'] = {}
140 auth_dict['auth']['scope'] = {}
141 auth_dict['auth']['scope']['project'] = {}
142 auth_dict['auth']['scope']['project']['domain'] = {}
143 auth_dict['auth']['scope']['project']['domain']["id"] = self.domain
144 auth_dict['auth']['scope']['project']['name'] = self.project
145 auth_dict['auth']['identity'] = {}
146 auth_dict['auth']['identity']['methods'] = ['password']
147 auth_dict['auth']['identity']['password'] = {}
148 auth_dict['auth']['identity']['password']['user'] = {}
149 auth_dict['auth']['identity']['password']['user']['name'] = self.user
150 auth_dict['auth']['identity']['password']['user']['password'] = self.password
151 auth_dict['auth']['identity']['password']['user']['domain'] = {}
152 auth_dict['auth']['identity']['password']['user']['domain']['id'] = self.domain
153 self.auth_dict = auth_dict
154 self.token = None
155
156 self.logger.info("Juniper Contrail Connector Initialized.")
157
158
159 def _get_token(self):
160 self.logger.debug('Current Token:'.format(str(self.token)))
161 auth_url = self.auth_url + 'auth/tokens'
162 if self.token is None:
163 if not self.auth_url:
164 self.token = ""
165 http_code, resp = self.http.post_cmd(url=auth_url, headers=self.http_header,
166 postfields_dict=self.auth_dict,
167 return_header = 'x-subject-token')
168 self.token = resp
169 self.logger.debug('Token: '.format(self.token))
170
171 if self.token:
172 self.headers['X-Auth-Token'] = self.token
173 else:
174 self.headers.pop('X-Auth-Token', None)
175 http_header = ['{}: {}'.format(key, val)
176 for (key, val) in list(self.headers.items())]
177
178
179 # Aux functions for testing
180 def get_url(self):
181 return self.url
182
183
184 def get_overlay_url(self):
185 return self.overlay_url
186
187 # Virtual network operations
188
189 def _create_virtual_network(self, controller_url, name, vni):
190 routetarget = '{}:{}'.format(self.asn,vni)
191 vnet_dict = {
192 "virtual-network": {
193 "virtual_network_properties": {
194 "vxlan_network_identifier": vni,
195 },
196 "parent_type": "project",
197 "fq_name": [
198 "default-domain",
199 "admin",
200 name
201 ],
202 "route_target_list": {
203 "route_target": [
204 "target:" + routetarget
205 ]
206 }
207 }
208 }
209 self._get_token()
210 endpoint = controller_url + 'virtual-networks'
211 http_code, resp = self.http.post_cmd(url = endpoint,
212 headers = self.http_header,
213 postfields_dict = vnet_dict)
214 if http_code not in (200, 201, 202, 204) or not resp:
215 raise SdnConnectorError('Unexpected http status code, or empty response')
216 vnet_info = json.loads(resp)
217 self.logger.debug("vnet_info: {}".format(vnet_info))
218 return vnet_info.get("virtual-network").get('uuid'), vnet_info.get("virtual-network")
219
220
221 def _get_virtual_networks(self, controller_url):
222 self._get_token()
223 endpoint = controller_url + 'virtual-networks'
224 print(endpoint)
225 http_code, resp = self.http.get_cmd(url=endpoint, headers=self.http_header)
226 if http_code not in (200, 201, 202, 204) or not resp:
227 raise SdnConnectorError('Unexpected http status code, or empty response')
228 vnets_info = json.loads(resp)
229 self.logger.debug("vnets_info: {}".format(vnets_info))
230 return vnets_info.get('virtual-networks')
231
232
233 def _get_virtual_network(self, controller_url, network_id):
234 self._get_token()
235 endpoint = controller_url + 'virtual-network/{}'.format(network_id)
236 http_code, resp = self.http.get_cmd(url=endpoint, headers=self.http_header)
237 if http_code not in (200, 201, 202, 204) or not resp:
238 if http_code == 404:
239 return None
240 raise SdnConnectorError('Unexpected http status code, or empty response')
241 vnet_info = json.loads(resp)
242 self.logger.debug("vnet_info: {}".format(vnet_info))
243 return vnet_info.get("virtual-network")
244
245
246 def _delete_virtual_network(self, controller_url, network_id):
247 self._get_token()
248 endpoint = controller_url + 'virtual-network/{}'.format(network_id)
249 http_code, _ = self.http.delete_cmd(url=endpoint, headers=self.http_header)
250 if http_code not in (200, 201, 202, 204):
251 raise SdnConnectorError('Unexpected http status code')
252 return
253
254
255 # Virtual port group operations
256
257 def _create_vpg(self, controller_url, switch_id, switch_port, network, vlan):
258 vpg_dict = {
259 "virtual-port-group": {
260 }
261 }
262 self._get_token()
263 endpoint = controller_url + 'virtual-port-groups'
264 http_code, resp = self.http.post_cmd(url = endpoint,
265 headers = self.http_header,
266 postfields_dict = vpg_dict)
267 if http_code not in (200, 201, 202, 204) or not resp:
268 raise SdnConnectorError('Unexpected http status code, or empty response')
269 vpg_info = json.loads(resp)
270 self.logger.debug("vpg_info: {}".format(vpg_info))
271 return vpg_info.get("virtual-port-group").get('uuid'), vpg_info.get("virtual-port-group")
272
273
274 def _get_vpgs(self, controller_url):
275 self._get_token()
276 endpoint = controller_url + 'virtual-port-groups'
277 http_code, resp = self.http.get_cmd(url=endpoint, headers=self.http_header)
278 if http_code not in (200, 201, 202, 204) or not resp:
279 raise SdnConnectorError('Unexpected http status code, or empty response')
280 vpgs_info = json.loads(resp)
281 self.logger.debug("vpgs_info: {}".format(vpgs_info))
282 return vpgs_info.get('virtual-port-groups')
283
284
285 def _get_vpg(self, controller_url, vpg_id):
286 self._get_token()
287 endpoint = controller_url + 'virtual-port-group/{}'.format(vpg_id)
288 http_code, resp = self.http.get_cmd(url=endpoint, headers=self.http_header)
289 if http_code not in (200, 201, 202, 204) or not resp:
290 if http_code == 404:
291 return None
292 raise SdnConnectorError('Unexpected http status code, or empty response')
293 vpg_info = json.loads(resp)
294 self.logger.debug("vpg_info: {}".format(vpg_info))
295 return vpg_info.get("virtual-port-group")
296
297
298 def _delete_vpg(self, controller_url, vpg_id):
299 self._get_token()
300 endpoint = controller_url + 'virtual-port-group/{}'.format(vpg_id)
301 http_code, resp = self.http.delete_cmd(url=endpoint, headers=self.http_header)
302 if http_code not in (200, 201, 202, 204):
303 raise SdnConnectorError('Unexpected http status code')
304 return
305
306
307 def check_credentials(self):
308 """Check if the connector itself can access the SDN/WIM with the provided url (wim.wim_url),
309 user (wim_account.user), and password (wim_account.password)
310
311 Raises:
312 SdnConnectorError: Issues regarding authorization, access to
313 external URLs, etc are detected.
314 """
315 self.logger.debug("")
316 self._get_token()
317 try:
318 http_code, resp = self.http.get_cmd(url=self.auth_url, headers=self.http_header)
319 if http_code not in (200, 201, 202, 204) or not resp:
320 raise SdnConnectorError('Unexpected http status code, or empty response')
321 except Exception as e:
322 self.logger.error('Error checking credentials')
323 raise SdnConnectorError('Error checking credentials', http_code=http_code)
324
325
326 def get_connectivity_service_status(self, service_uuid, conn_info=None):
327 """Monitor the status of the connectivity service established
328
329 Arguments:
330 service_uuid (str): UUID of the connectivity service
331 conn_info (dict or None): Information returned by the connector
332 during the service creation/edition and subsequently stored in
333 the database.
334
335 Returns:
336 dict: JSON/YAML-serializable dict that contains a mandatory key
337 ``sdn_status`` associated with one of the following values::
338
339 {'sdn_status': 'ACTIVE'}
340 # The service is up and running.
341
342 {'sdn_status': 'INACTIVE'}
343 # The service was created, but the connector
344 # cannot determine yet if connectivity exists
345 # (ideally, the caller needs to wait and check again).
346
347 {'sdn_status': 'DOWN'}
348 # Connection was previously established,
349 # but an error/failure was detected.
350
351 {'sdn_status': 'ERROR'}
352 # An error occurred when trying to create the service/
353 # establish the connectivity.
354
355 {'sdn_status': 'BUILD'}
356 # Still trying to create the service, the caller
357 # needs to wait and check again.
358
359 Additionally ``error_msg``(**str**) and ``sdn_info``(**dict**)
360 keys can be used to provide additional status explanation or
361 new information available for the connectivity service.
362 """
363 self.logger.debug("")
364 self._get_token()
365 try:
366 http_code, resp = self.http.get_cmd(endpoint='virtual-network/{}'.format(service_uuid))
367 if http_code not in (200, 201, 202, 204) or not resp:
368 raise SdnConnectorError('Unexpected http status code, or empty response')
369 if resp:
370 vnet_info = json.loads(resp)
371 return {'sdn_status': 'ACTIVE', 'sdn_info': vnet_info['virtual-network']}
372 else:
373 return {'sdn_status': 'ERROR', 'sdn_info': 'not found'}
374 except Exception as e:
375 self.logger.error('Exception getting connectivity service info: %s', e)
376 return {'sdn_status': 'ERROR', 'error_msg': str(e)}
377
378
379 def create_connectivity_service(self, service_type, connection_points, **kwargs):
380 """
381 Establish SDN/WAN connectivity between the endpoints
382 :param service_type: (str): ``ELINE`` (L2), ``ELAN`` (L2), ``ETREE`` (L2), ``L3``.
383 :param connection_points: (list): each point corresponds to
384 an entry point to be connected. For WIM: from the DC to the transport network.
385 For SDN: Compute/PCI to the transport network. One
386 connection point serves to identify the specific access and
387 some other service parameters, such as encapsulation type.
388 Each item of the list is a dict with:
389 "service_endpoint_id": (str)(uuid) Same meaning that for 'service_endpoint_mapping' (see __init__)
390 In case the config attribute mapping_not_needed is True, this value is not relevant. In this case
391 it will contain the string "device_id:device_interface_id"
392 "service_endpoint_encapsulation_type": None, "dot1q", ...
393 "service_endpoint_encapsulation_info": (dict) with:
394 "vlan": ..., (int, present if encapsulation is dot1q)
395 "vni": ... (int, present if encapsulation is vxlan),
396 "peers": [(ipv4_1), (ipv4_2)] (present if encapsulation is vxlan)
397 "mac": ...
398 "device_id": ..., same meaning that for 'service_endpoint_mapping' (see __init__)
399 "device_interface_id": same meaning that for 'service_endpoint_mapping' (see __init__)
400 "switch_dpid": ..., present if mapping has been found for this device_id,device_interface_id
401 "switch_port": ... present if mapping has been found for this device_id,device_interface_id
402 "service_mapping_info": present if mapping has been found for this device_id,device_interface_id
403 :param kwargs: For future versions:
404 bandwidth (int): value in kilobytes
405 latency (int): value in milliseconds
406 Other QoS might be passed as keyword arguments.
407 :return: tuple: ``(service_id, conn_info)`` containing:
408 - *service_uuid* (str): UUID of the established connectivity service
409 - *conn_info* (dict or None): Information to be stored at the database (or ``None``).
410 This information will be provided to the :meth:`~.edit_connectivity_service` and :obj:`~.delete`.
411 **MUST** be JSON/YAML-serializable (plain data structures).
412 :raises: SdnConnectorException: In case of error. Nothing should be created in this case.
413 Provide the parameter http_code
414 """
415 self.logger.debug("create_connectivity_service, service_type: {}, connection_points: {}".
416 format(service_type, connection_points))
417 if service_type.lower() != 'elan':
418 raise SdnConnectorError('Only ELAN network type is supported by Juniper Contrail.')
419
420 # Step 1. Check in the overlay controller the virtual network created by the VIM
421 # Best option: get network id of the VIM as param (if the VIM already created the network),
422 # and do a request to the controller of the virtual networks whose VIM network id is the provided
423 # Next best option: obtain the network by doing a request to the controller
424 # of the virtual networks using the VLAN ID of any service endpoint.
425 # 1.1 Read VLAN ID from a service endpoint
426 # 1.2 Look for virtual networks with "Provider Network" including a VLAN ID.
427 # 1.3 If more than one, ERROR
428 # Step 2. Modify the existing virtual network in the overlay controller
429 # 2.1 Add VNI (VxLAN Network Identifier - one free from the provided range)
430 # 2.2 Add RouteTarget (RT) ('ASN:VNI', ASN = Autonomous System Number, provided as param or read from controller config)
431 # Step 3. Create a virtual network in the underlay controller
432 # 3.1 Create virtual network (name, VNI, RT)
433 # If the network already existed in the overlay controller, we should use the same name
434 # name = 'osm-plugin-' + overlay_name
435 # Else:
436 # name = 'osm-plugin-' + VNI
437 try:
438 name = 'test-test-1'
439 vni = 999999
440 network_id, network_info = self._create_virtual_network(self.url, name, vni)
441 except SdnConnectorError:
442 raise SdnConnectorError('Failed to create connectivity service {}'.format(name))
443 except Exception as e:
444 self.logger.error('Exception creating connection_service: %s', e, exc_info=True)
445 raise SdnConnectorError("Exception creating connectivity service: {}".format(str(e)))
446 return service_id
447
448
449 def edit_connectivity_service(self, service_uuid, conn_info = None, connection_points = None, **kwargs):
450 """ Change an existing connectivity service.
451
452 This method's arguments and return value follow the same convention as
453 :meth:`~.create_connectivity_service`.
454
455 :param service_uuid: UUID of the connectivity service.
456 :param conn_info: (dict or None): Information previously returned by last call to create_connectivity_service
457 or edit_connectivity_service
458 :param connection_points: (list): If provided, the old list of connection points will be replaced.
459 :param kwargs: Same meaning that create_connectivity_service
460 :return: dict or None: Information to be updated and stored at the database.
461 When ``None`` is returned, no information should be changed.
462 When an empty dict is returned, the database record will be deleted.
463 **MUST** be JSON/YAML-serializable (plain data structures).
464 Raises:
465 SdnConnectorException: In case of error.
466 """
467 #TODO: to be done. This comes from ONOS VPLS plugin
468 self.logger.debug("edit connectivity service, service_uuid: {}, conn_info: {}, "
469 "connection points: {} ".format(service_uuid, conn_info, connection_points))
470
471 conn_info = conn_info or []
472 # Obtain current configuration
473 config_orig = self._get_onos_netconfig()
474 config = copy.deepcopy(config_orig)
475
476 # get current service data and check if it does not exists
477 #TODO: update
478 for vpls in config.get('apps', {}).get('org.onosproject.vpls', {}).get('vpls', {}).get('vplsList', {}):
479 if vpls['name'] == service_uuid:
480 self.logger.debug("service exists")
481 curr_interfaces = vpls.get("interfaces", [])
482 curr_encapsulation = vpls.get("encapsulation")
483 break
484 else:
485 raise SdnConnectorError("service uuid: {} does not exist".format(service_uuid))
486
487 self.logger.debug("current interfaces: {}".format(curr_interfaces))
488 self.logger.debug("current encapsulation: {}".format(curr_encapsulation))
489
490 # new interfaces names
491 new_interfaces = [port['service_endpoint_id'] for port in new_connection_points]
492
493 # obtain interfaces to delete, list will contain port
494 ifs_delete = list(set(curr_interfaces) - set(new_interfaces))
495 ifs_add = list(set(new_interfaces) - set(curr_interfaces))
496 self.logger.debug("interfaces to delete: {}".format(ifs_delete))
497 self.logger.debug("interfaces to add: {}".format(ifs_add))
498
499 # check if some data of the interfaces that already existed has changed
500 # in that case delete it and add it again
501 ifs_remain = list(set(new_interfaces) & set(curr_interfaces))
502 for port in connection_points:
503 if port['service_endpoint_id'] in ifs_remain:
504 # check if there are some changes
505 curr_port_name, curr_vlan = self._get_current_port_data(config, port['service_endpoint_id'])
506 new_port_name = 'of:{}/{}'.format(port['service_endpoint_encapsulation_info']['switch_dpid'],
507 port['service_endpoint_encapsulation_info']['switch_port'])
508 new_vlan = port['service_endpoint_encapsulation_info']['vlan']
509 if (curr_port_name != new_port_name or curr_vlan != new_vlan):
510 self.logger.debug("TODO: must update data interface: {}".format(port['service_endpoint_id']))
511 ifs_delete.append(port['service_endpoint_id'])
512 ifs_add.append(port['service_endpoint_id'])
513
514 new_encapsulation = self._get_encapsulation(connection_points)
515
516 try:
517 # Delete interfaces, only will delete interfaces that are in provided conn_info
518 # because these are the ones that have been created for this service
519 if ifs_delete:
520 for port in config['ports'].values():
521 for port_interface in port['interfaces']:
522 interface_name = port_interface['name']
523 self.logger.debug("interface name: {}".format(port_interface['name']))
524 if interface_name in ifs_delete and interface_name in conn_info:
525 self.logger.debug("delete interface name: {}".format(interface_name))
526 port['interfaces'].remove(port_interface)
527 conn_info.remove(interface_name)
528
529 # Add new interfaces
530 for port in connection_points:
531 if port['service_endpoint_id'] in ifs_add:
532 created_ifz = self._append_port_to_config(port, config)
533 if created_ifz:
534 conn_info.append(created_ifz[1])
535 self._pop_last_update_time(config)
536 self._post_netconfig(config)
537
538 self.logger.debug("contrail config after updating interfaces: {}".format(config))
539 self.logger.debug("conn_info after updating interfaces: {}".format(conn_info))
540
541 # Update interfaces list in vpls service
542 for vpls in config.get('apps', {}).get('org.onosproject.vpls', {}).get('vpls', {}).get('vplsList', {}):
543 if vpls['name'] == service_uuid:
544 vpls['interfaces'] = new_interfaces
545 vpls['encapsulation'] = new_encapsulation
546
547 self._pop_last_update_time(config)
548 self._post_netconfig(config)
549 return conn_info
550 except Exception as e:
551 self.logger.error('Exception add connection_service: %s', e)
552 # try to rollback push original config
553 try:
554 self._post_netconfig(config_orig)
555 except Exception as e2:
556 self.logger.error('Exception rolling back to original config: %s', e2)
557 # raise exception
558 if isinstance(e, SdnConnectorError):
559 raise
560 else:
561 raise SdnConnectorError("Exception create_connectivity_service: {}".format(e))
562
563
564 def delete_connectivity_service(self, service_uuid, conn_info=None):
565 """
566 Disconnect multi-site endpoints previously connected
567
568 :param service_uuid: The one returned by create_connectivity_service
569 :param conn_info: The one returned by last call to 'create_connectivity_service' or 'edit_connectivity_service'
570 if they do not return None
571 :return: None
572 :raises: SdnConnectorException: In case of error. The parameter http_code must be filled
573 """
574 self.logger.debug("delete_connectivity_service uuid: {}".format(service_uuid))
575 try:
576 #TO DO: check if virtual port groups have to be deleted
577 self._delete_virtual_network(self.url, service_uuid)
578 except SdnConnectorError:
579 raise SdnConnectorError('Failed to delete service uuid {}'.format(service_uuid))
580 except Exception as e:
581 self.logger.error('Exception deleting connection_service: %s', e, exc_info=True)
582 raise SdnConnectorError("Exception deleting connectivity service: {}".format(str(e)))
583
584
585 if __name__ == '__main__':
586 # Init logger
587 log_format = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(funcName)s(): %(message)s"
588 log_formatter = logging.Formatter(log_format, datefmt='%Y-%m-%dT%H:%M:%S')
589 handler = logging.StreamHandler()
590 handler.setFormatter(log_formatter)
591 logger = logging.getLogger('openmano.sdnconn.junipercontrail')
592 #logger.setLevel(level=logging.ERROR)
593 logger.setLevel(level=logging.INFO)
594 #logger.setLevel(level=logging.DEBUG)
595 logger.addHandler(handler)
596
597 # Read config
598 with open('test.yaml') as f:
599 config = yaml.safe_load(f.read())
600 wim = {'wim_url': config.pop('wim_url')}
601 wim_account = {'user': config.pop('user'), 'password': config.pop('password')}
602 logger.info('wim: {}, wim_account: {}, config: {}'.format(wim, wim_account, config))
603
604 # Init controller
605 juniper_contrail = JuniperContrail(wim=wim, wim_account=wim_account, config=config, logger=logger)
606
607 # Tests
608 # 0. Check credentials
609 print('0. Check credentials')
610 juniper_contrail.check_credentials()
611
612 underlay_url = juniper_contrail.get_url()
613 overlay_url = juniper_contrail.get_overlay_url()
614 # 1. Read virtual networks from overlay controller
615 print('1. Read virtual networks from overlay controller')
616 try:
617 vnets = juniper_contrail._get_virtual_networks(overlay_url)
618 logger.debug(yaml.safe_dump(vnets, indent=4, default_flow_style=False))
619 print('OK')
620 except Exception as e:
621 logger.error('Exception reading virtual networks from overlay controller: %s', e)
622 print('FAILED')
623
624 # 2. Read virtual networks from underlay controller
625 print('2. Read virtual networks from underlay controller')
626 vnets = juniper_contrail._get_virtual_networks(underlay_url)
627 logger.debug(yaml.safe_dump(vnets, indent=4, default_flow_style=False))
628 print('OK')
629 # 3. Delete virtual networks gerardoX from underlay controller
630 print('3. Delete virtual networks gerardoX from underlay controller')
631 for vn in vnets:
632 name = vn['fq_name'][2]
633 logger.debug('Virtual network: {}'.format(name))
634 for vn in vnets:
635 name = vn['fq_name'][2]
636 if 'gerardo' in name:
637 logger.info('Virtual Network *gerardo*: {}, {}'.format(name,vn['uuid']))
638 if name != "gerardo":
639 print('Deleting Virtual Network: {}, {}'.format(name,vn['uuid']))
640 logger.info('Deleting Virtual Network: {}, {}'.format(name,vn['uuid']))
641 juniper_contrail._delete_virtual_network(underlay_url, vn['uuid'])
642 print('OK')
643 # 4. Get virtual network (gerardo) from underlay controller
644 print('4. Get virtual network (gerardo) from underlay controller')
645 vnet1_info = juniper_contrail._get_virtual_network(underlay_url, 'c5d332f7-420a-4e2b-a7b1-b56a59f20c97')
646 print(yaml.safe_dump(vnet1_info, indent=4, default_flow_style=False))
647 print('OK')
648 # 5. Create virtual network in underlay controller
649 print('5. Create virtual network in underlay controller')
650 myname = 'gerardo4'
651 myvni = 20004
652 vnet2_id, _ = juniper_contrail._create_virtual_network(underlay_url, myname, myvni)
653 vnet2_info = juniper_contrail._get_virtual_network(underlay_url, vnet2_id)
654 print(yaml.safe_dump(vnet2_info, indent=4, default_flow_style=False))
655 print('OK')
656 # 6. Delete virtual network in underlay controller
657 print('6. Delete virtual network in underlay controller')
658 juniper_contrail._delete_virtual_network(underlay_url, vnet2_id)
659 print('OK')
660 # 7. Read previously deleted virtual network in underlay controller
661 print('7. Read previously deleted virtual network in underlay controller')
662 try:
663 vnet2_info = juniper_contrail._get_virtual_network(underlay_url, vnet2_id)
664 if vnet2_info:
665 print('FAILED. Network {} exists'.format(vnet2_id))
666 else:
667 print('OK. Network {} does not exist because it has been deleted'.format(vnet2_id))
668 except Exception as e:
669 logger.info('Exception reading virtual networks from overlay controller: %s', e)
670 exit(0)
671
672
673 #TODO: to be deleted (it comes from ONOS VPLS plugin)
674 service_type = 'ELAN'
675 conn_point_0 = {
676 "service_endpoint_id": "switch1:ifz1",
677 "service_endpoint_encapsulation_type": "dot1q",
678 "service_endpoint_encapsulation_info": {
679 "switch_dpid": "0000000000000011",
680 "switch_port": "1",
681 "vlan": "600"
682 }
683 }
684 conn_point_1 = {
685 "service_endpoint_id": "switch3:ifz1",
686 "service_endpoint_encapsulation_type": "dot1q",
687 "service_endpoint_encapsulation_info": {
688 "switch_dpid": "0000000000000031",
689 "switch_port": "3",
690 "vlan": "600"
691 }
692 }
693 connection_points = [conn_point_0, conn_point_1]
694 # service_uuid, created_items = juniper_contrail.create_connectivity_service(service_type, connection_points)
695 #print(service_uuid)
696 #print(created_items)
697 #sleep(10)
698 #juniper_contrail.delete_connectivity_service("5496dfea-27dc-457d-970d-b82bac266e5c"))
699
700
701 conn_info = None
702 conn_info = ['switch1:ifz1', 'switch3_ifz3']
703 juniper_contrail.delete_connectivity_service("f7afc4de-556d-4b5a-8a12-12b5ef97d269", conn_info)
704
705 conn_point_0 = {
706 "service_endpoint_id": "switch1:ifz1",
707 "service_endpoint_encapsulation_type": "dot1q",
708 "service_endpoint_encapsulation_info": {
709 "switch_dpid": "0000000000000011",
710 "switch_port": "1",
711 "vlan": "500"
712 }
713 }
714 conn_point_2 = {
715 "service_endpoint_id": "switch1:ifz3",
716 "service_endpoint_encapsulation_type": "dot1q",
717 "service_endpoint_encapsulation_info": {
718 "switch_dpid": "0000000000000011",
719 "switch_port": "3",
720 "vlan": "500"
721 }
722 }
723 conn_point_3 = {
724 "service_endpoint_id": "switch3_ifz3",
725 "service_endpoint_encapsulation_type": "dot1q",
726 "service_endpoint_encapsulation_info": {
727 "switch_dpid": "0000000000000033",
728 "switch_port": "3",
729 "vlan": "500"
730 }
731 }
732 new_connection_points = [conn_point_0, conn_point_3]
733 #conn_info = juniper_contrail.edit_connectivity_service("f7afc4de-556d-4b5a-8a12-12b5ef97d269", conn_info, new_connection_points)
734 #print(conn_info)