create base package 'osm_ro_plugin' for plugin
[osm/RO.git] / RO-SDN-juniper_contrail / osm_rosdn_juniper_contrail / sdn_assist_juniper_contrail.py
index 32b80dd..43bfb87 100644 (file)
 #
 
 import logging
 #
 
 import logging
-import uuid
-import copy
-import json
 import yaml
 import yaml
+import random
 
 
-#import requests
-#from requests.auth import HTTPBasicAuth
-from osm_ro.wim.sdnconn import SdnConnectorBase, SdnConnectorError
-from osm_rosdn_juniper_contrail.rest_lib import Http
-
-from io import BytesIO
-import pycurl
+from osm_ro_plugin.sdnconn import SdnConnectorBase, SdnConnectorError
+# from osm_rosdn_juniper_contrail.rest_lib import ContrailHttp
+# from osm_rosdn_juniper_contrail.rest_lib import NotFound
+from osm_rosdn_juniper_contrail.rest_lib import DuplicateFound
+from osm_rosdn_juniper_contrail.rest_lib import HttpException
+from osm_rosdn_juniper_contrail.sdn_api import UnderlayApi
 
 
 class JuniperContrail(SdnConnectorBase):
 
 
 class JuniperContrail(SdnConnectorBase):
@@ -38,7 +35,8 @@ class JuniperContrail(SdnConnectorBase):
     whose API details can be found in these links:
 
     - https://github.com/tonyliu0592/contrail/wiki/API-Configuration-REST
     whose API details can be found in these links:
 
     - https://github.com/tonyliu0592/contrail/wiki/API-Configuration-REST
-    - https://www.juniper.net/documentation/en_US/contrail19/information-products/pathway-pages/api-guide-1910/tutorial_with_rest.html
+    - https://www.juniper.net/documentation/en_US/contrail19/information-products/pathway-pages/api-guide-1910/
+      tutorial_with_rest.html
     - https://github.com/tonyliu0592/contrail-toolbox/blob/master/sriov/sriov
     """
     _WIM_LOGGER = "openmano.sdnconn.junipercontrail"
     - https://github.com/tonyliu0592/contrail-toolbox/blob/master/sriov/sriov
     """
     _WIM_LOGGER = "openmano.sdnconn.junipercontrail"
@@ -73,21 +71,21 @@ class JuniperContrail(SdnConnectorBase):
         self.user = wim_account.get("user")
         self.password = wim_account.get("password")
 
         self.user = wim_account.get("user")
         self.password = wim_account.get("password")
 
-        url = wim.get("wim_url")
+        url = wim.get("wim_url")  # underlay url
         auth_url = None
         auth_url = None
-        overlay_url = None
         self.project = None
         self.domain = None
         self.asn = None
         self.fabric = None
         self.project = None
         self.domain = None
         self.asn = None
         self.fabric = None
+        overlay_url = None
         self.vni_range = None
         if config:
             auth_url = config.get("auth_url")
         self.vni_range = None
         if config:
             auth_url = config.get("auth_url")
-            overlay_url = config.get("overlay_url")
             self.project = config.get("project")
             self.domain = config.get("domain")
             self.asn = config.get("asn")
             self.fabric = config.get("fabric")
             self.project = config.get("project")
             self.domain = config.get("domain")
             self.asn = config.get("asn")
             self.fabric = config.get("fabric")
+            self.overlay_url = config.get("overlay_url")
             self.vni_range = config.get("vni_range")
 
         if not url:
             self.vni_range = config.get("vni_range")
 
         if not url:
@@ -98,29 +96,11 @@ class JuniperContrail(SdnConnectorBase):
             url = url + "/"
         self.url = url
 
             url = url + "/"
         self.url = url
 
-        if not self.project:
-            raise SdnConnectorError("'project' must be provided")
-        if not self.asn:
-            # TODO: Get ASN from controller config; otherwise raise ERROR for the moment
-            raise SdnConnectorError("'asn' was not provided and was not possible to obtain it")
-        if not self.fabric:
-            # TODO: Get FABRIC from controller config; otherwise raise ERROR for the moment
-            raise SdnConnectorError("'fabric' was not provided and was not possible to obtain it")
-        if not self.domain:
-            self.domain = 'default'
-            self.logger.info("No domain was provided. Using 'default'")
         if not self.vni_range:
             self.vni_range = ['1000001-2000000']
             self.logger.info("No vni_range was provided. Using ['1000001-2000000']")
         self.used_vni = set()
 
         if not self.vni_range:
             self.vni_range = ['1000001-2000000']
             self.logger.info("No vni_range was provided. Using ['1000001-2000000']")
         self.used_vni = set()
 
-        if overlay_url:
-            if not overlay_url.startswith("http"):
-                overlay_url = "http://" + overlay_url
-            if not overlay_url.endswith("/"):
-                overlay_url = overlay_url + "/"
-        self.overlay_url = overlay_url
-
         if auth_url:
             if not auth_url.startswith("http"):
                 auth_url = "http://" + auth_url
         if auth_url:
             if not auth_url.startswith("http"):
                 auth_url = "http://" + auth_url
@@ -128,34 +108,36 @@ class JuniperContrail(SdnConnectorBase):
                 auth_url = auth_url + "/"
         self.auth_url = auth_url
 
                 auth_url = auth_url + "/"
         self.auth_url = auth_url
 
-        # Init http lib
-        self.http = Http(self.logger)
-
-        # Init http headers for all requests
-        self.headers = {'Content-Type': 'application/json'}
-        self.http_header = ['{}: {}'.format(key, val)
-                           for (key, val) in list(self.headers.items())]
-
-        auth_dict = {}
-        auth_dict['auth'] = {}
-        auth_dict['auth']['scope'] = {}
-        auth_dict['auth']['scope']['project'] = {}
-        auth_dict['auth']['scope']['project']['domain'] = {}
-        auth_dict['auth']['scope']['project']['domain']["id"] = self.domain
-        auth_dict['auth']['scope']['project']['name'] = self.project
-        auth_dict['auth']['identity'] = {}
-        auth_dict['auth']['identity']['methods'] = ['password']
-        auth_dict['auth']['identity']['password'] = {}
-        auth_dict['auth']['identity']['password']['user'] = {}
-        auth_dict['auth']['identity']['password']['user']['name'] = self.user
-        auth_dict['auth']['identity']['password']['user']['password'] = self.password
-        auth_dict['auth']['identity']['password']['user']['domain'] = {}
-        auth_dict['auth']['identity']['password']['user']['domain']['id'] = self.domain
-        self.auth_dict = auth_dict
-        self.token = None
+        if overlay_url:
+            if not overlay_url.startswith("http"):
+                overlay_url = "http://" + overlay_url
+            if not overlay_url.endswith("/"):
+                overlay_url = overlay_url + "/"
+        self.overlay_url = overlay_url
 
 
-        self.logger.info("Juniper Contrail Connector Initialized.")
+        if not self.project:
+            raise SdnConnectorError("'project' must be provided")
+        if not self.asn:
+            # TODO: Get ASN from controller config; otherwise raise ERROR for the moment
+            raise SdnConnectorError("'asn' was not provided and it was not possible to obtain it")
+        if not self.fabric:
+            # TODO: Get FABRIC from controller config; otherwise raise ERROR for the moment
+            raise SdnConnectorError("'fabric' was not provided and was not possible to obtain it")
+        if not self.domain:
+            self.domain = 'default-domain'
+            self.logger.info("No domain was provided. Using 'default-domain'")
+
+        underlay_api_config = {
+            "auth_url": self.auth_url,
+            "project": self.project,
+            "domain": self.domain,
+            "asn": self.asn,
+            "fabric": self.fabric
+        }
+        self.underlay_api = UnderlayApi(url, underlay_api_config, user=self.user, password=self.password, logger=logger)
 
 
+        self._max_duplicate_retry = 2
+        self.logger.info("Juniper Contrail Connector Initialized.")
 
     def _generate_vni(self):
         """
 
     def _generate_vni(self):
         """
