1 # -*- coding: utf-8 -*-
3 # Copyright 2020 ETSI OSM
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
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, WITHOUT
15 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 # License for the specific language governing permissions and limitations
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
31 from io
import BytesIO
35 class JuniperContrail(SdnConnectorBase
):
37 Juniper Contrail SDN plugin. The plugin interacts with Juniper Contrail Controller,
38 whose API details can be found in these links:
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
44 _WIM_LOGGER
= "openmano.sdnconn.junipercontrail"
46 def __init__(self
, wim
, wim_account
, config
=None, logger
=None):
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.
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
)
73 self
.user
= wim_account
.get("user")
74 self
.password
= wim_account
.get("password")
76 url
= wim
.get("wim_url")
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")
94 raise SdnConnectorError("'url' must be provided")
95 if not url
.startswith("http"):
97 if not url
.endswith("/"):
102 raise SdnConnectorError("'project' must be provided")
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")
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")
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 self
.used_vni
= set()
118 if not overlay_url
.startswith("http"):
119 overlay_url
= "http://" + overlay_url
120 if not overlay_url
.endswith("/"):
121 overlay_url
= overlay_url
+ "/"
122 self
.overlay_url
= overlay_url
125 if not auth_url
.startswith("http"):
126 auth_url
= "http://" + auth_url
127 if not auth_url
.endswith("/"):
128 auth_url
= auth_url
+ "/"
129 self
.auth_url
= auth_url
132 self
.http
= Http(self
.logger
)
134 # Init http headers for all requests
135 self
.headers
= {'Content-Type': 'application/json'}
136 self
.http_header
= ['{}: {}'.format(key
, val
)
137 for (key
, val
) in list(self
.headers
.items())]
140 auth_dict
['auth'] = {}
141 auth_dict
['auth']['scope'] = {}
142 auth_dict
['auth']['scope']['project'] = {}
143 auth_dict
['auth']['scope']['project']['domain'] = {}
144 auth_dict
['auth']['scope']['project']['domain']["id"] = self
.domain
145 auth_dict
['auth']['scope']['project']['name'] = self
.project
146 auth_dict
['auth']['identity'] = {}
147 auth_dict
['auth']['identity']['methods'] = ['password']
148 auth_dict
['auth']['identity']['password'] = {}
149 auth_dict
['auth']['identity']['password']['user'] = {}
150 auth_dict
['auth']['identity']['password']['user']['name'] = self
.user
151 auth_dict
['auth']['identity']['password']['user']['password'] = self
.password
152 auth_dict
['auth']['identity']['password']['user']['domain'] = {}
153 auth_dict
['auth']['identity']['password']['user']['domain']['id'] = self
.domain
154 self
.auth_dict
= auth_dict
157 self
.logger
.info("Juniper Contrail Connector Initialized.")
160 def _generate_vni(self
):
162 Method to get unused VxLAN Network Identifier (VNI)
169 for vlanID_range
in self
.vni_range
:
171 start_vni
, end_vni
= map(int, vlanID_range
.replace(" ", "").split("-"))
172 for vni
in range(start_vni
, end_vni
+ 1):
173 if vni
not in self
.used_vni
:
175 except Exception as exp
:
176 raise SdnConnectorError("Exception {} occurred while searching a free VNI.".format(exp
))
178 raise SdnConnectorError("Unable to create the virtual network."\
179 " All VNI in VNI range {} are in use.".format(self
.vni_range
))
182 def _get_token(self
):
183 self
.logger
.debug('Current Token:'.format(str(self
.token
)))
184 auth_url
= self
.auth_url
+ 'auth/tokens'
185 if self
.token
is None:
186 if not self
.auth_url
:
188 http_code
, resp
= self
.http
.post_cmd(url
=auth_url
, headers
=self
.http_header
,
189 postfields_dict
=self
.auth_dict
,
190 return_header
= 'x-subject-token')
192 self
.logger
.debug('Token: '.format(self
.token
))
195 self
.headers
['X-Auth-Token'] = self
.token
197 self
.headers
.pop('X-Auth-Token', None)
198 http_header
= ['{}: {}'.format(key
, val
)
199 for (key
, val
) in list(self
.headers
.items())]
202 # Aux functions for testing
207 def get_overlay_url(self
):
208 return self
.overlay_url
210 # Virtual network operations
212 def _create_virtual_network(self
, controller_url
, name
, vni
):
213 routetarget
= '{}:{}'.format(self
.asn
,vni
)
216 "virtual_network_properties": {
217 "vxlan_network_identifier": vni
,
219 "parent_type": "project",
225 "route_target_list": {
227 "target:" + routetarget
233 endpoint
= controller_url
+ 'virtual-networks'
234 http_code
, resp
= self
.http
.post_cmd(url
= endpoint
,
235 headers
= self
.http_header
,
236 postfields_dict
= vnet_dict
)
237 if http_code
not in (200, 201, 202, 204) or not resp
:
238 raise SdnConnectorError('Unexpected http status code, or empty response')
239 vnet_info
= json
.loads(resp
)
240 self
.logger
.debug("vnet_info: {}".format(vnet_info
))
241 return vnet_info
.get("virtual-network").get('uuid'), vnet_info
.get("virtual-network")
244 def _get_virtual_networks(self
, controller_url
):
246 endpoint
= controller_url
+ 'virtual-networks'
248 http_code
, resp
= self
.http
.get_cmd(url
=endpoint
, headers
=self
.http_header
)
249 if http_code
not in (200, 201, 202, 204) or not resp
:
250 raise SdnConnectorError('Unexpected http status code, or empty response')
251 vnets_info
= json
.loads(resp
)
252 self
.logger
.debug("vnets_info: {}".format(vnets_info
))
253 return vnets_info
.get('virtual-networks')
256 def _get_virtual_network(self
, controller_url
, network_id
):
258 endpoint
= controller_url
+ 'virtual-network/{}'.format(network_id
)
259 http_code
, resp
= self
.http
.get_cmd(url
=endpoint
, headers
=self
.http_header
)
260 if http_code
not in (200, 201, 202, 204) or not resp
:
263 raise SdnConnectorError('Unexpected http status code, or empty response')
264 vnet_info
= json
.loads(resp
)
265 self
.logger
.debug("vnet_info: {}".format(vnet_info
))
266 return vnet_info
.get("virtual-network")
269 def _delete_virtual_network(self
, controller_url
, network_id
):
271 endpoint
= controller_url
+ 'virtual-network/{}'.format(network_id
)
272 http_code
, _
= self
.http
.delete_cmd(url
=endpoint
, headers
=self
.http_header
)
273 if http_code
not in (200, 201, 202, 204):
274 raise SdnConnectorError('Unexpected http status code')
278 # Virtual port group operations
280 def _create_vpg(self
, controller_url
, switch_id
, switch_port
, network
, vlan
):
282 "virtual-port-group": {
286 endpoint
= controller_url
+ 'virtual-port-groups'
287 http_code
, resp
= self
.http
.post_cmd(url
= endpoint
,
288 headers
= self
.http_header
,
289 postfields_dict
= vpg_dict
)
290 if http_code
not in (200, 201, 202, 204) or not resp
:
291 raise SdnConnectorError('Unexpected http status code, or empty response')
292 vpg_info
= json
.loads(resp
)
293 self
.logger
.debug("vpg_info: {}".format(vpg_info
))
294 return vpg_info
.get("virtual-port-group").get('uuid'), vpg_info
.get("virtual-port-group")
297 def _get_vpgs(self
, controller_url
):
299 endpoint
= controller_url
+ 'virtual-port-groups'
300 http_code
, resp
= self
.http
.get_cmd(url
=endpoint
, headers
=self
.http_header
)
301 if http_code
not in (200, 201, 202, 204) or not resp
:
302 raise SdnConnectorError('Unexpected http status code, or empty response')
303 vpgs_info
= json
.loads(resp
)
304 self
.logger
.debug("vpgs_info: {}".format(vpgs_info
))
305 return vpgs_info
.get('virtual-port-groups')
308 def _get_vpg(self
, controller_url
, vpg_id
):
310 endpoint
= controller_url
+ 'virtual-port-group/{}'.format(vpg_id
)
311 http_code
, resp
= self
.http
.get_cmd(url
=endpoint
, headers
=self
.http_header
)
312 if http_code
not in (200, 201, 202, 204) or not resp
:
315 raise SdnConnectorError('Unexpected http status code, or empty response')
316 vpg_info
= json
.loads(resp
)
317 self
.logger
.debug("vpg_info: {}".format(vpg_info
))
318 return vpg_info
.get("virtual-port-group")
321 def _delete_vpg(self
, controller_url
, vpg_id
):
323 endpoint
= controller_url
+ 'virtual-port-group/{}'.format(vpg_id
)
324 http_code
, resp
= self
.http
.delete_cmd(url
=endpoint
, headers
=self
.http_header
)
325 if http_code
not in (200, 201, 202, 204):
326 raise SdnConnectorError('Unexpected http status code')
330 def check_credentials(self
):
331 """Check if the connector itself can access the SDN/WIM with the provided url (wim.wim_url),
332 user (wim_account.user), and password (wim_account.password)
335 SdnConnectorError: Issues regarding authorization, access to
336 external URLs, etc are detected.
338 self
.logger
.debug("")
341 http_code
, resp
= self
.http
.get_cmd(url
=self
.auth_url
, headers
=self
.http_header
)
342 if http_code
not in (200, 201, 202, 204) or not resp
:
343 raise SdnConnectorError('Unexpected http status code, or empty response')
344 except Exception as e
:
345 self
.logger
.error('Error checking credentials')
346 raise SdnConnectorError('Error checking credentials', http_code
=http_code
)
349 def get_connectivity_service_status(self
, service_uuid
, conn_info
=None):
350 """Monitor the status of the connectivity service established
353 service_uuid (str): UUID of the connectivity service
354 conn_info (dict or None): Information returned by the connector
355 during the service creation/edition and subsequently stored in
359 dict: JSON/YAML-serializable dict that contains a mandatory key
360 ``sdn_status`` associated with one of the following values::
362 {'sdn_status': 'ACTIVE'}
363 # The service is up and running.
365 {'sdn_status': 'INACTIVE'}
366 # The service was created, but the connector
367 # cannot determine yet if connectivity exists
368 # (ideally, the caller needs to wait and check again).
370 {'sdn_status': 'DOWN'}
371 # Connection was previously established,
372 # but an error/failure was detected.
374 {'sdn_status': 'ERROR'}
375 # An error occurred when trying to create the service/
376 # establish the connectivity.
378 {'sdn_status': 'BUILD'}
379 # Still trying to create the service, the caller
380 # needs to wait and check again.
382 Additionally ``error_msg``(**str**) and ``sdn_info``(**dict**)
383 keys can be used to provide additional status explanation or
384 new information available for the connectivity service.
386 self
.logger
.debug("")
389 http_code
, resp
= self
.http
.get_cmd(endpoint
='virtual-network/{}'.format(service_uuid
))
390 if http_code
not in (200, 201, 202, 204) or not resp
:
391 raise SdnConnectorError('Unexpected http status code, or empty response')
393 vnet_info
= json
.loads(resp
)
394 return {'sdn_status': 'ACTIVE', 'sdn_info': vnet_info
['virtual-network']}
396 return {'sdn_status': 'ERROR', 'sdn_info': 'not found'}
397 except Exception as e
:
398 self
.logger
.error('Exception getting connectivity service info: %s', e
)
399 return {'sdn_status': 'ERROR', 'error_msg': str(e
)}
402 def create_connectivity_service(self
, service_type
, connection_points
, **kwargs
):
404 Establish SDN/WAN connectivity between the endpoints
405 :param service_type: (str): ``ELINE`` (L2), ``ELAN`` (L2), ``ETREE`` (L2), ``L3``.
406 :param connection_points: (list): each point corresponds to
407 an entry point to be connected. For WIM: from the DC to the transport network.
408 For SDN: Compute/PCI to the transport network. One
409 connection point serves to identify the specific access and
410 some other service parameters, such as encapsulation type.
411 Each item of the list is a dict with:
412 "service_endpoint_id": (str)(uuid) Same meaning that for 'service_endpoint_mapping' (see __init__)
413 In case the config attribute mapping_not_needed is True, this value is not relevant. In this case
414 it will contain the string "device_id:device_interface_id"
415 "service_endpoint_encapsulation_type": None, "dot1q", ...
416 "service_endpoint_encapsulation_info": (dict) with:
417 "vlan": ..., (int, present if encapsulation is dot1q)
418 "vni": ... (int, present if encapsulation is vxlan),
419 "peers": [(ipv4_1), (ipv4_2)] (present if encapsulation is vxlan)
421 "device_id": ..., same meaning that for 'service_endpoint_mapping' (see __init__)
422 "device_interface_id": same meaning that for 'service_endpoint_mapping' (see __init__)
423 "switch_dpid": ..., present if mapping has been found for this device_id,device_interface_id
424 "switch_port": ... present if mapping has been found for this device_id,device_interface_id
425 "service_mapping_info": present if mapping has been found for this device_id,device_interface_id
426 :param kwargs: For future versions:
427 bandwidth (int): value in kilobytes
428 latency (int): value in milliseconds
429 Other QoS might be passed as keyword arguments.
430 :return: tuple: ``(service_id, conn_info)`` containing:
431 - *service_uuid* (str): UUID of the established connectivity service
432 - *conn_info* (dict or None): Information to be stored at the database (or ``None``).
433 This information will be provided to the :meth:`~.edit_connectivity_service` and :obj:`~.delete`.
434 **MUST** be JSON/YAML-serializable (plain data structures).
435 :raises: SdnConnectorException: In case of error. Nothing should be created in this case.
436 Provide the parameter http_code
438 self
.logger
.debug("create_connectivity_service, service_type: {}, connection_points: {}".
439 format(service_type
, connection_points
))
440 if service_type
.lower() != 'elan':
441 raise SdnConnectorError('Only ELAN network type is supported by Juniper Contrail.')
443 # Step 1. Check in the overlay controller the virtual network created by the VIM
444 # Best option: get network id of the VIM as param (if the VIM already created the network),
445 # and do a request to the controller of the virtual networks whose VIM network id is the provided
446 # Next best option: obtain the network by doing a request to the controller
447 # of the virtual networks using the VLAN ID of any service endpoint.
448 # 1.1 Read VLAN ID from a service endpoint
449 # 1.2 Look for virtual networks with "Provider Network" including a VLAN ID.
450 # 1.3 If more than one, ERROR
451 # Step 2. Modify the existing virtual network in the overlay controller
452 # 2.1 Add VNI (VxLAN Network Identifier - one free from the provided range)
453 # 2.2 Add RouteTarget (RT) ('ASN:VNI', ASN = Autonomous System Number, provided as param or read from controller config)
454 # Step 3. Create a virtual network in the underlay controller
455 # 3.1 Create virtual network (name, VNI, RT)
456 # If the network already existed in the overlay controller, we should use the same name
457 # name = 'osm-plugin-' + overlay_name
459 # name = 'osm-plugin-' + VNI
463 network_id
, network_info
= self
._create
_virtual
_network
(self
.url
, name
, vni
)
464 except SdnConnectorError
:
465 raise SdnConnectorError('Failed to create connectivity service {}'.format(name
))
466 except Exception as e
:
467 self
.logger
.error('Exception creating connection_service: %s', e
, exc_info
=True)
468 raise SdnConnectorError("Exception creating connectivity service: {}".format(str(e
)))
472 def edit_connectivity_service(self
, service_uuid
, conn_info
= None, connection_points
= None, **kwargs
):
473 """ Change an existing connectivity service.
475 This method's arguments and return value follow the same convention as
476 :meth:`~.create_connectivity_service`.
478 :param service_uuid: UUID of the connectivity service.
479 :param conn_info: (dict or None): Information previously returned by last call to create_connectivity_service
480 or edit_connectivity_service
481 :param connection_points: (list): If provided, the old list of connection points will be replaced.
482 :param kwargs: Same meaning that create_connectivity_service
483 :return: dict or None: Information to be updated and stored at the database.
484 When ``None`` is returned, no information should be changed.
485 When an empty dict is returned, the database record will be deleted.
486 **MUST** be JSON/YAML-serializable (plain data structures).
488 SdnConnectorException: In case of error.
490 #TODO: to be done. This comes from ONOS VPLS plugin
491 self
.logger
.debug("edit connectivity service, service_uuid: {}, conn_info: {}, "
492 "connection points: {} ".format(service_uuid
, conn_info
, connection_points
))
494 conn_info
= conn_info
or []
495 # Obtain current configuration
496 config_orig
= self
._get
_onos
_netconfig
()
497 config
= copy
.deepcopy(config_orig
)
499 # get current service data and check if it does not exists
501 for vpls
in config
.get('apps', {}).get('org.onosproject.vpls', {}).get('vpls', {}).get('vplsList', {}):
502 if vpls
['name'] == service_uuid
:
503 self
.logger
.debug("service exists")
504 curr_interfaces
= vpls
.get("interfaces", [])
505 curr_encapsulation
= vpls
.get("encapsulation")
508 raise SdnConnectorError("service uuid: {} does not exist".format(service_uuid
))
510 self
.logger
.debug("current interfaces: {}".format(curr_interfaces
))
511 self
.logger
.debug("current encapsulation: {}".format(curr_encapsulation
))
513 # new interfaces names
514 new_interfaces
= [port
['service_endpoint_id'] for port
in new_connection_points
]
516 # obtain interfaces to delete, list will contain port
517 ifs_delete
= list(set(curr_interfaces
) - set(new_interfaces
))
518 ifs_add
= list(set(new_interfaces
) - set(curr_interfaces
))
519 self
.logger
.debug("interfaces to delete: {}".format(ifs_delete
))
520 self
.logger
.debug("interfaces to add: {}".format(ifs_add
))
522 # check if some data of the interfaces that already existed has changed
523 # in that case delete it and add it again
524 ifs_remain
= list(set(new_interfaces
) & set(curr_interfaces
))
525 for port
in connection_points
:
526 if port
['service_endpoint_id'] in ifs_remain
:
527 # check if there are some changes
528 curr_port_name
, curr_vlan
= self
._get
_current
_port
_data
(config
, port
['service_endpoint_id'])
529 new_port_name
= 'of:{}/{}'.format(port
['service_endpoint_encapsulation_info']['switch_dpid'],
530 port
['service_endpoint_encapsulation_info']['switch_port'])
531 new_vlan
= port
['service_endpoint_encapsulation_info']['vlan']
532 if (curr_port_name
!= new_port_name
or curr_vlan
!= new_vlan
):
533 self
.logger
.debug("TODO: must update data interface: {}".format(port
['service_endpoint_id']))
534 ifs_delete
.append(port
['service_endpoint_id'])
535 ifs_add
.append(port
['service_endpoint_id'])
537 new_encapsulation
= self
._get
_encapsulation
(connection_points
)
540 # Delete interfaces, only will delete interfaces that are in provided conn_info
541 # because these are the ones that have been created for this service
543 for port
in config
['ports'].values():
544 for port_interface
in port
['interfaces']:
545 interface_name
= port_interface
['name']
546 self
.logger
.debug("interface name: {}".format(port_interface
['name']))
547 if interface_name
in ifs_delete
and interface_name
in conn_info
:
548 self
.logger
.debug("delete interface name: {}".format(interface_name
))
549 port
['interfaces'].remove(port_interface
)
550 conn_info
.remove(interface_name
)
553 for port
in connection_points
:
554 if port
['service_endpoint_id'] in ifs_add
:
555 created_ifz
= self
._append
_port
_to
_config
(port
, config
)
557 conn_info
.append(created_ifz
[1])
558 self
._pop
_last
_update
_time
(config
)
559 self
._post
_netconfig
(config
)
561 self
.logger
.debug("contrail config after updating interfaces: {}".format(config
))
562 self
.logger
.debug("conn_info after updating interfaces: {}".format(conn_info
))
564 # Update interfaces list in vpls service
565 for vpls
in config
.get('apps', {}).get('org.onosproject.vpls', {}).get('vpls', {}).get('vplsList', {}):
566 if vpls
['name'] == service_uuid
:
567 vpls
['interfaces'] = new_interfaces
568 vpls
['encapsulation'] = new_encapsulation
570 self
._pop
_last
_update
_time
(config
)
571 self
._post
_netconfig
(config
)
573 except Exception as e
:
574 self
.logger
.error('Exception add connection_service: %s', e
)
575 # try to rollback push original config
577 self
._post
_netconfig
(config_orig
)
578 except Exception as e2
:
579 self
.logger
.error('Exception rolling back to original config: %s', e2
)
581 if isinstance(e
, SdnConnectorError
):
584 raise SdnConnectorError("Exception create_connectivity_service: {}".format(e
))
587 def delete_connectivity_service(self
, service_uuid
, conn_info
=None):
589 Disconnect multi-site endpoints previously connected
591 :param service_uuid: The one returned by create_connectivity_service
592 :param conn_info: The one returned by last call to 'create_connectivity_service' or 'edit_connectivity_service'
593 if they do not return None
595 :raises: SdnConnectorException: In case of error. The parameter http_code must be filled
597 self
.logger
.debug("delete_connectivity_service uuid: {}".format(service_uuid
))
599 #TO DO: check if virtual port groups have to be deleted
600 self
._delete
_virtual
_network
(self
.url
, service_uuid
)
601 except SdnConnectorError
:
602 raise SdnConnectorError('Failed to delete service uuid {}'.format(service_uuid
))
603 except Exception as e
:
604 self
.logger
.error('Exception deleting connection_service: %s', e
, exc_info
=True)
605 raise SdnConnectorError("Exception deleting connectivity service: {}".format(str(e
)))
608 if __name__
== '__main__':
610 log_format
= "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(funcName)s(): %(message)s"
611 log_formatter
= logging
.Formatter(log_format
, datefmt
='%Y-%m-%dT%H:%M:%S')
612 handler
= logging
.StreamHandler()
613 handler
.setFormatter(log_formatter
)
614 logger
= logging
.getLogger('openmano.sdnconn.junipercontrail')
615 #logger.setLevel(level=logging.ERROR)
616 logger
.setLevel(level
=logging
.INFO
)
617 #logger.setLevel(level=logging.DEBUG)
618 logger
.addHandler(handler
)
621 with
open('test.yaml') as f
:
622 config
= yaml
.safe_load(f
.read())
623 wim
= {'wim_url': config
.pop('wim_url')}
624 wim_account
= {'user': config
.pop('user'), 'password': config
.pop('password')}
625 logger
.info('wim: {}, wim_account: {}, config: {}'.format(wim
, wim_account
, config
))
628 juniper_contrail
= JuniperContrail(wim
=wim
, wim_account
=wim_account
, config
=config
, logger
=logger
)
633 vni
= juniper_contrail
._generate
_vni
()
634 juniper_contrail
.used_vni
.add(vni
)
635 print(juniper_contrail
.used_vni
)
636 juniper_contrail
.used_vni
.remove(1000003)
637 print(juniper_contrail
.used_vni
)
639 vni
= juniper_contrail
._generate
_vni
()
640 juniper_contrail
.used_vni
.add(vni
)
641 print(juniper_contrail
.used_vni
)
642 # 0. Check credentials
643 print('0. Check credentials')
644 juniper_contrail
.check_credentials()
646 underlay_url
= juniper_contrail
.get_url()
647 overlay_url
= juniper_contrail
.get_overlay_url()
648 # 1. Read virtual networks from overlay controller
649 print('1. Read virtual networks from overlay controller')
651 vnets
= juniper_contrail
._get
_virtual
_networks
(overlay_url
)
652 logger
.debug(yaml
.safe_dump(vnets
, indent
=4, default_flow_style
=False))
654 except Exception as e
:
655 logger
.error('Exception reading virtual networks from overlay controller: %s', e
)
658 # 2. Read virtual networks from underlay controller
659 print('2. Read virtual networks from underlay controller')
660 vnets
= juniper_contrail
._get
_virtual
_networks
(underlay_url
)
661 logger
.debug(yaml
.safe_dump(vnets
, indent
=4, default_flow_style
=False))
663 # 3. Delete virtual networks gerardoX from underlay controller
664 print('3. Delete virtual networks gerardoX from underlay controller')
666 name
= vn
['fq_name'][2]
667 logger
.debug('Virtual network: {}'.format(name
))
669 name
= vn
['fq_name'][2]
670 if 'gerardo' in name
:
671 logger
.info('Virtual Network *gerardo*: {}, {}'.format(name
,vn
['uuid']))
672 if name
!= "gerardo":
673 print('Deleting Virtual Network: {}, {}'.format(name
,vn
['uuid']))
674 logger
.info('Deleting Virtual Network: {}, {}'.format(name
,vn
['uuid']))
675 juniper_contrail
._delete
_virtual
_network
(underlay_url
, vn
['uuid'])
677 # 4. Get virtual network (gerardo) from underlay controller
678 print('4. Get virtual network (gerardo) from underlay controller')
679 vnet1_info
= juniper_contrail
._get
_virtual
_network
(underlay_url
, 'c5d332f7-420a-4e2b-a7b1-b56a59f20c97')
680 print(yaml
.safe_dump(vnet1_info
, indent
=4, default_flow_style
=False))
682 # 5. Create virtual network in underlay controller
683 print('5. Create virtual network in underlay controller')
686 vnet2_id
, _
= juniper_contrail
._create
_virtual
_network
(underlay_url
, myname
, myvni
)
687 vnet2_info
= juniper_contrail
._get
_virtual
_network
(underlay_url
, vnet2_id
)
688 print(yaml
.safe_dump(vnet2_info
, indent
=4, default_flow_style
=False))
690 # 6. Delete virtual network in underlay controller
691 print('6. Delete virtual network in underlay controller')
692 juniper_contrail
._delete
_virtual
_network
(underlay_url
, vnet2_id
)
694 # 7. Read previously deleted virtual network in underlay controller
695 print('7. Read previously deleted virtual network in underlay controller')
697 vnet2_info
= juniper_contrail
._get
_virtual
_network
(underlay_url
, vnet2_id
)
699 print('FAILED. Network {} exists'.format(vnet2_id
))
701 print('OK. Network {} does not exist because it has been deleted'.format(vnet2_id
))
702 except Exception as e
:
703 logger
.info('Exception reading virtual networks from overlay controller: %s', e
)
707 #TODO: to be deleted (it comes from ONOS VPLS plugin)
708 service_type
= 'ELAN'
710 "service_endpoint_id": "switch1:ifz1",
711 "service_endpoint_encapsulation_type": "dot1q",
712 "service_endpoint_encapsulation_info": {
713 "switch_dpid": "0000000000000011",
719 "service_endpoint_id": "switch3:ifz1",
720 "service_endpoint_encapsulation_type": "dot1q",
721 "service_endpoint_encapsulation_info": {
722 "switch_dpid": "0000000000000031",
727 connection_points
= [conn_point_0
, conn_point_1
]
728 # service_uuid, created_items = juniper_contrail.create_connectivity_service(service_type, connection_points)
730 #print(created_items)
732 #juniper_contrail.delete_connectivity_service("5496dfea-27dc-457d-970d-b82bac266e5c"))
736 conn_info
= ['switch1:ifz1', 'switch3_ifz3']
737 juniper_contrail
.delete_connectivity_service("f7afc4de-556d-4b5a-8a12-12b5ef97d269", conn_info
)
740 "service_endpoint_id": "switch1:ifz1",
741 "service_endpoint_encapsulation_type": "dot1q",
742 "service_endpoint_encapsulation_info": {
743 "switch_dpid": "0000000000000011",
749 "service_endpoint_id": "switch1:ifz3",
750 "service_endpoint_encapsulation_type": "dot1q",
751 "service_endpoint_encapsulation_info": {
752 "switch_dpid": "0000000000000011",
758 "service_endpoint_id": "switch3_ifz3",
759 "service_endpoint_encapsulation_type": "dot1q",
760 "service_endpoint_encapsulation_info": {
761 "switch_dpid": "0000000000000033",
766 new_connection_points
= [conn_point_0
, conn_point_3
]
767 #conn_info = juniper_contrail.edit_connectivity_service("f7afc4de-556d-4b5a-8a12-12b5ef97d269", conn_info, new_connection_points)