Initial VM restore work
This commit is contained in:
		@@ -31,6 +31,7 @@ from concurrent.futures import ThreadPoolExecutor
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from shutil import rmtree
 | 
			
		||||
from json import dump as jdump
 | 
			
		||||
from json import load as jload
 | 
			
		||||
 | 
			
		||||
import daemon_lib.common as common
 | 
			
		||||
 | 
			
		||||
@@ -1361,6 +1362,9 @@ def backup_vm(
 | 
			
		||||
    else:
 | 
			
		||||
        export_fileext = "rbdimg"
 | 
			
		||||
 | 
			
		||||
    # 2c. Validate that there's enough space on the target
 | 
			
		||||
    # TODO
 | 
			
		||||
 | 
			
		||||
    # 3. Set datestring in YYYYMMDDHHMMSS format
 | 
			
		||||
    now = datetime.now()
 | 
			
		||||
    datestring = now.strftime("%Y%m%d%H%M%S")
 | 
			
		||||
@@ -1495,3 +1499,94 @@ def backup_vm(
 | 
			
		||||
        retmsg = f"WARNING: Failed to remove snapshot as requested for volume(s) {', '.join(which_snapshot_remove_failed)}: {', '.join(msg_snapshot_remove_failed)}\n{retmsg}"
 | 
			
		||||
 | 
			
		||||
    return True, retmsg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def restore_vm(zkhandler, domain, datestring, source_path, incremental_parent=None):
 | 
			
		||||
 | 
			
		||||
    tstart = time.time()
 | 
			
		||||
 | 
			
		||||
    # 0. Validations
 | 
			
		||||
    # Validate that VM does not exist in cluster
 | 
			
		||||
    dom_uuid = getDomainUUID(zkhandler, domain)
 | 
			
		||||
    if dom_uuid:
 | 
			
		||||
        return (
 | 
			
		||||
            False,
 | 
			
		||||
            f'ERROR: VM "{domain}" already exists in the cluster! Remove or rename it before restoring a backup.',
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # Validate that the target path exists
 | 
			
		||||
    if not re.match(r"^/", source_path):
 | 
			
		||||
        return (
 | 
			
		||||
            False,
 | 
			
		||||
            f"ERROR: Source path {source_path} is not a valid absolute path on the primary coordinator!",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # Ensure that source_path (on this node) exists
 | 
			
		||||
    if not os.path.isdir(source_path):
 | 
			
		||||
        return False, f"ERROR: Source path {source_path} does not exist!"
 | 
			
		||||
 | 
			
		||||
    # Ensure that domain path (on this node) exists
 | 
			
		||||
    vm_source_path = f"{source_path}/{domain}"
 | 
			
		||||
    if not os.path.isdir(vm_source_path):
 | 
			
		||||
        return False, f"ERROR: Source VM path {vm_source_path} does not exist!"
 | 
			
		||||
 | 
			
		||||
    # Ensure that the archives are present
 | 
			
		||||
    vm_source_pvcbackup_file = f"{vm_source_path}/{domain}.{datestring}.pvcbackup"
 | 
			
		||||
    vm_source_pvcdisks_file = f"{vm_source_path}/{domain}.{datestring}.pvcdisks"
 | 
			
		||||
    if not os.path.isfile(vm_source_pvcbackup_file) or not os.path.isfile(
 | 
			
		||||
        vm_source_pvcdisks_file
 | 
			
		||||
    ):
 | 
			
		||||
        return False, f"ERROR: The specified source backup files do not exist!"
 | 
			
		||||
 | 
			
		||||
    if incremental_parent is not None:
 | 
			
		||||
        vm_source_parent_pvcbackup_file = (
 | 
			
		||||
            f"{vm_source_path}/{domain}.{incremental_parent}.pvcbackup"
 | 
			
		||||
        )
 | 
			
		||||
        vm_source_parent_pvcdisks_file = (
 | 
			
		||||
            f"{vm_source_path}/{domain}.{incremental_parent}.pvcdisks"
 | 
			
		||||
        )
 | 
			
		||||
        if not os.path.isfile(vm_source_parent_pvcbackup_file) or not os.path.isfile(
 | 
			
		||||
            vm_source_parent_pvcdisks_file
 | 
			
		||||
        ):
 | 
			
		||||
            return (
 | 
			
		||||
                False,
 | 
			
		||||
                f"ERROR: The specified incremental parent source backup files do not exist!",
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    # 1. Read the backup file and get VM details
 | 
			
		||||
    try:
 | 
			
		||||
        with open(vm_source_pvcbackup_file) as fh:
 | 
			
		||||
            vm_source_details = jload(fh)
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return False, f"ERROR: Failed to read source backup details: {e}"
 | 
			
		||||
 | 
			
		||||
    # 2. Import VM config and metadata in provision state
 | 
			
		||||
    vm_config_xml = vm_source_details.get("xml")
 | 
			
		||||
    vm_config_meta = {
 | 
			
		||||
        "node": vm_source_details.get("node"),
 | 
			
		||||
        "node_limit": vm_source_details.get("node_limit"),
 | 
			
		||||
        "node_selector": vm_source_details.get("node_selector"),
 | 
			
		||||
        "node_autostart": vm_source_details.get("node_autostart"),
 | 
			
		||||
        "migration_method": vm_source_details.get("migration_method"),
 | 
			
		||||
        "tags": vm_source_details.get("tags"),
 | 
			
		||||
        "description": vm_source_details.get("description"),
 | 
			
		||||
        "profile": vm_source_details.get("profile"),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    define_vm(
 | 
			
		||||
        zkhandler,
 | 
			
		||||
        vm_confing_xml,
 | 
			
		||||
        vm_source_details.get("node"),
 | 
			
		||||
        vm_source_details.get("node_limit"),
 | 
			
		||||
        vm_source_details.get("node_selector"),
 | 
			
		||||
        vm_source_details.get("node_autostart"),
 | 
			
		||||
        vm_source_details.get("migration_method"),
 | 
			
		||||
        vm_source_details.get("profile"),
 | 
			
		||||
        vm_source_details.get("tags"),
 | 
			
		||||
        "restore",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # 4. Import parent snapshot disks (if applicable)
 | 
			
		||||
 | 
			
		||||
    # 5. Apply diffs (if applicable)
 | 
			
		||||
    # 5. Start VM
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user