@@ -165,167 +147,83 @@ class JuniperContrail(SdnConnectorBase):
             Returns:
                 VNI
         """
             Returns:
                 VNI
         """
-        #find unused VLAN ID
+        # find unused VLAN ID
         for vlanID_range in self.vni_range:
             try:
         for vlanID_range in self.vni_range:
             try:
-                start_vni , end_vni = map(int, vlanID_range.replace(" ", "").split("-"))
-                for vni in range(start_vni, end_vni + 1):
+                start_vni, end_vni = map(int, vlanID_range.replace(" ", "").split("-"))
+                for i in range(start_vni, end_vni + 1):
+                    vni = random.randrange(start_vni, end_vni, 1)
                     if vni not in self.used_vni:
                         return vni
             except Exception as exp:
                 raise SdnConnectorError("Exception {} occurred while searching a free VNI.".format(exp))
         else:
                     if vni not in self.used_vni:
                         return vni
             except Exception as exp:
                 raise SdnConnectorError("Exception {} occurred while searching a free VNI.".format(exp))
         else:
-            raise SdnConnectorError("Unable to create the virtual network."\
-                " All VNI in VNI range {} are in use.".format(self.vni_range))
-
-
-    def _get_token(self):
-        self.logger.debug('Current Token:'.format(str(self.token)))
-        auth_url = self.auth_url + 'auth/tokens'
-        if self.token is None:
-            if not self.auth_url:
-                self.token = ""
-            http_code, resp = self.http.post_cmd(url=auth_url, headers=self.http_header,
-                                                 postfields_dict=self.auth_dict,
-                                                 return_header = 'x-subject-token')
-            self.token = resp
-            self.logger.debug('Token: '.format(self.token))
-
-            if self.token:
-                self.headers['X-Auth-Token'] = self.token
-            else:
-                self.headers.pop('X-Auth-Token', None)
-            http_header = ['{}: {}'.format(key, val)
-                           for (key, val) in list(self.headers.items())]
+            raise SdnConnectorError("Unable to create the virtual network."
+                                    " All VNI in VNI range {} are in use.".format(self.vni_range))
 
 
-    
     # Aux functions for testing
     def get_url(self):
         return self.url
 
     # Aux functions for testing
     def get_url(self):
         return self.url
 
-
     def get_overlay_url(self):
         return self.overlay_url
 
     def get_overlay_url(self):
         return self.overlay_url
 
