diff --git a/libvirtnbdbackup/common.py b/libvirtnbdbackup/common.py index c86c1c6b..c14e9eec 100644 --- a/libvirtnbdbackup/common.py +++ b/libvirtnbdbackup/common.py @@ -209,6 +209,9 @@ def copy(args: Namespace, source: str, target: str) -> None: if args.sshClient: args.sshClient.copy(source, target) else: + if hasattr(args, "restore_root") and args.restore_root is not None: + dir, _ = os.path.split(target) + os.makedirs(dir, exist_ok=True) shutil.copyfile(source, target) except OSError as e: log.warning("Failed to copy [%s] to [%s]: [%s]", source, target, e) diff --git a/libvirtnbdbackup/restore/files.py b/libvirtnbdbackup/restore/files.py index 52a19fce..88a069b6 100644 --- a/libvirtnbdbackup/restore/files.py +++ b/libvirtnbdbackup/restore/files.py @@ -31,13 +31,20 @@ from libvirtnbdbackup.exceptions import RestoreError -def restore(args: Namespace, vmConfig: str, virtClient: virt.client) -> None: +def restore( + args: Namespace, vmConfig: str, virtClient: virt.client, restConfig: bytes +) -> bytes: """Notice user if backed up vm had loader / nvram""" config = vmconfig.read(vmConfig) info = virtClient.getDomainInfo(config) + restored_files = {} for setting, val in info.items(): f = lib.getLatest(args.input, f"*{os.path.basename(val)}*", -1) + if args.restore_root is not None: + _, _, val_as_relative = os.path.splitroot(val) + val = os.path.join(args.restore_root, val_as_relative) + restored_files[setting] = os.path.abspath(val) if lib.exists(args, val): logging.info( "File [%s]: for boot option [%s] already exists, skipping.", @@ -50,6 +57,10 @@ def restore(args: Namespace, vmConfig: str, virtClient: virt.client) -> None: "Restoring configured file [%s] for boot option [%s]", val, setting ) lib.copy(args, f[0], val) + if restConfig != b"" and args.adjust_config is True: + return vmconfig.apply_paths(restConfig, restored_files) + else: + return restConfig def verify(args: Namespace, dataFiles: List[str]) -> bool: diff --git a/libvirtnbdbackup/restore/vmconfig.py b/libvirtnbdbackup/restore/vmconfig.py index b7df3b19..0c7f6801 100644 --- a/libvirtnbdbackup/restore/vmconfig.py +++ b/libvirtnbdbackup/restore/vmconfig.py @@ -25,6 +25,8 @@ from libvirtnbdbackup.virt import xml from libvirtnbdbackup.virt import disktype +from typing import Dict + def read(ConfigFile: str) -> str: """Read saved virtual machine config'""" @@ -138,3 +140,11 @@ def restore( lib.copy(args, vmConfig, targetFile) logging.info("Copied original vm config to [%s]", targetFile) logging.info("Note: virtual machine config must be adjusted manually.") + + +def apply_paths(config: bytes, restored_files: Dict[str, str]): + tree = xml.asTree(config) + os_config = tree.find("os") + for flag, val in restored_files.items(): + os_config.find(flag).text = val + return xml.ElementTree.tostring(tree, encoding="utf8", method="xml") diff --git a/virtnbdrestore b/virtnbdrestore index 429e691f..473c9452 100755 --- a/virtnbdrestore +++ b/virtnbdrestore @@ -175,6 +175,13 @@ def main() -> None: action="store_true", help="Preallocate restored qcow images. (default: %(default)s)", ) + opt.add_argument( + "-R", + "--restore-root", + default=None, + type=str, + help="Store restored misc VM files (loader, firmware vars, etc) under alternative root directory (default /)", + ) remopt = parser.add_argument_group("Remote Restore options") argopt.addRemoteArgs(remopt) @@ -271,7 +278,7 @@ def main() -> None: logging.error("Disk restore failed: [%s]", errmsg) sys.exit(1) - files.restore(args, ConfigFile, virtClient) + restConfig = files.restore(args, ConfigFile, virtClient, restConfig) vmconfig.restore(args, ConfigFile, restConfig, args.config_file) virtClient.refreshPool(args.output) if args.define is True: