X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=RO-VIM-openstack%2Fosm_rovim_openstack%2Fvimconn_openstack.py;h=2f7a5ef1db241f1053c2618c2f6737de63d0bfad;hb=2f99505fff582e1213d5cafe9c87d06c87d9af7d;hp=993dccdfbb6940f3cef9430c8aac5895662c8592;hpb=13d0232214b1f8db522ee8861741b938ea37bd45;p=osm%2FRO.git diff --git a/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py b/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py index 993dccdf..2f7a5ef1 100644 --- a/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py +++ b/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py @@ -754,7 +754,7 @@ class vimconnector(vimconn.VimConnector): self._reload_connection() network_dict = {"name": net_name, "admin_state_up": True} - if net_type in ("data", "ptp"): + if net_type in ("data", "ptp") or provider_network_profile: provider_physical_network = None if provider_network_profile and provider_network_profile.get( @@ -1954,11 +1954,46 @@ class vimconnector(vimconn.VimConnector): availability_zone=vm_av_zone, ) boot_volume_id = volume.id - created_items["volume:" + str(volume.id)] = True - block_device_mapping["vd" + chr(base_disk_index)] = volume.id + self.update_block_device_mapping( + volume=volume, + block_device_mapping=block_device_mapping, + base_disk_index=base_disk_index, + disk=disk, + created_items=created_items, + ) return boot_volume_id + @staticmethod + def update_block_device_mapping( + volume: object, + block_device_mapping: dict, + base_disk_index: int, + disk: dict, + created_items: dict, + ) -> None: + """Add volume information to block device mapping dict. + Args: + volume (object): Created volume object + block_device_mapping (dict): Block device details + base_disk_index (int): Disk index + disk (dict): Disk details + created_items (dict): All created items belongs to VM + """ + if not volume: + raise vimconn.VimConnException("Volume is empty.") + + if not hasattr(volume, "id"): + raise vimconn.VimConnException( + "Created volume is not valid, does not have id attribute." + ) + + volume_txt = "volume:" + str(volume.id) + if disk.get("keep"): + volume_txt += ":keep" + created_items[volume_txt] = True + block_device_mapping["vd" + chr(base_disk_index)] = volume.id + def _prepare_non_root_persistent_volumes( self, name: str, @@ -1998,8 +2033,13 @@ class vimconnector(vimconn.VimConnector): # Make sure volume is in the same AZ as the VM to be attached to availability_zone=vm_av_zone, ) - created_items["volume:" + str(volume.id)] = True - block_device_mapping["vd" + chr(base_disk_index)] = volume.id + self.update_block_device_mapping( + volume=volume, + block_device_mapping=block_device_mapping, + base_disk_index=base_disk_index, + disk=disk, + created_items=created_items, + ) def _wait_for_created_volumes_availability( self, elapsed_time: int, created_items: dict @@ -2017,7 +2057,10 @@ class vimconnector(vimconn.VimConnector): while elapsed_time < volume_timeout: for created_item in created_items: - v, _, volume_id = created_item.partition(":") + v, volume_id = ( + created_item.split(":")[0], + created_item.split(":")[1], + ) if v == "volume": if self.cinder.volumes.get(volume_id).status != "available": break @@ -2262,14 +2305,13 @@ class vimconnector(vimconn.VimConnector): return self.neutron.show_floatingip(free_floating_ip) def _get_free_floating_ip( - self, server: object, floating_network: dict, created_items: dict + self, server: object, floating_network: dict ) -> Optional[str]: """Get the free floating IP address. Args: server (object): Server Object floating_network (dict): Floating network details - created_items (dict): All created items belongs to new VM instance Returns: free_floating_ip (str): Free floating ip addr @@ -2281,9 +2323,7 @@ class vimconnector(vimconn.VimConnector): # Randomize random.shuffle(floating_ips) - return self._find_floating_ip( - server, floating_ips, floating_network, created_items - ) + return self._find_floating_ip(server, floating_ips, floating_network) def _prepare_external_network_for_vminstance( self, @@ -2313,7 +2353,7 @@ class vimconnector(vimconn.VimConnector): while not assigned: free_floating_ip = self._get_free_floating_ip( - server, floating_network, created_items + server, floating_network ) if not free_floating_ip: @@ -2478,6 +2518,7 @@ class vimconnector(vimconn.VimConnector): the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc. Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same as not present. + """ self.logger.debug( "new_vminstance input: image='%s' flavor='%s' nics='%s'", @@ -2589,6 +2630,10 @@ class vimconnector(vimconn.VimConnector): server_id = server.id try: + created_items = self.remove_keep_tag_from_persistent_volumes( + created_items + ) + self.delete_vminstance(server_id, created_items) except Exception as e2: @@ -2596,6 +2641,21 @@ class vimconnector(vimconn.VimConnector): self._format_exception(e) + @staticmethod + def remove_keep_tag_from_persistent_volumes(created_items: Dict) -> Dict: + """Removes the keep flag from persistent volumes. So, those volumes could be removed. + + Args: + created_items (dict): All created items belongs to VM + + Returns: + updated_created_items (dict): Dict which does not include keep flag for volumes. + + """ + return { + key.replace(":keep", ""): value for (key, value) in created_items.items() + } + def get_vminstance(self, vm_id): """Returns the VM instance information from VIM""" # self.logger.debug("Getting VM from VIM") @@ -2689,76 +2749,180 @@ class vimconnector(vimconn.VimConnector): ) as e: self._format_exception(e) - def delete_vminstance(self, vm_id, created_items=None, volumes_to_hold=None): - """Removes a VM instance from VIM. Returns the old identifier""" - # print "osconnector: Getting VM from VIM" + def _delete_ports_by_id_wth_neutron(self, k_id: str) -> None: + """Neutron delete ports by id. + Args: + k_id (str): Port id in the VIM + """ + try: + + port_dict = self.neutron.list_ports() + existing_ports = [port["id"] for port in port_dict["ports"] if port_dict] + + if k_id in existing_ports: + self.neutron.delete_port(k_id) + + except Exception as e: + + self.logger.error("Error deleting port: {}: {}".format(type(e).__name__, e)) + + def _delete_volumes_by_id_wth_cinder( + self, k: str, k_id: str, volumes_to_hold: list, created_items: dict + ) -> bool: + """Cinder delete volume by id. + Args: + k (str): Full item name in created_items + k_id (str): ID of floating ip in VIM + volumes_to_hold (list): Volumes not to delete + created_items (dict): All created items belongs to VM + """ + try: + if k_id in volumes_to_hold: + return + + if self.cinder.volumes.get(k_id).status != "available": + return True + + else: + self.cinder.volumes.delete(k_id) + created_items[k] = None + + except Exception as e: + self.logger.error( + "Error deleting volume: {}: {}".format(type(e).__name__, e) + ) + + def _delete_floating_ip_by_id(self, k: str, k_id: str, created_items: dict) -> None: + """Neutron delete floating ip by id. + Args: + k (str): Full item name in created_items + k_id (str): ID of floating ip in VIM + created_items (dict): All created items belongs to VM + """ + try: + self.neutron.delete_floatingip(k_id) + created_items[k] = None + + except Exception as e: + self.logger.error( + "Error deleting floating ip: {}: {}".format(type(e).__name__, e) + ) + + @staticmethod + def _get_item_name_id(k: str) -> Tuple[str, str]: + k_item, _, k_id = k.partition(":") + return k_item, k_id + + def _delete_vm_ports_attached_to_network(self, created_items: dict) -> None: + """Delete VM ports attached to the networks before deleting virtual machine. + Args: + created_items (dict): All created items belongs to VM + """ + + for k, v in created_items.items(): + if not v: # skip already deleted + continue + + try: + k_item, k_id = self._get_item_name_id(k) + if k_item == "port": + self._delete_ports_by_id_wth_neutron(k_id) + + except Exception as e: + self.logger.error( + "Error deleting port: {}: {}".format(type(e).__name__, e) + ) + + def _delete_created_items( + self, created_items: dict, volumes_to_hold: list, keep_waiting: bool + ) -> bool: + """Delete Volumes and floating ip if they exist in created_items.""" + for k, v in created_items.items(): + if not v: # skip already deleted + continue + + try: + k_item, k_id = self._get_item_name_id(k) + + if k_item == "volume": + + unavailable_vol = self._delete_volumes_by_id_wth_cinder( + k, k_id, volumes_to_hold, created_items + ) + + if unavailable_vol: + keep_waiting = True + + elif k_item == "floating_ip": + + self._delete_floating_ip_by_id(k, k_id, created_items) + + except Exception as e: + self.logger.error("Error deleting {}: {}".format(k, e)) + + return keep_waiting + + @staticmethod + def _extract_items_wth_keep_flag_from_created_items(created_items: dict) -> dict: + """Remove the volumes which has key flag from created_items + + Args: + created_items (dict): All created items belongs to VM + + Returns: + created_items (dict): Persistent volumes eliminated created_items + """ + return { + key: value + for (key, value) in created_items.items() + if len(key.split(":")) == 2 + } + + def delete_vminstance( + self, vm_id: str, created_items: dict = None, volumes_to_hold: list = None + ) -> None: + """Removes a VM instance from VIM. Returns the old identifier. + Args: + vm_id (str): Identifier of VM instance + created_items (dict): All created items belongs to VM + volumes_to_hold (list): Volumes_to_hold + """ if created_items is None: created_items = {} + if volumes_to_hold is None: + volumes_to_hold = [] try: - self._reload_connection() - # delete VM ports attached to this networks before the virtual machine - for k, v in created_items.items(): - if not v: # skip already deleted - continue + created_items = self._extract_items_wth_keep_flag_from_created_items( + created_items + ) - try: - k_item, _, k_id = k.partition(":") - if k_item == "port": - port_dict = self.neutron.list_ports() - existing_ports = [ - port["id"] for port in port_dict["ports"] if port_dict - ] - if k_id in existing_ports: - self.neutron.delete_port(k_id) - except Exception as e: - self.logger.error( - "Error deleting port: {}: {}".format(type(e).__name__, e) - ) + self._reload_connection() - # #commented because detaching the volumes makes the servers.delete not work properly ?!? - # #dettach volumes attached - # server = self.nova.servers.get(vm_id) - # volumes_attached_dict = server._info["os-extended-volumes:volumes_attached"] #volume["id"] - # #for volume in volumes_attached_dict: - # # self.cinder.volumes.detach(volume["id"]) + # Delete VM ports attached to the networks before the virtual machine + if created_items: + self._delete_vm_ports_attached_to_network(created_items) if vm_id: self.nova.servers.delete(vm_id) - # delete volumes. Although having detached, they should have in active status before deleting - # we ensure in this loop + # Although having detached, volumes should have in active status before deleting. + # We ensure in this loop keep_waiting = True elapsed_time = 0 while keep_waiting and elapsed_time < volume_timeout: keep_waiting = False - for k, v in created_items.items(): - if not v: # skip already deleted - continue - - try: - k_item, _, k_id = k.partition(":") - if k_item == "volume": - if self.cinder.volumes.get(k_id).status != "available": - keep_waiting = True - else: - if k_id not in volumes_to_hold: - self.cinder.volumes.delete(k_id) - created_items[k] = None - elif k_item == "floating_ip": # floating ip - self.neutron.delete_floatingip(k_id) - created_items[k] = None - - except Exception as e: - self.logger.error("Error deleting {}: {}".format(k, e)) + # Delete volumes and floating IP. + keep_waiting = self._delete_created_items( + created_items, volumes_to_hold, keep_waiting + ) if keep_waiting: time.sleep(1) elapsed_time += 1 - return None except ( nvExceptions.NotFound, ksExceptions.ClientException, @@ -3156,60 +3320,6 @@ class vimconnector(vimconn.VimConnector): ) ) - # NOT USED FUNCTIONS - - def new_external_port(self, port_data): - """Adds a external port to VIM - Returns the port identifier""" - # TODO openstack if needed - return ( - -vimconn.HTTP_Internal_Server_Error, - "osconnector.new_external_port() not implemented", - ) - - def connect_port_network(self, port_id, network_id, admin=False): - """Connects a external port to a network - Returns status code of the VIM response""" - # TODO openstack if needed - return ( - -vimconn.HTTP_Internal_Server_Error, - "osconnector.connect_port_network() not implemented", - ) - - def new_user(self, user_name, user_passwd, tenant_id=None): - """Adds a new user to openstack VIM - Returns the user identifier""" - self.logger.debug("osconnector: Adding a new user to VIM") - - try: - self._reload_connection() - user = self.keystone.users.create( - user_name, password=user_passwd, default_project=tenant_id - ) - # self.keystone.tenants.add_user(self.k_creds["username"], #role) - - return user.id - except ksExceptions.ConnectionError as e: - error_value = -vimconn.HTTP_Bad_Request - error_text = ( - type(e).__name__ - + ": " - + (str(e) if len(e.args) == 0 else str(e.args[0])) - ) - except ksExceptions.ClientException as e: # TODO remove - error_value = -vimconn.HTTP_Bad_Request - error_text = ( - type(e).__name__ - + ": " - + (str(e) if len(e.args) == 0 else str(e.args[0])) - ) - - # TODO insert exception vimconn.HTTP_Unauthorized - # if reaching here is because an exception - self.logger.debug("new_user " + error_text) - - return error_value, error_text - def delete_user(self, user_id): """Delete a user from openstack VIM Returns the user identifier"""