MAC Address option and ZFS ref link

This commit is contained in:
SimonFair
2025-06-03 17:29:04 +01:00
parent b08c70a5e7
commit 9736e09824
4 changed files with 94 additions and 10 deletions

View File

@@ -234,7 +234,8 @@ function VMClone(uuid, name){
var edit = box.find('#Edit').prop('checked') ? 'yes' : 'no';
var overwrite = box.find('#Overwrite').prop('checked') ? 'yes' : 'no';
var free = box.find('#Free').prop('checked') ? 'yes' : 'no';
scripturl = "VMClone.php " + encodeURIComponent("/usr/local/emhttp/plugins/dynamix.vm.manager/include/VMClone.php&" + $.param({action:"clone", name:name, clone:clone, overwrite:overwrite, edit:edit, start:start, free:free}));
var regenmac = box.find('#Regenmac').prop('checked') ? 'yes' : 'no';
scripturl = "VMClone.php " + encodeURIComponent("/usr/local/emhttp/plugins/dynamix.vm.manager/include/VMClone.php&" + $.param({action:"clone", name:name, clone:clone, overwrite:overwrite, edit:edit, start:start, free:free, regenmac:regenmac}));
openVMAction((scripturl),"VM Clone", "dynamix.vm.manager", "loadlist");
box.dialog('close');
},
@@ -572,6 +573,7 @@ _(Snapshot Name)_: <input type="text" id="targetsnap" hidden><label id="targetsn
<tr><td>_(Overwrite)_:</td><td><input type="checkbox" id="Overwrite" value="" checked></td></tr>
<tr hidden><td>_(Start Cloned VM)_:</td><td><input type="checkbox" id="Start" value="" ></td></tr>
<tr hidden><td>_(Edit VM after clone)_:</td><td><input type="checkbox" id="Edit" value="" ></td></tr>
<tr><td>_(Regenerate MAC addresses)_:</td><td><input type="checkbox" id="Regenmac" value="" checked ></td></tr>
<tr><td>_(Check free space)_:</td><td><input type="checkbox" id="Free" value="" checked></td></tr>
</table>
</div>

View File

