mirror of
https://github.com/unraid/webgui.git
synced 2026-04-30 06:49:24 -05:00
Merge branch 'Snapshots' into VM-Clone-and-Snapshot-merge
This commit is contained in:
@@ -163,6 +163,7 @@ private static $encoding = 'UTF-8';
|
||||
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt.php";
|
||||
require_once "$docroot/webGui/include/Custom.php";
|
||||
|
||||
// Load emhttp variables if needed.
|
||||
if (!isset($var)){
|
||||
@@ -1203,6 +1204,7 @@ private static $encoding = 'UTF-8';
|
||||
$arrDisks[] = [
|
||||
'new' => $strPath,
|
||||
'size' => '',
|
||||
'driver' => $disk['type'],
|
||||
'driver' => 'raw',
|
||||
'dev' => $disk['device'],
|
||||
'bus' => $disk['bus'],
|
||||
@@ -1582,4 +1584,649 @@ private static $encoding = 'UTF-8';
|
||||
|
||||
}
|
||||
|
||||
|
||||
function compare_creationtime($a, $b) {
|
||||
return strnatcmp($a['creationtime'], $b['creationtime']);
|
||||
}
|
||||
|
||||
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 write_snapshots_database($vm,$name) {
|
||||
global $lv ;
|
||||
$dbpath = "/etc/libvirt/qemu/snapshot/$vm" ;
|
||||
if (!is_dir($dbpath)) mkdir($dbpath) ;
|
||||
$snaps_json = file_get_contents($dbpath."/snapshots.db") ;
|
||||
$snaps = json_decode($snaps_json,true) ;
|
||||
$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) ;
|
||||
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) ;
|
||||
file_put_contents($dbpath."/snapshots.db",$value) ;
|
||||
}
|
||||
|
||||
function refresh_snapshots_database($vm) {
|
||||
global $lv ;
|
||||
$dbpath = "/etc/libvirt/qemu/snapshot/$vm" ;
|
||||
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)
|
||||
|
||||
$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" ;
|
||||
|
||||
$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) ;
|
||||
}
|
||||
|
||||
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) ;
|
||||
file_put_contents($dbpath."/snapshots.db",$value) ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
|
||||
function vm_snapshot($vm,$snapshotname, $snapshotdesc, $free = "yes", $memorysnap = "yes") {
|
||||
global $lv ;
|
||||
|
||||
#Get State
|
||||
$res = $lv->get_domain_by_name($vm);
|
||||
$dom = $lv->domain_get_info($res);
|
||||
$state = $lv->domain_state_translate($dom['state']);
|
||||
|
||||
#Get disks for --diskspec
|
||||
$disks =$lv->get_disk_stats($vm) ;
|
||||
$diskspec = "" ;
|
||||
$capacity = 0 ;
|
||||
if ($snapshotname == "--generate") $name= "S" . date("YmdHis") ; else $name=$snapshotname ;
|
||||
if ($snapshotdesc != "") $snapshotdesc = " --description '$snapshotdesc'" ;
|
||||
|
||||
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."'" ;
|
||||
$capacity = $capacity + $disk["capacity"] ;
|
||||
}
|
||||
$dirpath = $pathinfo["dirname"] ;
|
||||
#get memory
|
||||
$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' $snapshotdesc --atomic" ;
|
||||
|
||||
|
||||
if ($state == "running") {
|
||||
$cmdstr .= " --live ".$memspec.$diskspec ;
|
||||
$capacity = $capacity + $memory ;
|
||||
|
||||
} else {
|
||||
$cmdstr .= " --disk-only ".$diskspec ;
|
||||
}
|
||||
|
||||
#Check free space.
|
||||
$dirfree = disk_free_space($pathinfo["dirname"]) ;
|
||||
|
||||
$capacity *= 1 ;
|
||||
|
||||
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_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) ;
|
||||
|
||||
if (strpos(" ".$output[0],"error") ) {
|
||||
$arrResponse = ['error' => substr($output[0],6) ] ;
|
||||
} else {
|
||||
# Remove nvram snapshot
|
||||
$arrResponse = ['success' => true] ;
|
||||
}
|
||||
write_snapshots_database("$vm","$name") ;
|
||||
#remove meta data
|
||||
$ret = $lv->domain_snapshot_delete($vm, "$name" ,2) ;
|
||||
return $arrResponse ;
|
||||
|
||||
}
|
||||
|
||||
function vm_revert($vm, $snap="--current",$action="no",$actionmeta = 'yes') {
|
||||
global $lv ;
|
||||
$snapslist= getvmsnapshots($vm) ;
|
||||
$disks =$lv->get_disk_stats($vm) ;
|
||||
|
||||
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);
|
||||
$state = $lv->domain_state_translate($dom['state']);
|
||||
# if VM running shutdown. Record was running.
|
||||
if ($state != 'shutdown') $arrResponse = $lv->domain_destroy($vm) ;
|
||||
# Wait for shutdown?
|
||||
# GetXML
|
||||
$strXML= $lv->domain_get_xml($res) ;
|
||||
$xmlobj = custom::createArray('domain',$strXML) ;
|
||||
|
||||
# Process disks and update path.
|
||||
$disks=($snapslist[$snap]['disks']) ;
|
||||
foreach ($disks as $disk) {
|
||||
$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,$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) {
|
||||
$xmlobj['devices']['disk'][$ddk]['source']["@attributes"]['file'] = "$newpath" ;
|
||||
$xmlobj['devices']['disk'][$ddk]['driver']["@attributes"]['type'] = $json_info["format"] ;
|
||||
}
|
||||
}
|
||||
}
|
||||
$xml = custom::createXML('domain',$xmlobj)->saveXML();
|
||||
$new = $lv->domain_define($xml);
|
||||
if ($new)
|
||||
$arrResponse = ['success' => true] ; else
|
||||
$arrResponse = ['error' => $lv->get_last_error()] ;
|
||||
|
||||
# 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"] ;
|
||||
if (is_file($path) && $action == "yes") unlink("$path") ;
|
||||
$item = array_search($path,$snapslist[$snap]['backing']["r".$diskname]) ;
|
||||
$item++ ;
|
||||
while($item > 0)
|
||||
{
|
||||
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") ;
|
||||
}
|
||||
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' && $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 ;
|
||||
|
||||
}
|
||||
$arrResponse = ['success' => true] ;
|
||||
return($arrResponse) ;
|
||||
}
|
||||
|
||||
function vm_snapimages($vm, $snap, $only) {
|
||||
global $lv ;
|
||||
$snapslist= getvmsnapshots($vm) ;
|
||||
$data = "<br><br>Images and metadata to remove if tickbox checked.<br>" ;
|
||||
|
||||
$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"] ;
|
||||
}
|
||||
|
||||
$snapdisks= $snapslist[$snap]['disks'] ;
|
||||
|
||||
foreach ($snapdisks as $diskkey => $snapdisk) {
|
||||
$diskname = $snapdisk["@attributes"]["name"] ;
|
||||
if ($diskname == "hda" || $diskname == "hdb") continue ;
|
||||
$path = $snapdisk["source"]["@attributes"]["file"] ;
|
||||
if (is_file($path)) $data .= "$path<br>" ;
|
||||
$item = array_search($path,$snaps[$vm]["r".$diskname]) ;
|
||||
$item++ ;
|
||||
if ($only == 0) $item = 0 ;
|
||||
while($item > 0)
|
||||
{
|
||||
if (!isset($snaps[$vm]["r".$diskname][$item])) break ;
|
||||
$newpath = $snaps[$vm]["r".$diskname][$item] ;
|
||||
if (is_file($path)) $data .= "$newpath<br>" ;
|
||||
$item++ ;
|
||||
|
||||
}
|
||||
}
|
||||
$data .= "<br>Snapshots metadata to remove." ;
|
||||
if ($only == 0) {
|
||||
$data .= "<br>$snap";
|
||||
} else {
|
||||
uasort($snapslist,'compare_creationtimelt') ;
|
||||
foreach($snapslist as $s) {
|
||||
$name = $s['name'] ;
|
||||
$data .= "<br>$name";
|
||||
if ($s['name'] == $snap) break ;
|
||||
}
|
||||
}
|
||||
return($data) ;
|
||||
}
|
||||
|
||||
|
||||
function vm_snapremove($vm, $snap) {
|
||||
global $lv ;
|
||||
$snapslist= getvmsnapshots($vm) ;
|
||||
$res = $lv->get_domain_by_name($vm);
|
||||
$dom = $lv->domain_get_info($res);
|
||||
|
||||
$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) ;
|
||||
}
|
||||
|
||||
# GetXML
|
||||
$strXML= $lv->domain_get_xml($res) ;
|
||||
$xmlobj = custom::createArray('domain',$strXML) ;
|
||||
|
||||
# Process disks.
|
||||
$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!==false) {
|
||||
$data = ["error" => "Image currently active for this domain."] ;
|
||||
return ($data) ;
|
||||
}
|
||||
}
|
||||
|
||||
$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)) {
|
||||
if(!unlink("$path")) {
|
||||
$data = ["error" => "Unable to remove image file $path"] ;
|
||||
return ($data) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 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)
|
||||
$data = ["error" => "Unable to remove snap metadata $snap"] ;
|
||||
else
|
||||
$data = ["success => 'true"] ;
|
||||
|
||||
return($data) ;
|
||||
}
|
||||
|
||||
function vm_blockcommit($vm, $snap ,$path,$base,$top,$pivot,$action) {
|
||||
global $lv ;
|
||||
/*
|
||||
NAME
|
||||
blockcommit - Start a block commit operation.
|
||||
|
||||
SYNOPSIS
|
||||
blockcommit <domain> <path> [--bandwidth <number>] [--base <string>] [--shallow] [--top <string>] [--active] [--delete] [--wait] [--verbose] [--timeout <number>] [--pivot] [--keep-overlay] [--async] [--keep-relative] [--bytes]
|
||||
|
||||
DESCRIPTION
|
||||
Commit changes from a snapshot down to its backing image.
|
||||
|
||||
OPTIONS
|
||||
[--domain] <string> domain name, id or uuid
|
||||
[--path] <string> fully-qualified path of disk
|
||||
--bandwidth <number> bandwidth limit in MiB/s
|
||||
--base <string> path of base file to commit into (default bottom of chain)
|
||||
--shallow use backing file of top as base
|
||||
--top <string> 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 <number> 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
|
||||
|
||||
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) ;
|
||||
|
||||
foreach($disks as $disk) {
|
||||
$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' ";
|
||||
}
|
||||
|
||||
$error = execCommand_nchan($cmdstr,$path) ;
|
||||
if (!$error) {
|
||||
$arrResponse = ['error' => substr($output[0],6) ] ;
|
||||
return($arrResponse) ;
|
||||
} else {
|
||||
$arrResponse = ['success' => true] ;
|
||||
}
|
||||
|
||||
}
|
||||
# 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)
|
||||
$data = ["error" => "Unable to remove snap metadata $snap"] ;
|
||||
else
|
||||
$data = ["success => 'true"] ;
|
||||
return $data ;
|
||||
|
||||
}
|
||||
|
||||
function vm_blockpull($vm, $snap ,$path,$base,$top,$pivot,$action) {
|
||||
global $lv ;
|
||||
/*
|
||||
NAME
|
||||
blockpull - Populate a disk from its backing image.
|
||||
|
||||
SYNOPSIS
|
||||
blockpull <domain> <path> [--bandwidth <number>] [--base <string>] [--wait] [--verbose] [--timeout <number>] [--async] [--keep-relative] [--bytes]
|
||||
|
||||
DESCRIPTION
|
||||
Populate a disk from its backing image.
|
||||
|
||||
OPTIONS
|
||||
[--domain] <string> domain name, id or uuid
|
||||
[--path] <string> fully-qualified path of disk
|
||||
--bandwidth <number> bandwidth limit in MiB/s
|
||||
--base <string> path of backing file in chain for a partial pull
|
||||
--wait wait for job to finish
|
||||
--verbose with --wait, display the progress
|
||||
--timeout <number> 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
|
||||
|
||||
|
||||
*/
|
||||
$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,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" ;
|
||||
$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 ($action) $cmdstr .= " $action ";
|
||||
|
||||
|
||||
$error = execCommand_nchan($cmdstr,$path) ;
|
||||
|
||||
if (!$error) {
|
||||
$arrResponse = ['error' => substr($output[0],6) ] ;
|
||||
return($arrResponse) ;
|
||||
} else {
|
||||
# Remove nvram snapshot
|
||||
$arrResponse = ['success' => true] ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
refresh_snapshots_database($vm) ;
|
||||
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) {
|
||||
/*
|
||||
NAME
|
||||
blockcopy - Start a block copy operation.
|
||||
|
||||
SYNOPSIS
|
||||
blockcopy <domain> <path> [--dest <string>] [--bandwidth <number>] [--shallow] [--reuse-external] [--blockdev] [--wait] [--verbose] [--timeout <number>] [--pivot] [--finish] [--async] [--xml <string>] [--format <string>] [--granularity <number>] [--buf-size <number>] [--bytes] [--transient-job] [--synchronous-writes] [--print-xml]
|
||||
|
||||
DESCRIPTION
|
||||
Copy a disk backing image chain to dest.
|
||||
|
||||
OPTIONS
|
||||
[--domain] <string> domain name, id or uuid
|
||||
[--path] <string> fully-qualified path of source disk
|
||||
--dest <string> path of the copy to create
|
||||
--bandwidth <number> 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 <number> 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 <string> filename containing XML description of the copy destination
|
||||
--format <string> format of the destination file
|
||||
--granularity <number> power-of-two granularity to use during the copy
|
||||
--buf-size <number> 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
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user