From 8020bd3518614f1bd5f3d1a3aad5958fd90e37f6 Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Sat, 6 May 2023 00:00:36 +0100
Subject: [PATCH 01/18] Add snapshots to view
---
.../dynamix.vm.manager/include/VMMachines.php | 23 ++++++++++++++++---
plugins/dynamix.vm.manager/include/VMajax.php | 5 ++++
2 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/plugins/dynamix.vm.manager/include/VMMachines.php b/plugins/dynamix.vm.manager/include/VMMachines.php
index 7e103c663..c36e56026 100644
--- a/plugins/dynamix.vm.manager/include/VMMachines.php
+++ b/plugins/dynamix.vm.manager/include/VMMachines.php
@@ -51,6 +51,7 @@
$icon = $lv->domain_get_icon_url($res);
$image = substr($icon,-4)=='.png' ? "" : (substr($icon,0,5)=='icon-' ? "" : "");
$arrConfig = domain_to_config($uuid);
+ $snapshots = getvmsnapshots($vm) ;
if ($state == 'running') {
$mem = $dom['memory']/1024;
} else {
@@ -123,12 +124,13 @@
}
/* VM information */
+ if ($snapshots != null) $snapshotstr = _("(Snapshots :").count($snapshots).')' ; else $snapshotstr = _("(Snapshots :None)") ;
echo "
@@ -383,7 +447,7 @@ _(Snapshot Name)_:
_(Remove Images)_:
-
+
diff --git a/plugins/dynamix.vm.manager/include/VMajax.php b/plugins/dynamix.vm.manager/include/VMajax.php
index 6c7a05d90..fe1b839bf 100644
--- a/plugins/dynamix.vm.manager/include/VMajax.php
+++ b/plugins/dynamix.vm.manager/include/VMajax.php
@@ -263,6 +263,40 @@ function embed(&$syslinux, $key, $value) {
: ['error' => "Change Media Failed"];
break;
+case 'change-media-both':
+ requireLibvirt();
+ $res = $lv->get_domain_by_name($domName);
+ $cdroms = $lv->get_cdrom_stats($res) ;
+ $hda = $hdb = false ;
+ foreach ($lv->get_cdrom_stats($res) as $cd){
+ if ($cd['device'] == 'hda') $hda = true ;
+ if ($cd['device'] == 'hdb') $hdb = true ;
+ }
+ $file= $_REQUEST['file'];
+ if ($file != "" && $hda == false) {
+ $cmdstr = "virsh attach-disk '$domName' '$file' hda --type cdrom --targetbus sata --config" ;
+ } else {
+ if ($file == "") $cmdstr = "virsh change-media '$domName' hda --eject --current";
+ else $cmdstr = "virsh change-media '$domName' hda '$file'";
+ }
+ $rtn=shell_exec($cmdstr)
+ ? ['success' => true]
+ : ['error' => "Change Media Failed"];
+
+ if (isset($rtn['error'])) return ;
+
+ $file2 = $_REQUEST['file2'];
+ if ($file2 != "" && $hdb == false) {
+ $cmdstr = "virsh attach-disk '$domName' '$file2' hdb --type cdrom --targetbus sata --config" ;
+ } else {
+ if ($file2 == "") $cmdstr = "virsh change-media '$domName' hdb --eject --current";
+ else $cmdstr = "virsh change-media '$domName' hdb '$file2' ";
+ }
+ $rtn=shell_exec($cmdstr)
+ ? ['success' => true]
+ : ['error' => "Change Media Failed"];
+ break;
+
case 'memory-change':
requireLibvirt();
$arrResponse = $lv->domain_set_memory($domName, $_REQUEST['memory']*1024)
@@ -302,7 +336,7 @@ function embed(&$syslinux, $key, $value) {
case 'snap-create-external':
requireLibvirt();
- $arrResponse = vm_snapshot($domName,$_REQUEST['snapshotname']) ;
+ $arrResponse = vm_snapshot($domName,$_REQUEST['snapshotname'],$_REQUEST['free']) ;
break;
case 'snap-images':
diff --git a/plugins/dynamix.vm.manager/include/libvirt.php b/plugins/dynamix.vm.manager/include/libvirt.php
index 66a9c8e7f..bbf5bbac4 100644
--- a/plugins/dynamix.vm.manager/include/libvirt.php
+++ b/plugins/dynamix.vm.manager/include/libvirt.php
@@ -1846,7 +1846,7 @@ function nvram_restore($uuid) {
return false;
}
- function nvram_snapshot($uuid,$snapshotname) {
+ function nvram_create_snapshot($uuid,$snapshotname) {
// snapshot backup OVMF VARS if this domain had them
if (is_file('/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi.fd')) {
copy('/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi.fd', '/etc/libvirt/qemu/nvram/'.$uuid.$snapshotname.'_VARS-pure-efi.fd');
@@ -1859,6 +1859,21 @@ function nvram_snapshot($uuid,$snapshotname) {
return false;
}
+ function nvram_revert_snapshot($uuid,$snapshotname) {
+ // snapshot backup OVMF VARS if this domain had them
+ if (is_file('/etc/libvirt/qemu/nvram/'.$uuid.$snapshotname.'_VARS-pure-efi.fd')) {
+ copy('/etc/libvirt/qemu/nvram/'.$uuid.$snapshotname.'_VARS-pure-efi.fd', '/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi.fd');
+ unlink('/etc/libvirt/qemu/nvram/'.$uuid.$snapshotname.'_VARS-pure-efi.fd') ;
+ return true;
+ }
+ if (is_file('/etc/libvirt/qemu/nvram/'.$uuid.$snapshotname.'_VARS-pure-efi-tpm.fd')) {
+ copy('/etc/libvirt/qemu/nvram/'.$uuid.$snapshotname.'_VARS-pure-efi-tpm.fd', '/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi-tpm.fd');
+ unlink('/etc/libvirt/qemu/nvram/'.$uuid.$snapshotname.'_VARS-pure-efi-tpm.fd') ;
+ return true;
+ }
+ return false;
+ }
+
function is_dir_empty($dir) {
if (!is_readable($dir)) return NULL;
$handle = opendir($dir);
diff --git a/plugins/dynamix.vm.manager/include/libvirt_helpers.php b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
index 3a8c24a68..d7a14e3fe 100644
--- a/plugins/dynamix.vm.manager/include/libvirt_helpers.php
+++ b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
@@ -1482,14 +1482,14 @@ function getvmsnapshots($vm) {
$snaps[$vmsnap]["state"]= $b["state"];
$snaps[$vmsnap]["memory"]= $b["memory"];
$snaps[$vmsnap]["creationtime"]= $b["creationTime"];
- $snaps[$vmsnap]["disks"]= $b["disks"];
+ if (array_key_exists(0 , $b["disks"]["disk"])) $snaps[$vmsnap]["disks"]= $b["disks"]["disk"]; else $snaps[$vmsnap]["disks"][0]= $b["disks"]["disk"];
}
if (is_array($snaps)) uasort($snaps,'compare_creationtime') ;
return $snaps ;
}
- function vm_snapshot($vm,$snapshotname,$memorysnap = "yes") {
+ function vm_snapshot($vm,$snapshotname,$free = "yes", $memorysnap = "yes") {
global $lv ;
#Get State
@@ -1502,13 +1502,13 @@ function vm_snapshot($vm,$snapshotname,$memorysnap = "yes") {
$diskspec = "" ;
$capacity = 0 ;
if ($snapshotname == "--generate") $name= "S" . date("YmdHis") ; else $name=$snapshotname ;
- $cmdstr = "virsh snapshot-create-as $vm --name $name --atomic " ;
+ $cmdstr = "virsh snapshot-create-as '$vm' --name '$name' --atomic " ;
foreach($disks as $disk) {
$file = $disk["file"] ;
$pathinfo = pathinfo($file) ;
$filenew = $pathinfo["dirname"].'/'.$pathinfo["filename"].'.'.$name.'qcow2' ;
- $diskspec .= " --diskspec ".$disk["device"].",snapshot=external,file=".$filenew ;
+ $diskspec .= " --diskspec '".$disk["device"]."',snapshot=external,file='".$filenew."'" ;
$capacity = $capacity + $disk["capacity"] ;
}
@@ -1516,8 +1516,8 @@ function vm_snapshot($vm,$snapshotname,$memorysnap = "yes") {
$mem = $lv->domain_get_memory_stats($vm) ;
$memory = $mem[6] ;
- if ($memorysnap = "yes") $memspec = " --memspec ".$pathinfo["dirname"].'/memory'.$name.".mem,snapshot=external" ; else $memspec = "" ;
- $cmdstr = "virsh snapshot-create-as $vm --name $name --atomic" ;
+ if ($memorysnap = "yes") $memspec = " --memspec ".$pathinfo["dirname"].'/memory"'.$name.'".mem,snapshot=external' ; else $memspec = "" ;
+ $cmdstr = "virsh snapshot-create-as '$vm' --name '$name' --atomic" ;
if ($state == "running") {
@@ -1533,10 +1533,10 @@ function vm_snapshot($vm,$snapshotname,$memorysnap = "yes") {
$capacity *= 1 ;
- #if ($dirfree < $capacity) { $arrResponse = ['error' => _("Insufficent Storage for Snapshot")]; return $arrResponse ;}
+ if ($free == "yes" && $dirfree < $capacity) { $arrResponse = ['error' => _("Insufficent Storage for Snapshot")]; return $arrResponse ;}
#Copy nvram
- if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_snapshot($lv->domain_get_uuid($vm),$name) ;
+ if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_create_snapshot($lv->domain_get_uuid($vm),$name) ;
$test = false ;
if ($test) exec($cmdstr." --print-xml 2>&1",$output,$return) ; else exec($cmdstr." 2>&1",$output,$return) ;
@@ -1563,7 +1563,7 @@ function vm_revert($vm, $snap="--current",$action="no") {
foreach($disks as $disk) {
$file = $disk["file"] ;
$output = "" ;
- exec("qemu-img info --backing-chain -U $file | grep image:",$output) ;
+ exec("qemu-img info --backing-chain -U '$file' | grep image:",$output) ;
foreach($output as $key => $line) {
$line=str_replace("image: ","",$line) ;
$output[$key] = $line ;
@@ -1581,7 +1581,6 @@ function vm_revert($vm, $snap="--current",$action="no") {
switch ($snapslist[$snap]['state']) {
case "shutoff":
- case "running":
#VM must be shutdown.
$res = $lv->get_domain_by_name($vm);
$dom = $lv->domain_get_info($res);
@@ -1594,7 +1593,7 @@ function vm_revert($vm, $snap="--current",$action="no") {
$xmlobj = custom::createArray('domain',$strXML) ;
# Process disks and update path.
- $disks=($snapslist[$snap]['disks']["disk"]) ;
+ $disks=($snapslist[$snap]['disks']) ;
foreach ($disks as $disk) {
$diskname = $disk["@attributes"]["name"] ;
if ($diskname == "hda" || $diskname == "hdb") continue ;
@@ -1602,10 +1601,9 @@ function vm_revert($vm, $snap="--current",$action="no") {
$item = array_search($path,$snaps[$vm][$diskname]) ;
$newpath = $snaps[$vm][$diskname][$item + 1];
$json_info = getDiskImageInfo($newpath) ;
- #echo "Newpath: $newpath image type ".$json_info["format"]."\n" ;
foreach($xmlobj['devices']['disk'] as $ddk => $dd){
if ($dd['target']["@attributes"]['dev'] == $diskname) {
- $xmlobj['devices']['disk'][$ddk]['source']["@attributes"]['file'] = $newpath ;
+ $xmlobj['devices']['disk'][$ddk]['source']["@attributes"]['file'] = "$newpath" ;
$xmlobj['devices']['disk'][$ddk]['driver']["@attributes"]['type'] = $json_info["format"] ;
}
}
@@ -1615,27 +1613,21 @@ function vm_revert($vm, $snap="--current",$action="no") {
if ($new)
$arrResponse = ['success' => true] ; else
$arrResponse = ['error' => $lv->get_last_error()] ;
-
- #echo "update xml \n" ;
-
-
+
# remove snapshot meta data and images for all snpahots.
foreach ($disks as $disk) {
$diskname = $disk["@attributes"]["name"] ;
if ($diskname == "hda" || $diskname == "hdb") continue ;
$path = $disk["source"]["@attributes"]["file"] ;
- #echo "rm $path\n" ;
- if (is_file($path) && $action == "yes") unlink($path) ;
+ if (is_file($path) && $action == "yes") unlink("$path") ;
$item = array_search($path,$snaps[$vm]["r".$diskname]) ;
$item++ ;
while($item > 0)
{
if (!isset($snaps[$vm]["r".$diskname][$item])) break ;
$newpath = $snaps[$vm]["r".$diskname][$item] ;
- # rm $newpath
- if (is_file($path) && $action == "yes") unlink($newpath) ;
- #echo "rm $newpath\n" ;
+ if (is_file($path) && $action == "yes") unlink("$newpath") ;
$item++ ;
@@ -1644,23 +1636,20 @@ function vm_revert($vm, $snap="--current",$action="no") {
uasort($snapslist,'compare_creationtimelt') ;
foreach($snapslist as $s) {
- #var_dump($s['name']) ;
$name = $s['name'] ;
- #echo "delete snapshot --metadata ".$s["name"]."\n" ;
#Delete Metadata only.
if ($action == "yes") {
- $ret = $lv->domain_snapshot_delete($vm, $name ,2) ;
- #echo "Error: $ret\n" ;
- } #else echo "Run domain_snapshot_delete($vm, $name ,2\n" ;
+ $ret = $lv->domain_snapshot_delete($vm, "$name" ,2) ;
+ }
if ($s['name'] == $snap) break ;
}
#if VM was started restart.
if ($state == 'running') {
#echo "Restart VM\n" ;
$arrResponse = $lv->domain_start($vm) ;
- } #else echo "VM Shutdown\n" ;
-
+ }
+ if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_revert_snapshot($lv->domain_get_uuid($vm),$name) ;
break ;
@@ -1708,8 +1697,7 @@ function vm_snapimages($vm, $snap, $only) {
$line=str_replace("image: ","",$line) ;
$output[$key] = $line ;
}
-
- $snaps[$vm][$disk["device"]] = $output ;
+ $snaps[$vm][$disk["device"]] = $output ;
$rev = "r".$disk["device"] ;
$reversed = array_reverse($output) ;
$snaps[$vm][$rev] = $reversed ;
@@ -1718,13 +1706,12 @@ function vm_snapimages($vm, $snap, $only) {
$diskspec .= " --diskspec ".$disk["device"].",snapshot=external,file=".$filenew ;
$capacity = $capacity + $disk["capacity"] ;
}
- #var_dump($snaps) ;
- $disks=($snapslist[$snap]['disks']["disk"]) ;
- foreach ($disks as $disk) {
- $diskname = $disk["@attributes"]["name"] ;
+
+ $snapdisks= $snapslist[$snap]['disks'] ;
+ foreach ($snapdisks as $diskkey => $snapdisk) {
+ $diskname = $snapdisk["@attributes"]["name"] ;
if ($diskname == "hda" || $diskname == "hdb") continue ;
- $path = $disk["source"]["@attributes"]["file"] ;
- #echo "rm $path\n" ;
+ $path = $snapdisk["source"]["@attributes"]["file"] ;
if (is_file($path)) $data .= "$path
" ;
$item = array_search($path,$snaps[$vm]["r".$diskname]) ;
$item++ ;
@@ -1733,10 +1720,7 @@ function vm_snapimages($vm, $snap, $only) {
{
if (!isset($snaps[$vm]["r".$diskname][$item])) break ;
$newpath = $snaps[$vm]["r".$diskname][$item] ;
- # rm $newpath
- if (is_file($path)) $data .= "$path
" ;
- #echo "rm $newpath\n" ;
-
+ if (is_file($path)) $data .= "$newpath
" ;
$item++ ;
}
@@ -1787,26 +1771,24 @@ function vm_snapremove($vm, $snap) {
$xmlobj = custom::createArray('domain',$strXML) ;
# Process disks and update path.
- $disks=($snapslist[$snap]['disks']["disk"]) ;
+ $disks=($snapslist[$snap]['disks']) ;
foreach ($disks as $disk) {
$diskname = $disk["@attributes"]["name"] ;
if ($diskname == "hda" || $diskname == "hdb") continue ;
$path = $disk["source"]["@attributes"]["file"] ;
$item = array_search($path,$snaps[$vm][$diskname]) ;
if (!$item) {
- #if currently attached to VM error.
$data = ["error" => "Image currently active for this domain."] ;
return ($data) ;
- }
- #echo "Newpath: $newpath image type ".$json_info["format"]."\n" ;
+ }
}
- $disks=($snapslist[$snap]['disks']["disk"]) ;
+ $disks=($snapslist[$snap]['disks']) ;
foreach ($disks as $disk) {
$diskname = $disk["@attributes"]["name"] ;
if ($diskname == "hda" || $diskname == "hdb") continue ;
$path = $disk["source"]["@attributes"]["file"] ;
- #if (is_file($path)) unlink($path) ;
+ #if (is_file($path)) unlink("$path") ;
}
#$ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
From 3907be975848077f722cc136db94dddcd2eb47b3 Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Thu, 11 May 2023 17:25:53 +0100
Subject: [PATCH 06/18] Add CD view to main VM detail
---
.../dynamix.vm.manager/include/VMMachines.php | 29 +++++++++++++++++--
1 file changed, 26 insertions(+), 3 deletions(-)
diff --git a/plugins/dynamix.vm.manager/include/VMMachines.php b/plugins/dynamix.vm.manager/include/VMMachines.php
index a1df722ee..a19303379 100644
--- a/plugins/dynamix.vm.manager/include/VMMachines.php
+++ b/plugins/dynamix.vm.manager/include/VMMachines.php
@@ -52,6 +52,7 @@
$image = substr($icon,-4)=='.png' ? "
" : (substr($icon,0,5)=='icon-' ? "
" : "
");
$arrConfig = domain_to_config($uuid);
$snapshots = getvmsnapshots($vm) ;
+ $cdroms = $lv->get_cdrom_stats($res) ;
if ($state == 'running') {
$mem = $dom['memory']/1024;
} else {
@@ -124,13 +125,33 @@
}
/* VM information */
+
if ($snapshots != null) $snapshotstr = _("(Snapshots :").count($snapshots).')' ; else $snapshotstr = _("(Snapshots :None)") ;
+ $cdbus = $cdbus2 = $cdfile = $cdfile2 = "" ;
+ $cdromcount = 0 ;
+ foreach ($cdroms as $arrCD) {
+ $disk = $arrCD['file'] ?? $arrCD['partition'];
+ $dev = $arrCD['device'];
+ $bus = $arrValidDiskBuses[$arrCD['bus']] ?? 'VirtIO';
+ if ($dev == "hda") {
+ $cdbus = $arrValidDiskBuses[$arrCD['bus']] ?? 'VirtIO';
+ $cdfile = $arrCD['file'] ?? $arrCD['partition'];
+ if ($cdfile != "") $cdromcount++ ;
+ }
+ if ($dev == "hdb") {
+ $cdbus2 = $arrValidDiskBuses[$arrCD['bus']] ?? 'VirtIO';
+ $cdfile2 = $arrCD['file'] ?? $arrCD['partition'];
+ if ($cdfile2 != "") $cdromcount++ ;
+ }
+ }
+ $changemedia = "getisoimageboth(\"{$uuid}\",\"hda\",\"{$cdbus}\",\"{$cdfile}\",\"hdb\",\"{$cdbus2}\",\"{$cdfile2}\")";
+ $cdstr = $cdromcount." / 2
";
echo "
";
echo "$image$vm "._($status)." $snapshotstcount | ";
echo "$desc | ";
echo "$vcpu | ";
echo "$mem | ";
- echo "$disks $snapshotstr | ";
+ echo "$disks $cdstr $snapshotstr | ";
echo "$graphics | ";
echo " |
";
@@ -170,7 +191,7 @@
}
/* Display VM cdroms */
- foreach ($lv->get_cdrom_stats($res) as $arrCD) {
+ foreach ($cdroms as $arrCD) {
$capacity = $lv->format_size($arrCD['capacity'], 0);
$allocation = $lv->format_size($arrCD['allocation'], 0);
$disk = $arrCD['file'] ?? $arrCD['partition'];
@@ -226,13 +247,15 @@
echo "
"._('Snapshots')." | | "._('Date/Time')." | "._('Type')." | "._('Parent')." | "._('Memory')." |
---|
";
echo "
";
+ $tab = " " ;
foreach($snapshots as $snapshotname => $snapshot) {
$snapshotstate = _(ucfirst($snapshot["state"])) ;
$snapshotmemory = _(ucfirst($snapshot["memory"]["@attributes"]["snapshot"])) ;
$snapshotparent = $snapshot["parent"]["name"] ? $snapshot["parent"]["name"] : "None";
$snapshotdatetime = my_time($snapshot["creationtime"],"Y-m-d" )."
".my_time($snapshot["creationtime"],"H:i:s") ;
$snapmenu = sprintf("onclick=\"addVMSnapContext('%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,$snapshot["name"]);
- echo " ".$snapshot["name"]." | | $snapshotdatetime | $snapshotstate | $snapshotparent | $snapshotmemory |
";
+ echo "$tab|__ ".$snapshot["name"]." | | $snapshotdatetime | $snapshotstate | $snapshotparent | $snapshotmemory |
";
+ $tab .=" " ;
}
echo "";
}
From 8011ce4533d1e0e4842f5268295a6c076411cc07 Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Thu, 11 May 2023 18:57:43 +0100
Subject: [PATCH 07/18] Add snpshot option to Dashboard
---
plugins/dynamix.vm.manager/VMMachines.page | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/plugins/dynamix.vm.manager/VMMachines.page b/plugins/dynamix.vm.manager/VMMachines.page
index b90156384..fa0de4111 100644
--- a/plugins/dynamix.vm.manager/VMMachines.page
+++ b/plugins/dynamix.vm.manager/VMMachines.page
@@ -121,6 +121,7 @@ div.four label:nth-child(4n+4){float:none;clear:both}
div.four label.cpu1{width:32%}
div.four label.cpu2{width:26%}
div.template,div#dialogWindow,input#upload{display:none}
+div.template,div#dialogWindow2,input#upload{display:none}
table.domdisk thead tr th:nth-child(1){width:56%!important}
table.domdisk thead tr th:nth-child(n+2){width:8%!important}
table.domdisk thead tr th:nth-child(1){padding-left:72px}
@@ -200,7 +201,6 @@ function getisoimageboth(uuid,dev,bus,file,dev2,bus2,file2){
var target2 = box.find('#target2');
if (target2.length) {
target2 = target2.val();
-
} else target2 = '';
box.find('#target').prop('disabled',true);
box.find('#target2').prop('disabled',true);
@@ -301,7 +301,7 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist){
const x = box.find('#targetsnaprmv').prop('checked') ;
if (x) remove = 'yes' ; else remove = 'no' ;
}
- if (opt == "create") {
+ if (opt == "create") {
const x = box.find('#targetsnapfspc').prop('checked') ;
if (x) free = 'yes' ; else free = 'no' ;
}
From 4c835ba2faf9c431f81bf991d9b96b05a64713a9 Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Thu, 11 May 2023 18:58:04 +0100
Subject: [PATCH 08/18] Add snapshot option to Dashboard
---
plugins/dynamix/DashStats.page | 82 ++++++++++++++++++++++++++++++++++
1 file changed, 82 insertions(+)
diff --git a/plugins/dynamix/DashStats.page b/plugins/dynamix/DashStats.page
index 05274201c..85d5167e3 100644
--- a/plugins/dynamix/DashStats.page
+++ b/plugins/dynamix/DashStats.page
@@ -181,6 +181,7 @@ div.last{padding-bottom:12px}
div.leftside{float:left;width:66%}
div.rightside{float:right;margin:0;text-align:center}
div[id$=chart]{margin:-12px 8px -24px -18px}
+div.template,div#dialogWindow,input#upload{display:none}
span.ctrl{float:right;margin-top:0;margin-right:10px}
span.ctrl span{font-size:2rem!important}
span.outer{float:left}
@@ -723,6 +724,69 @@ var netchart = new ApexCharts(document.querySelector('#netchart'), options_net);
if ($.cookie('port_select')!=null && !ports.includes($.cookie('port_select'))) $.removeCookie('port_select');
var port_select = $.cookie('port_select')||ports[0];
+function selectsnapshot(uuid, name ,snaps, opt, getlist){
+
+ var root = = '"'.$domain_cfg["MEDIADIR"].'"';?>;
+ var match= ".iso";
+ var box = $("#dialogWindow2");
+ box.html($("#templatesnapshot"+opt).html());
+ var height = 200;
+ const Capopt = opt.charAt(0).toUpperCase() + opt.slice(1) ;
+ var optiontext = Capopt + " Snapshot" ;
+ box.find('#VMName').html(name) ;
+ box.find('#targetsnap').val(snaps) ;
+ box.find('#targetsnapl').html(snaps) ;
+ if (getlist) {
+ var only = 1 ;
+ if (opt == "remove") only = 0;
+ $.post("/plugins/dynamix.vm.manager/include/VMajax.php", {action:"snap-images", uuid:uuid , snapshotname:snaps, only:only}, function(data) {
+ if (data.html) {
+ box.find('#targetsnapimages').html(data.html) ;
+ }
+ },'json');
+ }
+
+
+ document.getElementById("targetsnapfspc").checked = true ;
+
+ box.dialog({
+ title: "_("+optiontext+ ")_",
+ resizable: false,
+ width: 600,
+ height: 500,
+ modal: true,
+ show: {effect:'fade', duration:250},
+ hide: {effect:'fade', duration:250},
+
+ buttons: {
+ "_(Proceed)_": function(){
+ var target = box.find('#targetsnap');
+ if (target.length) {
+ target = target.val();
+ if (!target ) {errorTarget(); return;}
+ } else target = '';
+ var remove = 'yes'
+ var free = 'yes'
+ box.find('#targetsnap').prop('disabled',true);
+ if (opt == "revert") {
+ const x = box.find('#targetsnaprmv').prop('checked') ;
+ if (x) remove = 'yes' ; else remove = 'no' ;
+ }
+ if (opt == "create") {
+ const x = box.find('#targetsnapfspc').prop('checked') ;
+ if (x) free = 'yes' ; else free = 'no' ;
+ }
+ ajaxVMDispatch({action:"snap-" + opt +'-external', uuid:uuid , snapshotname:target , remove:remove, free:free } , "loadlist");
+ box.dialog('close');
+ },
+ "_(Cancel)_": function(){
+ box.dialog('close');
+ }
+ }
+ });
+ dialogStyle();
+}
+
function initCharts(clear) {
$.post('/webGui/include/InitCharts.php',{cmd:'get'},function(data) {
data = JSON.parse(data);
@@ -1341,4 +1405,22 @@ $(function() {
$.post('/webGui/include/InitCharts.php',{cmd:'set',data:JSON.stringify(data)});
});
});
+ function dialogStyle() {
+ $('.ui-dialog-titlebar-close').css({'background':'transparent','border':'none','font-size':'1.8rem','margin-top':'-14px','margin-right':'-18px'}).html('
').prop('title');
+ $('.ui-dialog-title').css({'text-align':'center','width':'100%','font-size':'1.8rem'});
+ $('.ui-dialog-content').css({'padding-top':'15px','vertical-align':'bottom'});
+ $('.ui-button-text').css({'padding':'0px 5px'});
+}
+
+
+
+
+_(VM Name)_:
+
+
+_(Snapshot Name)_:
+
+ _(Check free space)_:
+
+
From 0ad1be3c23996abaf510b9e9a72c5296a827a63d Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Sat, 13 May 2023 12:46:35 +0100
Subject: [PATCH 09/18] Updates to snapshots
---
plugins/dynamix.vm.manager/VMMachines.page | 45 ++++--
.../dynamix.vm.manager/include/VMMachines.php | 3 +-
plugins/dynamix.vm.manager/include/VMajax.php | 4 +-
.../include/libvirt_helpers.php | 135 +++++++++++++++---
.../javascript/vmmanager.js | 2 +-
plugins/dynamix/DashStats.page | 16 ++-
6 files changed, 167 insertions(+), 38 deletions(-)
diff --git a/plugins/dynamix.vm.manager/VMMachines.page b/plugins/dynamix.vm.manager/VMMachines.page
index fa0de4111..5438d131a 100644
--- a/plugins/dynamix.vm.manager/VMMachines.page
+++ b/plugins/dynamix.vm.manager/VMMachines.page
@@ -135,7 +135,7 @@ i.mover{margin-right:8px;display:none}
.dropdown-menu{z-index:10001}
-_(Name)_ | _(Description)_ | _(CPUs)_ | _(Memory)_ | _(vDisks/vCDs)_ | _(Graphics)_ | _(Autostart)_ |
+_(Name)_ | _(Description)_ | _(CPUs)_ | _(Memory)_ | _(vDisks / vCDs)_ | _(Graphics)_ | _(Autostart)_ |
|
@@ -192,7 +192,7 @@ function getisoimageboth(uuid,dev,bus,file,dev2,bus2,file2){
show: {effect:'fade', duration:250},
hide: {effect:'fade', duration:250},
buttons: {
- "_(Insert)_": function(){
+ "_(Update)_": function(){
var target = box.find('#target');
if (target.length) {
target = target.val();
@@ -253,7 +253,7 @@ function getisoimage(uuid,dev,bus,file){
dialogStyle();
}
-function selectsnapshot(uuid, name ,snaps, opt, getlist){
+function selectsnapshot(uuid, name ,snaps, opt, getlist,state){
var root = = '"'.$domain_cfg["MEDIADIR"].'"';?>;
var match= ".iso";
@@ -276,6 +276,8 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist){
}
document.getElementById("targetsnaprmv").checked = true ;
+ document.getElementById("targetsnaprmvmeta").checked = true ;
+ document.getElementById("targetsnapkeep").checked = true ;
document.getElementById("targetsnapfspc").checked = true ;
box.dialog({
@@ -295,17 +297,25 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist){
if (!target ) {errorTarget(); return;}
} else target = '';
var remove = 'yes'
+ var keep = 'yes'
+ var removemeta = 'yes'
var free = 'yes'
+ var desc = ''
box.find('#targetsnap').prop('disabled',true);
if (opt == "revert") {
- const x = box.find('#targetsnaprmv').prop('checked') ;
+ var x = box.find('#targetsnaprmv').prop('checked') ;
if (x) remove = 'yes' ; else remove = 'no' ;
+ x = box.find('#targetsnaprmvmeta').prop('checked') ;
+ if (x) removemeta = 'yes' ; else removemeta = 'no' ;
+ x = box.find('#targetsnapkeep').prop('checked') ;
+ if (x) keep = 'yes' ; else keep = 'no' ;
}
if (opt == "create") {
- const x = box.find('#targetsnapfspc').prop('checked') ;
+ var x = box.find('#targetsnapfspc').prop('checked') ;
if (x) free = 'yes' ; else free = 'no' ;
+ var desc = box.find("#targetsnapdesc").prop('value') ;
}
- ajaxVMDispatch({action:"snap-" + opt +'-external', uuid:uuid , snapshotname:target , remove:remove, free:free } , "loadlist");
+ ajaxVMDispatch({action:"snap-" + opt +'-external', uuid:uuid , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep, desc:desc} , "loadlist");
box.dialog('close');
},
"_(Cancel)_": function(){
@@ -430,16 +440,21 @@ _(CD2 ISO Image)_:
-_(VM Name)_:
-
-
-_(Snapshot Name)_:
+
-
+
_(VM Name)_:
@@ -447,7 +462,11 @@ _(Snapshot Name)_:
_(Remove Images)_:
-
+
+_(Remove Meta)_:
+
+
+
diff --git a/plugins/dynamix.vm.manager/include/VMMachines.php b/plugins/dynamix.vm.manager/include/VMMachines.php
index a19303379..d269f27a8 100644
--- a/plugins/dynamix.vm.manager/include/VMMachines.php
+++ b/plugins/dynamix.vm.manager/include/VMMachines.php
@@ -250,11 +250,12 @@
$tab = " " ;
foreach($snapshots as $snapshotname => $snapshot) {
$snapshotstate = _(ucfirst($snapshot["state"])) ;
+ $snapshotdesc = $snapshot["desc"] ;
$snapshotmemory = _(ucfirst($snapshot["memory"]["@attributes"]["snapshot"])) ;
$snapshotparent = $snapshot["parent"]["name"] ? $snapshot["parent"]["name"] : "None";
$snapshotdatetime = my_time($snapshot["creationtime"],"Y-m-d" )."
".my_time($snapshot["creationtime"],"H:i:s") ;
$snapmenu = sprintf("onclick=\"addVMSnapContext('%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,$snapshot["name"]);
- echo "
$tab|__ ".$snapshot["name"]." | | $snapshotdatetime | $snapshotstate | $snapshotparent | $snapshotmemory |
";
+ echo "
$tab|__ ".$snapshot["name"]." | $snapshotdesc | $snapshotdatetime | $snapshotstate | $snapshotparent | $snapshotmemory |
";
$tab .=" " ;
}
echo "";
diff --git a/plugins/dynamix.vm.manager/include/VMajax.php b/plugins/dynamix.vm.manager/include/VMajax.php
index fe1b839bf..7f3906913 100644
--- a/plugins/dynamix.vm.manager/include/VMajax.php
+++ b/plugins/dynamix.vm.manager/include/VMajax.php
@@ -336,7 +336,7 @@ function embed(&$syslinux, $key, $value) {
case 'snap-create-external':
requireLibvirt();
- $arrResponse = vm_snapshot($domName,$_REQUEST['snapshotname'],$_REQUEST['free']) ;
+ $arrResponse = vm_snapshot($domName,$_REQUEST['snapshotname'],$_REQUEST['desc'],$_REQUEST['free']) ;
break;
case 'snap-images':
@@ -347,7 +347,7 @@ function embed(&$syslinux, $key, $value) {
case 'snap-revert-external':
requireLibvirt();
- $arrResponse = vm_revert($domName,$_REQUEST['snapshotname'],$_REQUEST['remove']) ;
+ $arrResponse = vm_revert($domName,$_REQUEST['snapshotname'],$_REQUEST['remove'], $_REQUEST['removemeta']) ;
break;
case 'snap-remove-external':
diff --git a/plugins/dynamix.vm.manager/include/libvirt_helpers.php b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
index d7a14e3fe..302b66e53 100644
--- a/plugins/dynamix.vm.manager/include/libvirt_helpers.php
+++ b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
@@ -1480,6 +1480,7 @@ function getvmsnapshots($vm) {
$snaps[$vmsnap]["name"]= $b["name"];
if(isset($b["parent"])) $snaps[$vmsnap]["parent"]= $b["parent"]; else $snaps[$vmsnap]["parent"]["name"] = "Base" ;
$snaps[$vmsnap]["state"]= $b["state"];
+ $snaps[$vmsnap]["desc"]= $b["description"];
$snaps[$vmsnap]["memory"]= $b["memory"];
$snaps[$vmsnap]["creationtime"]= $b["creationTime"];
if (array_key_exists(0 , $b["disks"]["disk"])) $snaps[$vmsnap]["disks"]= $b["disks"]["disk"]; else $snaps[$vmsnap]["disks"][0]= $b["disks"]["disk"];
@@ -1489,7 +1490,7 @@ function getvmsnapshots($vm) {
return $snaps ;
}
- function vm_snapshot($vm,$snapshotname,$free = "yes", $memorysnap = "yes") {
+ function vm_snapshot($vm,$snapshotname, $snapshotdesc, $free = "yes", $memorysnap = "yes") {
global $lv ;
#Get State
@@ -1502,8 +1503,8 @@ function vm_snapshot($vm,$snapshotname,$free = "yes", $memorysnap = "yes") {
$diskspec = "" ;
$capacity = 0 ;
if ($snapshotname == "--generate") $name= "S" . date("YmdHis") ; else $name=$snapshotname ;
- $cmdstr = "virsh snapshot-create-as '$vm' --name '$name' --atomic " ;
-
+ if ($snapshotdesc != "") $snapshotdesc = " --description '$snapshotdesc'" ;
+
foreach($disks as $disk) {
$file = $disk["file"] ;
$pathinfo = pathinfo($file) ;
@@ -1517,7 +1518,7 @@ function vm_snapshot($vm,$snapshotname,$free = "yes", $memorysnap = "yes") {
$memory = $mem[6] ;
if ($memorysnap = "yes") $memspec = " --memspec ".$pathinfo["dirname"].'/memory"'.$name.'".mem,snapshot=external' ; else $memspec = "" ;
- $cmdstr = "virsh snapshot-create-as '$vm' --name '$name' --atomic" ;
+ $cmdstr = "virsh snapshot-create-as '$vm' --name '$name' $snapshotdesc --atomic" ;
if ($state == "running") {
@@ -1552,12 +1553,9 @@ function vm_snapshot($vm,$snapshotname,$free = "yes", $memorysnap = "yes") {
}
- function vm_revert($vm, $snap="--current",$action="no") {
+ function vm_revert($vm, $snap="--current",$action="no",$actionmeta = 'yes') {
global $lv ;
$snapslist= getvmsnapshots($vm) ;
-
- #echo "Revert Vm: $vm snap: $snap\n" ;
-
$disks =$lv->get_disk_stats($vm) ;
foreach($disks as $disk) {
@@ -1627,7 +1625,7 @@ function vm_revert($vm, $snap="--current",$action="no") {
{
if (!isset($snaps[$vm]["r".$diskname][$item])) break ;
$newpath = $snaps[$vm]["r".$diskname][$item] ;
- if (is_file($path) && $action == "yes") unlink("$newpath") ;
+ if (is_file($newpath) && $action == "yes") unlink("$newpath") ;
$item++ ;
@@ -1638,14 +1636,13 @@ function vm_revert($vm, $snap="--current",$action="no") {
foreach($snapslist as $s) {
$name = $s['name'] ;
#Delete Metadata only.
- if ($action == "yes") {
+ if ($actionmeta == "yes") {
$ret = $lv->domain_snapshot_delete($vm, "$name" ,2) ;
}
if ($s['name'] == $snap) break ;
}
#if VM was started restart.
if ($state == 'running') {
- #echo "Restart VM\n" ;
$arrResponse = $lv->domain_start($vm) ;
}
@@ -1686,8 +1683,6 @@ function vm_snapimages($vm, $snap, $only) {
$snapslist= getvmsnapshots($vm) ;
$data = "
Images and metadata to remove if tickbox checked.
" ;
- #echo "Revert Vm: $vm snap: $snap\n" ;
-
$disks =$lv->get_disk_stats($vm) ;
foreach($disks as $disk) {
$file = $disk["file"] ;
@@ -1761,16 +1756,13 @@ function vm_snapremove($vm, $snap) {
$reversed = array_reverse($output) ;
$snaps[$vm][$rev] = $reversed ;
$pathinfo = pathinfo($file) ;
- $filenew = $pathinfo["dirname"].'/'.$pathinfo["filename"].'.'.$name.'qcow2' ;
- $diskspec .= " --diskspec ".$disk["device"].",snapshot=external,file=".$filenew ;
- $capacity = $capacity + $disk["capacity"] ;
}
# GetXML
$strXML= $lv->domain_get_xml($res) ;
$xmlobj = custom::createArray('domain',$strXML) ;
- # Process disks and update path.
+ # Process disks.
$disks=($snapslist[$snap]['disks']) ;
foreach ($disks as $disk) {
$diskname = $disk["@attributes"]["name"] ;
@@ -1788,12 +1780,115 @@ function vm_snapremove($vm, $snap) {
$diskname = $disk["@attributes"]["name"] ;
if ($diskname == "hda" || $diskname == "hdb") continue ;
$path = $disk["source"]["@attributes"]["file"] ;
- #if (is_file($path)) unlink("$path") ;
+ if (is_file($path)) {
+ if(!unlink("$path")) {
+ $data = ["error" => "Unable to remove image file $path"] ;
+ return ($data) ;
+ }
+ }
}
- #$ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
- $data = ["success => 'true"] ;
+ $ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
+ if($ret)
+ $data = ["error" => "Unable to remove snap metadata $snap"] ;
+ else
+ $data = ["success => 'true"] ;
+
return($data) ;
}
+
+function vm_blockcommit($vm,$path,$base,$top,$pivot,$action) {
+ /*
+ NAME
+ blockcommit - Start a block commit operation.
+
+ SYNOPSIS
+ blockcommit
[--bandwidth ] [--base ] [--shallow] [--top ] [--active] [--delete] [--wait] [--verbose] [--timeout ] [--pivot] [--keep-overlay] [--async] [--keep-relative] [--bytes]
+
+ DESCRIPTION
+ Commit changes from a snapshot down to its backing image.
+
+ OPTIONS
+ [--domain] domain name, id or uuid
+ [--path] fully-qualified path of disk
+ --bandwidth bandwidth limit in MiB/s
+ --base path of base file to commit into (default bottom of chain)
+ --shallow use backing file of top as base
+ --top path of top file to commit from (default top of chain)
+ --active trigger two-stage active commit of top file
+ --delete delete files that were successfully committed
+ --wait wait for job to complete (with --active, wait for job to sync)
+ --verbose with --wait, display the progress
+ --timeout implies --wait, abort if copy exceeds timeout (in seconds)
+ --pivot implies --active --wait, pivot when commit is synced
+ --keep-overlay implies --active --wait, quit when commit is synced
+ --async with --wait, don't wait for cancel to finish
+ --keep-relative keep the backing chain relatively referenced
+ --bytes the bandwidth limit is in bytes/s rather than MiB/s
+ */
+}
+
+function vm_blockcopy($vm,$path,$base,$top,$pivot,$action) {
+ /*
+ NAME
+ blockcopy - Start a block copy operation.
+
+ SYNOPSIS
+ blockcopy [--dest ] [--bandwidth ] [--shallow] [--reuse-external] [--blockdev] [--wait] [--verbose] [--timeout ] [--pivot] [--finish] [--async] [--xml ] [--format ] [--granularity ] [--buf-size ] [--bytes] [--transient-job] [--synchronous-writes] [--print-xml]
+
+ DESCRIPTION
+ Copy a disk backing image chain to dest.
+
+ OPTIONS
+ [--domain] domain name, id or uuid
+ [--path] fully-qualified path of source disk
+ --dest path of the copy to create
+ --bandwidth bandwidth limit in MiB/s
+ --shallow make the copy share a backing chain
+ --reuse-external reuse existing destination
+ --blockdev copy destination is block device instead of regular file
+ --wait wait for job to reach mirroring phase
+ --verbose with --wait, display the progress
+ --timeout implies --wait, abort if copy exceeds timeout (in seconds)
+ --pivot implies --wait, pivot when mirroring starts
+ --finish implies --wait, quit when mirroring starts
+ --async with --wait, don't wait for cancel to finish
+ --xml filename containing XML description of the copy destination
+ --format format of the destination file
+ --granularity power-of-two granularity to use during the copy
+ --buf-size maximum amount of in-flight data during the copy
+ --bytes the bandwidth limit is in bytes/s rather than MiB/s
+ --transient-job the copy job is not persisted if VM is turned off
+ --synchronous-writes the copy job forces guest writes to be synchronously written to the destination
+ --print-xml print the XML used to start the copy job instead of starting the job
+ */
+}
+
+function vm_blockpull($vm,$path,$base,$top,$pivot,$action) {
+ /*
+ NAME
+ blockpull - Populate a disk from its backing image.
+
+ SYNOPSIS
+ blockpull [--bandwidth ] [--base ] [--wait] [--verbose] [--timeout ] [--async] [--keep-relative] [--bytes]
+
+ DESCRIPTION
+ Populate a disk from its backing image.
+
+ OPTIONS
+ [--domain] domain name, id or uuid
+ [--path] fully-qualified path of disk
+ --bandwidth bandwidth limit in MiB/s
+ --base path of backing file in chain for a partial pull
+ --wait wait for job to finish
+ --verbose with --wait, display the progress
+ --timeout with --wait, abort if pull exceeds timeout (in seconds)
+ --async with --wait, don't wait for cancel to finish
+ --keep-relative keep the backing chain relatively referenced
+ --bytes the bandwidth limit is in bytes/s rather than MiB/s
+
+
+ */
+}
?>
diff --git a/plugins/dynamix.vm.manager/javascript/vmmanager.js b/plugins/dynamix.vm.manager/javascript/vmmanager.js
index d7ef4d57f..0654c87ac 100644
--- a/plugins/dynamix.vm.manager/javascript/vmmanager.js
+++ b/plugins/dynamix.vm.manager/javascript/vmmanager.js
@@ -151,7 +151,7 @@ function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, c
opts.push({divider:true});
opts.push({text:_("Create Snapshot"), icon:"fa-clone", action:function(e) {
e.preventDefault();
- selectsnapshot(uuid , name, "--generate" , "create",false) ;
+ selectsnapshot(uuid , name, "--generate" , "create",false,state) ;
}});
opts.push({text:_("Remove VM"), icon:"fa-minus", action:function(e) {
e.preventDefault();
diff --git a/plugins/dynamix/DashStats.page b/plugins/dynamix/DashStats.page
index 85d5167e3..f556fe7a3 100644
--- a/plugins/dynamix/DashStats.page
+++ b/plugins/dynamix/DashStats.page
@@ -766,17 +766,25 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist){
if (!target ) {errorTarget(); return;}
} else target = '';
var remove = 'yes'
+ var keep = 'yes'
+ var removemeta = 'yes'
var free = 'yes'
+ var desc = ''
box.find('#targetsnap').prop('disabled',true);
if (opt == "revert") {
const x = box.find('#targetsnaprmv').prop('checked') ;
if (x) remove = 'yes' ; else remove = 'no' ;
+ x = box.find('#targetsnaprmvmeta').prop('checked') ;
+ if (x) removemeta = 'yes' ; else removemeta = 'no' ;
+ x = box.find('#targetsnapkeep').prop('checked') ;
+ if (x) keep = 'yes' ; else keep = 'no' ;
}
if (opt == "create") {
const x = box.find('#targetsnapfspc').prop('checked') ;
if (x) free = 'yes' ; else free = 'no' ;
+ var desc = box.find("#targetsnapdesc").prop('value') ;
}
- ajaxVMDispatch({action:"snap-" + opt +'-external', uuid:uuid , snapshotname:target , remove:remove, free:free } , "loadlist");
+ ajaxVMDispatch({action:"snap-" + opt +'-external', uuid:uuid , snapshotname:target , remove:remove, free:free, desc:desc } , "loadlist");
box.dialog('close');
},
"_(Cancel)_": function(){
@@ -1416,6 +1424,7 @@ $(function() {
+
+
From 1911da7fc38e28c8310d273f33e1e48364b9fcee Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Sun, 21 May 2023 19:04:35 +0100
Subject: [PATCH 10/18] Add VMAjaxCall for Block commands
---
.../dynamix.vm.manager/scripts/VMAjaxCall.php | 76 +++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100644 plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
diff --git a/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php b/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
new file mode 100644
index 000000000..9e40bf79e
--- /dev/null
+++ b/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
@@ -0,0 +1,76 @@
+#!/usr/bin/php -q
+
+
+$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
+require_once "$docroot/webGui/include/Wrappers.php";
+require_once "$docroot/webGui/include/Translations.php";
+require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
+function write(...$messages){
+ $com = curl_init();
+ curl_setopt_array($com,[
+ CURLOPT_URL => 'http://localhost/pub/plugins?buffer_length=1',
+ CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
+ CURLOPT_POST => 1,
+ CURLOPT_RETURNTRANSFER => true
+ ]);
+ foreach ($messages as $message) {
+ curl_setopt($com, CURLOPT_POSTFIELDS, $message);
+ curl_exec($com);
+ }
+ curl_close($com);
+}
+function execCommand_nchan($command) {
+ $waitID = mt_rand();
+ [$cmd,$args] = explode(' ',$command,2);
+ write("","addLog\0","show_Wait\0$waitID");
+ $proc = popen("$command 2>&1",'r');
+ while ($out = fgets($proc)) {
+ $out = preg_replace("%[\t\n\x0B\f\r]+%", '',$out);
+ write("addLog\0".htmlspecialchars($out));
+ }
+ $retval = pclose($proc);
+ $out = $retval ? _('The command failed').'.' : _('The command finished successfully').'!';
+ write("stop_Wait\0$waitID","addLog\0
$out");
+ return $retval===0;
+ }
+
+#{action:"snap-", uuid:uuid , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep, desc:desc}
+$url = rawurldecode($argv[1]??'');
+$waitID = "bob" ;
+$style = ["";
+$path = "--current" ; $pivot = "yes" ; $action = " " ;
+write(implode($style)."");
+write("","addLog\0","show_Wait\0$waitID");
+
+foreach (explode('&', $url) as $chunk) {
+ $param = explode("=", $chunk);
+ if ($param) {
+# write("addLog\0Parm" . sprintf("Value for parameter \"%s\" is \"%s\"
\n", urldecode($param[0]), urldecode($param[1])));
+ ${urldecode($param[0])} = urldecode($param[1]) ;
+ }
+}
+
+write("VMName $name ");
+write("SNAP $snapshotname ");
+write("Base $targetbase ");
+write("Top $targettop ");
+$path = "--current" ; $pivot = "yes" ; $action = " " ;
+vm_blockcommit($name,$snapshotname,$path,$targetbase,$targettop,$pivot,$action) ;
+#execCommand_nchan("ls /root") ;
+write('_DONE_','');
+
+?>
\ No newline at end of file
From 34c3de339e40af42d91715feedac2d83dd1caa2a Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Sun, 21 May 2023 19:24:13 +0100
Subject: [PATCH 11/18] Updates for Blockcommit
---
plugins/dynamix.vm.manager/VMMachines.page | 108 ++++++++++++++++--
plugins/dynamix.vm.manager/include/VMajax.php | 19 +++
.../include/libvirt_helpers.php | 97 ++++++++++++++--
.../javascript/vmmanager.js | 19 ++-
plugins/dynamix/DashStats.page | 23 ++--
5 files changed, 231 insertions(+), 35 deletions(-)
diff --git a/plugins/dynamix.vm.manager/VMMachines.page b/plugins/dynamix.vm.manager/VMMachines.page
index 5438d131a..0290b97fc 100644
--- a/plugins/dynamix.vm.manager/VMMachines.page
+++ b/plugins/dynamix.vm.manager/VMMachines.page
@@ -18,6 +18,7 @@ Markdown="false"
*/
?>
+$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
$cpus = cpu_list();
@@ -278,7 +279,7 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist,state){
document.getElementById("targetsnaprmv").checked = true ;
document.getElementById("targetsnaprmvmeta").checked = true ;
document.getElementById("targetsnapkeep").checked = true ;
- document.getElementById("targetsnapfspc").checked = true ;
+ document.getElementById("targetsnapfspc").checked = false ;
box.dialog({
title: "_("+optiontext+ ")_",
@@ -326,6 +327,80 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist,state){
dialogStyle();
}
+function selectblock(uuid, name ,snaps, opt, getlist,state){
+
+ var root = = '"'.$domain_cfg["MEDIADIR"].'"';?>;
+ var match= ".iso";
+ var box = $("#dialogWindow2");
+ box.html($("#templateblock").html());
+ var height = 200;
+ const Capopt = opt.charAt(0).toUpperCase() + opt.slice(1) ;
+ var optiontext = Capopt + " Block Devices" ;
+ box.find('#VMName').html(name) ;
+ box.find('#targetsnap').val(snaps) ;
+ box.find('#targetsnapl').html(snaps) ;
+
+ getlist = true ;
+ if (getlist) {
+ var only = 1 ;
+ if (opt == "remove") only = 0;
+ $.post("/plugins/dynamix.vm.manager/include/VMajax.php", {action:"snap-list", uuid:uuid }, function(data) {
+ if (data.html) {
+ var targetbase = document.getElementById("targetblockbase") ;
+ htmlstrbase = ""
+ htmlstrtop = ""
+ $("select.targetblockbase").replaceWith(htmlstrbase) ;
+ $("select.targetblocktop").replaceWith(htmlstrtop) ;
+ }
+ },'json');
+ }
+
+ document.getElementById("targetsnaprmv").checked = true ;
+ document.getElementById("targetsnaprmvmeta").checked = true ;
+ document.getElementById("targetsnapkeep").checked = true ;
+ document.getElementById("targetsnapfspc").checked = true ;
+
+ box.dialog({
+ title: "_("+optiontext+ ")_",
+ resizable: false,
+ width: 600,
+ height: 500,
+ modal: true,
+ show: {effect:'fade', duration:250},
+ hide: {effect:'fade', duration:250},
+ buttons: {
+ _("Action")_: function(){
+ var target = box.find('#targetsnap');
+ if (target.length) {
+ target = target.val();
+ if (!target ) {errorTarget(); return;}
+ } else target = '';
+ var remove = 'yes'
+ var keep = 'yes'
+ var removemeta = 'yes'
+ var free = 'yes'
+ var desc = ''
+ box.find('#targetsnap').prop('disabled',true);
+ if (opt == "create") {
+ var x = box.find('#targetsnapfspc').prop('checked') ;
+ if (x) free = 'yes' ; else free = 'no' ;
+ var desc = box.find("#targetsnapdesc").prop('value') ;
+ }
+ var targetbase = $('#targetblockbase').val()
+ var targettop = $('#targetblocktop').val()
+ //ajaxVMDispatch({action:"snap-", uuid:uuid , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep, desc:desc} , "loadlist");
+ Ajaxurl = "VMAjaxCall.php " + encodeURIComponent("/usr/local/emhttp/plugins/dynamix.vm.manager/include/VMajax.php&" + $.param({action:"snap-commit", name:name ,targetbase:targetbase, targettop:targettop , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep})) ;
+ openPlugin((Ajaxurl),"Block Commit", "dynamix.vm.manager", "loadlist") ;
+ box.dialog('close');
+ },
+ "_(Cancel)_": function(){
+ box.dialog('close');
+ }
+ }
+ });
+ dialogStyle();
+}
+
// ajaxVMDispatch({action:"snap-create-external", uuid:uuid}, "loadlist");
// ajaxVMDispatch({action:"snap-revert-external", uuid:uuid , snapshotname:snapshotname , remove:'yes'}, "loadlist");
@@ -442,13 +517,12 @@ _(CD2 ISO Image)_:
@@ -485,5 +559,25 @@ _(Snapshot Name)_:
+
+_(VM Name)_:
+
+
+_(Snapshot Name)_:
+
+
+
+
+_(Base Image)_: |
+ |
+_(Top Image )_: |
+ |
+ |
+
+
+
+
\ No newline at end of file
diff --git a/plugins/dynamix.vm.manager/include/VMajax.php b/plugins/dynamix.vm.manager/include/VMajax.php
index 7f3906913..5c1fa042a 100644
--- a/plugins/dynamix.vm.manager/include/VMajax.php
+++ b/plugins/dynamix.vm.manager/include/VMajax.php
@@ -339,12 +339,31 @@ function embed(&$syslinux, $key, $value) {
$arrResponse = vm_snapshot($domName,$_REQUEST['snapshotname'],$_REQUEST['desc'],$_REQUEST['free']) ;
break;
+case 'snap-blockcommit':
+ requireLibvirt();
+ echo "Hello" ;
+ #$arrResponse = vm_snapshot($domName,'',$_REQUEST['base'],$_REQUEST['top'],"","") ;
+ break;
+
case 'snap-images':
requireLibvirt();
$html = vm_snapimages($domName,$_REQUEST['snapshotname'],$_REQUEST['only']) ;
$arrResponse = ['html' => $html , 'success' => true] ;
break;
+case 'snap-list':
+ requireLibvirt();
+ $arrResponse = ($data = getvmsnapshots($domName))
+ ? ['success' => true]
+ : ['error' => $lv->get_last_error()];
+ $datartn = "";
+ foreach($data as $snap=>$snapdetail) {
+ $snapshotdatetime = date("Y-m-d H:i:s",$snapdetail["creationtime"]) ;
+ $datartn .= "" ;
+ }
+ $arrResponse['html'] = $datartn ;
+ break;
+
case 'snap-revert-external':
requireLibvirt();
$arrResponse = vm_revert($domName,$_REQUEST['snapshotname'],$_REQUEST['remove'], $_REQUEST['removemeta']) ;
diff --git a/plugins/dynamix.vm.manager/include/libvirt_helpers.php b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
index 302b66e53..b19b97fe3 100644
--- a/plugins/dynamix.vm.manager/include/libvirt_helpers.php
+++ b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
@@ -1538,6 +1538,9 @@ function vm_snapshot($vm,$snapshotname, $snapshotdesc, $free = "yes", $memorysn
#Copy nvram
if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_create_snapshot($lv->domain_get_uuid($vm),$name) ;
+
+ $xmlfile = $pathinfo["dirname"].'/"'.$name.'".running' ;
+ if ($state == "running") exec("virsh dumpxml '$vm' > $xmlfile",$outxml,$rtnxml) ;
$test = false ;
if ($test) exec($cmdstr." --print-xml 2>&1",$output,$return) ; else exec($cmdstr." 2>&1",$output,$return) ;
@@ -1577,8 +1580,9 @@ function vm_revert($vm, $snap="--current",$action="no",$actionmeta = 'yes') {
$capacity = $capacity + $disk["capacity"] ;
}
- switch ($snapslist[$snap]['state']) {
- case "shutoff":
+ switch ($snapslist[$snap]['state']) {
+ case "shutoff":
+ case "running":
#VM must be shutdown.
$res = $lv->get_domain_by_name($vm);
$dom = $lv->domain_get_info($res);
@@ -1596,6 +1600,10 @@ function vm_revert($vm, $snap="--current",$action="no",$actionmeta = 'yes') {
$diskname = $disk["@attributes"]["name"] ;
if ($diskname == "hda" || $diskname == "hdb") continue ;
$path = $disk["source"]["@attributes"]["file"] ;
+ if ($diskname == "hdc") {
+ $primarypathinfo = pathinfo($path) ;
+ $primarypath = $primarypathinfo['dirname'] ;
+ }
$item = array_search($path,$snaps[$vm][$diskname]) ;
$newpath = $snaps[$vm][$diskname][$item + 1];
$json_info = getDiskImageInfo($newpath) ;
@@ -1638,7 +1646,13 @@ function vm_revert($vm, $snap="--current",$action="no",$actionmeta = 'yes') {
#Delete Metadata only.
if ($actionmeta == "yes") {
$ret = $lv->domain_snapshot_delete($vm, "$name" ,2) ;
- }
+ }
+ # Remove running XML and memory.
+ $xmlfile = $primarypath."/$name.running" ;
+ $memoryfile = $primarypath."/memory$name.mem" ;
+ #var_dump(is_file($xmlfile), is_file($memoryfile)) ;
+ if (is_file($memoryfile) && $action == "yes") unlink($memoryfile) ;
+ if (is_file($xmlfile) && $action == "yes") unlink($xmlfile) ;
if ($s['name'] == $snap) break ;
}
#if VM was started restart.
@@ -1687,7 +1701,7 @@ function vm_snapimages($vm, $snap, $only) {
foreach($disks as $disk) {
$file = $disk["file"] ;
$output = "" ;
- exec("qemu-img info --backing-chain -U $file | grep image:",$output) ;
+ exec("qemu-img info --backing-chain -U '$file' | grep image:",$output) ;
foreach($output as $key => $line) {
$line=str_replace("image: ","",$line) ;
$output[$key] = $line ;
@@ -1703,6 +1717,7 @@ function vm_snapimages($vm, $snap, $only) {
}
$snapdisks= $snapslist[$snap]['disks'] ;
+ #var_dump($snaps) ;
foreach ($snapdisks as $diskkey => $snapdisk) {
$diskname = $snapdisk["@attributes"]["name"] ;
if ($diskname == "hda" || $diskname == "hdb") continue ;
@@ -1763,7 +1778,8 @@ function vm_snapremove($vm, $snap) {
$xmlobj = custom::createArray('domain',$strXML) ;
# Process disks.
- $disks=($snapslist[$snap]['disks']) ;
+ $disks=($snapslist[$snap]['disks']) ;
+ var_dump($disks,$snaps) ;
foreach ($disks as $disk) {
$diskname = $disk["@attributes"]["name"] ;
if ($diskname == "hda" || $diskname == "hdb") continue ;
@@ -1781,14 +1797,14 @@ function vm_snapremove($vm, $snap) {
if ($diskname == "hda" || $diskname == "hdb") continue ;
$path = $disk["source"]["@attributes"]["file"] ;
if (is_file($path)) {
- if(!unlink("$path")) {
- $data = ["error" => "Unable to remove image file $path"] ;
- return ($data) ;
- }
+ #if(!unlink("$path")) {
+ # $data = ["error" => "Unable to remove image file $path"] ;
+ # return ($data) ;
+ #}
}
}
- $ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
+ #$ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
if($ret)
$data = ["error" => "Unable to remove snap metadata $snap"] ;
else
@@ -1797,7 +1813,8 @@ function vm_snapremove($vm, $snap) {
return($data) ;
}
-function vm_blockcommit($vm,$path,$base,$top,$pivot,$action) {
+function vm_blockcommit($vm, $snap ,$path,$base,$top,$pivot,$action) {
+ global $lv ;
/*
NAME
blockcommit - Start a block commit operation.
@@ -1825,7 +1842,65 @@ function vm_blockcommit($vm,$path,$base,$top,$pivot,$action) {
--async with --wait, don't wait for cancel to finish
--keep-relative keep the backing chain relatively referenced
--bytes the bandwidth limit is in bytes/s rather than MiB/s
+
+ blockcommit Debian --path /mnt/user/domains/Debian/vdisk1.S20230513120410qcow2 --verbose --pivot --delete
*/
+ # Error if VM Not running.
+
+ $snapslist= getvmsnapshots($vm) ;
+ $disks =$lv->get_disk_stats($vm) ;
+ #var_dump($disks) ;
+ foreach($disks as $disk) {
+ $path = $disk['file'] ;
+ $cmdstr = "virsh blockcommit '$vm' --path '$path' --verbose --pivot --delete" ;
+ # Process disks and update path.
+ $snapdisks=($snapslist[$snap]['disks']) ;
+ if ($base != "--base" && $base != "") {
+ #get file name from snapshot.
+ $snapdisks=($snapslist[$base]['disks']) ;
+ $basepath = "" ;
+ foreach ($snapdisks as $snapdisk) {
+ $diskname = $snapdisk["@attributes"]["name"] ;
+ if ($diskname != $disk['device']) continue ;
+ $basepath = $snapdisk["source"]["@attributes"]["file"] ;
+ }
+ if ($basepath != "") $cmdstr .= " --base '$basepath' ";
+ }
+ if ($top != "--top" && $top !="") {
+ #get file name from snapshot.
+ $snapdisks=($snapslist[$top]['disks']) ;
+ $toppath = "" ;
+ foreach ($snapdisks as $snapdisk) {
+ $diskname = $snapdisk["@attributes"]["name"] ;
+ if ($diskname != $disk['device']) continue ;
+ $toppath = $snapdisk["source"]["@attributes"]["file"] ;
+ }
+ if ($toppath != "") $cmdstr .= " --top '$toppath' ";
+ }
+ if ($action) $cmdstr .= " $action ";
+
+
+ $test = false ;
+ if ($test) { $cmdstr .= " --print-xml " ; execCommand_nchan($cmdstr) ; } else execCommand_nchan($cmdstr) ;
+ #var_dump($cmdstr,$output) ;
+ if (strpos(" ".$output[0],"error") ) {
+ $arrResponse = ['error' => substr($output[0],6) ] ;
+ #return($arrResponse) ;
+ } else {
+ # Remove nvram snapshot
+ $arrResponse = ['success' => true] ;
+ }
+ #Error Check
+ }
+ #If complete ok remove meta data for snapshots.
+ if (!$test) $ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
+ if($ret)
+ $data = ["error" => "Unable to remove snap metadata $snap"] ;
+ else
+ $data = ["success => 'true"] ;
+
+ return $data ;
+
}
function vm_blockcopy($vm,$path,$base,$top,$pivot,$action) {
diff --git a/plugins/dynamix.vm.manager/javascript/vmmanager.js b/plugins/dynamix.vm.manager/javascript/vmmanager.js
index 0654c87ac..940afe673 100644
--- a/plugins/dynamix.vm.manager/javascript/vmmanager.js
+++ b/plugins/dynamix.vm.manager/javascript/vmmanager.js
@@ -106,6 +106,11 @@ function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, c
e.preventDefault();
ajaxVMDispatch( {action:"domain-destroy", uuid:uuid}, "loadlist");
}});
+ opts.push({divider:true});
+ opts.push({text:_("Create Snapshot"), icon:"fa-clone", action:function(e) {
+ e.preventDefault();
+ selectsnapshot(uuid , name, "--generate" , "create",false,state) ;
+ }});
} else if (state == "pmsuspended") {
opts.push({text:_("Resume"), icon:"fa-play", action:function(e) {
e.preventDefault();
@@ -141,7 +146,8 @@ function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, c
ajaxVMDispatchconsoleRV({action:"domain-start-consoleRV", uuid:uuid, vmrcurl:vmrcurl}, "loadlist") ;
}});
}
- }}
+ }
+ }
opts.push({divider:true});
if (log !== "") {
opts.push({text:_("Logs"), icon:"fa-navicon", action:function(e){e.preventDefault(); openTerminal('log',name,log);}});
@@ -198,10 +204,11 @@ function addVMSnapContext(name, uuid, template, state, snapshotname){
// e.preventDefault();
// ajaxVMDispatch({action:"snapshot-revert-externa", uuid:uuid, snapshotname:snapshotname}, "loadlist");
// }});
-// opts.push({text:_("Block Commit"), icon:"fa-stop", action:function(e) {
-// e.preventDefault();
-// ajaxVMDispatch({action:"domain-stop", uuid:uuid}, "loadlist");
-// }});
+ opts.push({text:_("Block Commit"), icon:"fa-hdd-o", action:function(e) {
+ $('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
+ e.preventDefault();
+ selectblock(uuid, name, snapshotname, "commit",true) ;
+ }});
// opts.push({text:_("Block Copy"), icon:"fa-stop", action:function(e) {
// e.preventDefault();
// ajaxVMDispatch({action:"domain-stop", uuid:uuid}, "loadlist");
@@ -212,7 +219,7 @@ function addVMSnapContext(name, uuid, template, state, snapshotname){
$('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
selectsnapshot(uuid, name, snapshotname, "revert",true) ;
}});
- opts.push({text:_("Remove snapshot(Delete Disabled)"), icon:"fa-trash", action:function(e) {
+ opts.push({text:_("Remove snapshot"), icon:"fa-trash", action:function(e) {
e.preventDefault();
$('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
selectsnapshot(uuid, name, snapshotname, "remove",true) ;
diff --git a/plugins/dynamix/DashStats.page b/plugins/dynamix/DashStats.page
index f556fe7a3..996ba3281 100644
--- a/plugins/dynamix/DashStats.page
+++ b/plugins/dynamix/DashStats.page
@@ -1124,8 +1124,8 @@ function sortTables() {
});
}
function addProperties() {
- $('tbody.system').addClass('sortable').attr('sort','_system_information_');
- $('tbody').not('.system').each(function(){
+ $('div.frame').find('tbody.system').addClass('sortable').attr('sort','_system_information_');
+ $('div.frame').find('tbody').not('.system').each(function(){
$(this).addClass('sortable').attr('sort',$(this).attr('title').marker());
$(this).find('td:first').prepend("");
});
@@ -1425,16 +1425,17 @@ $(function() {
+
From c7b88354db031f1c896f6f633f7936098a337db6 Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Wed, 24 May 2023 07:27:12 +0100
Subject: [PATCH 12/18] Make Script executable
---
.../dynamix.vm.manager/scripts/VMAjaxCall.php | 77 ++++++++++++-------
1 file changed, 51 insertions(+), 26 deletions(-)
mode change 100644 => 100755 plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
diff --git a/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php b/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
old mode 100644
new mode 100755
index 9e40bf79e..7fc64bf76
--- a/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
+++ b/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
@@ -12,31 +12,39 @@
?>
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
+
require_once "$docroot/webGui/include/Wrappers.php";
+
+// add translations
+$_SERVER['REQUEST_URI'] = '';
+$login_locale = _var($display,'locale');
+require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
require_once "$docroot/webGui/include/Translations.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
function write(...$messages){
- $com = curl_init();
- curl_setopt_array($com,[
- CURLOPT_URL => 'http://localhost/pub/plugins?buffer_length=1',
- CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
- CURLOPT_POST => 1,
- CURLOPT_RETURNTRANSFER => true
- ]);
- foreach ($messages as $message) {
- curl_setopt($com, CURLOPT_POSTFIELDS, $message);
- curl_exec($com);
+ $com = curl_init();
+ curl_setopt_array($com,[
+ CURLOPT_URL => 'http://localhost/pub/vmaction?buffer_length=1',
+ CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
+ CURLOPT_POST => 1,
+ CURLOPT_RETURNTRANSFER => true
+ ]);
+ foreach ($messages as $message) {
+ curl_setopt($com, CURLOPT_POSTFIELDS, $message);
+ curl_exec($com);
+ }
+ curl_close($com);
}
- curl_close($com);
-}
function execCommand_nchan($command) {
$waitID = mt_rand();
[$cmd,$args] = explode(' ',$command,2);
write("","addLog\0","show_Wait\0$waitID");
+ write("addToID\0 99\0 $action") ;
$proc = popen("$command 2>&1",'r');
while ($out = fgets($proc)) {
$out = preg_replace("%[\t\n\x0B\f\r]+%", '',$out);
- write("addLog\0".htmlspecialchars($out));
+ write("addToID\0 99\0".htmlspecialchars($out));
+ sleep(5) ;
}
$retval = pclose($proc);
$out = $retval ? _('The command failed').'.' : _('The command finished successfully').'!';
@@ -46,31 +54,48 @@ function execCommand_nchan($command) {
#{action:"snap-", uuid:uuid , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep, desc:desc}
$url = rawurldecode($argv[1]??'');
-$waitID = "bob" ;
+$waitID = mt_rand();
$style = ["";
-$path = "--current" ; $pivot = "yes" ; $action = " " ;
-write(implode($style)."");
-write("","addLog\0","show_Wait\0$waitID");
-
+$path = "--current" ; $pivot = "yes" ;
foreach (explode('&', $url) as $chunk) {
$param = explode("=", $chunk);
if ($param) {
-# write("addLog\0Parm" . sprintf("Value for parameter \"%s\" is \"%s\"
\n", urldecode($param[0]), urldecode($param[1])));
${urldecode($param[0])} = urldecode($param[1]) ;
}
}
+$id = 1 ;
+write(implode($style)."");
+write("addLog\0".htmlspecialchars("VMName $name "));
+write("addLog\0".htmlspecialchars("SNAP $snapshotname "));
+write("addLog\0".htmlspecialchars("Base $targetbase "));
+write("addLog\0".htmlspecialchars("Top $targettop "));
+ sleep(3) ;
+ write("stop_Wait\0$waitID") ;
+write("","addLog\0","show_Wait\0$waitID");
+
+
+ sleep(3) ;
+ write("stop_Wait\0$waitID") ;
+$path = "--current" ; $pivot = "yes" ;
+write("addToID\0 99\0 $action") ;
+switch ($action) {
+ case "commit":
+ # vm_blockcommit($name,$snapshotname,$path,$targetbase,$targettop,$pivot,' ') ;
+ break ;
+ case "copy":
+ # vm_blockcopy($name,$snapshotname,$path,$targetbase,$targettop,$pivot,' ') ;
+ break;
+ case "pull":
+ vm_blockpull($name,$snapshotname,$path,$targetbase,$targettop,$pivot,' ') ;
+ break ;
-write("VMName $name ");
-write("SNAP $snapshotname ");
-write("Base $targetbase ");
-write("Top $targettop ");
-$path = "--current" ; $pivot = "yes" ; $action = " " ;
-vm_blockcommit($name,$snapshotname,$path,$targetbase,$targettop,$pivot,$action) ;
-#execCommand_nchan("ls /root") ;
+ }
+#execCommand_nchan("ls /") ;
+write("stop_Wait\0$waitID") ;
write('_DONE_','');
?>
\ No newline at end of file
From 0eec9f2ad72482dd478392654f1d6bf36da36c6f Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Wed, 24 May 2023 07:44:29 +0100
Subject: [PATCH 13/18] WIP Update
---
plugins/dynamix.vm.manager/VMMachines.page | 4 +-
.../include/libvirt_helpers.php | 60 +++++++++++++++-
.../javascript/vmmanager.js | 5 ++
plugins/dynamix/include/DefaultPageLayout.php | 72 ++++++++++++++++++-
4 files changed, 137 insertions(+), 4 deletions(-)
diff --git a/plugins/dynamix.vm.manager/VMMachines.page b/plugins/dynamix.vm.manager/VMMachines.page
index 0290b97fc..9b06fe820 100644
--- a/plugins/dynamix.vm.manager/VMMachines.page
+++ b/plugins/dynamix.vm.manager/VMMachines.page
@@ -389,8 +389,8 @@ function selectblock(uuid, name ,snaps, opt, getlist,state){
var targetbase = $('#targetblockbase').val()
var targettop = $('#targetblocktop').val()
//ajaxVMDispatch({action:"snap-", uuid:uuid , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep, desc:desc} , "loadlist");
- Ajaxurl = "VMAjaxCall.php " + encodeURIComponent("/usr/local/emhttp/plugins/dynamix.vm.manager/include/VMajax.php&" + $.param({action:"snap-commit", name:name ,targetbase:targetbase, targettop:targettop , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep})) ;
- openPlugin((Ajaxurl),"Block Commit", "dynamix.vm.manager", "loadlist") ;
+ Ajaxurl = "VMAjaxCall.php " + encodeURIComponent("/usr/local/emhttp/plugins/dynamix.vm.manager/include/VMajax.php&" + $.param({action:opt , name:name ,targetbase:targetbase, targettop:targettop , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep})) ;
+ openVMAction((Ajaxurl),"Block Commit", "dynamix.vm.manager", "loadlist") ;
box.dialog('close');
},
"_(Cancel)_": function(){
diff --git a/plugins/dynamix.vm.manager/include/libvirt_helpers.php b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
index b19b97fe3..d9f08e203 100644
--- a/plugins/dynamix.vm.manager/include/libvirt_helpers.php
+++ b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
@@ -1939,7 +1939,8 @@ function vm_blockcopy($vm,$path,$base,$top,$pivot,$action) {
*/
}
-function vm_blockpull($vm,$path,$base,$top,$pivot,$action) {
+function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
+ global $lv ;
/*
NAME
blockpull - Populate a disk from its backing image.
@@ -1964,6 +1965,63 @@ function vm_blockpull($vm,$path,$base,$top,$pivot,$action) {
*/
+ $snapslist= getvmsnapshots($vm) ;
+ $disks =$lv->get_disk_stats($vm) ;
+ #var_dump($disks) ;
+ foreach($disks as $disk) {
+ $path = $disk['file'] ;
+ $cmdstr = "virsh blockpull '$vm' --path '$path' --verbose --pivot --delete" ;
+ $cmdstr = "virsh blockpull '$vm' --path '$path' --verbose --wait" ;
+ # Process disks and update path.
+ $snapdisks=($snapslist[$snap]['disks']) ;
+ if ($base != "--base" && $base != "") {
+ #get file name from snapshot.
+ $snapdisks=($snapslist[$base]['disks']) ;
+ $basepath = "" ;
+ foreach ($snapdisks as $snapdisk) {
+ $diskname = $snapdisk["@attributes"]["name"] ;
+ if ($diskname != $disk['device']) continue ;
+ $basepath = $snapdisk["source"]["@attributes"]["file"] ;
+ }
+ if ($basepath != "") $cmdstr .= " --base '$basepath' ";
+ }
+ if ($top != "--top" && $top !="") {
+ #get file name from snapshot.
+ $snapdisks=($snapslist[$top]['disks']) ;
+ $toppath = "" ;
+ foreach ($snapdisks as $snapdisk) {
+ $diskname = $snapdisk["@attributes"]["name"] ;
+ if ($diskname != $disk['device']) continue ;
+ $toppath = $snapdisk["source"]["@attributes"]["file"] ;
+ }
+ if ($toppath != "") $cmdstr .= " --top '$toppath' ";
+ }
+ if ($action) $cmdstr .= " $action ";
+
+
+ $test = false ;
+ if ($test) { $cmdstr .= " --print-xml " ; execCommand_nchan($cmdstr) ; } else execCommand_nchan($cmdstr) ;
+ #var_dump($cmdstr,$output) ;
+ if (strpos(" ".$output[0],"error") ) {
+ $arrResponse = ['error' => substr($output[0],6) ] ;
+ #return($arrResponse) ;
+ } else {
+ # Remove nvram snapshot
+ $arrResponse = ['success' => true] ;
+ }
+ #Error Check
+ }
+ #If complete ok remove meta data for snapshots.
+ #if (!$test) $ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
+ if($ret)
+ $data = ["error" => "Unable to remove snap metadata $snap"] ;
+ else
+ $data = ["success => 'true"] ;
+
+ return $data ;
+
}
+
+
?>
diff --git a/plugins/dynamix.vm.manager/javascript/vmmanager.js b/plugins/dynamix.vm.manager/javascript/vmmanager.js
index 940afe673..133dcbf35 100644
--- a/plugins/dynamix.vm.manager/javascript/vmmanager.js
+++ b/plugins/dynamix.vm.manager/javascript/vmmanager.js
@@ -209,6 +209,11 @@ function addVMSnapContext(name, uuid, template, state, snapshotname){
e.preventDefault();
selectblock(uuid, name, snapshotname, "commit",true) ;
}});
+ opts.push({text:_("Block Pull"), icon:"fa-hdd-o", action:function(e) {
+ $('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
+ e.preventDefault();
+ selectblock(uuid, name, snapshotname, "pull",true) ;
+ }});
// opts.push({text:_("Block Copy"), icon:"fa-stop", action:function(e) {
// e.preventDefault();
// ajaxVMDispatch({action:"domain-stop", uuid:uuid}, "loadlist");
diff --git a/plugins/dynamix/include/DefaultPageLayout.php b/plugins/dynamix/include/DefaultPageLayout.php
index 9d236569c..80aedd800 100644
--- a/plugins/dynamix/include/DefaultPageLayout.php
+++ b/plugins/dynamix/include/DefaultPageLayout.php
@@ -342,6 +342,29 @@ function openDocker(cmd,title,plg,func,start=0,button=0) {
$('button.confirm').prop('disabled',button==0);
});
}
+function openVMAction(cmd,title,plg,func,start=0,button=0) {
+ // start = 0 : run command only when not already running (default)
+ // start = 1 : run command unconditionally
+ // button = 0 : hide CLOSE button (default)
+ // button = 1 : show CLOSE button
+ nchan_vmaction.start();
+ $.post('/webGui/include/StartCommand.php',{cmd:cmd,start:start},function(pid) {
+ if (pid==0) {
+ nchan_docker.stop();
+ $('div.spinner.fixed').hide();
+ $(".upgrade_notice").addClass('alert');
+ return;
+ }
+ swal({title:title,text:"
",html:true,animation:'none',showConfirmButton:button!=0,confirmButtonText:"=_('Close')?>"},function(close){
+ nchan_vmaction.stop();
+ $('div.spinner.fixed').hide();
+ $('.sweet-alert').hide('fast').removeClass('nchan');
+ setTimeout(function(){bannerAlert("=_('Attention - operation continues in background')?> ["+pid.toString().padStart(8,'0')+"]\" onclick='abortOperation("+pid+")'>",cmd,plg,func,start);});
+ });
+ $('.sweet-alert').addClass('nchan');
+ $('button.confirm').prop('disabled',button==0);
+ });
+}
function abortOperation(pid) {
swal({title:"=_('Abort background operation')?>",text:"=_('This may leave an unknown state')?>",html:true,animation:'none',type:'warning',showCancelButton:true,confirmButtonText:"=_('Proceed')?>",cancelButtonText:"=_('Cancel')?>"},function(){
$.post('/webGui/include/StartCommand.php',{kill:pid},function() {
@@ -915,7 +938,54 @@ function parseINI(data){
}
box.scrollTop(box[0].scrollHeight);
});
-
+var nchan_vmaction = new NchanSubscriber('/sub/vmaction',{subscriber:'websocket'});
+nchan_vmaction.on('message', function(data) {
+ if (!data || openDone(data)) return;
+ var box = $('pre#swaltext');
+ data = data.split('\0');
+ switch (data[0]) {
+ case 'addLog':
+ var rows = document.getElementsByClassName('logLine');
+ if (rows.length) {
+ var row = rows[rows.length-1];
+ row.innerHTML += data[1]+'
';
+ }
+ break;
+ case 'progress':
+ var rows = document.getElementsByClassName('progress-'+data[1]);
+ if (rows.length) {
+ rows[rows.length-1].textContent = data[2];
+ }
+ break;
+ case 'addToID':
+ var rows = document.getElementById(data[1]);
+ if (rows === null) {
+ rows = document.getElementsByClassName('logLine');
+ if (rows.length) {
+ var row = rows[rows.length-1];
+ row.innerHTML += 'VM ID ['+data[1]+']: '+data[2]+'.
';
+ }
+ } else {
+ var rows_content = rows.getElementsByClassName('content');
+ if (!rows_content.length || rows_content[rows_content.length-1].textContent != data[2]) {
+ rows.innerHTML += ''+data[2]+'.';
+ }
+ }
+ break;
+ case 'show_Wait':
+ progress_span[data[1]] = document.getElementById('wait-'+data[1]);
+ progress_dots[data[1]] = setInterval(function(){if (((progress_span[data[1]].innerHTML += '.').match(/\./g)||[]).length > 9) progress_span[data[1]].innerHTML = progress_span[data[1]].innerHTML.replace(/\.+$/,'');},500);
+ break;
+ case 'stop_Wait':
+ clearInterval(progress_dots[data[1]]);
+ progress_span[data[1]].innerHTML = '';
+ break;
+ default:
+ box.html(box.html()+data[0]);
+ break;
+ }
+ box.scrollTop(box[0].scrollHeight);
+});
var backtotopoffset = 250;
var backtotopduration = 500;
$(window).scroll(function() {
From ea5cc331356ef75e813990dca3b442903167a432 Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Sun, 28 May 2023 18:46:51 +0100
Subject: [PATCH 14/18] WIP Update
---
plugins/dynamix.vm.manager/VMMachines.page | 30 ++--
.../dynamix.vm.manager/include/VMMachines.php | 5 +-
plugins/dynamix.vm.manager/include/VMajax.php | 6 -
.../include/libvirt_helpers.php | 146 +++++++++---------
.../javascript/vmmanager.js | 54 ++-----
plugins/dynamix/include/DefaultPageLayout.php | 4 +-
6 files changed, 118 insertions(+), 127 deletions(-)
diff --git a/plugins/dynamix.vm.manager/VMMachines.page b/plugins/dynamix.vm.manager/VMMachines.page
index 9b06fe820..6a76fbd76 100644
--- a/plugins/dynamix.vm.manager/VMMachines.page
+++ b/plugins/dynamix.vm.manager/VMMachines.page
@@ -196,8 +196,7 @@ function getisoimageboth(uuid,dev,bus,file,dev2,bus2,file2){
"_(Update)_": function(){
var target = box.find('#target');
if (target.length) {
- target = target.val();
-
+ target = target.val();
} else target = '';
var target2 = box.find('#target2');
if (target2.length) {
@@ -359,6 +358,15 @@ function selectblock(uuid, name ,snaps, opt, getlist,state){
document.getElementById("targetsnaprmvmeta").checked = true ;
document.getElementById("targetsnapkeep").checked = true ;
document.getElementById("targetsnapfspc").checked = true ;
+ if (opt== "pull") {
+ $('.toprow').hide();
+ $('.targetpivotrow').hide();
+ $('.targetdeleterow').hide();
+ } else {
+ $('.toprow').show();
+ $('.targetpivotrow').show();
+ $('.targetdeleterow').show();
+ }
box.dialog({
title: "_("+optiontext+ ")_",
@@ -379,6 +387,8 @@ function selectblock(uuid, name ,snaps, opt, getlist,state){
var keep = 'yes'
var removemeta = 'yes'
var free = 'yes'
+ var delete_file = 'yes'
+ var pivot = 'yes'
var desc = ''
box.find('#targetsnap').prop('disabled',true);
if (opt == "create") {
@@ -388,7 +398,10 @@ function selectblock(uuid, name ,snaps, opt, getlist,state){
}
var targetbase = $('#targetblockbase').val()
var targettop = $('#targetblocktop').val()
- //ajaxVMDispatch({action:"snap-", uuid:uuid , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep, desc:desc} , "loadlist");
+ x = box.find('#targetpivot').prop('checked') ;
+ if (x) pivot = 'yes' ; else pivot = 'no' ;
+ x = box.find('#targetdelete').prop('checked') ;
+ if (x) delete_file = 'yes' ; else delete_file = 'no' ;
Ajaxurl = "VMAjaxCall.php " + encodeURIComponent("/usr/local/emhttp/plugins/dynamix.vm.manager/include/VMajax.php&" + $.param({action:opt , name:name ,targetbase:targetbase, targettop:targettop , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep})) ;
openVMAction((Ajaxurl),"Block Commit", "dynamix.vm.manager", "loadlist") ;
box.dialog('close');
@@ -401,9 +414,6 @@ function selectblock(uuid, name ,snaps, opt, getlist,state){
dialogStyle();
}
-// ajaxVMDispatch({action:"snap-create-external", uuid:uuid}, "loadlist");
- // ajaxVMDispatch({action:"snap-revert-external", uuid:uuid , snapshotname:snapshotname , remove:'yes'}, "loadlist");
-
function dialogStyle() {
$('.ui-dialog-titlebar-close').css({'background':'transparent','border':'none','font-size':'1.8rem','margin-top':'-14px','margin-right':'-18px'}).html('').prop('title');
$('.ui-dialog-title').css({'text-align':'center','width':'100%','font-size':'1.8rem'});
@@ -571,13 +581,15 @@ _(Snapshot Name)_:
_(Base Image)_: |
|
-_(Top Image )_: |
+ |
_(Top Image )_: |
|
+ |
_(Pivot)_: |
+ |
+_(Delete)_: |
+ |
-
-
\ No newline at end of file
diff --git a/plugins/dynamix.vm.manager/include/VMMachines.php b/plugins/dynamix.vm.manager/include/VMMachines.php
index d269f27a8..68c075827 100644
--- a/plugins/dynamix.vm.manager/include/VMMachines.php
+++ b/plugins/dynamix.vm.manager/include/VMMachines.php
@@ -21,6 +21,7 @@
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
$user_prefs = '/boot/config/plugins/dynamix.vm.manager/userprefs.cfg';
+if (file_exists('/boot/config/plugins/dynamix.vm.manager/vmpreview')) $vmpreview = true ; else $vmpreview = false ;
$vms = $lv->get_domains();
if (empty($vms)) {
echo '
'._('No Virtual Machines installed').' |
';
@@ -103,7 +104,7 @@
}
unset($dom);
if (!isset($domain_cfg["CONSOLE"])) $vmrcconsole = "web" ; else $vmrcconsole = $domain_cfg["CONSOLE"] ;
- $menu = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,addslashes($vmrcurl),strtoupper($vmrcprotocol),addslashes($log), $vmrcconsole);
+ $menu = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,addslashes($vmrcurl),strtoupper($vmrcprotocol),addslashes($log), $vmrcconsole,$vmpreview);
$kvm[] = "kvm.push({id:'$uuid',state:'$state'});";
switch ($state) {
case 'running':
@@ -254,7 +255,7 @@
$snapshotmemory = _(ucfirst($snapshot["memory"]["@attributes"]["snapshot"])) ;
$snapshotparent = $snapshot["parent"]["name"] ? $snapshot["parent"]["name"] : "None";
$snapshotdatetime = my_time($snapshot["creationtime"],"Y-m-d" )."
".my_time($snapshot["creationtime"],"H:i:s") ;
- $snapmenu = sprintf("onclick=\"addVMSnapContext('%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,$snapshot["name"]);
+ $snapmenu = sprintf("onclick=\"addVMSnapContext('%s','%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,$snapshot["name"],$vmpreview);
echo "
$tab|__ ".$snapshot["name"]." | $snapshotdesc | $snapshotdatetime | $snapshotstate | $snapshotparent | $snapshotmemory |
";
$tab .=" " ;
}
diff --git a/plugins/dynamix.vm.manager/include/VMajax.php b/plugins/dynamix.vm.manager/include/VMajax.php
index 5c1fa042a..b57025ace 100644
--- a/plugins/dynamix.vm.manager/include/VMajax.php
+++ b/plugins/dynamix.vm.manager/include/VMajax.php
@@ -339,12 +339,6 @@ function embed(&$syslinux, $key, $value) {
$arrResponse = vm_snapshot($domName,$_REQUEST['snapshotname'],$_REQUEST['desc'],$_REQUEST['free']) ;
break;
-case 'snap-blockcommit':
- requireLibvirt();
- echo "Hello" ;
- #$arrResponse = vm_snapshot($domName,'',$_REQUEST['base'],$_REQUEST['top'],"","") ;
- break;
-
case 'snap-images':
requireLibvirt();
$html = vm_snapimages($domName,$_REQUEST['snapshotname'],$_REQUEST['only']) ;
diff --git a/plugins/dynamix.vm.manager/include/libvirt_helpers.php b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
index d9f08e203..4d326c0a1 100644
--- a/plugins/dynamix.vm.manager/include/libvirt_helpers.php
+++ b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
@@ -1580,9 +1580,9 @@ function vm_revert($vm, $snap="--current",$action="no",$actionmeta = 'yes') {
$capacity = $capacity + $disk["capacity"] ;
}
- switch ($snapslist[$snap]['state']) {
+ switch ($snapslist[$snap]['state']) {
case "shutoff":
- case "running":
+ case "running":
#VM must be shutdown.
$res = $lv->get_domain_by_name($vm);
$dom = $lv->domain_get_info($res);
@@ -1650,7 +1650,7 @@ function vm_revert($vm, $snap="--current",$action="no",$actionmeta = 'yes') {
# Remove running XML and memory.
$xmlfile = $primarypath."/$name.running" ;
$memoryfile = $primarypath."/memory$name.mem" ;
- #var_dump(is_file($xmlfile), is_file($memoryfile)) ;
+
if (is_file($memoryfile) && $action == "yes") unlink($memoryfile) ;
if (is_file($xmlfile) && $action == "yes") unlink($xmlfile) ;
if ($s['name'] == $snap) break ;
@@ -1778,14 +1778,13 @@ function vm_snapremove($vm, $snap) {
$xmlobj = custom::createArray('domain',$strXML) ;
# Process disks.
- $disks=($snapslist[$snap]['disks']) ;
- var_dump($disks,$snaps) ;
+ $disks=($snapslist[$snap]['disks']) ;
foreach ($disks as $disk) {
$diskname = $disk["@attributes"]["name"] ;
if ($diskname == "hda" || $diskname == "hdb") continue ;
$path = $disk["source"]["@attributes"]["file"] ;
$item = array_search($path,$snaps[$vm][$diskname]) ;
- if (!$item) {
+ if ($item!==false) {
$data = ["error" => "Image currently active for this domain."] ;
return ($data) ;
}
@@ -1797,15 +1796,16 @@ function vm_snapremove($vm, $snap) {
if ($diskname == "hda" || $diskname == "hdb") continue ;
$path = $disk["source"]["@attributes"]["file"] ;
if (is_file($path)) {
- #if(!unlink("$path")) {
- # $data = ["error" => "Unable to remove image file $path"] ;
- # return ($data) ;
- #}
+ if(!unlink("$path")) {
+ $data = ["error" => "Unable to remove image file $path"] ;
+ return ($data) ;
+ }
}
}
- #$ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
- if($ret)
+ $ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
+ #var_dump($ret) ;
+ if(!$ret)
$data = ["error" => "Unable to remove snap metadata $snap"] ;
else
$data = ["success => 'true"] ;
@@ -1879,13 +1879,14 @@ function vm_blockcommit($vm, $snap ,$path,$base,$top,$pivot,$action) {
}
if ($action) $cmdstr .= " $action ";
-
$test = false ;
- if ($test) { $cmdstr .= " --print-xml " ; execCommand_nchan($cmdstr) ; } else execCommand_nchan($cmdstr) ;
+ if ($test) $cmdstr .= " --print-xml " ;
+
+ $error = execCommand_nchan($cmdstr,$path) ;
#var_dump($cmdstr,$output) ;
- if (strpos(" ".$output[0],"error") ) {
+ if (!$error) {
$arrResponse = ['error' => substr($output[0],6) ] ;
- #return($arrResponse) ;
+ return($arrResponse) ;
} else {
# Remove nvram snapshot
$arrResponse = ['success' => true] ;
@@ -1903,42 +1904,6 @@ function vm_blockcommit($vm, $snap ,$path,$base,$top,$pivot,$action) {
}
-function vm_blockcopy($vm,$path,$base,$top,$pivot,$action) {
- /*
- NAME
- blockcopy - Start a block copy operation.
-
- SYNOPSIS
- blockcopy
[--dest ] [--bandwidth ] [--shallow] [--reuse-external] [--blockdev] [--wait] [--verbose] [--timeout ] [--pivot] [--finish] [--async] [--xml ] [--format ] [--granularity ] [--buf-size ] [--bytes] [--transient-job] [--synchronous-writes] [--print-xml]
-
- DESCRIPTION
- Copy a disk backing image chain to dest.
-
- OPTIONS
- [--domain] domain name, id or uuid
- [--path] fully-qualified path of source disk
- --dest path of the copy to create
- --bandwidth bandwidth limit in MiB/s
- --shallow make the copy share a backing chain
- --reuse-external reuse existing destination
- --blockdev copy destination is block device instead of regular file
- --wait wait for job to reach mirroring phase
- --verbose with --wait, display the progress
- --timeout implies --wait, abort if copy exceeds timeout (in seconds)
- --pivot implies --wait, pivot when mirroring starts
- --finish implies --wait, quit when mirroring starts
- --async with --wait, don't wait for cancel to finish
- --xml filename containing XML description of the copy destination
- --format format of the destination file
- --granularity power-of-two granularity to use during the copy
- --buf-size maximum amount of in-flight data during the copy
- --bytes the bandwidth limit is in bytes/s rather than MiB/s
- --transient-job the copy job is not persisted if VM is turned off
- --synchronous-writes the copy job forces guest writes to be synchronously written to the destination
- --print-xml print the XML used to start the copy job instead of starting the job
- */
-}
-
function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
global $lv ;
/*
@@ -1967,11 +1932,26 @@ function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
*/
$snapslist= getvmsnapshots($vm) ;
$disks =$lv->get_disk_stats($vm) ;
+ foreach($disks as $disk) {
+ $file = $disk["file"] ;
+ $output = "" ;
+ exec("qemu-img info --backing-chain -U '$file' | grep image:",$output) ;
+ foreach($output as $key => $line) {
+ $line=str_replace("image: ","",$line) ;
+ $output[$key] = $line ;
+ }
+ $snaps[$vm][$disk["device"]] = $output ;
+ $rev = "r".$disk["device"] ;
+ $reversed = array_reverse($output) ;
+ $snaps[$vm][$rev] = $reversed ;
+ }
+ $snaps_json=json_encode($snaps) ;
+ file_put_contents("/tmp/snaps",$snaps_json) ;
#var_dump($disks) ;
foreach($disks as $disk) {
$path = $disk['file'] ;
$cmdstr = "virsh blockpull '$vm' --path '$path' --verbose --pivot --delete" ;
- $cmdstr = "virsh blockpull '$vm' --path '$path' --verbose --wait" ;
+ $cmdstr = "virsh blockpull '$vm' --path '$path' --verbose --wait " ;
# Process disks and update path.
$snapdisks=($snapslist[$snap]['disks']) ;
if ($base != "--base" && $base != "") {
@@ -1985,26 +1965,17 @@ function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
}
if ($basepath != "") $cmdstr .= " --base '$basepath' ";
}
- if ($top != "--top" && $top !="") {
- #get file name from snapshot.
- $snapdisks=($snapslist[$top]['disks']) ;
- $toppath = "" ;
- foreach ($snapdisks as $snapdisk) {
- $diskname = $snapdisk["@attributes"]["name"] ;
- if ($diskname != $disk['device']) continue ;
- $toppath = $snapdisk["source"]["@attributes"]["file"] ;
- }
- if ($toppath != "") $cmdstr .= " --top '$toppath' ";
- }
- if ($action) $cmdstr .= " $action ";
+ if ($action) $cmdstr .= " $action ";
$test = false ;
- if ($test) { $cmdstr .= " --print-xml " ; execCommand_nchan($cmdstr) ; } else execCommand_nchan($cmdstr) ;
- #var_dump($cmdstr,$output) ;
- if (strpos(" ".$output[0],"error") ) {
+ if ($test) $cmdstr .= " --print-xml " ;
+
+ $error = execCommand_nchan($cmdstr,$path) ;
+
+ if (!$error) {
$arrResponse = ['error' => substr($output[0],6) ] ;
- #return($arrResponse) ;
+ return($arrResponse) ;
} else {
# Remove nvram snapshot
$arrResponse = ['success' => true] ;
@@ -2012,7 +1983,7 @@ function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
#Error Check
}
#If complete ok remove meta data for snapshots.
- #if (!$test) $ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
+ $ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
if($ret)
$data = ["error" => "Unable to remove snap metadata $snap"] ;
else
@@ -2022,6 +1993,41 @@ function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
}
+function vm_blockcopy($vm,$path,$base,$top,$pivot,$action) {
+ /*
+ NAME
+ blockcopy - Start a block copy operation.
+
+ SYNOPSIS
+ blockcopy [--dest ] [--bandwidth ] [--shallow] [--reuse-external] [--blockdev] [--wait] [--verbose] [--timeout ] [--pivot] [--finish] [--async] [--xml ] [--format ] [--granularity ] [--buf-size ] [--bytes] [--transient-job] [--synchronous-writes] [--print-xml]
+
+ DESCRIPTION
+ Copy a disk backing image chain to dest.
+
+ OPTIONS
+ [--domain] domain name, id or uuid
+ [--path] fully-qualified path of source disk
+ --dest path of the copy to create
+ --bandwidth bandwidth limit in MiB/s
+ --shallow make the copy share a backing chain
+ --reuse-external reuse existing destination
+ --blockdev copy destination is block device instead of regular file
+ --wait wait for job to reach mirroring phase
+ --verbose with --wait, display the progress
+ --timeout implies --wait, abort if copy exceeds timeout (in seconds)
+ --pivot implies --wait, pivot when mirroring starts
+ --finish implies --wait, quit when mirroring starts
+ --async with --wait, don't wait for cancel to finish
+ --xml filename containing XML description of the copy destination
+ --format format of the destination file
+ --granularity power-of-two granularity to use during the copy
+ --buf-size maximum amount of in-flight data during the copy
+ --bytes the bandwidth limit is in bytes/s rather than MiB/s
+ --transient-job the copy job is not persisted if VM is turned off
+ --synchronous-writes the copy job forces guest writes to be synchronously written to the destination
+ --print-xml print the XML used to start the copy job instead of starting the job
+ */
+}
?>
diff --git a/plugins/dynamix.vm.manager/javascript/vmmanager.js b/plugins/dynamix.vm.manager/javascript/vmmanager.js
index 133dcbf35..41fe241bb 100644
--- a/plugins/dynamix.vm.manager/javascript/vmmanager.js
+++ b/plugins/dynamix.vm.manager/javascript/vmmanager.js
@@ -62,7 +62,7 @@ function ajaxVMDispatchconsoleRV(params, spin){
}
},'json');
}
-function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, console="web"){
+function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, console="web", preview=false){
var opts = [];
var path = location.pathname;
var x = path.indexOf("?");
@@ -107,10 +107,11 @@ function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, c
ajaxVMDispatch( {action:"domain-destroy", uuid:uuid}, "loadlist");
}});
opts.push({divider:true});
+ if (preview) {
opts.push({text:_("Create Snapshot"), icon:"fa-clone", action:function(e) {
e.preventDefault();
selectsnapshot(uuid , name, "--generate" , "create",false,state) ;
- }});
+ }}); }
} else if (state == "pmsuspended") {
opts.push({text:_("Resume"), icon:"fa-play", action:function(e) {
e.preventDefault();
@@ -192,7 +193,7 @@ function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, c
}
context.attach('#vm-'+uuid, opts);
}
-function addVMSnapContext(name, uuid, template, state, snapshotname){
+function addVMSnapContext(name, uuid, template, state, snapshotname, preview=false){
var opts = [];
var path = location.pathname;
var x = path.indexOf("?");
@@ -200,24 +201,28 @@ function addVMSnapContext(name, uuid, template, state, snapshotname){
context.settings({right:false,above:false});
if (state == "running") {
-// opts.push({text:_("Revert snapshot"), icon:"fa-stop", action:function(e) {
-// e.preventDefault();
-// ajaxVMDispatch({action:"snapshot-revert-externa", uuid:uuid, snapshotname:snapshotname}, "loadlist");
-// }});
+ if (preview) {
+ opts.push({text:_("Revert snapshot"), icon:"fa-stop", action:function(e) {
+ e.preventDefault();
+ ajaxVMDispatch({action:"snapshot-revert-externa", uuid:uuid, snapshotname:snapshotname}, "loadlist");
+ }});
+ }
opts.push({text:_("Block Commit"), icon:"fa-hdd-o", action:function(e) {
$('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
e.preventDefault();
selectblock(uuid, name, snapshotname, "commit",true) ;
}});
+ if (preview) {
opts.push({text:_("Block Pull"), icon:"fa-hdd-o", action:function(e) {
$('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
e.preventDefault();
selectblock(uuid, name, snapshotname, "pull",true) ;
}});
-// opts.push({text:_("Block Copy"), icon:"fa-stop", action:function(e) {
-// e.preventDefault();
-// ajaxVMDispatch({action:"domain-stop", uuid:uuid}, "loadlist");
-// }});
+
+ opts.push({text:_("Block Copy"), icon:"fa-stop", action:function(e) {
+ e.preventDefault();
+ ajaxVMDispatch({action:"domain-stop", uuid:uuid}, "loadlist");
+ }}); }
} else {
opts.push({text:_("Revert snapshot"), icon:"fa-fast-backward", action:function(e) {
e.preventDefault();
@@ -229,34 +234,7 @@ function addVMSnapContext(name, uuid, template, state, snapshotname){
$('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
selectsnapshot(uuid, name, snapshotname, "remove",true) ;
}});
-// opts.push({text:_("Block Commit"), icon:"fa-stop", action:function(e) {
-// e.preventDefault();
-// ajaxVMDispatch({action:"domain-stop", uuid:uuid}, "loadlist");
-// }});
-// opts.push({text:_("Block Copy"), icon:"fa-stop", action:function(e) {
-// e.preventDefault();
-// ajaxVMDispatch({action:"domain-stop", uuid:uuid}, "loadlist");
-// }});
}
-
- if (state == "shutoff") {
- opts.push({divider:true});
-// opts.push({text:_("Remove Snapshot"), icon:"fa-minus", action:function(e) {
-// e.preventDefault();
-// snapname = "test" ;
-// swal({
-// title:_("Are you sure?"),
-// text:_("Remove Snapshot:") + snapname + _("\nfor VM: ") + name +"\n Note all snapshots taken after will be become invalid.",
-// type:"warning",
-// showCancelButton:true,
-// confirmButtonText:_('Proceed'),
-// cancelButtonText:_('Cancel')
-// },function(){
-// $('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
-// ajaxVMDispatch({action:"snap-remove-external", uuid:uuid,snapshotname:snapshotname}, "loadlist");
-// });
-// }});
- }
context.attach('#vmsnap-'+uuid, opts);
}
function startAll() {
diff --git a/plugins/dynamix/include/DefaultPageLayout.php b/plugins/dynamix/include/DefaultPageLayout.php
index 80aedd800..53aaf4205 100644
--- a/plugins/dynamix/include/DefaultPageLayout.php
+++ b/plugins/dynamix/include/DefaultPageLayout.php
@@ -350,7 +350,7 @@ function openVMAction(cmd,title,plg,func,start=0,button=0) {
nchan_vmaction.start();
$.post('/webGui/include/StartCommand.php',{cmd:cmd,start:start},function(pid) {
if (pid==0) {
- nchan_docker.stop();
+ nchan_vmaction.stop();
$('div.spinner.fixed').hide();
$(".upgrade_notice").addClass('alert');
return;
@@ -963,7 +963,7 @@ function parseINI(data){
rows = document.getElementsByClassName('logLine');
if (rows.length) {
var row = rows[rows.length-1];
- row.innerHTML += 'VM ID ['+data[1]+']: '+data[2]+'.
';
+ row.innerHTML += ''+data[1]+': '+data[2]+'.
';
}
} else {
var rows_content = rows.getElementsByClassName('content');
From 67f67430601af0ca1b9e243804760334feb59251 Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Mon, 29 May 2023 17:07:04 +0100
Subject: [PATCH 15/18] WIP Update
Remove using snapshot metadata and create own record.
---
plugins/dynamix.vm.manager/VMMachines.page | 9 +-
plugins/dynamix.vm.manager/include/VMajax.php | 4 +-
.../include/libvirt_helpers.php | 161 ++++++++++++------
.../javascript/vmmanager.js | 12 +-
.../dynamix.vm.manager/scripts/VMAjaxCall.php | 35 ++--
5 files changed, 137 insertions(+), 84 deletions(-)
diff --git a/plugins/dynamix.vm.manager/VMMachines.page b/plugins/dynamix.vm.manager/VMMachines.page
index 6a76fbd76..6d32b5643 100644
--- a/plugins/dynamix.vm.manager/VMMachines.page
+++ b/plugins/dynamix.vm.manager/VMMachines.page
@@ -396,13 +396,14 @@ function selectblock(uuid, name ,snaps, opt, getlist,state){
if (x) free = 'yes' ; else free = 'no' ;
var desc = box.find("#targetsnapdesc").prop('value') ;
}
- var targetbase = $('#targetblockbase').val()
- var targettop = $('#targetblocktop').val()
+ var targetbase = box.find("#targetblockbase").prop('value') ;
+ var targettop = box.find("#targetblocktop").prop('value') ;
+
x = box.find('#targetpivot').prop('checked') ;
if (x) pivot = 'yes' ; else pivot = 'no' ;
x = box.find('#targetdelete').prop('checked') ;
if (x) delete_file = 'yes' ; else delete_file = 'no' ;
- Ajaxurl = "VMAjaxCall.php " + encodeURIComponent("/usr/local/emhttp/plugins/dynamix.vm.manager/include/VMajax.php&" + $.param({action:opt , name:name ,targetbase:targetbase, targettop:targettop , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep})) ;
+ Ajaxurl = "VMAjaxCall.php " + encodeURIComponent("/usr/local/emhttp/plugins/dynamix.vm.manager/include/VMajax.php&" + $.param({action:opt , name:name ,targetbase:targetbase, targettop:targettop , snapshotname:target , remove:remove, targetpivot:pivot ,removemeta:removemeta ,targetdelete:delete_file})) ;
openVMAction((Ajaxurl),"Block Commit", "dynamix.vm.manager", "loadlist") ;
box.dialog('close');
},
@@ -579,7 +580,7 @@ _(Snapshot Name)_:
_(Base Image)_: |
- |
_(Top Image )_: |
diff --git a/plugins/dynamix.vm.manager/include/VMajax.php b/plugins/dynamix.vm.manager/include/VMajax.php
index b57025ace..ed28dffe0 100644
--- a/plugins/dynamix.vm.manager/include/VMajax.php
+++ b/plugins/dynamix.vm.manager/include/VMajax.php
@@ -121,6 +121,7 @@ function embed(&$syslinux, $key, $value) {
$vvarray[] = "type=$protocol\n";
$vvarray[] = "host="._var($_SERVER,'HTTP_HOST')."\n" ;
$vvarray[] = "port=$port\n" ;
+ $vvarray[] = "delete-this-file=1\n" ;
if (!is_dir("/mnt/user/system/remoteviewer")) mkdir("/mnt/user/system/remoteviewer") ;
$vvfile = "/mnt/user/system/remoteviewer/rv"._var($_SERVER,'HTTP_HOST').".$port.vv" ;
file_put_contents($vvfile,$vvarray) ;
@@ -139,6 +140,7 @@ function embed(&$syslinux, $key, $value) {
$vvarray[] = "type=$protocol\n";
$vvarray[] = "host="._var($_SERVER,'HTTP_HOST')."\n" ;
$vvarray[] = "port=$port\n" ;
+ $vvarray[] = "delete-this-file=1\n" ;
if (!is_dir("/mnt/user/system/remoteviewer")) mkdir("/mnt/user/system/remoteviewer") ;
$vvfile = "/mnt/user/system/remoteviewer/rv"._var($_SERVER,'HTTP_HOST').".$port.vv" ;
file_put_contents($vvfile,$vvarray) ;
@@ -353,7 +355,7 @@ function embed(&$syslinux, $key, $value) {
$datartn = "";
foreach($data as $snap=>$snapdetail) {
$snapshotdatetime = date("Y-m-d H:i:s",$snapdetail["creationtime"]) ;
- $datartn .= "" ;
+ $datartn .= "" ;
}
$arrResponse['html'] = $datartn ;
break;
diff --git a/plugins/dynamix.vm.manager/include/libvirt_helpers.php b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
index 4d326c0a1..721f4fbc2 100644
--- a/plugins/dynamix.vm.manager/include/libvirt_helpers.php
+++ b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
@@ -1466,6 +1466,15 @@ function compare_creationtimelt($a, $b) {
}
function getvmsnapshots($vm) {
+ $snaps=array() ;
+
+ $dbpath = "/etc/libvirt/qemu/snapshot/$vm" ;
+ $snaps_json = file_get_contents($dbpath."/snapshots.db") ;
+ $snaps = json_decode($snaps_json,true) ;
+ if (is_array($snaps)) uasort($snaps,'compare_creationtime') ;
+ return $snaps ;
+ }
+ function getvmsnapshots2($vm) {
global $lv ;
$vmsnaps = $lv->domain_snapshots_list($lv->get_domain_object($vm)) ;
$snaps=array() ;
@@ -1489,6 +1498,47 @@ function getvmsnapshots($vm) {
if (is_array($snaps)) uasort($snaps,'compare_creationtime') ;
return $snaps ;
}
+ function write_snapshots_database($vm,$name) {
+ global $lv ;
+ $dbpath = "/etc/libvirt/qemu/snapshot/$vm" ;
+ #var_dump($dbpath) ;
+ if (!is_dir($dbpath)) mkdir($dbpath) ;
+ $snaps_json = file_get_contents($dbpath."/snapshots.db") ;
+ $snaps = json_decode($snaps_json,true) ;
+ #var_dump($snaps_json) ;
+ $snapshot_res=$lv->domain_snapshot_lookup_by_name($vm,$name) ;
+ $snapshot_xml=$lv->domain_snapshot_get_xml($snapshot_res) ;
+ $a = simplexml_load_string($snapshot_xml) ;
+ #if($a == false) continue ;
+ $a = json_encode($a) ;
+
+ $b= json_decode($a, TRUE);
+ $vmsnap = $b["name"] ;
+ $snaps[$vmsnap]["name"]= $b["name"];
+ if(isset($b["parent"])) $snaps[$vmsnap]["parent"]= $b["parent"]; else $snaps[$vmsnap]["parent"]["name"] = "Base" ;
+ $snaps[$vmsnap]["state"]= $b["state"];
+ $snaps[$vmsnap]["desc"]= $b["description"];
+ $snaps[$vmsnap]["memory"]= $b["memory"];
+ $snaps[$vmsnap]["creationtime"]= $b["creationTime"];
+ if (array_key_exists(0 , $b["disks"]["disk"])) $snaps[$vmsnap]["disks"]= $b["disks"]["disk"]; else $snaps[$vmsnap]["disks"][0]= $b["disks"]["disk"];
+
+ $value = json_encode($snaps,JSON_PRETTY_PRINT) ;
+ #var_dump($value) ;
+ file_put_contents($dbpath."/snapshots.db",$value) ;
+ }
+
+ function delete_snapshots_database($vm,$name) {
+ global $lv ;
+ $dbpath = "/etc/libvirt/qemu/snapshot/$vm" ;
+ $snaps_json = file_get_contents($dbpath."/snapshots.db") ;
+ $snaps = json_decode($snaps_json,true) ;
+ unset($snaps[$name]) ;
+ $value = json_encode($snaps,JSON_PRETTY_PRINT) ;
+ #var_dump($value) ;
+ file_put_contents($dbpath."/snapshots.db",$value) ;
+ return true ;
+ }
+
function vm_snapshot($vm,$snapshotname, $snapshotdesc, $free = "yes", $memorysnap = "yes") {
global $lv ;
@@ -1512,7 +1562,7 @@ function vm_snapshot($vm,$snapshotname, $snapshotdesc, $free = "yes", $memorysn
$diskspec .= " --diskspec '".$disk["device"]."',snapshot=external,file='".$filenew."'" ;
$capacity = $capacity + $disk["capacity"] ;
}
-
+ $dirpath = $pathinfo["dirname"] ;
#get memory
$mem = $lv->domain_get_memory_stats($vm) ;
$memory = $mem[6] ;
@@ -1551,7 +1601,9 @@ function vm_snapshot($vm,$snapshotname, $snapshotdesc, $free = "yes", $memorysn
# Remove nvram snapshot
$arrResponse = ['success' => true] ;
}
-
+ write_snapshots_database("$vm","$name") ;
+ #remove meta data
+ $ret = $lv->domain_snapshot_delete($vm, "$name" ,2) ;
return $arrResponse ;
}
@@ -1645,7 +1697,7 @@ function vm_revert($vm, $snap="--current",$action="no",$actionmeta = 'yes') {
$name = $s['name'] ;
#Delete Metadata only.
if ($actionmeta == "yes") {
- $ret = $lv->domain_snapshot_delete($vm, "$name" ,2) ;
+ $ret = delete_snapshots_database("$vm","$name") ;
}
# Remove running XML and memory.
$xmlfile = $primarypath."/$name.running" ;
@@ -1717,7 +1769,7 @@ function vm_snapimages($vm, $snap, $only) {
}
$snapdisks= $snapslist[$snap]['disks'] ;
- #var_dump($snaps) ;
+
foreach ($snapdisks as $diskkey => $snapdisk) {
$diskname = $snapdisk["@attributes"]["name"] ;
if ($diskname == "hda" || $diskname == "hdb") continue ;
@@ -1803,8 +1855,8 @@ function vm_snapremove($vm, $snap) {
}
}
- $ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
- #var_dump($ret) ;
+ $ret = delete_snapshots_database("$vm","$snap") ;
+
if(!$ret)
$data = ["error" => "Unable to remove snap metadata $snap"] ;
else
@@ -1849,57 +1901,54 @@ function vm_blockcommit($vm, $snap ,$path,$base,$top,$pivot,$action) {
$snapslist= getvmsnapshots($vm) ;
$disks =$lv->get_disk_stats($vm) ;
- #var_dump($disks) ;
+
foreach($disks as $disk) {
- $path = $disk['file'] ;
- $cmdstr = "virsh blockcommit '$vm' --path '$path' --verbose --pivot --delete" ;
- # Process disks and update path.
- $snapdisks=($snapslist[$snap]['disks']) ;
- if ($base != "--base" && $base != "") {
- #get file name from snapshot.
- $snapdisks=($snapslist[$base]['disks']) ;
- $basepath = "" ;
- foreach ($snapdisks as $snapdisk) {
- $diskname = $snapdisk["@attributes"]["name"] ;
- if ($diskname != $disk['device']) continue ;
- $basepath = $snapdisk["source"]["@attributes"]["file"] ;
- }
- if ($basepath != "") $cmdstr .= " --base '$basepath' ";
- }
- if ($top != "--top" && $top !="") {
- #get file name from snapshot.
- $snapdisks=($snapslist[$top]['disks']) ;
- $toppath = "" ;
- foreach ($snapdisks as $snapdisk) {
- $diskname = $snapdisk["@attributes"]["name"] ;
- if ($diskname != $disk['device']) continue ;
- $toppath = $snapdisk["source"]["@attributes"]["file"] ;
- }
- if ($toppath != "") $cmdstr .= " --top '$toppath' ";
- }
- if ($action) $cmdstr .= " $action ";
+ $path = $disk['file'] ;
+ $cmdstr = "virsh blockcommit '$vm' --path '$path' --verbose " ;
+ if ($pivot == "yes") $cmdstr .= " --pivot " ;
+ if ($action == "yes") $cmdstr .= " --delete " ;
+ # Process disks and update path.
+ $snapdisks=($snapslist[$snap]['disks']) ;
+ if ($base != "--base" && $base != "") {
+ #get file name from snapshot.
+ $snapdisks=($snapslist[$base]['disks']) ;
+ $basepath = "" ;
+ foreach ($snapdisks as $snapdisk) {
+ $diskname = $snapdisk["@attributes"]["name"] ;
+ if ($diskname != $disk['device']) continue ;
+ $basepath = $snapdisk["source"]["@attributes"]["file"] ;
+ }
+ if ($basepath != "") $cmdstr .= " --base '$basepath' ";
+ }
+ if ($top != "--top" && $top !="") {
+ #get file name from snapshot.
+ $snapdisks=($snapslist[$top]['disks']) ;
+ $toppath = "" ;
+ foreach ($snapdisks as $snapdisk) {
+ $diskname = $snapdisk["@attributes"]["name"] ;
+ if ($diskname != $disk['device']) continue ;
+ $toppath = $snapdisk["source"]["@attributes"]["file"] ;
+ }
+ if ($toppath != "") $cmdstr .= " --top '$toppath' ";
+ }
- $test = false ;
- if ($test) $cmdstr .= " --print-xml " ;
-
- $error = execCommand_nchan($cmdstr,$path) ;
- #var_dump($cmdstr,$output) ;
- if (!$error) {
- $arrResponse = ['error' => substr($output[0],6) ] ;
- return($arrResponse) ;
- } else {
- # Remove nvram snapshot
- $arrResponse = ['success' => true] ;
- }
- #Error Check
+ $error = execCommand_nchan($cmdstr,$path) ;
+ if (!$error) {
+ $arrResponse = ['error' => substr($output[0],6) ] ;
+ return($arrResponse) ;
+ } else {
+ $arrResponse = ['success' => true] ;
}
+
+ }
+ #Remove NVRAMs
+ #if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_revert_snapshot($lv->domain_get_uuid($vm),$name) ;
#If complete ok remove meta data for snapshots.
- if (!$test) $ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
+ $ret = $ret = delete_snapshots_database("$vm","$snap") ; ;
if($ret)
$data = ["error" => "Unable to remove snap metadata $snap"] ;
else
$data = ["success => 'true"] ;
-
return $data ;
}
@@ -1945,9 +1994,11 @@ function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
$reversed = array_reverse($output) ;
$snaps[$vm][$rev] = $reversed ;
}
- $snaps_json=json_encode($snaps) ;
- file_put_contents("/tmp/snaps",$snaps_json) ;
- #var_dump($disks) ;
+ $snaps_json=json_encode($snaps,JSON_PRETTY_PRINT) ;
+ $pathinfo = pathinfo($file) ;
+ $dirpath = $pathinfo["dirname"] ;
+ file_put_contents("$dirpath/image.tracker",$snaps_json) ;
+
foreach($disks as $disk) {
$path = $disk['file'] ;
$cmdstr = "virsh blockpull '$vm' --path '$path' --verbose --pivot --delete" ;
@@ -1968,9 +2019,7 @@ function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
if ($action) $cmdstr .= " $action ";
- $test = false ;
- if ($test) $cmdstr .= " --print-xml " ;
-
+
$error = execCommand_nchan($cmdstr,$path) ;
if (!$error) {
@@ -1983,7 +2032,7 @@ function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
#Error Check
}
#If complete ok remove meta data for snapshots.
- $ret = $lv->domain_snapshot_delete($vm, $snap ,2) ;
+ #$ret = delete_snapshots_database("$vm","$snap") ;
if($ret)
$data = ["error" => "Unable to remove snap metadata $snap"] ;
else
diff --git a/plugins/dynamix.vm.manager/javascript/vmmanager.js b/plugins/dynamix.vm.manager/javascript/vmmanager.js
index 41fe241bb..2a5929f43 100644
--- a/plugins/dynamix.vm.manager/javascript/vmmanager.js
+++ b/plugins/dynamix.vm.manager/javascript/vmmanager.js
@@ -229,12 +229,12 @@ function addVMSnapContext(name, uuid, template, state, snapshotname, preview=fal
$('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
selectsnapshot(uuid, name, snapshotname, "revert",true) ;
}});
- opts.push({text:_("Remove snapshot"), icon:"fa-trash", action:function(e) {
- e.preventDefault();
- $('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
- selectsnapshot(uuid, name, snapshotname, "remove",true) ;
- }});
-}
+ }
+ opts.push({text:_("Remove snapshot"), icon:"fa-trash", action:function(e) {
+ e.preventDefault();
+ $('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
+ selectsnapshot(uuid, name, snapshotname, "remove",true) ;
+ }});
context.attach('#vmsnap-'+uuid, opts);
}
function startAll() {
diff --git a/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php b/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
index 7fc64bf76..59b846f4d 100755
--- a/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
+++ b/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
@@ -35,16 +35,18 @@ function write(...$messages){
}
curl_close($com);
}
-function execCommand_nchan($command) {
+function execCommand_nchan($command,$idx) {
$waitID = mt_rand();
[$cmd,$args] = explode(' ',$command,2);
write("","addLog\0","show_Wait\0$waitID");
- write("addToID\0 99\0 $action") ;
+ write("addLog\0 ") ;
+ write("addToID\0$idx\0 $action") ;
$proc = popen("$command 2>&1",'r');
while ($out = fgets($proc)) {
- $out = preg_replace("%[\t\n\x0B\f\r]+%", '',$out);
- write("addToID\0 99\0".htmlspecialchars($out));
- sleep(5) ;
+ $out = preg_replace("%[\t\n\x0B\f\r]+%", '',$out);
+ if (substr($out,0,1) == "B") { ;
+ write("progress\0$idx\0".htmlspecialchars(substr($out,strrpos($out,"Block Pull")))) ;
+ } else echo write("addToID\0$idx\0 ".htmlspecialchars($out));
}
$retval = pclose($proc);
$out = $retval ? _('The command failed').'.' : _('The command finished successfully').'!';
@@ -53,6 +55,7 @@ function execCommand_nchan($command) {
}
#{action:"snap-", uuid:uuid , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep, desc:desc}
+#VM ID [ 99]: pull. .Block Pull: [ 0 %]Block Pull: [100 %].Pull complete.
$url = rawurldecode($argv[1]??'');
$waitID = mt_rand();
$style = ["";
-$path = "--current" ; $pivot = "yes" ;
+
foreach (explode('&', $url) as $chunk) {
$param = explode("=", $chunk);
if ($param) {
@@ -69,25 +72,23 @@ function execCommand_nchan($command) {
}
$id = 1 ;
write(implode($style)."");
+$process = " " ;
+write("","addLog\0");
write("addLog\0".htmlspecialchars("VMName $name "));
write("addLog\0".htmlspecialchars("SNAP $snapshotname "));
write("addLog\0".htmlspecialchars("Base $targetbase "));
-write("addLog\0".htmlspecialchars("Top $targettop "));
- sleep(3) ;
- write("stop_Wait\0$waitID") ;
-write("","addLog\0","show_Wait\0$waitID");
-
+if ($action == "commit") {
+ write("addLog\0".htmlspecialchars("Top $targettop "));
+ write("addLog\0".htmlspecialchars("Pivot $targetpivot "));
+ write("addLog\0".htmlspecialchars("Delete $targetdelete "));
+}
- sleep(3) ;
- write("stop_Wait\0$waitID") ;
-$path = "--current" ; $pivot = "yes" ;
-write("addToID\0 99\0 $action") ;
switch ($action) {
case "commit":
- # vm_blockcommit($name,$snapshotname,$path,$targetbase,$targettop,$pivot,' ') ;
+ vm_blockcommit($name,$snapshotname,$path,$targetbase,$targettop,$targetpivot,$targetdelete) ;
break ;
case "copy":
- # vm_blockcopy($name,$snapshotname,$path,$targetbase,$targettop,$pivot,' ') ;
+ vm_blockcopy($name,$snapshotname,$path,$targetbase,$targettop,$pivot,' ') ;
break;
case "pull":
vm_blockpull($name,$snapshotname,$path,$targetbase,$targettop,$pivot,' ') ;
From b16b9c35d2d414170742ef7501a9db88c56f2b14 Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Fri, 2 Jun 2023 16:32:48 +0100
Subject: [PATCH 16/18] WIP Update.
---
.../dynamix.vm.manager/include/VMMachines.php | 38 +++++++----
.../dynamix.vm.manager/include/libvirt.php | 2 +-
.../include/libvirt_helpers.php | 66 ++++++++++++++++++-
3 files changed, 90 insertions(+), 16 deletions(-)
diff --git a/plugins/dynamix.vm.manager/include/VMMachines.php b/plugins/dynamix.vm.manager/include/VMMachines.php
index 68c075827..e88ce6c8f 100644
--- a/plugins/dynamix.vm.manager/include/VMMachines.php
+++ b/plugins/dynamix.vm.manager/include/VMMachines.php
@@ -245,24 +245,34 @@
echo "";
/* Display VM Snapshots */
if ($snapshots != null) {
-
+ $i=0 ;
+ foreach($snapshots as $snap) {
+ if ($snap['parent'] == "" || $snap['parent'] == "Base") $i++;
+ $steps[$i] .= $snap['name'].';' ;
+ }
echo " "._('Snapshots')." | | "._('Date/Time')." | "._('Type')." | "._('Parent')." | "._('Memory')." | ";
echo "";
- $tab = " " ;
- foreach($snapshots as $snapshotname => $snapshot) {
- $snapshotstate = _(ucfirst($snapshot["state"])) ;
- $snapshotdesc = $snapshot["desc"] ;
- $snapshotmemory = _(ucfirst($snapshot["memory"]["@attributes"]["snapshot"])) ;
- $snapshotparent = $snapshot["parent"]["name"] ? $snapshot["parent"]["name"] : "None";
- $snapshotdatetime = my_time($snapshot["creationtime"],"Y-m-d" )." ".my_time($snapshot["creationtime"],"H:i:s") ;
- $snapmenu = sprintf("onclick=\"addVMSnapContext('%s','%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,$snapshot["name"],$vmpreview);
- echo "$tab|__ ".$snapshot["name"]." | $snapshotdesc | $snapshotdatetime | $snapshotstate | $snapshotparent | $snapshotmemory | ";
- $tab .=" " ;
+ foreach($steps as $stepsline)
+ {
+ $snapshotlist = explode(";",$stepsline) ;
+ $tab = " " ;
+ foreach($snapshotlist as $snapshotitem) {
+ if ($snapshotitem == "") continue ;
+ $snapshot = $snapshots[$snapshotitem] ;
+ $snapshotstate = _(ucfirst($snapshot["state"])) ;
+ $snapshotdesc = $snapshot["desc"] ;
+ $snapshotmemory = _(ucfirst($snapshot["memory"]["@attributes"]["snapshot"])) ;
+ $snapshotparent = $snapshot["parent"] ? $snapshot["parent"] : "None";
+ $snapshotdatetime = my_time($snapshot["creationtime"],"Y-m-d" )." ".my_time($snapshot["creationtime"],"H:i:s") ;
+ $snapmenu = sprintf("onclick=\"addVMSnapContext('%s','%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,$snapshot["name"],$vmpreview);
+ echo "$tab|__ ".$snapshot["name"]." | $snapshotdesc | $snapshotdatetime | $snapshotstate | $snapshotparent | $snapshotmemory | ";
+ $tab .=" " ;
+ }
+ echo "";
+ }
}
- echo "";
-}
echo " |
";
- echo "";
}
+
echo "\0".implode($kvm);
?>
diff --git a/plugins/dynamix.vm.manager/include/libvirt.php b/plugins/dynamix.vm.manager/include/libvirt.php
index bbf5bbac4..b476349d1 100644
--- a/plugins/dynamix.vm.manager/include/libvirt.php
+++ b/plugins/dynamix.vm.manager/include/libvirt.php
@@ -770,7 +770,7 @@ function config_to_xml($config) {
$vmrc = "
-
+
$vmrcmousemode
diff --git a/plugins/dynamix.vm.manager/include/libvirt_helpers.php b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
index 721f4fbc2..bbbe272ba 100644
--- a/plugins/dynamix.vm.manager/include/libvirt_helpers.php
+++ b/plugins/dynamix.vm.manager/include/libvirt_helpers.php
@@ -1515,11 +1515,34 @@ function write_snapshots_database($vm,$name) {
$b= json_decode($a, TRUE);
$vmsnap = $b["name"] ;
$snaps[$vmsnap]["name"]= $b["name"];
- if(isset($b["parent"])) $snaps[$vmsnap]["parent"]= $b["parent"]; else $snaps[$vmsnap]["parent"]["name"] = "Base" ;
+ #if(isset($b["parent"])) $snaps[$vmsnap]["parent"]= $b["parent"]; else $snaps[$vmsnap]["parent"]["name"] = "Base" ;
+ $snaps[$vmsnap]["parent"]= $b["parent"] ;
$snaps[$vmsnap]["state"]= $b["state"];
$snaps[$vmsnap]["desc"]= $b["description"];
$snaps[$vmsnap]["memory"]= $b["memory"];
$snaps[$vmsnap]["creationtime"]= $b["creationTime"];
+
+ $disks =$lv->get_disk_stats($vm) ;
+ foreach($disks as $disk) {
+ $file = $disk["file"] ;
+ $output = "" ;
+ exec("qemu-img info --backing-chain -U '$file' | grep image:",$output) ;
+ foreach($output as $key => $line) {
+ $line=str_replace("image: ","",$line) ;
+ $output[$key] = $line ;
+ }
+
+ $snaps[$vmsnap]['backing'][$disk["device"]] = $output ;
+ $rev = "r".$disk["device"] ;
+ $reversed = array_reverse($output) ;
+ $snaps[$vmsnap]['backing'][$rev] = $reversed ;
+ }
+ $parentfind = $snaps[$vmsnap]['backing'][$disk["device"]] ;
+ $parendfileinfo = pathinfo($parentfind[1]) ;
+ $snaps[$vmsnap]["parent"]= $parendfileinfo["extension"];
+ $snaps[$vmsnap]["parent"] = str_replace("qcow2",'',$snaps[$vmsnap]["parent"]) ;
+ if (isset($parentfind[1]) && !isset($parentfind[2])) $snaps[$vmsnap]["parent"]="Base" ;
+
if (array_key_exists(0 , $b["disks"]["disk"])) $snaps[$vmsnap]["disks"]= $b["disks"]["disk"]; else $snaps[$vmsnap]["disks"][0]= $b["disks"]["disk"];
$value = json_encode($snaps,JSON_PRETTY_PRINT) ;
@@ -1527,6 +1550,45 @@ function write_snapshots_database($vm,$name) {
file_put_contents($dbpath."/snapshots.db",$value) ;
}
+ function refresh_snapshots_database($vm) {
+ global $lv ;
+ $dbpath = "/etc/libvirt/qemu/snapshot/$vm" ;
+ #var_dump($dbpath) ;
+ if (!is_dir($dbpath)) mkdir($dbpath) ;
+ $snaps_json = file_get_contents($dbpath."/snapshots.db") ;
+ $snaps = json_decode($snaps_json,true) ;
+ foreach($snaps as $vmsnap=>$snap)
+ #var_dump($snaps_json) ;
+
+
+ $disks =$lv->get_disk_stats($vm) ;
+ foreach($disks as $disk) {
+ $file = $disk["file"] ;
+ $output = "" ;
+ exec("qemu-img info --backing-chain -U '$file' | grep image:",$output) ;
+ foreach($output as $key => $line) {
+ $line=str_replace("image: ","",$line) ;
+ $output[$key] = $line ;
+ }
+
+ $snaps[$vmsnap]['backing'][$disk["device"]] = $output ;
+ $rev = "r".$disk["device"] ;
+ $reversed = array_reverse($output) ;
+ $snaps[$vmsnap]['backing'][$rev] = $reversed ;
+ }
+ $parentfind = $snaps[$vmsnap]['backing'][$disk["device"]] ;
+ $parendfileinfo = pathinfo($parentfind[1]) ;
+ $snaps[$vmsnap]["parent"]= $parendfileinfo["extension"];
+ $snaps[$vmsnap]["parent"] = str_replace("qcow2",'',$snaps[$vmsnap]["parent"]) ;
+ if (isset($parentfind[1]) && !isset($parentfind[2])) $snaps[$vmsnap]["parent"]="Base" ;
+
+ #if (array_key_exists(0 , $b["disks"]["disk"])) $snaps[$vmsnap]["disks"]= $b["disks"]["disk"]; else $snaps[$vmsnap]["disks"][0]= $b["disks"]["disk"];
+ var_dump($snaps) ;
+ $value = json_encode($snaps,JSON_PRETTY_PRINT) ;
+
+ file_put_contents($dbpath."/snapshots.db",$value) ;
+ }
+
function delete_snapshots_database($vm,$name) {
global $lv ;
$dbpath = "/etc/libvirt/qemu/snapshot/$vm" ;
@@ -1944,6 +2006,7 @@ function vm_blockcommit($vm, $snap ,$path,$base,$top,$pivot,$action) {
#Remove NVRAMs
#if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_revert_snapshot($lv->domain_get_uuid($vm),$name) ;
#If complete ok remove meta data for snapshots.
+ refresh_snapshots_database($vm) ;
$ret = $ret = delete_snapshots_database("$vm","$snap") ; ;
if($ret)
$data = ["error" => "Unable to remove snap metadata $snap"] ;
@@ -2033,6 +2096,7 @@ function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
}
#If complete ok remove meta data for snapshots.
#$ret = delete_snapshots_database("$vm","$snap") ;
+ refresh_snapshots_database($vm) ;
if($ret)
$data = ["error" => "Unable to remove snap metadata $snap"] ;
else
From 8f69ed880d02321e3d26a354d615c53c65be9af0 Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Sat, 3 Jun 2023 07:47:48 +0100
Subject: [PATCH 17/18] Add snapshot directory setup to rc.libvirt
---
etc/rc.d/rc.libvirt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/etc/rc.d/rc.libvirt b/etc/rc.d/rc.libvirt
index e19f7790f..b2e4e11f0 100755
--- a/etc/rc.d/rc.libvirt
+++ b/etc/rc.d/rc.libvirt
@@ -159,6 +159,10 @@ start_libvirtd() {
cp -n /etc/libvirt-/*.conf /etc/libvirt &> /dev/null
# ensure tpm-states path exists
mkdir -p /etc/libvirt/qemu/swtpm/tpm-states
+ # Setup Snapshot persistance.
+ mkdir -p /etc/libvirt/qemu/snapshot/
+ rm -r /var/lib/libvirt/qemu/snapshot/
+ ln -fs /etc/libvirt/qemu/snapshot/ /var/lib/libvirt/qemu/
echo "Starting libvirtd..."
mkdir -p $(dirname $LIBVIRTD_PIDFILE)
check_processor
From c74236612eec7c7b1124e17536551e7770929716 Mon Sep 17 00:00:00 2001
From: SimonFair <39065407+SimonFair@users.noreply.github.com>
Date: Sun, 4 Jun 2023 12:09:36 +0100
Subject: [PATCH 18/18] WIP Update
---
.../dynamix.vm.manager/VMMachines.page | 4 +-
.../dynamix.vm.manager/include/VMMachines.php | 11 +-
.../dynamix.vm.manager/include/libvirt.php | 16 +-
.../include/libvirt_helpers.php | 185 +++++++-----------
.../javascript/vmmanager.js | 16 +-
5 files changed, 102 insertions(+), 130 deletions(-)
diff --git a/emhttp/plugins/dynamix.vm.manager/VMMachines.page b/emhttp/plugins/dynamix.vm.manager/VMMachines.page
index 0fdab5663..1391c5438 100644
--- a/emhttp/plugins/dynamix.vm.manager/VMMachines.page
+++ b/emhttp/plugins/dynamix.vm.manager/VMMachines.page
@@ -278,7 +278,7 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist,state){
document.getElementById("targetsnaprmv").checked = true ;
document.getElementById("targetsnaprmvmeta").checked = true ;
document.getElementById("targetsnapkeep").checked = true ;
- document.getElementById("targetsnapfspc").checked = false ;
+ document.getElementById("targetsnapfspc").checked = true ;
box.dialog({
title: "_("+optiontext+ ")_",
@@ -533,7 +533,7 @@ _(CD2 ISO Image)_:
_(Snapshot Name)_: |
_(Check free space)_:
- |
+
_(Description )_: |
|
diff --git a/emhttp/plugins/dynamix.vm.manager/include/VMMachines.php b/emhttp/plugins/dynamix.vm.manager/include/VMMachines.php
index e88ce6c8f..c79cbdba8 100644
--- a/emhttp/plugins/dynamix.vm.manager/include/VMMachines.php
+++ b/emhttp/plugins/dynamix.vm.manager/include/VMMachines.php
@@ -245,13 +245,14 @@
echo "";
/* Display VM Snapshots */
if ($snapshots != null) {
- $i=0 ;
+ $j=0 ;
+ $steps = array() ;
foreach($snapshots as $snap) {
- if ($snap['parent'] == "" || $snap['parent'] == "Base") $i++;
- $steps[$i] .= $snap['name'].';' ;
+ if ($snap['parent'] == "" || $snap['parent'] == "Base") $j++;
+ $steps[$j] .= $snap['name'].';' ;
}
- echo " "._('Snapshots')." | | "._('Date/Time')." | "._('Type')." | "._('Parent')." | "._('Memory')." |
";
- echo "";
+ echo " "._('Snapshots')." | | "._('Date/Time')." | "._('Type')." | "._('Parent')." | "._('Memory')." |
";
+ echo "";
foreach($steps as $stepsline)
{
$snapshotlist = explode(";",$stepsline) ;
diff --git a/emhttp/plugins/dynamix.vm.manager/include/libvirt.php b/emhttp/plugins/dynamix.vm.manager/include/libvirt.php
index b476349d1..05b272cb5 100644
--- a/emhttp/plugins/dynamix.vm.manager/include/libvirt.php
+++ b/emhttp/plugins/dynamix.vm.manager/include/libvirt.php
@@ -1446,7 +1446,7 @@ function domain_get_icon_url($domain) {
function domain_change_xml($domain, $xml) {
$dom = $this->get_domain_object($domain);
- if (!($old_xml = domain_get_xml($dom)))
+ if (!($old_xml = $this->domain_get_xml($dom)))
return $this->_set_last_error();
if (!libvirt_domain_undefine($dom))
return $this->_set_last_error();
@@ -1874,6 +1874,20 @@ function nvram_revert_snapshot($uuid,$snapshotname) {
return false;
}
+ function nvram_delete_snapshot($uuid,$snapshotname) {
+ // snapshot backup OVMF VARS if this domain had them
+ if (is_file('/etc/libvirt/qemu/nvram/'.$uuid.$snapshotname.'_VARS-pure-efi.fd')) {
+ unlink('/etc/libvirt/qemu/nvram/'.$uuid.$snapshotname.'_VARS-pure-efi.fd') ;
+ return true;
+ }
+ if (is_file('/etc/libvirt/qemu/nvram/'.$uuid.$snapshotname.'_VARS-pure-efi-tpm.fd')) {
+ unlink('/etc/libvirt/qemu/nvram/'.$uuid.$snapshotname.'_VARS-pure-efi-tpm.fd') ;
+ return true;
+ }
+ return false;
+ }
+
+
function is_dir_empty($dir) {
if (!is_readable($dir)) return NULL;
$handle = opendir($dir);
diff --git a/emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php b/emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php
index bbbe272ba..4e5f13f4f 100644
--- a/emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php
+++ b/emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php
@@ -1463,66 +1463,37 @@ function compare_creationtime($a, $b) {
function compare_creationtimelt($a, $b) {
return $a['creationtime'] < $b['creationtime'];
- }
+ }
function getvmsnapshots($vm) {
$snaps=array() ;
-
$dbpath = "/etc/libvirt/qemu/snapshot/$vm" ;
$snaps_json = file_get_contents($dbpath."/snapshots.db") ;
$snaps = json_decode($snaps_json,true) ;
if (is_array($snaps)) uasort($snaps,'compare_creationtime') ;
return $snaps ;
}
- function getvmsnapshots2($vm) {
- global $lv ;
- $vmsnaps = $lv->domain_snapshots_list($lv->get_domain_object($vm)) ;
- $snaps=array() ;
- foreach($vmsnaps as $vmsnap) {
- $snapshot_res=$lv->domain_snapshot_lookup_by_name($vm,$vmsnap) ;
- $snapshot_xml=$lv->domain_snapshot_get_xml($snapshot_res) ;
- $a = simplexml_load_string($snapshot_xml) ;
- if($a == false) continue ;
- $a = json_encode($a) ;
- $b= json_decode($a, TRUE);
- $vmsnap = $b["name"] ;
- $snaps[$vmsnap]["name"]= $b["name"];
- if(isset($b["parent"])) $snaps[$vmsnap]["parent"]= $b["parent"]; else $snaps[$vmsnap]["parent"]["name"] = "Base" ;
- $snaps[$vmsnap]["state"]= $b["state"];
- $snaps[$vmsnap]["desc"]= $b["description"];
- $snaps[$vmsnap]["memory"]= $b["memory"];
- $snaps[$vmsnap]["creationtime"]= $b["creationTime"];
- if (array_key_exists(0 , $b["disks"]["disk"])) $snaps[$vmsnap]["disks"]= $b["disks"]["disk"]; else $snaps[$vmsnap]["disks"][0]= $b["disks"]["disk"];
- }
-
- if (is_array($snaps)) uasort($snaps,'compare_creationtime') ;
- return $snaps ;
- }
+
function write_snapshots_database($vm,$name) {
global $lv ;
$dbpath = "/etc/libvirt/qemu/snapshot/$vm" ;
- #var_dump($dbpath) ;
if (!is_dir($dbpath)) mkdir($dbpath) ;
$snaps_json = file_get_contents($dbpath."/snapshots.db") ;
$snaps = json_decode($snaps_json,true) ;
- #var_dump($snaps_json) ;
- $snapshot_res=$lv->domain_snapshot_lookup_by_name($vm,$name) ;
- $snapshot_xml=$lv->domain_snapshot_get_xml($snapshot_res) ;
- $a = simplexml_load_string($snapshot_xml) ;
- #if($a == false) continue ;
- $a = json_encode($a) ;
-
- $b= json_decode($a, TRUE);
- $vmsnap = $b["name"] ;
- $snaps[$vmsnap]["name"]= $b["name"];
- #if(isset($b["parent"])) $snaps[$vmsnap]["parent"]= $b["parent"]; else $snaps[$vmsnap]["parent"]["name"] = "Base" ;
- $snaps[$vmsnap]["parent"]= $b["parent"] ;
- $snaps[$vmsnap]["state"]= $b["state"];
- $snaps[$vmsnap]["desc"]= $b["description"];
- $snaps[$vmsnap]["memory"]= $b["memory"];
- $snaps[$vmsnap]["creationtime"]= $b["creationTime"];
+ $snapshot_res=$lv->domain_snapshot_lookup_by_name($vm,$name) ;
+ $snapshot_xml=$lv->domain_snapshot_get_xml($snapshot_res) ;
+ $a = simplexml_load_string($snapshot_xml) ;
+ $a = json_encode($a) ;
+ $b= json_decode($a, TRUE);
+ $vmsnap = $b["name"] ;
+ $snaps[$vmsnap]["name"]= $b["name"];
+ $snaps[$vmsnap]["parent"]= $b["parent"] ;
+ $snaps[$vmsnap]["state"]= $b["state"];
+ $snaps[$vmsnap]["desc"]= $b["description"];
+ $snaps[$vmsnap]["memory"]= $b["memory"];
+ $snaps[$vmsnap]["creationtime"]= $b["creationTime"];
- $disks =$lv->get_disk_stats($vm) ;
+ $disks =$lv->get_disk_stats($vm) ;
foreach($disks as $disk) {
$file = $disk["file"] ;
$output = "" ;
@@ -1544,23 +1515,20 @@ function write_snapshots_database($vm,$name) {
if (isset($parentfind[1]) && !isset($parentfind[2])) $snaps[$vmsnap]["parent"]="Base" ;
if (array_key_exists(0 , $b["disks"]["disk"])) $snaps[$vmsnap]["disks"]= $b["disks"]["disk"]; else $snaps[$vmsnap]["disks"][0]= $b["disks"]["disk"];
-
+
+
$value = json_encode($snaps,JSON_PRETTY_PRINT) ;
- #var_dump($value) ;
file_put_contents($dbpath."/snapshots.db",$value) ;
}
function refresh_snapshots_database($vm) {
global $lv ;
$dbpath = "/etc/libvirt/qemu/snapshot/$vm" ;
- #var_dump($dbpath) ;
if (!is_dir($dbpath)) mkdir($dbpath) ;
$snaps_json = file_get_contents($dbpath."/snapshots.db") ;
$snaps = json_decode($snaps_json,true) ;
foreach($snaps as $vmsnap=>$snap)
- #var_dump($snaps_json) ;
-
-
+
$disks =$lv->get_disk_stats($vm) ;
foreach($disks as $disk) {
$file = $disk["file"] ;
@@ -1582,9 +1550,29 @@ function refresh_snapshots_database($vm) {
$snaps[$vmsnap]["parent"] = str_replace("qcow2",'',$snaps[$vmsnap]["parent"]) ;
if (isset($parentfind[1]) && !isset($parentfind[2])) $snaps[$vmsnap]["parent"]="Base" ;
- #if (array_key_exists(0 , $b["disks"]["disk"])) $snaps[$vmsnap]["disks"]= $b["disks"]["disk"]; else $snaps[$vmsnap]["disks"][0]= $b["disks"]["disk"];
- var_dump($snaps) ;
$value = json_encode($snaps,JSON_PRETTY_PRINT) ;
+ $res = $lv->get_domain_by_name($vm);
+ if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_create_snapshot($lv->domain_get_uuid($vm),$name) ;
+
+
+ #Remove any NVRAMs that are no longer valid.
+ # Get uuid
+ $vmuuid = $lv->domain_get_uuid($vm) ;
+ #Get list of files
+ #$filepath = "/etc/libvirt/qemu/nvram/'.$uuid*" ; #$snapshotname"
+ $filepath = "/etc/libvirt/qemu/nvram/$vmuuid*" ; #$snapshotname"
+ $nvram_files=glob($filepath) ;
+ foreach($nvram_files as $key => $nvram_file) {
+ if ($nvram_file == "/etc/libvirt/qemu/nvram/$vmuuid"."_VARS-pure-efi.fd" || $nvram_file == "/etc/libvirt/qemu/nvram/$vmuuid"."_VARS-pure-efi-tpm.fd" ) unset($nvram_files[$key]) ;
+ foreach ($snaps as $snapshotname => $snap) {
+ $tpmfilename = "/etc/libvirt/qemu/nvram/".$vmuuid.$snapshotname."_VARS-pure-efi-tpm.fd" ;
+ $nontpmfilename = "/etc/libvirt/qemu/nvram/".$vmuuid.$snapshotname."_VARS-pure-efi.fd" ;
+ if ($nvram_file == $tpmfilename || $nvram_file == $nontpmfilename ) {
+ unset($nvram_files[$key]) ;}
+ }
+ }
+ foreach ($nvram_files as $nvram_file) unlink($nvram_file) ;
+
file_put_contents($dbpath."/snapshots.db",$value) ;
}
@@ -1596,7 +1584,6 @@ function delete_snapshots_database($vm,$name) {
$snaps = json_decode($snaps_json,true) ;
unset($snaps[$name]) ;
$value = json_encode($snaps,JSON_PRETTY_PRINT) ;
- #var_dump($value) ;
file_put_contents($dbpath."/snapshots.db",$value) ;
return true ;
}
@@ -1675,25 +1662,6 @@ function vm_revert($vm, $snap="--current",$action="no",$actionmeta = 'yes') {
$snapslist= getvmsnapshots($vm) ;
$disks =$lv->get_disk_stats($vm) ;
- foreach($disks as $disk) {
- $file = $disk["file"] ;
- $output = "" ;
- exec("qemu-img info --backing-chain -U '$file' | grep image:",$output) ;
- foreach($output as $key => $line) {
- $line=str_replace("image: ","",$line) ;
- $output[$key] = $line ;
- }
-
- $snaps[$vm][$disk["device"]] = $output ;
- $rev = "r".$disk["device"] ;
- $reversed = array_reverse($output) ;
- $snaps[$vm][$rev] = $reversed ;
- $pathinfo = pathinfo($file) ;
- $filenew = $pathinfo["dirname"].'/'.$pathinfo["filename"].'.'.$name.'qcow2' ;
- $diskspec .= " --diskspec ".$disk["device"].",snapshot=external,file=".$filenew ;
- $capacity = $capacity + $disk["capacity"] ;
- }
-
switch ($snapslist[$snap]['state']) {
case "shutoff":
case "running":
@@ -1718,8 +1686,8 @@ function vm_revert($vm, $snap="--current",$action="no",$actionmeta = 'yes') {
$primarypathinfo = pathinfo($path) ;
$primarypath = $primarypathinfo['dirname'] ;
}
- $item = array_search($path,$snaps[$vm][$diskname]) ;
- $newpath = $snaps[$vm][$diskname][$item + 1];
+ $item = array_search($path,$snapslist[$snap]['backing'][$diskname]) ;
+ $newpath = $snapslist[$snap]['backing'][$diskname][$item + 1];
$json_info = getDiskImageInfo($newpath) ;
foreach($xmlobj['devices']['disk'] as $ddk => $dd){
if ($dd['target']["@attributes"]['dev'] == $diskname) {
@@ -1741,66 +1709,53 @@ function vm_revert($vm, $snap="--current",$action="no",$actionmeta = 'yes') {
if ($diskname == "hda" || $diskname == "hdb") continue ;
$path = $disk["source"]["@attributes"]["file"] ;
if (is_file($path) && $action == "yes") unlink("$path") ;
- $item = array_search($path,$snaps[$vm]["r".$diskname]) ;
+ $item = array_search($path,$snapslist[$snap]['backing']["r".$diskname]) ;
$item++ ;
while($item > 0)
{
- if (!isset($snaps[$vm]["r".$diskname][$item])) break ;
- $newpath = $snaps[$vm]["r".$diskname][$item] ;
+ if (!isset($snapslist[$snap]['backing']["r".$diskname][$item])) break ;
+ $newpath = $snapslist[$snap]['backing']["r".$diskname][$item] ;
if (is_file($newpath) && $action == "yes") unlink("$newpath") ;
-
$item++ ;
-
}
}
uasort($snapslist,'compare_creationtimelt') ;
foreach($snapslist as $s) {
$name = $s['name'] ;
+
+ $xmlfile = $primarypath."/$name.running" ;
+ $memoryfile = $primarypath."/memory$name.mem" ;
+
+ if ($snapslist[$snap]['state'] == "running") {
+ # Set XML to saved XML
+ $xml = file_get_contents($xmlfile) ;
+ $xmlobj = custom::createArray('domain',$xml) ;
+ $xml = custom::createXML('domain',$xmlobj)->saveXML();
+ $rtn = $lv->domain_define($xml) ;
+
+ # Resotre Memeory.
+
+ $makerun = true ;
+ if ($makerun == true) exec("virsh restore $memoryfile") ;
+ #exec("virsh restore $memoryfile") ;
+ }
#Delete Metadata only.
if ($actionmeta == "yes") {
$ret = delete_snapshots_database("$vm","$name") ;
}
- # Remove running XML and memory.
- $xmlfile = $primarypath."/$name.running" ;
- $memoryfile = $primarypath."/memory$name.mem" ;
-
if (is_file($memoryfile) && $action == "yes") unlink($memoryfile) ;
if (is_file($xmlfile) && $action == "yes") unlink($xmlfile) ;
if ($s['name'] == $snap) break ;
}
#if VM was started restart.
- if ($state == 'running') {
+ if ($state == 'running' && $snapslist[$snap]['state'] != "running") {
$arrResponse = $lv->domain_start($vm) ;
}
if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_revert_snapshot($lv->domain_get_uuid($vm),$name) ;
break ;
-
- case "other":
- break ;
- #VM must be shutdown.
- # if VM running shutdown. Record was running.
- # Replace disk paths
- # remove snapshot meta data and images for all snpahots.
- # if VM was started restart.
-
-
- #type running
- #Non Live restores
- #VM must be shutdown.
- # if VM running shutdown. Record was running.
- # Replace disk paths
- # remove snapshot meta data and images for all snpahots.
- # if VM was started restart.
-
- #Live restore(currently not supported.)
- # Freeze VM
- # Replace disk paths
- # Replace Mem
- # remove snapshot meta data and images for all snpahots.
- # Unfreeze VM
}
$arrResponse = ['success' => true] ;
return($arrResponse) ;
@@ -1917,6 +1872,9 @@ function vm_snapremove($vm, $snap) {
}
}
+ # Delete NVRAM
+ if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_delete_snapshot($lv->domain_get_uuid($vm),$snap) ;
+
$ret = delete_snapshots_database("$vm","$snap") ;
if(!$ret)
@@ -2003,9 +1961,9 @@ function vm_blockcommit($vm, $snap ,$path,$base,$top,$pivot,$action) {
}
}
- #Remove NVRAMs
- #if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_revert_snapshot($lv->domain_get_uuid($vm),$name) ;
- #If complete ok remove meta data for snapshots.
+ # Delete NVRAM
+ #if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_delete_snapshot($lv->domain_get_uuid($vm),$snap) ;
+
refresh_snapshots_database($vm) ;
$ret = $ret = delete_snapshots_database("$vm","$snap") ; ;
if($ret)
@@ -2092,10 +2050,9 @@ function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
# Remove nvram snapshot
$arrResponse = ['success' => true] ;
}
- #Error Check
+
}
- #If complete ok remove meta data for snapshots.
- #$ret = delete_snapshots_database("$vm","$snap") ;
+
refresh_snapshots_database($vm) ;
if($ret)
$data = ["error" => "Unable to remove snap metadata $snap"] ;
diff --git a/emhttp/plugins/dynamix.vm.manager/javascript/vmmanager.js b/emhttp/plugins/dynamix.vm.manager/javascript/vmmanager.js
index 2a5929f43..a3907ffe0 100644
--- a/emhttp/plugins/dynamix.vm.manager/javascript/vmmanager.js
+++ b/emhttp/plugins/dynamix.vm.manager/javascript/vmmanager.js
@@ -107,11 +107,11 @@ function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, c
ajaxVMDispatch( {action:"domain-destroy", uuid:uuid}, "loadlist");
}});
opts.push({divider:true});
- if (preview) {
+
opts.push({text:_("Create Snapshot"), icon:"fa-clone", action:function(e) {
e.preventDefault();
selectsnapshot(uuid , name, "--generate" , "create",false,state) ;
- }}); }
+ }});
} else if (state == "pmsuspended") {
opts.push({text:_("Resume"), icon:"fa-play", action:function(e) {
e.preventDefault();
@@ -201,24 +201,24 @@ function addVMSnapContext(name, uuid, template, state, snapshotname, preview=fal
context.settings({right:false,above:false});
if (state == "running") {
- if (preview) {
- opts.push({text:_("Revert snapshot"), icon:"fa-stop", action:function(e) {
+
+ opts.push({text:_("Revert snapshot"), icon:"fa-fast-backward", action:function(e) {
e.preventDefault();
- ajaxVMDispatch({action:"snapshot-revert-externa", uuid:uuid, snapshotname:snapshotname}, "loadlist");
+ selectsnapshot(uuid, name, snapshotname, "revert",true) ;
}});
- }
+
opts.push({text:_("Block Commit"), icon:"fa-hdd-o", action:function(e) {
$('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
e.preventDefault();
selectblock(uuid, name, snapshotname, "commit",true) ;
}});
- if (preview) {
+
opts.push({text:_("Block Pull"), icon:"fa-hdd-o", action:function(e) {
$('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
e.preventDefault();
selectblock(uuid, name, snapshotname, "pull",true) ;
}});
-
+ if (preview) {
opts.push({text:_("Block Copy"), icon:"fa-stop", action:function(e) {
e.preventDefault();
ajaxVMDispatch({action:"domain-stop", uuid:uuid}, "loadlist");