From: lloretgalleg Date: Fri, 22 May 2020 14:34:16 +0000 (+0200) Subject: Corrected bugs found during unit and integration testing X-Git-Tag: v7.1.0rc5^2~1 X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FRO.git;a=commitdiff_plain;h=c047048a86c3ad82c3f31c9aa5e0eacb47f9caf3 Corrected bugs found during unit and integration testing Change-Id: Ied98adbc364f7bdd16c38e019abfee05c18afdef Signed-off-by: lloretgalleg --- diff --git a/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/rest_lib.py b/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/rest_lib.py index 7ec2401a..2586d410 100644 --- a/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/rest_lib.py +++ b/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/rest_lib.py @@ -96,18 +96,19 @@ class ContrailHttp(object): return resp.text def _get_token(self, headers): - self._logger.debug('Current Token:'.format(self.token)) - auth_url = self.auth_url + 'auth/tokens' - if self.token is None or self._token_expired(): - if not self.auth_url: - self.token = "" - resp = self._request_noauth(url=auth_url, op="POST", headers=headers, - data=self.auth_dict) - self.token = resp.headers.get('x-subject-token') - self.last_token_time = time.time() - self._logger.debug('Obtained token: '.format(self.token)) - - return self.token + if self.auth_url: + self._logger.debug('Current Token:'.format(self.token)) + auth_url = self.auth_url + 'auth/tokens' + if self.token is None or self._token_expired(): + if not self.auth_url: + self.token = "" + resp = self._request_noauth(url=auth_url, op="POST", headers=headers, + data=self.auth_dict) + self.token = resp.headers.get('x-subject-token') + self.last_token_time = time.time() + self._logger.debug('Obtained token: '.format(self.token)) + + return self.token def _token_expired(self): current_time = time.time() diff --git a/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/sdn_api.py b/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/sdn_api.py index 1c5b5285..2bf0b52f 100644 --- a/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/sdn_api.py +++ b/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/sdn_api.py @@ -39,7 +39,7 @@ class UnderlayApi: url = url + "/" self.url = url - auth_url = None + self.auth_url = None self.project = None self.domain = None self.asn = None @@ -56,10 +56,13 @@ class UnderlayApi: if user: self.user = user - if password: self.password = password + self.logger.debug("Config parameters for the underlay controller: auth_url: {}, project: {}," + " domain: {}, user: {}, password: {}".format(self.auth_url, self.project, + self.domain, self.user, self.password)) + auth_dict = {} auth_dict['auth'] = {} auth_dict['auth']['scope'] = {} @@ -130,6 +133,19 @@ class UnderlayApi: else: return None + def delete_ref(self, type, uuid, ref_type, ref_uuid, ref_fq_name): + payload = { + "type": type, + "uuid": uuid, + "ref-type": ref_type, + "ref-fq-name": ref_fq_name, + "operation": "DELETE" + } + endpoint = self.controller_url + "ref-update" + resp = self.http.post_cmd(url=endpoint, + headers=self.http_header, + post_fields_dict=payload) + return resp # Aux methods to avoid code duplication of name conventions def get_vpg_name(self, switch_id, switch_port): @@ -303,3 +319,6 @@ class UnderlayApi: self.delete_by_uuid(self.controller_url, 'virtual-machine-interface', uuid) self.logger.debug("deleted vmi: {}".format(uuid)) + def unref_vmi_vpg(self, vpg_id, vmi_id, vmi_fq_name): + self.delete_ref("virtual-port-group", vpg_id, "virtual-machine-interface", vmi_id, vmi_fq_name) + diff --git a/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/sdn_assist_juniper_contrail.py b/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/sdn_assist_juniper_contrail.py index 2ba53bd5..2cd9fdac 100644 --- a/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/sdn_assist_juniper_contrail.py +++ b/RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/sdn_assist_juniper_contrail.py @@ -187,38 +187,44 @@ class JuniperContrail(SdnConnectorBase): # 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 - 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) - 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: - # 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: + uuid = vmi.get("uuid") + 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), @@ -276,17 +282,17 @@ class JuniperContrail(SdnConnectorBase): """ 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: - vnet_info = json.loads(resp) + vnet_info = resp # 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: - 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: @@ -360,11 +366,38 @@ class JuniperContrail(SdnConnectorBase): 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 + vlans = set() + 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") + vlans.add(cp.get("service_endpoint_encapsulation_info").get("vlan")) + 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 + 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} + work_cps[cp_name] = add_cp + else: + # add service_endpoint_id to list + service_endpoint_ids = add_cp["service_endpoint_ids"] + service_endpoint_ids.append(service_endpoint_id) + + # check vlan + if len(vlans) == 1: + vlan = vlans.pop() + self.logger.debug("Provided vlan: {}".format(vlan)) + else: + raise SdnConnectorError("Provided more than one vlan") # 2 - Obtain free VNI vni = self._generate_vni() @@ -391,23 +424,23 @@ class JuniperContrail(SdnConnectorBase): conn_info = { "vnet": { "uuid": vnet_id, - "name": vnet_name + "name": vnet_name, + "vlan": vlan }, - "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 - 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") 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)) + return vnet_id, conn_info + except Exception as e: # Log error if isinstance(e, SdnConnectorError) or isinstance(e, HttpException): @@ -421,13 +454,11 @@ class JuniperContrail(SdnConnectorBase): 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 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): @@ -446,16 +477,18 @@ class JuniperContrail(SdnConnectorBase): 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) + work_cps = conn_info["connection_points"] + vlan = conn_info["vnet"]["vlan"] # 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"), vlan) # 2: Delete vnet self.underlay_api.delete_virtual_network(vnet_uuid) + self.logger.info("deleted connectivity_service vnet_name: {}, connection_points: {}". + format(service_uuid, conn_info)) except SdnConnectorError: raise except HttpException as e: @@ -508,24 +541,24 @@ class JuniperContrail(SdnConnectorBase): # 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) + old_vlan = conn_info.get("vnet", {}).get("vlan") # 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": - 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") 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) + # 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: @@ -542,21 +575,48 @@ class JuniperContrail(SdnConnectorBase): # Check and obtain what should be added and deleted, if there is an error here raise an exception try: + vlans = set() + 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") + vlans.add(cp.get("service_endpoint_encapsulation_info").get("vlan")) + 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 + 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} + work_cps[cp_name] = add_cp + else: + # add service_endpoint_id to list + service_endpoint_ids = add_cp["service_endpoint_ids"] + service_endpoint_ids.append(service_endpoint_id) + + # check vlan + if len(vlans) == 1: + vlan = vlans.pop() + self.logger.debug("Provided vlan: {}".format(vlan)) + else: + raise SdnConnectorError("Provided more than one vlan") - 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)) + 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 @@ -564,7 +624,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))) - # 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 +631,33 @@ class JuniperContrail(SdnConnectorBase): 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("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") self.logger.debug("delete port switch_id, switch_port: {}".format(switch_id, switch_port)) 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 - for cp in connection_points: + for port_name, cp in work_cps.items(): 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") + switch_id = cp.get("switch_dpid") + switch_port = cp.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] + 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 @@ -606,20 +675,19 @@ class JuniperContrail(SdnConnectorBase): 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 + 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 - else: # Connection points have not changed, so do nothing self.logger.info("no new connection_points provided, nothing to be done")