From 4a0680b27fea6e26cb008a72640c5ff12f482d6a Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 20 Aug 2024 13:59:05 -0400 Subject: [PATCH] Fix issues with snapshot imports --- daemon-common/vm.py | 187 +++++++++++++++++++++++++++++--------------- 1 file changed, 125 insertions(+), 62 deletions(-) diff --git a/daemon-common/vm.py b/daemon-common/vm.py index 2d568b8b..0852ebf4 100644 --- a/daemon-common/vm.py +++ b/daemon-common/vm.py @@ -1484,7 +1484,7 @@ def export_vm_snapshot( def write_export_json( result=False, result_message="", - vm_configuration=None, + vm_detail=None, export_files=None, export_files_size=0, ttot=None, @@ -1645,9 +1645,6 @@ def export_vm_snapshot( write_export_json( result=False, result_message=f"ERROR: {error_message}", - vm_detail=vm_detail, - export_files=export_files, - export_files_size=export_files_size, ) return ( False, @@ -1666,7 +1663,7 @@ def export_vm_snapshot( write_export_json( result=True, result_message=result_message, - vm_configuration=snapshot_xml, + vm_detail=vm_detail, export_files=export_files, export_files_size=export_files_size, ttot=ttot, @@ -1739,31 +1736,12 @@ def import_vm_snapshot( f"ERROR: Failed to read source incremental parent export details: {e}", ) - # 2. Import VM config and metadata in provision state - try: - retcode, retmsg = define_vm( - zkhandler, - export_source_details["vm_detail"]["xml"], - export_source_details["vm_detail"]["node"], - export_source_details["vm_detail"]["node_limit"], - export_source_details["vm_detail"]["node_selector"], - export_source_details["vm_detail"]["node_autostart"], - export_source_details["vm_detail"]["migration_method"], - export_source_details["vm_detail"]["migration_max_downtime"], - export_source_details["vm_detail"]["profile"], - export_source_details["vm_detail"]["tags"], - "import", - ) - if not retcode: - return False, f"ERROR: Failed to define imported VM: {retmsg}" - except Exception as e: - return False, f"ERROR: Failed to parse VM export details: {e}" - # 4. Import volumes is_snapshot_remove_failed = False which_snapshot_remove_failed = list() if incremental_parent is not None: for volume_file, volume_size in export_source_details.get("export_files"): + volume_size = f"{volume_size}B" pool, volume, _ = volume_file.split("/")[-1].split(".") try: parent_volume_file = [ @@ -1796,7 +1774,7 @@ def import_vm_snapshot( f"ERROR: Failed to remove temporary RBD volume '{pool}/{volume}': {stderr}", ) - # Next we import the parent images + # Next we import the parent image retcode, stdout, stderr = common.run_os_command( f"rbd import --export-format 2 --dest-pool {pool} {import_path}/{domain}/{incremental_parent}/{parent_volume_file} {volume}" ) @@ -1806,6 +1784,41 @@ def import_vm_snapshot( f"ERROR: Failed to import parent export image {parent_volume_file}: {stderr}", ) + # Import VM config and metadata in import state, from the *source* details + try: + retcode, retmsg = define_vm( + zkhandler, + export_source_parent_details["vm_detail"]["xml"], + export_source_parent_details["vm_detail"]["node"], + export_source_parent_details["vm_detail"]["node_limit"], + export_source_parent_details["vm_detail"]["node_selector"], + export_source_parent_details["vm_detail"]["node_autostart"], + export_source_parent_details["vm_detail"]["migration_method"], + export_source_parent_details["vm_detail"]["migration_max_downtime"], + export_source_parent_details["vm_detail"]["profile"], + export_source_parent_details["vm_detail"]["tags"], + "import", + ) + if not retcode: + return False, f"ERROR: Failed to define imported VM: {retmsg}" + except Exception as e: + return False, f"ERROR: Failed to parse VM export details: {e}" + + # Handle the VM snapshots + if retain_snapshot: + # Create the parent snapshot + retcode, retmsg = create_vm_snapshot( + zkhandler, domain, snapshot_name=incremental_parent, zk_only=True + ) + if not retcode: + return ( + False, + f"ERROR: Failed to create imported snapshot for {incremental_parent} (parent): {retmsg}", + ) + + for volume_file, volume_size in export_source_details.get("export_files"): + volume_size = f"{volume_size}B" + pool, volume, _ = volume_file.split("/")[-1].split(".") # Then we import the incremental diffs retcode, stdout, stderr = common.run_os_command( f"rbd import-diff {import_path}/{domain}/{snapshot_name}/{volume_file} {pool}/{volume}" @@ -1816,36 +1829,69 @@ def import_vm_snapshot( f"ERROR: Failed to import incremental export image {volume_file}: {stderr}", ) - # Finally we remove the parent and child snapshots (no longer required required) - if retain_snapshot: - retcode, retmsg = ceph.add_snapshot( - zkhandler, - pool, - volume, - f"{incremental_parent}", - zk_only=True, - ) - if not retcode: - return ( - False, - f"ERROR: Failed to add imported image snapshot for {parent_volume_file}: {retmsg}", - ) - else: + if not retain_snapshot: retcode, stdout, stderr = common.run_os_command( f"rbd snap rm {pool}/{volume}@{incremental_parent}" ) if retcode: is_snapshot_remove_failed = True which_snapshot_remove_failed.append(f"{pool}/{volume}") - retcode, stdout, stderr = common.run_os_command( - f"rbd snap rm {pool}/{volume}@{snapshot_name}" - ) - if retcode: - is_snapshot_remove_failed = True - which_snapshot_remove_failed.append(f"{pool}/{volume}") + retcode, stdout, stderr = common.run_os_command( + f"rbd snap rm {pool}/{volume}@{snapshot_name}" + ) + if retcode: + is_snapshot_remove_failed = True + which_snapshot_remove_failed.append(f"{pool}/{volume}") + + # Now update VM config and metadata, from the *current* details + try: + retcode, retmsg = modify_vm( + zkhandler, + domain, + False, + export_source_details["vm_detail"]["xml"], + ) + if not retcode: + return False, f"ERROR: Failed to modify imported VM: {retmsg}" + + retcode, retmsg = move_vm( + zkhandler, + domain, + export_source_details["vm_detail"]["node"], + ) + if not retcode: + # We don't actually care if this fails, because it just means the vm was never moved + pass + + retcode, retmsg = modify_vm_metadata( + zkhandler, + domain, + export_source_details["vm_detail"]["node_limit"], + export_source_details["vm_detail"]["node_selector"], + export_source_details["vm_detail"]["node_autostart"], + export_source_details["vm_detail"]["profile"], + export_source_details["vm_detail"]["migration_method"], + export_source_details["vm_detail"]["migration_max_downtime"], + ) + if not retcode: + return False, f"ERROR: Failed to modify imported VM: {retmsg}" + except Exception as e: + return False, f"ERROR: Failed to parse VM export details: {e}" + + if retain_snapshot: + # Create the child snapshot + retcode, retmsg = create_vm_snapshot( + zkhandler, domain, snapshot_name=snapshot_name, zk_only=True + ) + if not retcode: + return ( + False, + f"ERROR: Failed to create imported snapshot for {snapshot_name}: {retmsg}", + ) else: for volume_file, volume_size in export_source_details.get("export_files"): + volume_size = f"{volume_size}B" pool, volume, _ = volume_file.split("/")[-1].split(".") # First we create the expected volumes then clean them up @@ -1876,21 +1922,7 @@ def import_vm_snapshot( f"ERROR: Failed to import export image {volume_file}: {stderr}", ) - # Finally we remove the source snapshot (not required) - if retain_snapshot: - retcode, retmsg = ceph.add_snapshot( - zkhandler, - pool, - volume, - f"{snapshot_name}", - zk_only=True, - ) - if not retcode: - return ( - False, - f"ERROR: Failed to add imported image snapshot for {volume_file}: {retmsg}", - ) - else: + if not retain_snapshot: retcode, stdout, stderr = common.run_os_command( f"rbd snap rm {pool}/{volume}@{snapshot_name}" ) @@ -1900,6 +1932,37 @@ def import_vm_snapshot( f"ERROR: Failed to remove imported image snapshot for {volume_file}: {stderr}", ) + # 2. Import VM config and metadata in provision state + try: + retcode, retmsg = define_vm( + zkhandler, + export_source_details["vm_detail"]["xml"], + export_source_details["vm_detail"]["node"], + export_source_details["vm_detail"]["node_limit"], + export_source_details["vm_detail"]["node_selector"], + export_source_details["vm_detail"]["node_autostart"], + export_source_details["vm_detail"]["migration_method"], + export_source_details["vm_detail"]["migration_max_downtime"], + export_source_details["vm_detail"]["profile"], + export_source_details["vm_detail"]["tags"], + "import", + ) + if not retcode: + return False, f"ERROR: Failed to define imported VM: {retmsg}" + except Exception as e: + return False, f"ERROR: Failed to parse VM export details: {e}" + + # Finally we handle the VM snapshot + if retain_snapshot: + retcode, retmsg = create_vm_snapshot( + zkhandler, domain, snapshot_name=snapshot_name, zk_only=True + ) + if not retcode: + return ( + False, + f"ERROR: Failed to create imported snapshot for {snapshot_name}: {retmsg}", + ) + # 5. Start VM retcode, retmsg = start_vm(zkhandler, domain) if not retcode: