Fixing bug 1437
[osm/RO.git] / RO-SDN-juniper_contrail / osm_rosdn_juniper_contrail / sdn_assist_juniper_contrail.py
index 2ba53bd..a1cc3de 100644 (file)
 #
 
 import logging
 #
 
 import logging
-import json
 import yaml
 import yaml
+import random
 
 
-from osm_ro.wim.sdnconn import SdnConnectorBase, SdnConnectorError
-from osm_rosdn_juniper_contrail.rest_lib import ContrailHttp
-from osm_rosdn_juniper_contrail.rest_lib import NotFound
+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.rest_lib import DuplicateFound
 from osm_rosdn_juniper_contrail.rest_lib import HttpException
-
 from osm_rosdn_juniper_contrail.sdn_api import UnderlayApi
 
 
 from osm_rosdn_juniper_contrail.sdn_api import UnderlayApi
 
 
@@ -36,10 +35,11 @@ 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
     """
     - https://github.com/tonyliu0592/contrail-toolbox/blob/master/sriov/sriov
     """
-    _WIM_LOGGER = "openmano.sdnconn.junipercontrail"
+    _WIM_LOGGER = "ro.sdn.junipercontrail"
 
     def __init__(self, wim, wim_account, config=None, logger=None):
         """
 
     def __init__(self, wim, wim_account, config=None, logger=None):
         """
@@ -62,7 +62,7 @@ class JuniperContrail(SdnConnectorBase):
                 datacenter_id           vim_account                 vim_account
                 id: (internal, do not use)
                 wim_id: (internal, do not use)
                 datacenter_id           vim_account                 vim_account
                 id: (internal, do not use)
                 wim_id: (internal, do not use)
-        :param logger (logging.Logger): optional logger object. If none is passed 'openmano.sdn.sdnconn' is used.
+        :param logger (logging.Logger): optional logger object. If none is passed 'ro.sdn.sdnconn' is used.
         """
         self.logger = logger or logging.getLogger(self._WIM_LOGGER)
         self.logger.debug('wim: {}, wim_account: {}, config: {}'.format(wim, wim_account, config))
         """
         self.logger = logger or logging.getLogger(self._WIM_LOGGER)
         self.logger.debug('wim: {}, wim_account: {}, config: {}'.format(wim, wim_account, config))
@@ -71,7 +71,7 @@ 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") # underlay url
+        url = wim.get("wim_url")  # underlay url
         auth_url = None
         self.project = None
         self.domain = None
         auth_url = None
         self.project = None
         self.domain = None
@@ -147,20 +147,20 @@ 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))
+            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
@@ -187,38 +187,43 @@ class JuniperContrail(SdnConnectorBase):
             # Assign vpg_id from vpg
             vpg_id = vpg.get("uuid")
 
             # Assign vpg_id from vpg
             vpg_id = vpg.get("uuid")
 
-        # 3 - Create vmi
+        # 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
 
         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, vpg_id, vmi_id):
-        self.logger.debug("delete port, vpg_id: {}, vmi_id: {}".format(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)
 
         # 1 - Obtain vpg by id (if not vpg_id must have been error creating ig, nothing to be done)
-        if vpg_id:
-            vpg = self.underlay_api.get_by_uuid("virtual-port-group", vpg_id)
-            if not vpg:
-                self.logger.warning("vpg: {} to be deleted not found".format(vpg_id))
+        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:
             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:
-                        uuid = vmi.get("uuid")
-                        if uuid == vmi_id:
-                            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"))
+                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),
@@ -276,17 +281,17 @@ class JuniperContrail(SdnConnectorBase):
         """
         self.logger.debug("")
         try:
         """
         self.logger.debug("")
         try:
-            resp = self.http.get_cmd(endpoint='virtual-network/{}'.format(service_uuid))
+            resp = self.underlay_api.get_virtual_network(service_uuid)
             if not resp:
                 raise SdnConnectorError('Empty response')
             if resp:
             if not resp:
                 raise SdnConnectorError('Empty response')
             if resp:
-                vnet_info = json.loads(resp)
+                vnet_info = resp
 
                 # Check if conn_info reports error
                 if conn_info.get("sdn_status") == "ERROR":
 
                 # Check if conn_info reports error
                 if conn_info.get("sdn_status") == "ERROR":