-    # Virtual network operations
-
-    def _create_virtual_network(self, controller_url, name, vni):
-        routetarget = '{}:{}'.format(self.asn,vni)
-        vnet_dict = {
-                        "virtual-network": {
-                            "virtual_network_properties": {
-                                "vxlan_network_identifier": vni,
-                            },
-                            "parent_type": "project",
-                            "fq_name": [
-                                "default-domain",
-                                "admin",
-                                name
-                            ],
-                            "route_target_list": {
-                                "route_target": [
-                                    "target:" + routetarget
-                                ]
-                            }
-                        }
-                    }
-        self._get_token()
-        endpoint = controller_url + 'virtual-networks'
-        http_code, resp = self.http.post_cmd(url = endpoint,
-                                             headers = self.http_header,
-                                             postfields_dict = vnet_dict)
-        if http_code not in (200, 201, 202, 204) or not resp:
-           raise SdnConnectorError('Unexpected http status code, or empty response')
-        vnet_info = json.loads(resp)
-        self.logger.debug("vnet_info: {}".format(vnet_info))
-        return vnet_info.get("virtual-network").get('uuid'), vnet_info.get("virtual-network")
-
-
-    def _get_virtual_networks(self, controller_url):
-        self._get_token()
-        endpoint = controller_url + 'virtual-networks'
-        print(endpoint)
-        http_code, resp = self.http.get_cmd(url=endpoint, headers=self.http_header)
-        if http_code not in (200, 201, 202, 204) or not resp:
-            raise SdnConnectorError('Unexpected http status code, or empty response')
-        vnets_info = json.loads(resp)
-        self.logger.debug("vnets_info: {}".format(vnets_info))
-        return vnets_info.get('virtual-networks')
-
-
-    def _get_virtual_network(self, controller_url, network_id):
-        self._get_token()
-        endpoint = controller_url + 'virtual-network/{}'.format(network_id)
-        http_code, resp = self.http.get_cmd(url=endpoint, headers=self.http_header)
-        if http_code not in (200, 201, 202, 204) or not resp:
-            if http_code == 404:
-                return None
-            raise SdnConnectorError('Unexpected http status code, or empty response')
-        vnet_info = json.loads(resp)
-        self.logger.debug("vnet_info: {}".format(vnet_info))
-        return vnet_info.get("virtual-network")
-
-
-    def _delete_virtual_network(self, controller_url, network_id):
-        self._get_token()
-        endpoint = controller_url + 'virtual-network/{}'.format(network_id)
-        http_code, _ = self.http.delete_cmd(url=endpoint, headers=self.http_header)
-        if http_code not in (200, 201, 202, 204):
-            raise SdnConnectorError('Unexpected http status code')
-        return
-
-
-    # Virtual port group operations
-
-    def _create_vpg(self, controller_url, switch_id, switch_port, network, vlan):
-        vpg_dict = {
-                       "virtual-port-group": {
-                       }
-                   }
-        self._get_token()
-        endpoint = controller_url + 'virtual-port-groups'
-        http_code, resp = self.http.post_cmd(url = endpoint,
-                                               headers = self.http_header,
-                                               postfields_dict = vpg_dict)
-        if http_code not in (200, 201, 202, 204) or not resp:
-           raise SdnConnectorError('Unexpected http status code, or empty response')
-        vpg_info = json.loads(resp)
-        self.logger.debug("vpg_info: {}".format(vpg_info))
-        return vpg_info.get("virtual-port-group").get('uuid'), vpg_info.get("virtual-port-group")
-
-
-    def _get_vpgs(self, controller_url):
-        self._get_token()
-        endpoint = controller_url + 'virtual-port-groups'
-        http_code, resp = self.http.get_cmd(url=endpoint, headers=self.http_header)
-        if http_code not in (200, 201, 202, 204) or not resp:
-            raise SdnConnectorError('Unexpected http status code, or empty response')
-        vpgs_info = json.loads(resp)
-        self.logger.debug("vpgs_info: {}".format(vpgs_info))
-        return vpgs_info.get('virtual-port-groups')
-
-
-    def _get_vpg(self, controller_url, vpg_id):
-        self._get_token()
-        endpoint = controller_url + 'virtual-port-group/{}'.format(vpg_id)
-        http_code, resp = self.http.get_cmd(url=endpoint, headers=self.http_header)
-        if http_code not in (200, 201, 202, 204) or not resp:
-            if http_code == 404:
-                return None
-            raise SdnConnectorError('Unexpected http status code, or empty response')
-        vpg_info = json.loads(resp)
-        self.logger.debug("vpg_info: {}".format(vpg_info))
-        return vpg_info.get("virtual-port-group")
-
-
-    def _delete_vpg(self, controller_url, vpg_id):
-        self._get_token()
-        endpoint = controller_url + 'virtual-port-group/{}'.format(vpg_id)
-        http_code, resp = self.http.delete_cmd(url=endpoint, headers=self.http_header)
-        if http_code not in (200, 201, 202, 204):
-            raise SdnConnectorError('Unexpected http status code')
-        return
+    def _create_port(self, switch_id, switch_port, network, vlan):
+        """
+        1 - Look for virtual port groups for provided switch_id, switch_port using name
+        2 - It the virtual port group does not exist, create it
+        3 - Create virtual machine interface for the indicated network and vlan
+        """
+        self.logger.debug("create_port: switch_id: {}, switch_port: {}, network: {}, vlan: {}".format(
+            switch_id, switch_port, network, vlan))
+
+        # 1 - Check if the vpg exists
+        vpg_name = self.underlay_api.get_vpg_name(switch_id, switch_port)
+        vpg = self.underlay_api.get_vpg_by_name(vpg_name)
+        if not vpg:
+            # 2 - If it does not exist create it
+            vpg_id, _ = self.underlay_api.create_vpg(switch_id, switch_port)
+        else:
+            # Assign vpg_id from vpg
+            vpg_id = vpg.get("uuid")
+
+        # 3 - Check if the vmi alreaady exists
+        vmi_id, _ = self.underlay_api.create_vmi(switch_id, switch_port, network, vlan)
+        self.logger.debug("port created")
+
+        return vpg_id, vmi_id
+
+    def _delete_port(self, switch_id, switch_port, vlan):
+        self.logger.debug("delete port, switch_id: {}, switch_port: {}, vlan: {}".format(switch_id, switch_port, vlan))
 
 
+        vpg_name = self.underlay_api.get_vpg_name(switch_id, switch_port)
+        vmi_name = self.underlay_api.get_vmi_name(switch_id, switch_port, vlan)
+
+        # 1 - Obtain vpg by id (if not vpg_id must have been error creating ig, nothing to be done)
+        vpg_fqdn = ["default-global-system-config", self.fabric, vpg_name]
+        vpg = self.underlay_api.get_by_fq_name("virtual-port-group", vpg_fqdn)
+        if not vpg:
+            self.logger.warning("vpg: {} to be deleted not found".format(vpg_name))
+        else:
+            # 2 - Get vmi interfaces from vpg
+            vmi_list = vpg.get("virtual_machine_interface_refs")
+            if not vmi_list:
+                # must have been an error during port creation when vmi is created
+                # may happen if there has been an error during creation
+                self.logger.warning("vpg: {} has not vmi, will delete nothing".format(vpg))
+            else:
+                num_vmis = len(vmi_list)
+                for vmi in vmi_list:
+                    fqdn = vmi.get("to")
+                    # check by name
+                    if fqdn[2] == vmi_name:
+                        self.underlay_api.unref_vmi_vpg(vpg.get("uuid"), vmi.get("uuid"), fqdn)
+                        self.underlay_api.delete_vmi(vmi.get("uuid"))
+                        num_vmis = num_vmis - 1
+
+            # 3 - If there are no more vmi delete the vpg
+            if not vmi_list or num_vmis == 0:
+                self.underlay_api.delete_vpg(vpg.get("uuid"))
 
     def check_credentials(self):
         """Check if the connector itself can access the SDN/WIM with the provided url (wim.wim_url),
 
     def check_credentials(self):
         """Check if the connector itself can access the SDN/WIM with the provided url (wim.wim_url),
@@ -336,15 +234,13 @@ class JuniperContrail(SdnConnectorBase):
                 external URLs, etc are detected.
         """
         self.logger.debug("")
                 external URLs, etc are detected.
         """
         self.logger.debug("")
-        self._get_token()
         try:
         try:
-            http_code, resp = self.http.get_cmd(url=self.auth_url, headers=self.http_header)
-            if http_code not in (200, 201, 202, 204) or not resp:
-                raise SdnConnectorError('Unexpected http status code, or empty response')
+            resp = self.underlay_api.check_auth()
+            if not resp:
+                raise SdnConnectorError('Empty response')
         except Exception as e:
             self.logger.error('Error checking credentials')
         except Exception as e:
             self.logger.error('Error checking credentials')
-            raise SdnConnectorError('Error checking credentials', http_code=http_code)
-
+            raise SdnConnectorError('Error checking credentials: {}'.format(str(e)))
 
     def get_connectivity_service_status(self, service_uuid, conn_info=None):
         """Monitor the status of the connectivity service established
 
     def get_connectivity_service_status(self, service_uuid, conn_info=None):
         """Monitor the status of the connectivity service established
@@ -384,21 +280,29 @@ class JuniperContrail(SdnConnectorBase):
                 new information available for the connectivity service.
         """
         self.logger.debug("")
                 new information available for the connectivity service.
         """
         self.logger.debug("")
-        self._get_token()
         try:
         try:
-            http_code, resp = self.http.get_cmd(endpoint='virtual-network/{}'.format(service_uuid))
-            if http_code not in (200, 201, 202, 204) or not resp:
-                raise SdnConnectorError('Unexpected http status code, or empty response')
+            resp = self.underlay_api.get_virtual_network(service_uuid)
+            if not resp:
+                raise SdnConnectorError('Empty response')
             if resp:
             if resp:
-                vnet_info = json.loads(resp)
-                return {'sdn_status': 'ACTIVE', 'sdn_info': vnet_info['virtual-network']}
+                vnet_info = resp
+
+                # Check if conn_info reports error
+                if conn_info.get("sdn_status") == "ERROR":
+                    return {'sdn_status': 'ERROR', 'sdn_info': conn_info}
+                else:
+                    return {'sdn_status': 'ACTIVE', 'sdn_info': vnet_info}
             else:
                 return {'sdn_status': 'ERROR', 'sdn_info': 'not found'}
             else:
                 return {'sdn_status': 'ERROR', 'sdn_info': 'not found'}
+        except SdnConnectorError:
+            raise
+        except HttpException as e:
+            self.logger.error("Error getting connectivity service: {}".format(e))
+            raise SdnConnectorError("Exception deleting connectivity service: {}".format(str(e)))
         except Exception as e:
         except Exception as e:
-            self.logger.error('Exception getting connectivity service info: %s', e)
+            self.logger.error('Exception getting connectivity service info: %s', e, exc_info=True)
             return {'sdn_status': 'ERROR', 'error_msg': str(e)}
 
             return {'sdn_status': 'ERROR', 'error_msg': str(e)}
 
-
     def create_connectivity_service(self, service_type, connection_points, **kwargs):
         """
         Establish SDN/WAN connectivity between the endpoints
     def create_connectivity_service(self, service_type, connection_points, **kwargs):
         """
         Establish SDN/WAN connectivity between the endpoints
@@ -435,11 +339,6 @@ class JuniperContrail(SdnConnectorBase):
         :raises: SdnConnectorException: In case of error. Nothing should be created in this case.
             Provide the parameter http_code
         """
         :raises: SdnConnectorException: In case of error. Nothing should be created in this case.
             Provide the parameter http_code
         """
-        self.logger.debug("create_connectivity_service, service_type: {}, connection_points: {}".
-                          format(service_type, connection_points))
-        if service_type.lower() != 'elan':
-            raise SdnConnectorError('Only ELAN network type is supported by Juniper Contrail.')
-
         # Step 1. Check in the overlay controller the virtual network created by the VIM
         #   Best option: get network id of the VIM as param (if the VIM already created the network),
         #    and do a request to the controller of the virtual networks whose VIM network id is the provided
         # Step 1. Check in the overlay controller the virtual network created by the VIM
         #   Best option: get network id of the VIM as param (if the VIM already created the network),
         #    and do a request to the controller of the virtual networks whose VIM network id is the provided
@@ -450,26 +349,150 @@ class JuniperContrail(SdnConnectorBase):
         #   1.3 If more than one, ERROR
         # Step 2. Modify the existing virtual network in the overlay controller
         #   2.1 Add VNI (VxLAN Network Identifier - one free from the provided range)
         #   1.3 If more than one, ERROR
         # Step 2. Modify the existing virtual network in the overlay controller
         #   2.1 Add VNI (VxLAN Network Identifier - one free from the provided range)
-        #   2.2 Add RouteTarget (RT) ('ASN:VNI', ASN = Autonomous System Number, provided as param or read from controller config)
+        #   2.2 Add RouteTarget (RT) ('ASN:VNI', ASN = Autonomous System Number, provided as param or read from
+        #   controller config)
         # Step 3. Create a virtual network in the underlay controller
         #   3.1 Create virtual network (name, VNI, RT)
         #      If the network already existed in the overlay controller, we should use the same name
         #         name = 'osm-plugin-' + overlay_name
         #      Else:
         #         name = 'osm-plugin-' + VNI
         # Step 3. Create a virtual network in the underlay controller
         #   3.1 Create virtual network (name, VNI, RT)
         #      If the network already existed in the overlay controller, we should use the same name
         #         name = 'osm-plugin-' + overlay_name
         #      Else:
         #         name = 'osm-plugin-' + VNI
+        self.logger.info("create_connectivity_service, service_type: {}, connection_points: {}".
+                         format(service_type, connection_points))
+        if service_type.lower() != 'elan':
+            raise SdnConnectorError('Only ELAN network type is supported by Juniper Contrail.')
+
         try:
         try:
-            name = 'test-test-1'
-            vni = 999999
-            network_id, network_info = self._create_virtual_network(self.url, name, vni)
-        except SdnConnectorError:
-            raise SdnConnectorError('Failed to create connectivity service {}'.format(name))
+            # Initialize data
+            conn_info = None
+
+            # 1 - Filter connection_points (transform cp to a dictionary with no duplicates)
+            # This data will be returned even if no cp can be created if something is created
+            work_cps = {}
+            for cp in connection_points:
+                switch_id = cp.get("service_endpoint_encapsulation_info").get("switch_dpid")
+                switch_port = cp.get("service_endpoint_encapsulation_info").get("switch_port")
+                service_endpoint_id = cp.get("service_endpoint_id")
+                cp_name = self.underlay_api.get_vpg_name(switch_id, switch_port)
+                add_cp = work_cps.get(cp_name)
+                if not add_cp:
+                    # check cp has vlan
+                    vlan = cp.get("service_endpoint_encapsulation_info").get("vlan")
+                    if vlan:
+                        # add cp to dict
+                        service_endpoint_ids = []
+                        service_endpoint_ids.append(service_endpoint_id)
+                        add_cp = {"service_endpoint_ids": service_endpoint_ids,
+                                  "switch_dpid": switch_id,
+                                  "switch_port": switch_port,
+                                  "vlan": vlan}
+                        work_cps[cp_name] = add_cp
+                    else:
+                        self.logger.warning("cp service_endpoint_id : {} has no vlan, ignore".format(
+                            service_endpoint_id))
+                else:
+                    # add service_endpoint_id to list
+                    service_endpoint_ids = add_cp["service_endpoint_ids"]
+                    service_endpoint_ids.append(service_endpoint_id)
+
+            # 2 - Obtain free VNI
+            vni = self._generate_vni()
+            self.logger.debug("VNI: {}".format(vni))
+
+            # 3 - Create virtual network (name, VNI, RT), by the moment the name will use VNI
+            retry = 0
+            while retry < self._max_duplicate_retry:
+                try:
+                    vnet_name = 'osm-plugin-' + str(vni)
+                    vnet_id, _ = self.underlay_api.create_virtual_network(vnet_name, vni)
+                    self.used_vni.add(vni)
+                    break
+                except DuplicateFound as e:
+                    self.logger.debug("Duplicate error for vnet_name: {}".format(vnet_name))
+                    self.used_vni.add(vni)
+                    retry += 1
+                    if retry >= self._max_duplicate_retry:
+                        raise e
+                    else:
+                        # Try to obtain a new vni
+                        vni = self._generate_vni()
+                        continue
+            conn_info = {
+                "vnet": {
+                    "uuid": vnet_id,
+                    "name": vnet_name
+                },
+                "connection_points": work_cps  # dict with port_name as key
+            }
+
+            # 4 - Create a port for each endpoint
+            for cp in work_cps.values():
+                switch_id = cp.get("switch_dpid")
+                switch_port = cp.get("switch_port")
+                vlan = cp.get("vlan")
+                vpg_id, vmi_id = self._create_port(switch_id, switch_port, vnet_name, vlan)
+                cp["vpg_id"] = vpg_id
+                cp["vmi_id"] = vmi_id
+
+            self.logger.info("created connectivity service, uuid: {}, name: {}".format(vnet_id, vnet_name))
+            return vnet_id, conn_info
+
         except Exception as e:
         except Exception as e:
