From 97c33bcf7bd132feda74707e2a1f0a580dd0ce03 Mon Sep 17 00:00:00 2001 From: Richard <82816963+richard-tns@users.noreply.github.com> Date: Tue, 29 Nov 2022 10:03:57 +0000 Subject: [PATCH 1/2] Update backup.py Added --clone-to-vm and --export-to-nfs options, false by default, to control if the script should create a VM clone and/or export the snapshot to the defined NFS export --- backup.py | 186 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 64 deletions(-) diff --git a/backup.py b/backup.py index c72cf2b..2ed440e 100644 --- a/backup.py +++ b/backup.py @@ -107,6 +107,26 @@ def create_argparser(): default=None, ) + dsk = p.add_argument_group("VM's disks arguments") + dsk.add_argument( + "--bootable_only", + help="Backup Bootable Disk Only", + dest="bootable_only", + default=None, + ) + dsk.add_argument( + "--with_disks_deactivated", + help="Backup deactivated disks", + dest="with_disks_deactivated", + default=None, + ) + dsk.add_argument( + "--disks_id_exclude", + help="an array of exclude disks id", + dest="disks_id_exclude", + default=None, + ) + dcg = p.add_argument_group("Data Centre's related options") dcg.add_argument( "--export-domain", @@ -140,6 +160,18 @@ def create_argparser(): dest="snapshot_description", default=None, ) + mscg.add_argument( + "--clone-to-vm", + help="Should we clone to new VM", + dest="clone_to_vm", + default=None, + ) + mscg.add_argument( + "--export-to-nfs", + help="Should we export to NFS domain (also clones to new VM)", + dest="export_to_nfs", + default=None, + ) mscg.add_argument( "--timeout", help="Timeout in seconds to wait for time consuming operation", @@ -333,8 +365,31 @@ def main(argv): vm = vm[0] + # Get the Attachments Disk + disk_attachments = vms_service.vm_service(vm.id).disk_attachments_service().list() + disks_backup = [] + bootable_only = config.get_bootable_only() + for disk_attachment in disk_attachments: + if bootable_only: + if disk_attachment.bootable == True: + disks_backup.append(disk_attachment) + break + elif config.get_with_disks_deactivated() or disk_attachment.active: + disks_backup.append(disk_attachment) + + if not bootable_only: + disks_id_exclude=config.get_disks_id_exclude() + if disks_id_exclude: + disks_backup = [disk for disk in disks_backup if disk.id not in disks_id_exclude] + + for disk_attachment in disks_backup: + if disk_attachment.bootable == True: + logger.info(f"Finding bootable disk: {disk_attachment.id}") + else: + logger.info(f"Finding disk: {disk_attachment.id}") + # Delete old backup snapshots - VMTools.delete_snapshots(api, vm, config, vm_from_list) + # VMTools.delete_snapshots(api, vm, config, vm_from_list) # Check free space on the storage VMTools.check_free_space(api, config, vm) @@ -349,6 +404,7 @@ def main(argv): types.Snapshot( description=config.get_snapshot_description(), persist_memorystate=config.get_persist_memorystate(), + disk_attachments=disks_backup, ), ) VMTools.wait_for_snapshot_operation(api, vm, config, "creation") @@ -361,49 +417,50 @@ def main(argv): # Workaround for some SDK problems see issue #17 time.sleep(config.get_timeout()) - # Clone the snapshot into a VM - snapshots = snapshots_service.list() - snap = None - for i in snapshots: - if i.description == config.get_snapshot_description(): - snap = i + if config.get_clone_to_vm(): + # Clone the snapshot into a VM + snapshots = snapshots_service.list() + snap = None + for i in snapshots: + if i.description == config.get_snapshot_description(): + snap = i - if not snap: - logger.error("!!! No snapshot found !!!") - has_errors = True - continue + if not snap: + logger.error("!!! No snapshot found !!!") + has_errors = True + continue - logger.info("Clone into VM (%s) started ..." % vm_clone_name) - if not config.get_dry_run(): - cloned_vm = vms_service.add( - vm=types.Vm( - name=vm_clone_name, - memory=vm.memory, - snapshots=[ - types.Snapshot( - id=snap.id + logger.info("Clone into VM (%s) started ..." % vm_clone_name) + if not config.get_dry_run(): + cloned_vm = vms_service.add( + vm=types.Vm( + name=vm_clone_name, + memory=vm.memory, + snapshots=[ + types.Snapshot( + id=snap.id + ) + ], + cluster=types.Cluster( + name=config.get_cluster_name() ) - ], - cluster=types.Cluster( - name=config.get_cluster_name() ) ) - ) - # Find the service that manages the cloned virtual machine: - cloned_vm_service = vms_service.vm_service(cloned_vm.id) - # Wait till the virtual machine is down, as that means that the creation - # of the disks of the virtual machine has been completed: - while True: - time.sleep(config.get_timeout()) - logger.debug("Cloning into VM (%s) in progress ..." % vm_clone_name) - cloned_vm = cloned_vm_service.get() - if cloned_vm.status == types.VmStatus.DOWN: - break + # Find the service that manages the cloned virtual machine: + cloned_vm_service = vms_service.vm_service(cloned_vm.id) + # Wait till the virtual machine is down, as that means that the creation + # of the disks of the virtual machine has been completed: + while True: + time.sleep(config.get_timeout()) + logger.debug("Cloning into VM (%s) in progress ..." % vm_clone_name) + cloned_vm = cloned_vm_service.get() + if cloned_vm.status == types.VmStatus.DOWN: + break - logger.info("Cloning finished") + logger.info("Cloning finished") - # Delete backup snapshots - VMTools.delete_snapshots(api, vm, config, vm_from_list) + # Delete backup snapshots + VMTools.delete_snapshots(api, vm, config, vm_from_list) # Delete old backups if config.get_backup_keep_count(): @@ -411,34 +468,35 @@ def main(argv): if config.get_backup_keep_count_by_number(): VMTools.delete_old_backups_by_number(api, config, vm_from_list) - # Export the VM - try: - vm_clone = api.system_service().vms_service().list(search='name=%s' % vm_clone_name)[0] - logger.info("Export of VM (%s) started ..." % vm_clone_name) - if not config.get_dry_run(): - cloned_vm_service = vms_service.vm_service(vm_clone.id) - cloned_vm_service.export( - exclusive=True, - discard_snapshots=True, - storage_domain=types.StorageDomain( - name=config.get_export_domain() + if config.get_export_to_nfs(): + # Export the VM + try: + vm_clone = api.system_service().vms_service().list(search='name=%s' % vm_clone_name)[0] + logger.info("Export of VM (%s) started ..." % vm_clone_name) + if not config.get_dry_run(): + cloned_vm_service = vms_service.vm_service(vm_clone.id) + cloned_vm_service.export( + exclusive=True, + discard_snapshots=True, + storage_domain=types.StorageDomain( + name=config.get_export_domain() + ) ) - ) - while True: - time.sleep(config.get_timeout()) - cloned_vm = cloned_vm_service.get() - if cloned_vm.status == types.VmStatus.DOWN: - break - - logger.info("Exporting finished") - except Exception as e: - logger.info("Can't export cloned VM (%s) to domain: %s", vm_clone_name, config.get_export_domain()) - logger.info("DEBUG: %s", e) - has_errors = True - continue - - # Delete the CLONED VM - VMTools.delete_vm(api, config, vm_from_list) + while True: + time.sleep(config.get_timeout()) + cloned_vm = cloned_vm_service.get() + if cloned_vm.status == types.VmStatus.DOWN: + break + + logger.info("Exporting finished") + except Exception as e: + logger.info("Can't export cloned VM (%s) to domain: %s", vm_clone_name, config.get_export_domain()) + logger.info("DEBUG: %s", e) + has_errors = True + continue + + # Delete the CLONED VM + VMTools.delete_vm(api, config, vm_from_list) time_end = int(time.time()) time_diff = (time_end - time_start) From 6d49b0fea03f058117a85288fef70260ec847737 Mon Sep 17 00:00:00 2001 From: Richard <82816963+richard-tns@users.noreply.github.com> Date: Thu, 15 Dec 2022 15:56:06 +0000 Subject: [PATCH 2/2] PR #87 and #89 changes --- config.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/config.py b/config.py index 5fb32c2..f834644 100644 --- a/config.py +++ b/config.py @@ -50,6 +50,11 @@ def __init__(self, fd, debug, arguments): self.__backup_keep_count = config_parser.get(section, "backup_keep_count") self.__backup_keep_count_by_number = config_parser.get(section, "backup_keep_count_by_number") self.__dry_run = config_parser.getboolean(section, "dry_run") + self.__clone_to_vm = config_parser.getboolean(section, "clone_to_vm") + self.__export_to_nfs = config_parser.getboolean(section, "export_to_nfs") + self.__bootable_only = config_parser.getboolean(section, "bootable_only", fallback=False) + self.__with_disks_deactivated = config_parser.getboolean(section, "with_disks_deactivated", fallback=False) + self.__disks_id_exclude = json.loads(config_parser.get(section, "disks_id_exclude", fallback="[]")) self.__debug = debug self.__vm_name_max_length = config_parser.getint(section, "vm_name_max_length") self.__use_short_suffix = config_parser.getboolean(section, "use_short_suffix") @@ -126,6 +131,21 @@ def get_backup_keep_count_by_number(self): def get_dry_run(self): return self.__dry_run + def get_clone_to_vm(self): + return self.__clone_to_vm + + def get_export_to_nfs(self): + return self.__export_to_nfs + + def get_bootable_only(self): + return self.__bootable_only + + def get_with_disks_deactivated(self): + return self.__with_disks_deactivated + + def get_disks_id_exclude(self): + return self.__disks_id_exclude + def get_debug(self): return self.__debug