-                    return {'sdn_status': 'ACTIVE', 'sdn_info': "conn_info indicates pending error"}
+                    return {'sdn_status': 'ERROR', 'sdn_info': conn_info}
                 else:
                 else:
-                    return {'sdn_status': 'ACTIVE', 'sdn_info': vnet_info['virtual-network']}
+                    return {'sdn_status': 'ACTIVE', 'sdn_info': vnet_info}
             else:
                 return {'sdn_status': 'ERROR', 'sdn_info': 'not found'}
         except SdnConnectorError:
             else:
                 return {'sdn_status': 'ERROR', 'sdn_info': 'not found'}
         except SdnConnectorError:
@@ -298,7 +303,6 @@ class JuniperContrail(SdnConnectorBase):
             self.logger.error('Exception getting connectivity service info: %s', e, exc_info=True)
             return {'sdn_status': 'ERROR', 'error_msg': str(e)}
 
             self.logger.error('Exception getting connectivity service info: %s', e, exc_info=True)
             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
@@ -345,7 +349,8 @@ 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
         # 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
@@ -360,11 +365,35 @@ class JuniperContrail(SdnConnectorBase):
         try:
             # Initialize data
             conn_info = None
         try:
             # Initialize data
             conn_info = None
-            conn_info_cp = {}
 
 
-            # 1 - Obtain VLAN-ID
-            vlan = self._get_vlan(connection_points)
-            self.logger.debug("Provided vlan: {}".format(vlan))
+            # 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()
 
             # 2 - Obtain free VNI
             vni = self._generate_vni()
@@ -393,21 +422,21 @@ class JuniperContrail(SdnConnectorBase):
                     "uuid": vnet_id,
                     "name": vnet_name
                 },
                     "uuid": vnet_id,
                     "name": vnet_name
                 },
-                "connection_points": conn_info_cp # dict with port_name as key
+                "connection_points": work_cps  # dict with port_name as key
             }
 
             # 4 - Create a port for each endpoint
             }
 
             # 4 - Create a port for each endpoint
-            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")
+            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)
                 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[self.underlay_api.get_vpg_name(switch_id, switch_port)] = cp_added
+                cp["vpg_id"] = vpg_id
+                cp["vmi_id"] = vmi_id
 
 
-            return vnet_id, conn_info
             self.logger.info("created connectivity service, uuid: {}, name: {}".format(vnet_id, vnet_name))
             self.logger.info("created connectivity service, uuid: {}, name: {}".format(vnet_id, vnet_name))
+            return vnet_id, conn_info
+
         except Exception as e:
             # Log error
             if isinstance(e, SdnConnectorError) or isinstance(e, HttpException):
         except Exception as e:
             # Log error
             if isinstance(e, SdnConnectorError) or isinstance(e, HttpException):
@@ -415,19 +444,16 @@ class JuniperContrail(SdnConnectorBase):
             else:
                 self.logger.error("Error creating connectivity service: {}".format(e), exc_info=True)
 
             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"
             # 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
                 # iterate over not added connection_points and add but marking them as error
-                for cp in connection_points[len(conn_info_cp):]:
-                    cp_error = cp.copy()
-                    cp_error["sdn_status"] = "ERROR"
-                    switch_id = cp.get("service_endpoint_encapsulation_info").get("switch_dpid")
-                    switch_port = cp.get("service_endpoint_encapsulation_info").get("switch_port")
-                    conn_info_cp[self.underlay_api.get_vpg_name(switch_id, switch_port)] = cp_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):
                 return vnet_id, conn_info
 
     def delete_connectivity_service(self, service_uuid, conn_info=None):