-            self.logger.error('Exception creating connection_service: %s', e, exc_info=True)
-            raise SdnConnectorError("Exception creating connectivity service: {}".format(str(e)))
-        return service_id
+            # Log error
+            if isinstance(e, SdnConnectorError) or isinstance(e, HttpException):
+                self.logger.error("Error creating connectivity service: {}".format(e))
+            else:
+                self.logger.error("Error creating connectivity service: {}".format(e), exc_info=True)
+
+            # If nothing is created raise error else return what has been created and mask as error
+            if not conn_info:
+                raise SdnConnectorError("Exception create connectivity service: {}".format(str(e)))
+            else:
+                conn_info["sdn_status"] = "ERROR"
+                conn_info["sdn_info"] = repr(e)
+                # iterate over not added connection_points and add but marking them as error
+                for cp in work_cps.values():
+                    if not cp.get("vmi_id") or not cp.get("vpg_id"):
+                        cp["sdn_status"] = "ERROR"
+                return vnet_id, conn_info
+
+    def delete_connectivity_service(self, service_uuid, conn_info=None):
+        """
+        Disconnect multi-site endpoints previously connected
+
+        :param service_uuid: The one returned by create_connectivity_service
+        :param conn_info: The one returned by last call to 'create_connectivity_service' or 'edit_connectivity_service'
+            if they do not return None
+        :return: None
+        :raises: SdnConnectorException: In case of error. The parameter http_code must be filled
+        """
+        self.logger.info("delete_connectivity_service vnet_name: {}, connection_points: {}".
+                         format(service_uuid, conn_info))
 
 
+        try:
+            vnet_uuid = service_uuid
+            # vnet_name = conn_info["vnet"]["name"]   # always should exist as the network is the first thing created
+            work_cps = conn_info["connection_points"]
+
+            # 1: For each connection point delete vlan from vpg and it is is the
+            # last one, delete vpg
+            for cp in work_cps.values():
+                self._delete_port(cp.get("switch_dpid"), cp.get("switch_port"), cp.get("vlan"))
+
+            # 2: Delete vnet
+            self.underlay_api.delete_virtual_network(vnet_uuid)
+            self.logger.info("deleted connectivity_service vnet_uuid: {}, connection_points: {}".
+                             format(service_uuid, conn_info))
+        except SdnConnectorError:
+            raise
+        except HttpException as e:
+            self.logger.error("Error deleting connectivity service: {}".format(e))
+            raise SdnConnectorError("Exception deleting connectivity service: {}".format(str(e)))
+        except Exception as e:
+            self.logger.error("Error deleting connectivity service: {}".format(e), exc_info=True)
+            raise SdnConnectorError("Exception deleting connectivity service: {}".format(str(e)))
 
 
-    def edit_connectivity_service(self, service_uuid, conn_info = None, connection_points = None, **kwargs):
+    def edit_connectivity_service(self, service_uuid, conn_info=None, connection_points=None, **kwargs):
         """ Change an existing connectivity service.
 
         This method's arguments and return value follow the same convention as
         """ Change an existing connectivity service.
 
         This method's arguments and return value follow the same convention as
@@ -487,122 +510,168 @@ class JuniperContrail(SdnConnectorBase):
         Raises:
             SdnConnectorException: In case of error.
         """
         Raises:
             SdnConnectorException: In case of error.
         """
-        #TODO: to be done. This comes from ONOS VPLS plugin
-        self.logger.debug("edit connectivity service, service_uuid: {}, conn_info: {}, "
-                          "connection points: {} ".format(service_uuid, conn_info, connection_points))
-
-        conn_info = conn_info or []
-        # Obtain current configuration
-        config_orig = self._get_onos_netconfig()
-        config = copy.deepcopy(config_orig)
-
-        # get current service data and check if it does not exists
-        #TODO: update
-        for vpls in config.get('apps', {}).get('org.onosproject.vpls', {}).get('vpls', {}).get('vplsList', {}):
-            if vpls['name'] == service_uuid:
-                self.logger.debug("service exists")
-                curr_interfaces = vpls.get("interfaces", [])
-                curr_encapsulation = vpls.get("encapsulation")
-                break
-        else:
-            raise SdnConnectorError("service uuid: {} does not exist".format(service_uuid))
-
-        self.logger.debug("current interfaces: {}".format(curr_interfaces))
-        self.logger.debug("current encapsulation: {}".format(curr_encapsulation))
-
-        # new interfaces names
-        new_interfaces = [port['service_endpoint_id'] for port in new_connection_points]
-
-        # obtain interfaces to delete, list will contain port
-        ifs_delete = list(set(curr_interfaces) - set(new_interfaces))
-        ifs_add = list(set(new_interfaces) - set(curr_interfaces))
-        self.logger.debug("interfaces to delete: {}".format(ifs_delete))
-        self.logger.debug("interfaces to add: {}".format(ifs_add))
-
-        # check if some data of the interfaces that already existed has changed
-        # in that case delete it and add it again
-        ifs_remain = list(set(new_interfaces) & set(curr_interfaces))
-        for port in connection_points:
-            if port['service_endpoint_id'] in ifs_remain:
-                # check if there are some changes
-                curr_port_name, curr_vlan = self._get_current_port_data(config, port['service_endpoint_id'])
-                new_port_name = 'of:{}/{}'.format(port['service_endpoint_encapsulation_info']['switch_dpid'],
-                                        port['service_endpoint_encapsulation_info']['switch_port'])
-                new_vlan = port['service_endpoint_encapsulation_info']['vlan']
-                if (curr_port_name != new_port_name or curr_vlan != new_vlan):
-                    self.logger.debug("TODO: must update data interface: {}".format(port['service_endpoint_id']))
-                    ifs_delete.append(port['service_endpoint_id'])
-                    ifs_add.append(port['service_endpoint_id'])
-
-        new_encapsulation = self._get_encapsulation(connection_points)
-
+        # 0 - Check if there are connection_points marked as error and delete them
+        # 1 - Compare conn_info (old connection points) and connection_points (new ones to be applied):
+        #     Obtain list of connection points to be added and to be deleted
+        #     Obtain vlan and check it has not changed
+        # 2 - Obtain network: Check vnet exists and obtain name
+        # 3 - Delete unnecesary ports
+        # 4 - Add new ports
+        self.logger.info("edit connectivity service, service_uuid: {}, conn_info: {}, "
+                         "connection points: {} ".format(service_uuid, conn_info, connection_points))
+
+        # conn_info should always exist and have connection_points and vnet elements
+        old_cp = conn_info.get("connection_points", {})
+
+        # Check if an element of old_cp is marked as error, in case it is delete it
+        # Not return a new conn_info in this case because it is only partial information
+        # Current conn_info already marks ports as error
         try:
         try:
-            # Delete interfaces, only will delete interfaces that are in provided conn_info
-            # because these are the ones that have been created for this service
-            if ifs_delete:
-                for port in config['ports'].values():
-                    for port_interface in port['interfaces']:
-                        interface_name = port_interface['name']
-                        self.logger.debug("interface name: {}".format(port_interface['name']))
-                        if interface_name in ifs_delete and interface_name in conn_info:
-                            self.logger.debug("delete interface name: {}".format(interface_name))
-                            port['interfaces'].remove(port_interface)
-                            conn_info.remove(interface_name)
-
-            # Add new interfaces
-            for port in connection_points:
-                if port['service_endpoint_id'] in ifs_add:
-                    created_ifz = self._append_port_to_config(port, config)
-                    if created_ifz:
-                        conn_info.append(created_ifz[1])
-            self._pop_last_update_time(config)
-            self._post_netconfig(config)
-
-            self.logger.debug("contrail config after updating interfaces: {}".format(config))
-            self.logger.debug("conn_info after updating interfaces: {}".format(conn_info))
-
-            # Update interfaces list in vpls service
-            for vpls in config.get('apps', {}).get('org.onosproject.vpls', {}).get('vpls', {}).get('vplsList', {}):
-                if vpls['name'] == service_uuid:
-                    vpls['interfaces'] = new_interfaces
-                    vpls['encapsulation'] = new_encapsulation
-
-            self._pop_last_update_time(config)
-            self._post_netconfig(config)
-            return conn_info
+            deleted_ports = []
+            for cp in old_cp.values():
+                if cp.get("sdn_status") == "ERROR":
+                    switch_id = cp.get("switch_dpid")
+                    switch_port = cp.get("switch_port")
+                    old_vlan = cp.get("vlan")
+                    self._delete_port(switch_id, switch_port, old_vlan)
+                    deleted_ports.append(self.underlay_api.get_vpg_name(switch_id, switch_port))
+
+            for port in deleted_ports:
+                del old_cp[port]
+
+            # Delete sdn_status and sdn_info if exists (possibly marked as error)
+            if conn_info.get("vnet", {}).get("sdn_status"):
+                del conn_info["vnet"]["sdn_status"]
+        except HttpException as e:
+            self.logger.error("Error trying to delete old ports marked as error: {}".format(e))
+            raise SdnConnectorError(e)
+        except SdnConnectorError as e:
+            self.logger.error("Error trying to delete old ports marked as error: {}".format(e))
+            raise
         except Exception as e:
         except Exception as e:
-            self.logger.error('Exception add connection_service: %s', e)
-            # try to rollback push original config
+            self.logger.error("Error trying to delete old ports marked as error: {}".format(e), exc_info=True)
+            raise SdnConnectorError("Error trying to delete old ports marked as error: {}".format(e))
+
+        if connection_points:
+
+            # Check and obtain what should be added and deleted, if there is an error here raise an exception
             try:
             try:
-                self._post_netconfig(config_orig)
-            except Exception as e2:
-                self.logger.error('Exception rolling back to original config: %s', e2)
-            # raise exception
-            if isinstance(e, SdnConnectorError):
+                work_cps = {}
+                for cp in connection_points:
+                    switch_id = cp.get("service_endpoint_encapsulation_info").get("switch_dpid")
+                    switch_port = cp.get("service_endpoint_encapsulation_info").get("switch_port")
+                    service_endpoint_id = cp.get("service_endpoint_id")
+                    cp_name = self.underlay_api.get_vpg_name(switch_id, switch_port)
+                    add_cp = work_cps.get(cp_name)
+                    if not add_cp:
+                        # add cp to dict
+                        # check cp has vlan
+                        vlan = cp.get("service_endpoint_encapsulation_info").get("vlan")
+                        if vlan:
+                            service_endpoint_ids = []
+                            service_endpoint_ids.append(service_endpoint_id)
+                            add_cp = {"service_endpoint_ids": service_endpoint_ids,
+                                      "switch_dpid": switch_id,
+                                      "switch_port": switch_port,
+                                      "vlan": vlan}
+                            work_cps[cp_name] = add_cp
+                        else:
+                            self.logger.warning("cp service_endpoint_id : {} has no vlan, ignore".
+                                                format(service_endpoint_id))
+                    else:
+                        # add service_endpoint_id to list
+                        service_endpoint_ids = add_cp["service_endpoint_ids"]
+                        service_endpoint_ids.append(service_endpoint_id)
+
+                old_port_list = list(old_cp.keys())
+                port_list = list(work_cps.keys())
+                to_delete_ports = list(set(old_port_list) - set(port_list))
+                to_add_ports = list(set(port_list) - set(old_port_list))
+                self.logger.debug("ports to delete: {}".format(to_delete_ports))
+                self.logger.debug("ports to add: {}".format(to_add_ports))
+
+                # Obtain network (check it is correctly created)
+                vnet = self.underlay_api.get_virtual_network(service_uuid)
+                if vnet:
+                    vnet_name = vnet["name"]
+                else:
+                    raise SdnConnectorError("vnet uuid: {} not found".format(service_uuid))
+
+            except SdnConnectorError:
                 raise
                 raise