@@ -1727,7 +1727,7 @@ class Array2XML {
return $copypaste;
}
function vm_clone($vm, $clone ,$overwrite,$start,$edit, $free, $waitID) {
function vm_clone($vm, $clone ,$overwrite,$start,$edit, $free, $waitID, $regenmac) {
global $lv,$domain_cfg,$arrDisplayOptions;
/*
Clone.
@@ -1800,9 +1800,12 @@ class Array2XML {
$config["domain"]["name"] = $clone;
$config["domain"]["uuid"] = $lv->domain_generate_uuid();
foreach($config["nic"] as $index => $detail) {
$config["nic"][$index]["mac"] = $lv->generate_random_mac_addr();
}
if ($regenmac == "yes") {
write("addLog\0".htmlspecialchars(_("Regenerate MACs")));
foreach($config["nic"] as $index => $detail) {
$config["nic"][$index]["mac"] = $lv->generate_random_mac_addr();
}
} else write("addLog\0".htmlspecialchars(_("Retain existing MACs")));
$config["domain"]["type"] = "kvm";
$usbs = getVMUSBs($vmxml);
@@ -1831,7 +1834,7 @@ class Array2XML {
if (!is_dir($clonedir)) {
my_mkdir($clonedir,0777,true);
}
write("addLog\0".htmlspecialchars("Checking for image files"));
write("addLog\0".htmlspecialchars(_("Checking for image files")));
if ($file_exists && $overwrite != "yes") { write("addLog\0".htmlspecialchars(_("New image file names exist and Overwrite is not allowed"))); return( false); }
#Create duplicate files.
@@ -1844,8 +1847,18 @@ class Array2XML {
$reptgt = str_replace('/mnt/user/', "/mnt/$sourcerealdisk/", $target);
$repsrc = str_replace('/mnt/user/', "/mnt/$sourcerealdisk/", $source);
}
$cmdstr = "cp --reflink=always '$repsrc' '$reptgt'";
if ($reflink == true) { $refcmd = $cmdstr; } else {$refcmd = false; }
$refresult = getFilesystemAndReflinkMode($repsrc);
$refcmdaction = $refresult['reflink_mode'];
$refcmdfs = $refresult['filesystem'];
$cmdstr = "cp --reflink=$refcmdaction '$repsrc' '$reptgt'";
write("addLog\0".htmlspecialchars(_("Reflink Action")." ".$refcmdaction." "._("for filesystem")." ".$refcmdfs));
if ($reflink == true) { $refcmd = $cmdstr; } else {$refcmd = false; }
if ($refresult['filesystem'] == "zfs" && $refresult['reflink_mode'] == "skip") {
write("addLog\0".htmlspecialchars(_("Reflink copy not supported on ZFS skipping")));
$refcmd = false;
}
$cmdstr = "rsync -ahPIXS --out-format=%f --info=flist0,misc0,stats0,name1,progress2 '$repsrc' '$reptgt'";
$error = execCommand_nchan_clone($cmdstr,$target,$refcmd);
if (!$error) { write("addLog\0".htmlspecialchars("Image copied failed.")); return( false); }
@@ -2936,4 +2949,71 @@ function get_storage_fstype($storage="default") {
$fstype = trim(shell_exec(" stat -f -c '%T' $storage"));
return $fstype;
}
function getFilesystemAndReflinkMode(string $filePath): array {
// Default return values
$result = [
'filesystem' => 'unknown',
'reflink_mode' => 'always' // safest fallback if unsupported
];
$realPath = realpath($filePath);
if ($realPath === false || !file_exists($realPath)) {
return $result;
}
$dirname = dirname($realPath);
// Handle Unraid-style virtual paths
if (strpos($dirname, '/mnt/user/') === 0) {
error_log("Getting real disks\n");
$realdisk = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION " . escapeshellarg($dirname) . " 2>/dev/null"));
if (!empty($realdisk)) {
$dirname = str_replace('/mnt/user/', "/mnt/$realdisk/", $dirname);
}
}
// Get filesystem type
$fstype = trim(shell_exec("stat -f -c '%T' " . escapeshellarg($dirname)));
$result['filesystem'] = $fstype;
// If not ZFS, return early
if ($fstype !== 'zfs') {
return $result;
}
$result['reflink_mode'] = 'skip';
// Get dataset from the file
$dataset = trim(shell_exec("zfs list -H -o name " . escapeshellarg($dirname) . " 2>/dev/null"));
if (empty($dataset)) {
return $result;
}
// Get pool from dataset
$parts = explode('/', $dataset);
$pool = $parts[0] ?? '';
if (empty($pool)) {
return $result;
}
// Check ZFS block_cloning feature
$output = [];
$resultCode = 0;
exec("zpool get -H -o value feature@block_cloning " . escapeshellarg($pool) . " 2>/dev/null", $output, $resultCode);
if ($resultCode !== 0 || empty($output) || (trim($output[0]) !== 'enabled' && trim($output[0]) !== 'active')) {
return $result;
}
// Check if bclone is enabled at runtime
$bcloneValue = @file_get_contents("/sys/module/zfs/parameters/zfs_bclone_enabled");
if ($bcloneValue === false || trim($bcloneValue) !== '1') {
return $result;
}
// If both checks passed, block cloning is supported
$result['reflink_mode'] = 'auto';
return $result;
}
?>

View File

@@ -99,7 +99,7 @@ write("addLog\0".htmlspecialchars("Cloning $name to $clone"));
switch ($action) {
case "clone":
$rtn = vm_clone($name,$clone,$overwrite,$start,$edit,$free,$waitID) ;
$rtn = vm_clone($name,$clone,$overwrite,$start,$edit,$free,$waitID,$regenmac) ;
break ;
}
write("stop_Wait\0$waitID") ;

View File

@@ -698,6 +698,7 @@ function hideShow() {
<tr><td>_(Overwrite)_:</td><td><input type="checkbox" id="Overwrite" value="" ></td></tr>
<tr hidden><td>_(Start Cloned VM)_:</td><td><input type="checkbox" id="Start" value="" ></td></tr>
<tr hidden><td>_(Edit VM after clone)_:</td><td><input type="checkbox" id="Edit" value="" ></td></tr>
<tr><td>_(Regenerate MAC addresses)_:</td><td><input type="checkbox" id="Edit" value="" checked ></td></tr>
<tr><td>_(Check free space)_:</td><td><input type="checkbox" id="Free" value="" ></td></tr>
</table>
</div>
@@ -1464,7 +1465,8 @@ function VMClone(uuid, name){
var edit = box.find('#Edit').prop('checked') ? 'yes' : 'no';
var overwrite = box.find('#Overwrite').prop('checked') ? 'yes' : 'no';
var free = box.find('#Free').prop('checked') ? 'yes' : 'no';
var scripturl = "VMClone.php " + encodeURIComponent("/usr/local/emhttp/plugins/dynamix.vm.manager/include/VMClone.php&" + $.param({action:"clone", name:name, clone:clone, overwrite:overwrite, edit:edit, start:start, free:free}));
var regenmac = box.find('#Regenmac').prop('checked') ? 'yes' : 'no';
var scripturl = "VMClone.php " + encodeURIComponent("/usr/local/emhttp/plugins/dynamix.vm.manager/include/VMClone.php&" + $.param({action:"clone", name:name, clone:clone, overwrite:overwrite, edit:edit, start:start, free:free, regenmac:regenmac}));
openVMAction((scripturl),"VM Clone", "dynamix.vm.manager", "loadlist");
box.dialog('close');
},