Finished CPU support in anvil-manage-server-system!

* Updated Get->available_resources() to record the maximum cores that
  can be allocated to a server. This is N-1 for hosts with 4 or less
  cores, or N-2 cores otherwise.

Signed-off-by: digimer <mkelly@alteeve.ca>
main
digimer 1 year ago
parent b4037fade5
commit 8ce1f04335
  1. 22
      Anvil/Tools/Get.pm
  2. 9
      man/anvil-manage-server-system.8
  3. 11
      share/words.xml
  4. 351
      tools/anvil-manage-server-system

@ -464,6 +464,7 @@ Data is store in the following hashes;
anvil_resources::<anvil_uuid>::cpu::cores
anvil_resources::<anvil_uuid>::cpu::threads
anvil_resources::<anvil_uuid>::cpu::available
anvil_resources::<anvil_uuid>::ram::available
anvil_resources::<anvil_uuid>::ram::allocated
anvil_resources::<anvil_uuid>::ram::hardware
@ -529,6 +530,7 @@ sub available_resources
# This will store the available resources based on the least of the nodes.
$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores} = 0;
$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads} = 0;
$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{available} = 0;
$anvil->data->{anvil_resources}{$anvil_uuid}{ram}{hardware} = 0;
foreach my $host_uuid ($node1_host_uuid, $node2_host_uuid)
{
@ -637,6 +639,26 @@ WHERE
}});
}
# How many can be allocated to a single VM?
$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{available} = $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"anvil_resources::${anvil_uuid}::cpu::available" => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{available},
}});
if ($anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{available} <= 4)
{
$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{available} -= 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"anvil_resources::${anvil_uuid}::cpu::available" => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{available},
}});
}
else
{
$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{available} -= 2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"anvil_resources::${anvil_uuid}::cpu::available" => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{available},
}});
}
if ((not $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available}) or
($scan_hardware_ram_total < $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{hardware}))
{

@ -29,6 +29,15 @@ When called without a value, it shows if the boot menu is enabled or not. If cal
\fB\-\-boot\-order\fR <dev1,dev2,...,devN>
When called without a value, it shows if the current order of boot devices. To set a new boot device order, use a comma-separated list of boot devices. Note that any boot devices that are not specified will be moved in their original order to boot after the specified devices are moved up. For this reason, you can just specify the device you want to boot, and it will be moved to the front of the list.
.TP
\fB\-\-cpu\fR <cores or sockets,cores or sockets,cores,threads>
If called without a value, the current CPU core allocation is shown. To change it, you can specify simply a core count, which will appear as a single socket CPU with the specified number of cores. The maximum total number of cores is two-less than the total cores on the host Anvil! node.
.TP
.BR
Alternatively, you can specify '--cput x,y', where 'x' will be the number of sockets to emulate, and 'y' will be the number of cores per socket. Example; '--cpu 2,4' would give 8 total cores, emulates as 2 sockets, each with a 4-core CPU.
.TP
.BR
Lastly, you can specify '--cput x,y,z', where the 'z' adds how many threads to use. Example; '--cpu 1,2,2' would give 8 total cores, emulates as 1 socket, each with a 2-core CPU, and each core being 2 threads.
.TP
\fB\-\-job\-uuid\fR
This is the jobs -> job_uuid to execute. Generally this is only used by other programs.
.TP

@ -748,6 +748,8 @@ The XML that failed sanity check was:
<key name="error_0473">[ Error ] - The requested size: [#!variable!requested_ram!#] is not a valid size. Please specify the size in bytes, or specify the size with a base-2 suffix, like '8GiB'.</key>
<key name="error_0474">[ Error ] - The requested size: [#!variable!requested_ram!#] is greater than the maximum RAM: [#!variable!max_ram!#].</key>
<key name="error_0475">[ Error ] - The requested size: [#!variable!requested_ram!#] is less than 640 KiB, this must be a mistake.</key>
<key name="error_0476">[ Error ] - The requested CPU: [#!variable!requested_cpu!#] is not valid. Valid options are '--cpu X', '--cpu Y,X' or '--cpu 'Y,X,Z' where 'X' is the number of cores per socket, 'Y' is the number of sockets and 'Z' is the number of threads per core.</key>
<key name="error_0477">[ Error ] - The requested number of cores: [#!variable!requested_cores!#] (sockets: [#!variable!new_sockets!], cores per socket: [#!variable!new_cores!#], threads per core: [#!variable!new_threads!#]).</key>
<!-- Files templates -->
<!-- NOTE: Translating these files requires an understanding of which lines are translatable -->
@ -3260,6 +3262,15 @@ proceeding.
<key name="message_0378">- RAM in use, [#!variable!ram_in_use!#], configured: [#!variable!configured_ram!#], maximum: [#!variable!max_ram!#]
- RAM Available: [#!variable!available_ram!#]</key>
<key name="message_0379">Update the system RAM from: [#!variable!configured_ram!#] to: [#!variable!requested_ram!#]?</key>
<key name="message_0380">- The CPU core count is: [#!variable!total_cores!#] specified as;
- Sockets: [#!variable!sockets!#], Cores per socket: [#!variable!cores!#], Threads per code: [#!variable!threads!#]
- The Anvil! node has: [#!variable!host_cores!#] cores and: [#!variable!host_threads!#] threads.
- The maximum that can be allocated is: [#!variable!max_cores!#], with the recommended max cores being: [#!variable!recommended_max!#].</key>
<key name="message_0381"><![CDATA[- Changing the CPU allocation;
- Sockets: ........ [#!variable!old_sockets!#] -> [#!variable!new_sockets!#]
- Cores per Socket: [#!variable!old_cores!#] -> [#!variable!new_cores!#]
- Threads per Core: [#!variable!old_threads!#] -> [#!variable!new_threads!#]
- Total Cores: .... [#!variable!old_total_cores!#] -> [#!variable!new_total_cores!#]]]></key>
<!-- Translate names (protocols, etc) -->
<key name="name_0001">Normal Password</key> <!-- none in mail-server -->

@ -43,6 +43,7 @@ $anvil->Get->switches({list => [
"boot-menu",
"boot-order",
"confirm",
"cpu",
"disk",
"eject",
"job-uuid",
@ -104,7 +105,7 @@ validate_server($anvil);
if ($anvil->data->{switches}{cpu})
{
#manage_cpu($anvil);
manage_cpu($anvil);
}
elsif ($anvil->data->{switches}{ram})
{
@ -135,6 +136,329 @@ $anvil->nice_exit({exit_code => 0});
# Functions #
#############################################################################################################
sub manage_cpu
{
my ($anvil) = @_;
my $short_host_name = $anvil->Get->short_host_name;
my $host_uuid = $anvil->Get->host_uuid;
my $server_name = $anvil->data->{switches}{server_name};
my $server_uuid = $anvil->data->{switches}{server_uuid};
my $server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid};
my $anvil_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid};
my $anvil_name = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name};
my $server_host_name = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:short_host_name' => $short_host_name,
's2:host_uuid' => $host_uuid,
's3:server_name' => $server_name,
's4:server_uuid' => $server_uuid,
's5:server_host_uuid' => $server_host_uuid,
's6:anvil_uuid' => $anvil_uuid,
's7:anvil_name' => $anvil_name,
}});
my $from_source = get_definition_source($anvil);
my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
from_source => $from_source,
server_state => $server_state,
}});
if ($server_state eq "running")
{
$server_host_name = $anvil->Database->get_host_from_uuid({
short => 1,
host_uuid => $server_host_uuid,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_host_name => $server_host_name }});
}
# Get available resources.
$anvil->Get->available_resources({
debug => 2,
anvil_uuid => $anvil_uuid,
});
### TODO: Add support for changing the model, fallback, etc, and features
my $total_cores = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{total_cores};
my $sockets = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{sockets};
my $cores = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{cores};
my $threads = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{threads};
my $model_name = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{model_name};
my $model_fallback = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{model_fallback};
my $match = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{match};
my $vendor = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{vendor};
my $mode = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{mode};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
total_cores => $total_cores,
sockets => $sockets,
cores => $cores,
threads => $threads,
model_name => $model_name,
model_fallback => $model_fallback,
match => $match,
vendor => $vendor,
mode => $mode,
}});
foreach my $name (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{feature}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
's1:name' => $name,
's2:value' => $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{cpu}{feature}{$name},
}});
}
my $host_cores = $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores};
my $host_threads = $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads};
my $max_cores = $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{available};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:host_cores' => $host_cores,
's2:host_threads' => $host_threads,
's3:max_cores' => $max_cores,
}});
my $recommended_max = $host_cores - 2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { recommended_max => $recommended_max }});
if ($host_cores <= 2)
{
$recommended_max = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { recommended_max => $recommended_max }});
}
# Show the user or change?
if ($anvil->data->{switches}{cpu} eq "#!SET!#")
{
# Show the CPU info
my $variables = {
total_cores => $total_cores,
sockets => $sockets,
cores => $cores,
threads => $threads,
host_cores => $host_cores,
host_threads => $host_threads,
max_cores => $max_cores,
recommended_max => $recommended_max,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0380", variables => $variables});
$anvil->Job->update_progress({
progress => 100,
message => "message_0380",
variables => $variables,
}) if $anvil->data->{switches}{'job-uuid'};
return(0);
}
my $new_sockets = 1;
my $new_cores = 1;
my $new_threads = 1;
if ($anvil->data->{switches}{cpu} =~ /^(\d+),(\d+),(\d+)$/)
{
$new_sockets = $1;
$new_cores = $2;
$new_threads = $3;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:new_sockets' => $new_sockets,
's2:new_cores' => $new_cores,
's3:new_threads' => $new_threads,
}});
}
elsif ($anvil->data->{switches}{cpu} =~ /^(\d+),(\d+)$/)
{
$new_sockets = $1;
$new_cores = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:new_sockets' => $new_sockets,
's2:new_cores' => $new_cores,
}});
}
elsif ($anvil->data->{switches}{cpu} =~ /^(\d+)$/)
{
$new_cores = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_cores => $new_cores }});
}
else
{
# Bad formatting.
my $variables = { requested_cpu => $anvil->data->{switches}{cpu} };
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0476", variables => $variables});
$anvil->Job->update_progress({
progress => 100,
message => "error_0476",
variables => $variables,
}) if $anvil->data->{switches}{'job-uuid'};
$anvil->nice_exit({exit_code => 1});
}
# Is the requested number of cores sane?
my $requested_cores = $new_sockets * $new_cores * $new_threads;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { requested_cores => $requested_cores }});
if (($requested_cores < 1) or ($requested_cores > $max_cores))
{
my $variables = {
requested_cores => $requested_cores,
new_sockets => $new_sockets,
new_cores => $new_cores,
new_threads => $new_threads,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0477", variables => $variables});
$anvil->Job->update_progress({
progress => 100,
message => "error_0477",
variables => $variables,
}) if $anvil->data->{switches}{'job-uuid'};
$anvil->nice_exit({exit_code => 1});
}
# Still alive?
my $in_cpu = 0;
my $topology_seen = 0;
my $topology_line = "<topology sockets=\"".$new_sockets."\" dies=\"1\" cores=\"".$new_cores."\" threads=\"".$new_threads."\"/>";
my $new_server_definition = "";
foreach my $line (split/\n/, $anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
if ($line =~ /<vcpu/)
{
$line =~ s/<vcpu (.*?)>\d+<\/vcpu>/<vcpu $1>$requested_cores<\/vcpu>/;
$line =~ s/<vcpu>\d+<\/vcpu>/<vcpu>$requested_cores<\/vcpu>/;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
}
if ($in_cpu)
{
if ($line =~ /<\/cpu>/)
{
if (not $topology_seen)
{
$new_server_definition .= " ".$topology_line."\n";
}
$in_cpu = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_cpu => $in_cpu }});
}
if ($line =~ /<topology /)
{
$line = " ".$topology_line;
$topology_seen = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { topology_seen => $topology_seen }});
}
}
if ($line =~ /<cpu/)
{
# <cpu mode="host-passthrough" check="none" migratable="on"/>
if ($line =~ /<cpu (.*?)\/>/)
{
# Adjust to add topology
my $arguments = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { arguments => $arguments }});
$new_server_definition .= " <cpu ".$arguments.">\n";
$new_server_definition .= " ".$topology_line."\n";
$new_server_definition .= " </cpu>\n";
next;
}
$in_cpu = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_cpu => $in_cpu }});
}
$new_server_definition .= $line."\n";
}
# Confirm the new definition is valid
my $problem = $anvil->Server->parse_definition({
debug => 2,
host => $short_host_name,
server => $server_name,
source => "test_xml",
definition => $new_server_definition,
});
if ($problem)
{
# The new definition is bad
my $variables = { new_server_definition => $new_server_definition };
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0470", variables => $variables});
$anvil->Job->update_progress({
progress => 100,
message => "error_0470",
variables => $variables,
}) if $anvil->data->{switches}{'job-uuid'};
$anvil->nice_exit({exit_code => 1});
}
# Did the user confirm?
if ($anvil->data->{switches}{'job-uuid'})
{
# Running as a job, don't prompt
$anvil->Job->update_progress({
progress => 75,
message => "job_0480",
});
}
elsif ($anvil->data->{switches}{confirm})
{
# User confirmed
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0359"});
}
else
{
# Ask the user to confirm.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0381", variables => {
old_total_cores => $total_cores,
old_sockets => $sockets,
old_cores => $cores,
old_threads => $threads,
new_total_cores => $requested_cores,
new_sockets => $new_sockets,
new_cores => $new_cores,
new_threads => $new_threads,
}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0021"});
my $answer = <STDIN>;
chomp $answer;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "log_0828", variables => { answer => $answer }});
if ((lc($answer) eq "y") or (lc($answer) eq "yes"))
{
# Proceed
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0175"});
}
else
{
# Abort
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0022"});
$anvil->nice_exit({exit_code => 0});
}
}
# Record the new config.
$problem = $anvil->Server->update_definition({
debug => 2,
server => $server_uuid,
new_definition_xml => $new_server_definition,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
if ($problem)
{
# Failed!
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0471"});
$anvil->Job->update_progress({
progress => 100,
message => "error_0471",
}) if $anvil->data->{switches}{'job-uuid'};
$anvil->nice_exit({exit_code => 1});
}
else
{
# Done!
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "log_0750"});
$anvil->Job->update_progress({
progress => 100,
message => "log_0750",
}) if $anvil->data->{switches}{'job-uuid'};
}
return(0);
}
sub manage_ram
{
my ($anvil) = @_;
@ -201,7 +525,7 @@ sub manage_ram
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0378", variables => $variables});
$anvil->Job->update_progress({
progress => 25,
progress => 100,
message => "message_0378",
variables => $variables,
}) if $anvil->data->{switches}{'job-uuid'};
@ -296,6 +620,27 @@ sub manage_ram
$new_server_definition .= $line."\n";
}
# Confirm the new definition is valid
my $problem = $anvil->Server->parse_definition({
debug => 2,
host => $short_host_name,
server => $server_name,
source => "test_xml",
definition => $new_server_definition,
});
if ($problem)
{
# The new definition is bad
my $variables = { new_server_definition => $new_server_definition };
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0470", variables => $variables});
$anvil->Job->update_progress({
progress => 100,
message => "error_0470",
variables => $variables,
}) if $anvil->data->{switches}{'job-uuid'};
$anvil->nice_exit({exit_code => 1});
}
# Did the user confirm?
if ($anvil->data->{switches}{'job-uuid'})
{
@ -335,7 +680,7 @@ sub manage_ram
}
# Record the new config.
my $problem = $anvil->Server->update_definition({
$problem = $anvil->Server->update_definition({
debug => 2,
server => $server_uuid,
new_definition_xml => $new_server_definition,

Loading…
Cancel
Save