-            else:
-                raise SdnConnectorError("Exception create_connectivity_service: {}".format(e))
-
+            except Exception as e:
+                self.logger.error("Error edit connectivity service: {}".format(e), exc_info=True)
+                raise SdnConnectorError("Exception edit connectivity service: {}".format(str(e)))
 
 
-    def delete_connectivity_service(self, service_uuid, conn_info=None):
-        """
-        Disconnect multi-site endpoints previously connected
+            # Delete unneeded ports and add new ones: if there is an error return conn_info
+            try:
+                # Connection points returned in con_info should reflect what has (and should as ERROR) be done
+                # Start with old cp dictionary and modify it as we work
+                conn_info_cp = old_cp
+
+                # Delete unneeded ports
+                deleted_ports = []
+                for port_name in conn_info_cp.keys():
+                    if port_name in to_delete_ports:
+                        cp = conn_info_cp[port_name]
+                        switch_id = cp.get("switch_dpid")
+                        switch_port = cp.get("switch_port")
+                        self.logger.debug("delete port switch_id={}, switch_port={}".format(switch_id, switch_port))
+                        self._delete_port(switch_id, switch_port, vlan)
+                        deleted_ports.append(port_name)
+
+                # Delete ports
+                for port_name in deleted_ports:
+                    del conn_info_cp[port_name]
+
+                # Add needed ports
+                for port_name, cp in work_cps.items():
+                    if port_name in to_add_ports:
+                        switch_id = cp.get("switch_dpid")
+                        switch_port = cp.get("switch_port")
+                        vlan = cp.get("vlan")
+                        self.logger.debug("add port switch_id={}, switch_port={}".format(switch_id, switch_port))
+                        vpg_id, vmi_id = self._create_port(switch_id, switch_port, vnet_name, vlan)
+                        cp_added = cp.copy()
+                        cp_added["vpg_id"] = vpg_id
+                        cp_added["vmi_id"] = vmi_id
+                        conn_info_cp[port_name] = cp_added
+                    # replace endpoints in case they have changed
+                    conn_info_cp[port_name]["service_endpoint_ids"] = cp["service_endpoint_ids"]
+
+                conn_info["connection_points"] = conn_info_cp
+                return conn_info
+
+            except Exception as e:
+                # Log error
+                if isinstance(e, SdnConnectorError) or isinstance(e, HttpException):
+                    self.logger.error("Error edit connectivity service: {}".format(e), exc_info=True)
+                else:
+                    self.logger.error("Error edit connectivity service: {}".format(e))
+
+                # There has been an error mount conn_info_cp marking as error cp that should
+                # have been deleted but have not or should have been added
+                for port_name, cp in conn_info_cp.items():
+                    if port_name in to_delete_ports:
+                        cp["sdn_status"] = "ERROR"
+
+                for port_name, cp in work_cps.items():
+                    curr_cp = conn_info_cp.get(port_name)
+                    if not curr_cp:
+                        cp_error = work_cps.get(port_name).copy()
+                        cp_error["sdn_status"] = "ERROR"
+                        conn_info_cp[port_name] = cp_error
+                    conn_info_cp[port_name]["service_endpoint_ids"] = cp["service_endpoint_ids"]
+
+                conn_info["sdn_status"] = "ERROR"
+                conn_info["sdn_info"] = repr(e)
+                conn_info["connection_points"] = conn_info_cp
+                return conn_info
 
 
-        :param service_uuid: The one returned by create_connectivity_service
-        :param conn_info: The one returned by last call to 'create_connectivity_service' or 'edit_connectivity_service'
-            if they do not return None
-        :return: None
-        :raises: SdnConnectorException: In case of error. The parameter http_code must be filled
-        """
-        self.logger.debug("delete_connectivity_service uuid: {}".format(service_uuid))
-        try:
-            #TO DO: check if virtual port groups have to be deleted
-            self._delete_virtual_network(self.url, service_uuid)
-        except SdnConnectorError:
-            raise SdnConnectorError('Failed to delete service uuid {}'.format(service_uuid))
-        except Exception as e:
-            self.logger.error('Exception deleting connection_service: %s', e, exc_info=True)
-            raise SdnConnectorError("Exception deleting connectivity service: {}".format(str(e)))
+        else:
+            # Connection points have not changed, so do nothing
+            self.logger.info("no new connection_points provided, nothing to be done")
+            return
 
 
 if __name__ == '__main__':
 
 
 if __name__ == '__main__':
@@ -612,9 +681,9 @@ if __name__ == '__main__':
     handler = logging.StreamHandler()
     handler.setFormatter(log_formatter)
     logger = logging.getLogger('openmano.sdnconn.junipercontrail')
     handler = logging.StreamHandler()
     handler.setFormatter(log_formatter)
     logger = logging.getLogger('openmano.sdnconn.junipercontrail')
-    #logger.setLevel(level=logging.ERROR)
-    logger.setLevel(level=logging.INFO)
-    #logger.setLevel(level=logging.DEBUG)
+    # logger.setLevel(level=logging.ERROR)
+    logger.setLevel(level=logging.INFO)
+    logger.setLevel(level=logging.DEBUG)
     logger.addHandler(handler)
 
     # Read config
     logger.addHandler(handler)
 
     # Read config
@@ -633,136 +702,76 @@ if __name__ == '__main__':
         vni = juniper_contrail._generate_vni()
         juniper_contrail.used_vni.add(vni)
     print(juniper_contrail.used_vni)
         vni = juniper_contrail._generate_vni()
         juniper_contrail.used_vni.add(vni)
     print(juniper_contrail.used_vni)
-    juniper_contrail.used_vni.remove(1000003)
+    juniper_contrail.used_vni.remove(1000003)
     print(juniper_contrail.used_vni)
     for i in range(2):
         vni = juniper_contrail._generate_vni()
         juniper_contrail.used_vni.add(vni)
     print(juniper_contrail.used_vni)
     print(juniper_contrail.used_vni)
     for i in range(2):
         vni = juniper_contrail._generate_vni()
         juniper_contrail.used_vni.add(vni)
     print(juniper_contrail.used_vni)
+
     # 0. Check credentials
     print('0. Check credentials')
     # 0. Check credentials
     print('0. Check credentials')
