diff --git a/emhttp/plugins/dynamix.vm.manager/VMMachines.page b/emhttp/plugins/dynamix.vm.manager/VMMachines.page index 3cfef97f2..682ef6122 100644 --- a/emhttp/plugins/dynamix.vm.manager/VMMachines.page +++ b/emhttp/plugins/dynamix.vm.manager/VMMachines.page @@ -31,6 +31,8 @@ function showCPUs($uuid) { global $cpus; $vm = domain_to_config($uuid); $vcpu = $vm['domain']['vcpu']; + $nopining = ""; + if (!is_array($vcpu)) $nopining = _("No pinning set"); echo "
"; foreach ($cpus as $pair) { unset($cpu1,$cpu2); @@ -44,6 +46,7 @@ function showCPUs($uuid) { echo ""; } } + echo "
vCPUs: {$vm['domain']['vcpus']} $nopining"; echo "
"; } function vsize($size,$expand=true) { diff --git a/emhttp/plugins/dynamix.vm.manager/include/libvirt.php b/emhttp/plugins/dynamix.vm.manager/include/libvirt.php index c4401447b..288c1a75e 100644 --- a/emhttp/plugins/dynamix.vm.manager/include/libvirt.php +++ b/emhttp/plugins/dynamix.vm.manager/include/libvirt.php @@ -376,7 +376,7 @@ $metadata .= ""; } - $vcpus = 1; + $vcpus = $domain['vcpus']; $vcpupinstr = ''; if (!empty($domain['vcpu']) && is_array($domain['vcpu'])) { @@ -384,13 +384,8 @@ foreach($domain['vcpu'] as $i => $vcpu) { $vcpupinstr .= ""; } - } elseif (!empty($domain['vcpus'])) { - $vcpus = $domain['vcpus']; - for ($i=0; $i < $vcpus; $i++) { - $vcpupinstr .= ""; - } - } - + } + $intCores = $vcpus; $intThreads = 1; $intCPUThreadsPerCore = 1; diff --git a/emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php b/emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php index 32553cf43..024b0f21e 100644 --- a/emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php +++ b/emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php @@ -496,13 +496,45 @@ + + + + + + +
_(CPU Cores)_ + + +
+
+
+

There are two CPU modes available to choose:

+

+ vCPUs Allocated
+ Set the number of vCPUs allocated to the VM when not using pinning. The host will dynamically allocate workload for the VM across the whole system. +

+
+ "; $cpuset = implode(';',$cpus); -function create() { +function create($type = "") { // create the table header. Make multiple rows when CPU cores are many ;) global $total, $cpus; $loop = floor(($total-1)/32) + 1; @@ -38,6 +38,7 @@ function create() { } } $label = implode('
', array_fill(0, $loop, 'CPU:' . ($cpu2 ? '
CPU:' : ''))); + if ($type == "vm") echo ""; echo "" . implode(array_map(function($t) { return ""; }, $text)); @@ -59,7 +60,8 @@ function apply(form) { var args = { 'id': id, 'names': form.names.value.encode(), - 'cpus': {} + 'cpus': {}, + 'cores': {} }; /* get the 'checked' cpus */ @@ -69,6 +71,13 @@ function apply(form) { } }); + /* get the vcpus */ + $(form).find('select').each(function() { + if ($(this).prop('id')) { + args['cores'][$(this).prop('name').encode()] = $(this).prop('value'); + } + }); + /* show the instant wait message */ $('#wait-' + id).show(); @@ -94,7 +103,8 @@ function apply(form) { wait = data.length; for (var i = 0; i < data.length; i++) { var name = data[i]; - $('#' + id + '-' + name.strip()).show('slow'); + //$('#' + id + '-' + name.strip()).show('slow'); + $('#' + CSS.escape(id + '-' + name.strip().replace(/!/g, '%21'))).show('slow'); /* step 2: apply the changes by updating the vm or container */ $.post('/webGui/include/UpdateTwo.php', { @@ -215,6 +225,26 @@ function reset(form) { } } +function vcpupins($button) { + + var $name = $button.id.split(";")[1]; + var $value = $button.value; + var $form = $('form[name="vm"]'); + + if ($value == "_(Select All)_") { + $("[class='vcpu-" + $name + "']").prop('checked', true); + var $cores = $("[class='vcpu-" + $name + "']:checked"); + $("[class~='vcpus-" + $name + "']").prop("disabled", true).prop("value", $cores.length); + $button.value = "_(Deselect All)_"; + $($form).find('input[value="_(Apply)_"]').prop('disabled', false); + } else { + $("[class='vcpu-" + $name + "']").prop('checked', false); + $("[class~='vcpus-" + $name + "']").prop("disabled", false).prop("value", 1); + $button.value = "_(Select All)_"; + $($form).find('input[value="_(Apply)_"]').prop('disabled', false); + } +} + /* Function to handle checkbox interactions and enable/disable form buttons */ function buttons(form) { $(form).find('input[type=checkbox]').each(function() { @@ -224,11 +254,32 @@ function buttons(form) { var cores = $(form).find(checked).length; /* Ensure VMs have at least one core selected */ - if ($(form).prop('name') == 'vm') $(form).find(checked).prop('disabled', cores < 2); - + if ($(form).prop('name') == 'vm' ) { + value = $(this).prop('name').split(':')[0]; + var testname = "vmbtnvCPUSelect" + value ; + if (cores < 1) + { + $(form).find('select[name="' + $(this).prop('name').split(':')[0] + '"]').prop('disabled', false).prop("value",1); + $(form).find('input[name="vmbtnvCPUSelect' + $(this).prop('name').split(':')[0] + '"]').prop("value","_(Select All)_"); + } else { + $(form).find('select[name="' + $(this).prop('name').split(':')[0] + '"]').prop('disabled', true).prop("value",cores); + $(form).find('input[name="vmbtnvCPUSelect' + $(this).prop('name').split(':')[0] + '"]').prop("value","_(Deselect All)_"); + } + } /* Ensure isolation does not have all cores selected */ if ($(form).prop('name') == 'is' && $(this).prop('checked')) $(this).prop('checked', cores < total); + /* Enable Apply and Done buttons when changes are made */ + $(form).find('input[value="_(Apply)_"]').prop('disabled', false); + $(form).find('input[value="_(Done)_"]').val("_(Reset)_").prop('onclick', null).off('click').click(function() { reset(form); }); + }); + }); + $(form).find('select').each(function() { + $(this).on('change', function() { + var total = $(form).find('input[type=checkbox]').length; + var checked = 'input[name^="' + $(this).prop('name').split(':')[0] + ':' + '"]:checked'; + var cores = $(form).find(checked).length; + /* Enable Apply and Done buttons when changes are made */ $(form).find('input[value="_(Apply)_"]').prop('disabled', false); $(form).find('input[value="_(Done)_"]').val("_(Reset)_").prop('onclick', null).off('click').click(function() { reset(form); }); @@ -252,7 +303,7 @@ $(function() {
_(Logical CPUs)_:
-
VPCUS$label$t
- +
_(VM)_
_(VM)_
diff --git a/emhttp/plugins/dynamix/include/CPUset.php b/emhttp/plugins/dynamix/include/CPUset.php index bbc4100e0..bca85c112 100644 --- a/emhttp/plugins/dynamix/include/CPUset.php +++ b/emhttp/plugins/dynamix/include/CPUset.php @@ -19,6 +19,12 @@ $_SERVER['REQUEST_URI'] = 'settings'; require_once "$docroot/webGui/include/Translations.php"; $cpus = explode(';',$_POST['cpus']??''); +$corecount = 0; +foreach ($cpus as $pair) { + unset($cpu1,$cpu2); + [$cpu1, $cpu2] = my_preg_split('/[,-]/',$pair); + if (!$cpu2) $corecount++; else $corecount=$corecount+2; +} function scan($area, $text) { return isset($area) ? strpos($area,$text)!==false : false; @@ -30,9 +36,10 @@ function create($id, $name, $vcpu) { $total = count($cpus); $loop = floor(($total-1)/32)+1; $text = []; - $unit = str_replace([' ','(',')','[',']'],'',$name); + $unit = urlencode(str_replace([' ','(',')','[',']'],'',$name)); $name = urlencode($name); echo ""; + if ($id == "vm") $checkclass = "class=\"vcpu-".htmlspecialchars($name)."\""; else $checkclass = ""; for ($c = 0; $c < $loop; $c++) { $max = ($c == $loop-1 ? ($total%32?:32) : 32); for ($n = 0; $n < $max; $n++) { @@ -41,8 +48,8 @@ function create($id, $name, $vcpu) { $check1 = ($vcpu && in_array($cpu1, $vcpu)) ? 'checked':''; $check2 = $cpu2 ? ($vcpu && (in_array($cpu2, $vcpu)) ? 'checked':''):''; if (empty($text[$n])) $text[$n] = ''; - $text[$n] .="
"; - if ($cpu2) $text[$n] .= "
"; + $text[$n] .="
"; + if ($cpu2) $text[$n] .= "
"; } } echo implode(array_map(function($t){return "$t";},$text)); @@ -66,6 +73,15 @@ case 'vm': $uuid = $lv->domain_get_uuid($lv->get_domain_by_name($vm)); $cfg = domain_to_config($uuid); echo "$vm"; + if ($cfg['domain']['vcpu'] >0 ) $disabled = "disabled"; else $disabled = ""; + $vmenc = urlencode($vm); + $vcpuselect="'; + if ($disabled == "disabled") $buttontext = htmlspecialchars("Deselect All"); else $buttontext = htmlspecialchars("Select All"); + echo "$vcpuselect"; create('vm', $vm, $cfg['domain']['vcpu']); echo ""; } diff --git a/emhttp/plugins/dynamix/include/UpdateOne.php b/emhttp/plugins/dynamix/include/UpdateOne.php index 8a5d2b576..8d0a4ec8e 100644 --- a/emhttp/plugins/dynamix/include/UpdateOne.php +++ b/emhttp/plugins/dynamix/include/UpdateOne.php @@ -16,7 +16,7 @@ require_once "$docroot/webGui/include/Helpers.php"; $_SERVER['REQUEST_URI'] = 'settings'; require_once "$docroot/webGui/include/Translations.php"; -$map = $changes = []; +$vcpus = $map = $changes = []; $data = json_decode($_POST['data'], true); @@ -30,6 +30,9 @@ foreach ($data['cpus'] as $key => $val) { [$name, $cpu] = my_explode(':', $key); $map[decode($name)] .= "$cpu,"; } +foreach ($data['cores'] as $name => $val) { + $vcpu[decode($name)] .= $val; +} /* map holds the list of each vm, container or isolcpus and its newly proposed cpu assignments */ $map = array_map(function($d) { return substr($d, 0, -1); @@ -40,13 +43,13 @@ switch ($data['id']) { /* report changed vms in temporary file */ require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php"; foreach ($map as $name => $cpuset) { - if (!strlen($cpuset)) { - $reply = ['error' => _("Not allowed to assign ZERO cores")]; - break 2; - } $uuid = $lv->domain_get_uuid($lv->get_domain_by_name($name)); $cfg = domain_to_config($uuid); - $cpus = implode(',', $cfg['domain']['vcpu']); + if ($cfg['domain']['vcpu']) $cpus = implode(',', $cfg['domain']['vcpu']); else $cpus = ""; + if (!strlen($cpuset)) { + $cpuset = -1 * $vcpu[$name]; + $cpus = -1 * $cfg['domain']['vcpus']; + } /* only act on changes */ if ($cpus != $cpuset || strlen($cpus) != strlen($cpuset)) { $changes[] = $name; diff --git a/emhttp/plugins/dynamix/include/UpdateTwo.php b/emhttp/plugins/dynamix/include/UpdateTwo.php index 1c63408d9..d81628d0c 100644 --- a/emhttp/plugins/dynamix/include/UpdateTwo.php +++ b/emhttp/plugins/dynamix/include/UpdateTwo.php @@ -40,7 +40,8 @@ case 'vm': /* Read new CPU assignments and delete the temporary file */ $cpuset = explode(',', file_get_contents($file)); unlink($file); - $vcpus = count($cpuset); + $nopin = false; + if ($cpuset[0] >= 0) $vcpus = count($cpuset); else {$vcpus = -1 * $cpuset[0]; $nopin = true; } /* Initial cores/threads assignment */ $cores = $vcpus; @@ -68,22 +69,26 @@ case 'vm': /* Preserve existing emulatorpin attributes */ $pin = []; - foreach ($xml->cputune->emulatorpin->attributes() as $key => $value) { - $pin[$key] = (string) $value; + if (isset($xml->cputune)) { + foreach ($xml->cputune->emulatorpin->attributes() as $key => $value) { + $pin[$key] = (string) $value; + } } unset($xml->cputune); /* Add new cputune configuration */ - $xml->addChild('cputune'); - for ($i = 0; $i < $vcpus; $i++) { - $vcpu = $xml->cputune->addChild('vcpupin'); - $vcpu['vcpu'] = $i; - $vcpu['cpuset'] = _var($cpuset, $i); - } - if ($pin) { - $attr = $xml->cputune->addChild('emulatorpin'); - foreach ($pin as $key => $value) { - $attr[$key] = $value; + if (!$nopin) { + $xml->addChild('cputune'); + for ($i = 0; $i < $vcpus; $i++) { + $vcpu = $xml->cputune->addChild('vcpupin'); + $vcpu['vcpu'] = $i; + $vcpu['cpuset'] = _var($cpuset, $i); + } + if ($pin) { + $attr = $xml->cputune->addChild('emulatorpin'); + foreach ($pin as $key => $value) { + $attr[$key] = $value; + } } } @@ -109,7 +114,7 @@ case 'vm': /* Backup NVRAM, undefine the domain, and restore NVRAM */ $lv->nvram_backup($uuid); - $lv->domain_undefine($dom); + #$lv->domain_undefine($dom); $lv->nvram_restore($uuid); /* Define the domain with the updated XML configuration */ diff --git a/emhttp/plugins/dynamix/sheets/CPUvms.css b/emhttp/plugins/dynamix/sheets/CPUvms.css index bbb3cdabf..d5f13a907 100644 --- a/emhttp/plugins/dynamix/sheets/CPUvms.css +++ b/emhttp/plugins/dynamix/sheets/CPUvms.css @@ -1,6 +1,7 @@ table.tablesorter tr>th+th{text-align:right;vertical-align:top} -table.tablesorter tr>td+td+td{vertical-align:top} +table.tablesorter tr>td+td+td+td{vertical-align:top} table.tablesorter tr>th+th+th,table.tablesorter tr>td+td{text-align:center} table.tablesorter th:first-child,table.tablesorter td:first-child{width:180px;text-align:left} input.flat{margin:0} input.lock{margin:0} +select.narrow{min-width:25px;align-items:right;}