diff --git a/emhttp/languages/en_US/helptext.txt b/emhttp/languages/en_US/helptext.txt index 16c31f19d9..13fc39f033 100644 --- a/emhttp/languages/en_US/helptext.txt +++ b/emhttp/languages/en_US/helptext.txt @@ -1711,7 +1711,11 @@ Stop VMs from Autostarting\Starting when VM Manager starts or open is run from t :end :vms_libvirt_volume_help: -This is the libvirt volume. +This is the libvirt volume/directory. +:end + +:vms_libvirt_secondary_volume_help: +This is a location for storing previous versions of xml and nvram at change. :end :vms_libvirt_vdisk_size_help: @@ -1720,7 +1724,11 @@ To resize an existing image file, specify the new size here. Next time the Libvi :end :vms_libvirt_location_help: -You must specify an image file for Libvirt. The system will automatically create this file when the Libvirt service is first started. +You must specify an image file/directory for Libvirt. The system will automatically create this file/directory when the Libvirt service is first started. +:end + +:vms_libvirt_secondary_location_help: +This is a directory for storing previous versions of xml and nvram at change. Does not need to be specified. :end :vms_libvirt_storage_help: diff --git a/emhttp/plugins/dynamix.vm.manager/VMSettings.page b/emhttp/plugins/dynamix.vm.manager/VMSettings.page index 5832f35f78..8ce9d5dee8 100644 --- a/emhttp/plugins/dynamix.vm.manager/VMSettings.page +++ b/emhttp/plugins/dynamix.vm.manager/VMSettings.page @@ -123,7 +123,7 @@ $libvirt_log = file_exists("/var/log/libvirt/libvirtd.log"); - + _(Enable VMs)_: : :vms_libvirt_vdisk_size_help: _(Libvirt storage location)_: -: +: @@ -179,6 +181,7 @@ _(Libvirt storage location)_: :vms_libvirt_location_help: + _(Default VM storage path)_: : @@ -299,7 +302,7 @@ _(VFIO allow unsafe interrupts)_: - +
_(Libvirt volume info)_
_(btrfs filesystem show)_: diff --git a/emhttp/plugins/dynamix.vm.manager/scripts/libvirt_init b/emhttp/plugins/dynamix.vm.manager/scripts/libvirt_init index 4b98d16f0c..1189383f11 100755 --- a/emhttp/plugins/dynamix.vm.manager/scripts/libvirt_init +++ b/emhttp/plugins/dynamix.vm.manager/scripts/libvirt_init @@ -5,6 +5,64 @@ # run & log functions . /etc/rc.d/rc.runlog + +# Sync domain data if IMAGE_FILE and OLD_IMAGE_FILE differ +DOMAIN_CFG=/boot/config/domain.cfg + +# Read values from domain.cfg +eval $(grep -E '^(IMAGE_FILE|OLD_IMAGE_FILE)=' "$DOMAIN_CFG") + +# Remove quotes +IMAGE_FILE="${IMAGE_FILE%\"}" +IMAGE_FILE="${IMAGE_FILE#\"}" +OLD_IMAGE_FILE="${OLD_IMAGE_FILE%\"}" +OLD_IMAGE_FILE="${OLD_IMAGE_FILE#\"}" + +# Proceed only if both variables are set and OLD_IMAGE_FILE exists +if [ -n "$IMAGE_FILE" ] && [ -n "$OLD_IMAGE_FILE" ] && [ "$IMAGE_FILE" != "$OLD_IMAGE_FILE" ]; then + if [ ! -e "$OLD_IMAGE_FILE" ]; then + log "OLD_IMAGE_FILE not found: $OLD_IMAGE_FILE — skipping sync" + else + log "IMAGE_FILE and OLD_IMAGE_FILE differ, syncing..." + + TMP_MNT=/etc/libvirt-sync + IMG_FILE_NAME=$(basename "$IMAGE_FILE") + OLD_IMG_FILE_NAME=$(basename "$OLD_IMAGE_FILE") + TIMESTAMP=$(date +%Y%m%d-%H%M%S) + + if [[ "$OLD_IMAGE_FILE" == *.img ]]; then + # Backup image before mounting + BACKUP_PATH="${OLD_IMAGE_FILE%.img}.bak-${TIMESTAMP}.img" + log "Creating backup of OLD_IMAGE_FILE: $BACKUP_PATH" + cp -p "$OLD_IMAGE_FILE" "$BACKUP_PATH" + + log "Mounting $OLD_IMAGE_FILE to $TMP_MNT" + mkdir -p "$TMP_MNT" + mount "$OLD_IMAGE_FILE" "$TMP_MNT" + log "Copying full contents from image to directory $IMAGE_FILE" + rsync -a --exclude="$OLD_IMG_FILE_NAME" "$TMP_MNT/" "$IMAGE_FILE/" + umount "$TMP_MNT" + elif [[ "$IMAGE_FILE" == *.img ]]; then + log "Mounting $IMAGE_FILE to $TMP_MNT" + mkdir -p "$TMP_MNT" + mount "$IMAGE_FILE" "$TMP_MNT" + log "Copying full contents from directory $OLD_IMAGE_FILE to image" + rsync -a --exclude="$IMG_FILE_NAME" --exclude='*.bak-*.img' "$OLD_IMAGE_FILE/" "$TMP_MNT/" + umount "$TMP_MNT" + else + log "Both IMAGE_FILE and OLD_IMAGE_FILE are directories, copying full contents" + rsync -a --exclude="$IMG_FILE_NAME" "$OLD_IMAGE_FILE/" "$IMAGE_FILE/" + fi + + # Update OLD_IMAGE_FILE in domain.cfg + log "Updating OLD_IMAGE_FILE in $DOMAIN_CFG" + sed -i "s|^OLD_IMAGE_FILE=.*|OLD_IMAGE_FILE=\"$IMAGE_FILE\"|" "$DOMAIN_CFG" + fi +else + log "IMAGE_FILE and OLD_IMAGE_FILE match, or one is unset — skipping sync" +fi + + # missing qemu directory would indicate new libvirt image file created if [ ! -d /etc/libvirt/qemu ]; then log "initializing /etc/libvirt" diff --git a/emhttp/plugins/dynamix.vm.manager/scripts/libvirtconfig b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtconfig index 12ae126341..5d6e0f269e 100755 --- a/emhttp/plugins/dynamix.vm.manager/scripts/libvirtconfig +++ b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtconfig @@ -15,7 +15,8 @@ $cfgfile = "/boot/config/domain.cfg"; $cfg_defaults = [ "SERVICE" => "disable", - "IMAGE_FILE" => "/mnt/user/system/libvirt/libvirt.img", + "IMAGE_FILE" => "/mnt/user/system/libvirt/", + "OLD_IMAGE_FILE" => "/mnt/user/system/libvirt/", "IMAGE_SIZE" => "1", "DEBUG" => "no", "DOMAINDIR" => "/mnt/user/domains/", diff --git a/emhttp/plugins/dynamix.vm.manager/scripts/libvirtcopy b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtcopy new file mode 100755 index 0000000000..222ce20051 --- /dev/null +++ b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtcopy @@ -0,0 +1,150 @@ +#!/usr/bin/php + +name; + $uuid = (string)$sx->uuid; + + /* ----------------------------------------------------- + * Read storage metadata (Unraid vmtemplate) + * ----------------------------------------------------- */ + $metadata_storage = null; + + if (isset($sx->metadata)) { + foreach ($sx->metadata->children() as $child) { + if ($child->getName() === 'vmtemplate') { + $metadata_storage = trim((string)$child['storage']); + break; + } + } + } + + /* ----------------------------------------------------- + * Resolve filesystem path + * Treat empty, null, or "default" as DOMAINDIR + * ----------------------------------------------------- */ + if ($metadata_storage === null || $metadata_storage === '' || strtolower($metadata_storage) === 'default') { + /* TRUE default storage */ + $path_root = $default_domain_dir; // e.g. /mnt/user/domains2 + $storage_name = 'default'; + } else { + /* Explicit Unraid pool */ + $path_root = '/mnt/user/' . $metadata_storage; + $storage_name = $metadata_storage; + } + + + $path = $path_root + ? $path_root . '/' . $vm_name + : null; + + + /* Filesystem existence check (remove quotes for is_dir) */ + $exists = ($path_root && is_dir($path_root . '/' . $vm_name)); + + /* ----------------------------------------------------- + * Store result + * ----------------------------------------------------- */ + $vms[$vm_name] = [ + 'uuid' => $uuid, + 'storage' => $storage_name, + 'path' => $path, + 'path_shell' => $path ? escapeshellarg($path) : null, + 'exists' => $exists, + ]; +} + +/* --------------------------------------------------------- + * Output + * --------------------------------------------------------- */ +#print_r($vms); + +file_put_contents("/boot/config/plugins/dynamix.vm.manager/vms.json",json_encode($vms,JSON_PRETTY_PRINT)); + + +foreach ($vms as $vm => $vmdetail) { + file_put_contents("/tmp/Stopcopy",""); + $from_file = "/etc/libvirt/qemu/$vm.xml"; + $to_file = $vmdetail['path']."/$vm.xml"; + #echo " from:$from_file to:$to_file"; + if ($vmdetail['exists']) { + file_put_contents("/tmp/Stopcopy","$vm from:$from_file to:$to_file\n",FILE_APPEND); #echo " from:$from_file to:$to_file"; + #copy($from_file,$to_file); + } else file_put_contents("/tmp/Stopcopy","Nocpy $vm from:$from_file to:$to_file\n",FILE_APPEND); #echo " from:$from_file to:$to_file"; +} +?> diff --git a/emhttp/plugins/dynamix.vm.manager/scripts/libvirtrestore b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtrestore new file mode 100755 index 0000000000..e153fc6bd5 --- /dev/null +++ b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtrestore @@ -0,0 +1,28 @@ +#!/usr/bin/php + + $vmdetail) { + file_put_contents("/tmp/Stopcopy",""); + $to_file = "/etc/libvirt/qemu/$vm.xml"; + $from_file = $vmdetail['path']."/$vm.xml"; + #echo " from:$from_file to:$to_file"; + if (file_exists($from_file)) { + file_put_contents("/tmp/libvirtrestore","$vm from:$from_file to:$to_file\n",FILE_APPEND); #echo " from:$from_file to:$to_file"; + #copy($from_file,$to_file); + } else file_put_contents("/tmp/libvirtrestore","Nocpy $vm from:$from_file to:$to_file\n",FILE_APPEND); #echo " from:$from_file to:$to_file"; +} +?> diff --git a/emhttp/plugins/dynamix.vm.manager/scripts/savehook.php b/emhttp/plugins/dynamix.vm.manager/scripts/savehook.php new file mode 100644 index 0000000000..f06e25279d --- /dev/null +++ b/emhttp/plugins/dynamix.vm.manager/scripts/savehook.php @@ -0,0 +1,12 @@ +#!/usr/bin/env php + diff --git a/etc/rc.d/rc.libvirt b/etc/rc.d/rc.libvirt index ae1c88e7b7..a53c4d237e 100755 --- a/etc/rc.d/rc.libvirt +++ b/etc/rc.d/rc.libvirt @@ -247,6 +247,9 @@ libvirtd_start(){ } libvirtd_stop(){ + # Save VM locations + /usr/local/emhttp/plugins/dynamix.vm.manager/scripts/libvirtcopy + # log "Stopping $DAEMON..." if [[ ! -f $LIBVIRTD_PIDFILE ]]; then log "$DAEMON... Already stopped."