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,
# 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
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
existing_vim_volumes: list,
created_items: dict,
vm_av_zone: list,
+ block_device_mapping: dict,
disk_list: list = None,
) -> None:
"""Prepare all volumes for new VM instance.
existing_vim_volumes (list): List of existing volumes
created_items (dict): All created items belongs to VM
vm_av_zone (list): VM availability zone
+ block_device_mapping (dict): Block devices to be attached to VM
disk_list (list): List of disks
"""
boot_volume_id = None
elapsed_time = 0
- block_device_mapping = {}
for disk in disk_list:
if "image_id" in disk:
# Root persistent volume
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
# 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,
while not assigned:
free_floating_ip = self._get_free_floating_ip(
- server, floating_network, created_items
+ server, floating_network
)
if not free_floating_ip:
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'",
external_network = []
# List of ports with port-security disabled
no_secured_ports = []
- block_device_mapping = None
+ block_device_mapping = {}
existing_vim_volumes = []
server_group_id = None
scheduller_hints = {}
existing_vim_volumes=existing_vim_volumes,
created_items=created_items,
vm_av_zone=vm_av_zone,
+ block_device_mapping=block_device_mapping,
disk_list=disk_list,
)
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:
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")
) 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,