@@ -441,21 +467,22 @@ class JuniperContrail(SdnConnectorBase):
         :raises: SdnConnectorException: In case of error. The parameter http_code must be filled
         """
         self.logger.info("delete_connectivity_service vnet_name: {}, connection_points: {}".
         :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))
+                         format(service_uuid, conn_info))
 
         try:
             vnet_uuid = service_uuid
 
         try:
             vnet_uuid = service_uuid
-            vnet_name = conn_info["vnet"]["name"]   # always should exist as the network is the first thing created
-            connection_points = conn_info["connection_points"].values()
-            vlan = self._get_vlan(connection_points)
+            # 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
 
             # 1: For each connection point delete vlan from vpg and it is is the
             # last one, delete vpg
-            for cp in connection_points:
-                self._delete_port(cp.get("vpg_id"), cp.get("vmi_id"))
+            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)
 
             # 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:
         except SdnConnectorError:
             raise
         except HttpException as e:
@@ -465,20 +492,7 @@ class JuniperContrail(SdnConnectorBase):
             self.logger.error("Error deleting connectivity service: {}".format(e), exc_info=True)
             raise SdnConnectorError("Exception deleting connectivity service: {}".format(str(e)))
 
             self.logger.error("Error deleting connectivity service: {}".format(e), exc_info=True)
             raise SdnConnectorError("Exception deleting connectivity service: {}".format(str(e)))
 
-    # Helper methods
-    @staticmethod
-    def _get_vlan(connection_points):
-        vlan = None
-        for cp in connection_points:
-            cp_vlan = cp.get("service_endpoint_encapsulation_info").get("vlan")
-            if not vlan:
-                vlan = cp_vlan
-            else:
-                if vlan != cp_vlan:
-                    raise SdnConnectorError("More that one cp provided")
-        return vlan
-
-    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
@@ -504,29 +518,29 @@ class JuniperContrail(SdnConnectorBase):
         # 3 - Delete unnecesary ports
         # 4 - Add new ports
         self.logger.info("edit connectivity service, service_uuid: {}, conn_info: {}, "
         # 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))
+                         "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", {})
 
         # conn_info should always exist and have connection_points and vnet elements
         old_cp = conn_info.get("connection_points", {})
-        old_vlan = self._get_vlan(old_cp)
 
         # 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:
 
         # 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:
-            delete_conn_info = []
-            for cp in old_cp:
+            deleted_ports = []
+            for cp in old_cp.values():
                 if cp.get("sdn_status") == "ERROR":
                 if cp.get("sdn_status") == "ERROR":
-                    switch_id = cp.get("service_endpoint_encapsulation_info").get("switch_dpid")
-                    switch_port = cp.get("service_endpoint_encapsulation_info").get("switch_port")
+                    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)
                     self._delete_port(switch_id, switch_port, old_vlan)
-                    delete_conn_info.append(self.underlay_api.get_vpg_name(switch_id, switch_port))
+                    deleted_ports.append(self.underlay_api.get_vpg_name(switch_id, switch_port))
 
 
-            for i in delete_conn_info:
-                del old_cp[i]
+            for port in deleted_ports:
+                del old_cp[port]
 
 
-            # Delete vnet status if exists (possibly marked as error)
-            if conn_info.get("vnet",{}).get("sdn_status"):
+            # 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))
                 del conn_info["vnet"]["sdn_status"]
         except HttpException as e:
             self.logger.error("Error trying to delete old ports marked as error: {}".format(e))
@@ -542,21 +556,46 @@ class JuniperContrail(SdnConnectorBase):
 
             # Check and obtain what should be added and deleted, if there is an error here raise an exception
             try:
 
             # Check and obtain what should be added and deleted, if there is an error here raise an exception
             try:
+                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)
 
 
-                vlan = self._get_vlan(connection_points)
-
-                old_port_list = ["{}_{}".format(cp["service_endpoint_encapsulation_info"]["switch_dpid"],
-                                          cp["service_endpoint_encapsulation_info"]["switch_port"])
-                           for cp in old_cp.values()]
-                port_list = ["{}_{}".format(cp["service_endpoint_encapsulation_info"]["switch_dpid"],
-                                          cp["service_endpoint_encapsulation_info"]["switch_port"])
-                           for cp in connection_points]
+                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))
                 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
-                vnet = self.underlay_api.get_virtual_network(self.get_url(), service_uuid)
-                vnet_name = vnet["name"]
+                # 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
 
             except SdnConnectorError:
                 raise
@@ -564,7 +603,6 @@ class JuniperContrail(SdnConnectorBase):
                 self.logger.error("Error edit connectivity service: {}".format(e), exc_info=True)
                 raise SdnConnectorError("Exception edit connectivity service: {}".format(str(e)))
 
                 self.logger.error("Error edit connectivity service: {}".format(e), exc_info=True)
                 raise SdnConnectorError("Exception edit connectivity service: {}".format(str(e)))
 
-
             # 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
             # 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
@@ -572,23 +610,34 @@ class JuniperContrail(SdnConnectorBase):
                 conn_info_cp = old_cp
 
                 # Delete unneeded ports
                 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]
                 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("service_endpoint_encapsulation_info").get("switch_dpid")
-                        switch_port = cp.get("service_endpoint_encapsulation_info").get("switch_port")
-                        self.logger.debug("delete port switch_id, switch_port: {}".format(switch_id, switch_port))
+                        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)
                         self._delete_port(switch_id, switch_port, vlan)
-                        del conn_info_cp[port_name]
+                        deleted_ports.append(port_name)
+
+                # Delete ports
+                for port_name in deleted_ports:
+                    del conn_info_cp[port_name]
 
                 # Add needed ports
 
                 # Add needed ports
-                for cp in connection_points:
+                for port_name, cp in work_cps.items():
                     if port_name in to_add_ports:
                     if port_name in to_add_ports:
-                        switch_id = cp.get("service_endpoint_encapsulation_info").get("switch_dpid")
-                        switch_port = cp.get("service_endpoint_encapsulation_info").get("switch_port")
-                        self.logger.debug("add port switch_id, switch_port: {}".format(switch_id, switch_port))
-                        self._create_port(switch_id, switch_port, vnet_name, vlan)
-                        conn_info_cp[port_name]
+                        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
 
                 conn_info["connection_points"] = conn_info_cp
                 return conn_info
@@ -606,20 +655,19 @@ class JuniperContrail(SdnConnectorBase):
                     if port_name in to_delete_ports:
                         cp["sdn_status"] = "ERROR"
 
                     if port_name in to_delete_ports:
                         cp["sdn_status"] = "ERROR"
 
-                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")
-                    port_name = self.underlay_api.get_vpg_name(switch_id, switch_port)
-                    if port_name in to_add_ports:
-                        cp_error = cp.copy()
+                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
                         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_status"] = "ERROR"
+                conn_info["sdn_info"] = repr(e)
                 conn_info["connection_points"] = conn_info_cp
                 return conn_info
 
                 conn_info["connection_points"] = conn_info_cp
                 return conn_info
 
-
         else:
             # Connection points have not changed, so do nothing
             self.logger.info("no new connection_points provided, nothing to be done")
         else:
             # Connection points have not changed, so do nothing
             self.logger.info("no new connection_points provided, nothing to be done")
@@ -632,9 +680,9 @@ if __name__ == '__main__':
     log_formatter = logging.Formatter(log_format, datefmt='%Y-%m-%dT%H:%M:%S')
     handler = logging.StreamHandler()
     handler.setFormatter(log_formatter)
     log_formatter = logging.Formatter(log_format, datefmt='%Y-%m-%dT%H:%M:%S')
     handler = logging.StreamHandler()
     handler.setFormatter(log_formatter)
-    logger = logging.getLogger('openmano.sdnconn.junipercontrail')
-    #logger.setLevel(level=logging.ERROR)
-    #logger.setLevel(level=logging.INFO)
+    logger = logging.getLogger('ro.sdn.junipercontrail')
+    # logger.setLevel(level=logging.ERROR)
+    # logger.setLevel(level=logging.INFO)
     logger.setLevel(level=logging.DEBUG)
     logger.addHandler(handler)
 
     logger.setLevel(level=logging.DEBUG)
     logger.addHandler(handler)
 
@@ -654,167 +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()
-    # Generate VNI
-    for i in range(5):
-        vni = juniper_contrail._generate_vni()
-        juniper_contrail.used_vni.add(vni)
-    print(juniper_contrail.used_vni)
-    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)
-    # 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)
-
-    # Test CRUD:
-    net_name = "gerardo"
-    net_vni = "2000"
-    net_vlan = "501"
-    switch_1 = "LEAF-2"
-    port_1 = "xe-0/0/18"
-    switch_2 = "LEAF-1"
-    port_2 = "xe-0/0/18"
-
-    # 1 - Create a new virtual network
-    vnet2_id, vnet2_created = juniper_contrail._create_virtual_network(underlay_url, net_name, net_vni)
-    print("Created virtual network:")
-    print(vnet2_id)
-    print(yaml.safe_dump(vnet2_created, indent=4, default_flow_style=False))
-    print("Get virtual network:")
-    vnet2_info = juniper_contrail._get_virtual_network(underlay_url, vnet2_id)
-    print(json.dumps(vnet2_info, indent=4))
-    print('OK')
-
-    # 2 - Create a new virtual port group
-    vpg_id, vpg_info = juniper_contrail._create_vpg(underlay_url, switch_1, port_1, net_name, net_vlan)
-    print("Created virtual port group:")
-    print(vpg_id)
-    print(json.dumps(vpg_info, indent=4))
-
-    print("Get virtual network:")
-    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')
-
-    # 3 - Create a new virtual machine interface
-    vmi_id, vmi_info = juniper_contrail._create_vmi(underlay_url, switch_1, port_1, net_name, net_vlan)
-    print("Created virtual machine interface:")
-    print(vmi_id)
-    print(yaml.safe_dump(vmi_info, indent=4, default_flow_style=False))
-
-    # 4 - Create a second virtual port group
-    # 5 - Create a second virtual machine interface
-
-    ### Test rapido de modificación de requests:
-    # Ver que metodos siguen funcionando y cuales no e irlos corrigiendo
-
-    """
-    vnets = juniper_contrail._get_virtual_networks(underlay_url)
-    logger.debug("Virtual networks:")
-    logger.debug(json.dumps(vnets, indent=2))
-
-    vpgs = juniper_contrail._get_vpgs(underlay_url)
-    logger.debug("Virtual port groups:")
-    logger.debug(json.dumps(vpgs, indent=2))
-    """
-    # Get by uuid
-
-    """
-    # 3 - Get vmi
-    vmi_uuid = "dbfd2099-b895-459e-98af-882d77d968c1"
-    vmi = juniper_contrail._get_vmi(underlay_url, vmi_uuid)
-    logger.debug("Virtual machine interface:")
-    logger.debug(json.dumps(vmi, indent=2))
-
-    # Delete vmi    
-    logger.debug("Delete vmi")
-    juniper_contrail._delete_vmi(underlay_url, vmi_uuid)
-    """
-
-    """
-    # 2 - Get vpg
-    vpg_uuid = "85156474-d1a5-44c0-9d8b-8f690f39d27e"
-    vpg = juniper_contrail._get_vpg(underlay_url, vpg_uuid)
-    logger.debug("Virtual port group:")
-    logger.debug(json.dumps(vpg, indent=2))
-    # Delete vpg
-    vpg = juniper_contrail._delete_vpg(underlay_url, vpg_uuid)
-    """
-
-    # 1 - Obtain virtual network
-    """
-    vnet_uuid = "68457d61-6558-4d38-a03d-369a9de803ea"
-    vnet = juniper_contrail._get_virtual_network(underlay_url, vnet_uuid)
-    logger.debug("Virtual network:")
-    logger.debug(json.dumps(vnet, indent=2))
-    # Delete virtual network
-    juniper_contrail._delete_virtual_network(underlay_url, vnet_uuid)
-    """
+    # juniper_contrail.check_credentials()
+
+    # 1 - Create and delete connectivity service
+    conn_point_0 = {
+        "service_endpoint_id": "0000:83:11.4",
+        "service_endpoint_encapsulation_type": "dot1q",
+        "service_endpoint_encapsulation_info": {
+            "switch_dpid": "LEAF-1",
+            "switch_port": "xe-0/0/17",
+            "vlan": "501"
+        }
+    }
+    conn_point_1 = {
+        "service_endpoint_id": "0000:81:10.3",
+        "service_endpoint_encapsulation_type": "dot1q",
+        "service_endpoint_encapsulation_info": {
+            "switch_dpid": "LEAF-2",
+            "switch_port": "xe-0/0/16",
+            "vlan": "501"
+        }
+    }
+    conn_point_2 = {
+        "service_endpoint_id": "0000:08:11.7",
+        "service_endpoint_encapsulation_type": "dot1q",
+        "service_endpoint_encapsulation_info": {
+            "switch_dpid": "LEAF-2",
+            "switch_port": "xe-0/0/16",
+            "vlan": "502"
+        }
+    }
+    conn_point_3 = {
+        "service_endpoint_id": "0000:83:10.4",
+        "service_endpoint_encapsulation_type": "dot1q",
+        "service_endpoint_encapsulation_info": {
+            "switch_dpid": "LEAF-1",
+            "switch_port": "xe-0/0/17",
+            "vlan": "502"
+        }
+    }
+
+    # 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")