-    juniper_contrail.check_credentials()
-
-    underlay_url = juniper_contrail.get_url()
-    overlay_url = juniper_contrail.get_overlay_url()
-    # 1. Read virtual networks from overlay controller
-    print('1. Read virtual networks from overlay controller')
-    try:
-        vnets = juniper_contrail._get_virtual_networks(overlay_url)
-        logger.debug(yaml.safe_dump(vnets, indent=4, default_flow_style=False))
-        print('OK')
-    except Exception as e:
-        logger.error('Exception reading virtual networks from overlay controller: %s', e)
-        print('FAILED')
-
-    # 2. Read virtual networks from underlay controller
-    print('2. Read virtual networks from underlay controller')
-    vnets = juniper_contrail._get_virtual_networks(underlay_url)
-    logger.debug(yaml.safe_dump(vnets, indent=4, default_flow_style=False))
-    print('OK')
-    # 3. Delete virtual networks gerardoX from underlay controller
-    print('3. Delete virtual networks gerardoX from underlay controller')
-    for vn in vnets:
-        name = vn['fq_name'][2]
-        logger.debug('Virtual network: {}'.format(name))
-    for vn in vnets:
-        name = vn['fq_name'][2]
-        if 'gerardo' in name:
-            logger.info('Virtual Network *gerardo*: {}, {}'.format(name,vn['uuid']))
-            if name != "gerardo":
-                print('Deleting Virtual Network: {}, {}'.format(name,vn['uuid']))
-                logger.info('Deleting Virtual Network: {}, {}'.format(name,vn['uuid']))
-                juniper_contrail._delete_virtual_network(underlay_url, vn['uuid'])
-                print('OK')
-    # 4. Get virtual network (gerardo) from underlay controller
-    print('4. Get virtual network (gerardo) from underlay controller')
-    vnet1_info = juniper_contrail._get_virtual_network(underlay_url, 'c5d332f7-420a-4e2b-a7b1-b56a59f20c97')
-    print(yaml.safe_dump(vnet1_info, indent=4, default_flow_style=False))
-    print('OK')
-    # 5. Create virtual network in underlay controller
-    print('5. Create virtual network in underlay controller')
-    myname = 'gerardo4'
-    myvni = 20004
-    vnet2_id, _ = juniper_contrail._create_virtual_network(underlay_url, myname, myvni)
-    vnet2_info = juniper_contrail._get_virtual_network(underlay_url, vnet2_id)
-    print(yaml.safe_dump(vnet2_info, indent=4, default_flow_style=False))
-    print('OK')
-    # 6. Delete virtual network in underlay controller
-    print('6. Delete virtual network in underlay controller')
-    juniper_contrail._delete_virtual_network(underlay_url, vnet2_id)
-    print('OK')
-    # 7. Read previously deleted virtual network in underlay controller
-    print('7. Read previously deleted virtual network in underlay controller')
-    try:
-        vnet2_info = juniper_contrail._get_virtual_network(underlay_url, vnet2_id)
-        if vnet2_info:
-            print('FAILED. Network {} exists'.format(vnet2_id))
-        else:
-            print('OK. Network {} does not exist because it has been deleted'.format(vnet2_id))
-    except Exception as e:
-        logger.info('Exception reading virtual networks from overlay controller: %s', e)
-    exit(0)
+    # juniper_contrail.check_credentials()
 
 
-
-    #TODO: to be deleted (it comes from ONOS VPLS plugin)
-    service_type = 'ELAN'
+    # 1 - Create and delete connectivity service
     conn_point_0 = {
     conn_point_0 = {
-        "service_endpoint_id": "switch1:ifz1",
+        "service_endpoint_id": "0000:83:11.4",
         "service_endpoint_encapsulation_type": "dot1q",
         "service_endpoint_encapsulation_info": {
         "service_endpoint_encapsulation_type": "dot1q",
         "service_endpoint_encapsulation_info": {
-            "switch_dpid": "0000000000000011",
-            "switch_port": "1",
-            "vlan": "600"
+            "switch_dpid": "LEAF-1",
+            "switch_port": "xe-0/0/17",
+            "vlan": "501"
         }
     }
     conn_point_1 = {
         }
     }
     conn_point_1 = {
-        "service_endpoint_id": "switch3:ifz1",
-        "service_endpoint_encapsulation_type": "dot1q",
-        "service_endpoint_encapsulation_info": {
-            "switch_dpid": "0000000000000031",
-            "switch_port": "3",
-            "vlan": "600"
-        }
-    }
-    connection_points = [conn_point_0, conn_point_1]
-    # service_uuid, created_items = juniper_contrail.create_connectivity_service(service_type, connection_points)
-    #print(service_uuid)
-    #print(created_items)
-    #sleep(10)
-    #juniper_contrail.delete_connectivity_service("5496dfea-27dc-457d-970d-b82bac266e5c"))
-
-
-    conn_info = None
-    conn_info = ['switch1:ifz1', 'switch3_ifz3']
-    juniper_contrail.delete_connectivity_service("f7afc4de-556d-4b5a-8a12-12b5ef97d269", conn_info)
-
-    conn_point_0 = {
-        "service_endpoint_id": "switch1:ifz1",
+        "service_endpoint_id": "0000:81:10.3",
         "service_endpoint_encapsulation_type": "dot1q",
         "service_endpoint_encapsulation_info": {
         "service_endpoint_encapsulation_type": "dot1q",
         "service_endpoint_encapsulation_info": {
-            "switch_dpid": "0000000000000011",
-            "switch_port": "1",
-            "vlan": "500"
+            "switch_dpid": "LEAF-2",
+            "switch_port": "xe-0/0/16",
+            "vlan": "501"
         }
     }
     conn_point_2 = {
         }
     }
     conn_point_2 = {
-        "service_endpoint_id": "switch1:ifz3",
+        "service_endpoint_id": "0000:08:11.7",
         "service_endpoint_encapsulation_type": "dot1q",
         "service_endpoint_encapsulation_info": {
         "service_endpoint_encapsulation_type": "dot1q",
         "service_endpoint_encapsulation_info": {
-            "switch_dpid": "0000000000000011",
-            "switch_port": "3",
-            "vlan": "500"
+            "switch_dpid": "LEAF-2",
+            "switch_port": "xe-0/0/16",
+            "vlan": "502"
         }
     }
     conn_point_3 = {
         }
     }
     conn_point_3 = {
-        "service_endpoint_id": "switch3_ifz3",
+        "service_endpoint_id": "0000:83:10.4",
         "service_endpoint_encapsulation_type": "dot1q",
         "service_endpoint_encapsulation_info": {
         "service_endpoint_encapsulation_type": "dot1q",
         "service_endpoint_encapsulation_info": {
-            "switch_dpid": "0000000000000033",
-            "switch_port": "3",
-            "vlan": "500"
+            "switch_dpid": "LEAF-1",
+            "switch_port": "xe-0/0/17",
+            "vlan": "502"
         }
     }
         }
     }
-    new_connection_points = [conn_point_0, conn_point_3]
-    #conn_info = juniper_contrail.edit_connectivity_service("f7afc4de-556d-4b5a-8a12-12b5ef97d269", conn_info, new_connection_points)
-    #print(conn_info)
+
+    # 1 - Define connection points
+    logger.debug("create first connection service")
+    print("Create connectivity service")
+    connection_points = [conn_point_0, conn_point_1]
+    service_id, conn_info = juniper_contrail.create_connectivity_service("ELAN", connection_points)
+    logger.info("Created connectivity service 1")
+    logger.info(service_id)
+    logger.info(yaml.safe_dump(conn_info, indent=4, default_flow_style=False))
+
+    logger.debug("create second connection service")
+    print("Create connectivity service")
+    connection_points = [conn_point_2, conn_point_3]
+    service_id2, conn_info2 = juniper_contrail.create_connectivity_service("ELAN", connection_points)
+    logger.info("Created connectivity service 2")
+    logger.info(service_id2)
+    logger.info(yaml.safe_dump(conn_info2, indent=4, default_flow_style=False))
+
+    logger.debug("Delete connectivity service 1")
+    juniper_contrail.delete_connectivity_service(service_id, conn_info)
+    logger.debug("Delete Ok")
+
+    logger.debug("Delete connectivity service 2")
+    juniper_contrail.delete_connectivity_service(service_id2, conn_info2)
+    logger.debug("Delete Ok")