diff --git a/Anvil/Tools/Cluster.pm b/Anvil/Tools/Cluster.pm index f04751dd..d773501d 100644 --- a/Anvil/Tools/Cluster.pm +++ b/Anvil/Tools/Cluster.pm @@ -14,8 +14,10 @@ our $VERSION = "3.0.0"; my $THIS_FILE = "Cluster.pm"; ### Methods; +# assemble_storage_groups # boot_server # check_node_status +# get_anvil_name # get_anvil_uuid # get_peers # migrate_server @@ -83,6 +85,268 @@ sub parent # Public methods # ############################################################################################################# + +=head2 assemble_storage_groups + +This method takes an Anvil! UUID and sees if there are any ungrouped LVM VGs that can be automatically grouped together. + +Parameters; + +=head3 anvil_uuid (required) + +This is the Anvil! UUID that we're looking for ungrouped VGs in. + +=cut +sub assemble_storage_groups +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 2; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Cluster->assemble_storage_groups()" }}); + + my $anvil_uuid = defined $parameter->{anvil_uuid} ? $parameter->{anvil_uuid} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + anvil_uuid=> $anvil_uuid, + }}); + + if (not $anvil_uuid) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Cluster->assemble_storage_groups()", parameter => "anvil_uuid" }}); + return("!!error!!"); + } + + # Get the node UUIDs for this anvil. + my $query = " +SELECT + anvil_name, + anvil_node1_host_uuid, + anvil_node2_host_uuid, + anvil_dr1_host_uuid +FROM + anvils +WHERE + anvil_uuid = ".$anvil->Database->quote($anvil_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + if (not $count) + { + # Not found. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0169", variables => { anvil_uuid => $anvil_uuid }}); + return("!!error!!"); + } + + # Get the details. + my $anvil_name = $results->[0]->[0]; + my $node1_host_uuid = $results->[0]->[1]; + my $node2_host_uuid = $results->[0]->[2]; + my $dr1_host_uuid = defined $results->[0]->[3] ? $results->[0]->[3] : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + anvil_name => $anvil_name, + node1_host_uuid => $node1_host_uuid, + node2_host_uuid => $node2_host_uuid, + dr1_host_uuid => $dr1_host_uuid, + }}); + + # Load known storage groups. + $anvil->Database->get_storage_group_data({debug => $debug}); + + # Look for ungrouped VGs and see if we can group them by matching identical sizes together. + foreach my $host_uuid ($node1_host_uuid, $node2_host_uuid, $dr1_host_uuid) + { + # If DR isn't defined, it'll be blank. + next if not $host_uuid; + my $this_is = "node1"; + if ($host_uuid eq $node2_host_uuid) { $this_is = "node2"; } + elsif ($host_uuid eq $dr1_host_uuid) { $this_is = "dr1"; } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_is => $this_is }}); + + $anvil->data->{ungrouped_vg_count}{$this_is} = 0; + + my $query = " +SELECT + scan_lvm_vg_uuid, + scan_lvm_vg_name, + scan_lvm_vg_extent_size, + scan_lvm_vg_size, + scan_lvm_vg_free, + scan_lvm_vg_internal_uuid +FROM + scan_lvm_vgs +WHERE + scan_lvm_vg_host_uuid = ".$anvil->Database->quote($host_uuid)." +ORDER BY + scan_lvm_vg_size ASC; +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + + foreach my $row (@{$results}) + { + my $scan_lvm_vg_size = $row->[3]; + my $scan_lvm_vg_internal_uuid = $row->[5]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + scan_lvm_vg_size => $scan_lvm_vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $scan_lvm_vg_size}).")", + scan_lvm_vg_internal_uuid => $scan_lvm_vg_internal_uuid, + }}); + + # Skip VGs that are in a group already. + if ((exists $anvil->data->{storage_groups}{vg_uuid}{$scan_lvm_vg_internal_uuid}) && + ($anvil->data->{storage_groups}{vg_uuid}{$scan_lvm_vg_internal_uuid}{storage_group_uuid})) + { + # Already in a group, we can skip it. We log this data for debugging reasons + # only. + my $storage_group_uuid = $anvil->data->{storage_groups}{vg_uuid}{$scan_lvm_vg_internal_uuid}{storage_group_uuid}; + my $group_name = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{group_name}; + my $storage_group_member_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$host_uuid}{vg_uuid}{$scan_lvm_vg_internal_uuid}{storage_group_member_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + anvil_uuid => $anvil_uuid, + host_uuid => $host_uuid, + storage_group_uuid => $storage_group_uuid, + scan_lvm_vg_internal_uuid => $scan_lvm_vg_internal_uuid, + storage_group_member_uuid => $storage_group_member_uuid, + }}); + next; + } + + $anvil->data->{ungrouped_vg_count}{$this_is}++; + $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_uuid} = $row->[0]; + $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_name} = $row->[1]; + $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_extent_size} = $row->[2]; + $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_size} = $row->[3]; + $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_free} = $row->[4]; + $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid} = $row->[5]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "ungrouped_vg_count::${this_is}" => $anvil->data->{ungrouped_vg_count}{$this_is}, + "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::count" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{count}, + "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_uuid" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_uuid}, + "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_name" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_name}, + "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_extent_size" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_extent_size}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_extent_size}}).")", + "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_size" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_size}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_size}}).")", + "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_free" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_free}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_free}}).")", + "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_internal_uuid" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid}, + }}); + } + } + + # Fing ungrouped VGs and see if we can group them. First by looking for identical sizes. + my $reload_storage_groups = 0; + foreach my $scan_lvm_vg_size (sort {$a cmp $b} keys %{$anvil->data->{ungrouped_vgs}}) + { + # If there are two or three VGs, we can create a group. + my $count = keys %{$anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}}; + if (($count == 2) or ($count == 3)) + { + # Create the volume group ... group. First we need a group number + my $storage_group_uuid = $anvil->Database->insert_or_update_storage_groups({ + debug => $debug, + storage_group_anvil_uuid => $anvil_uuid, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_uuid => $storage_group_uuid }}); + + foreach my $host_uuid (keys %{$anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}}) + { + my $this_is = "node1"; + if ($host_uuid eq $node2_host_uuid) { $this_is = "node2"; } + elsif ($host_uuid eq $dr1_host_uuid) { $this_is = "dr1"; } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_is => $this_is }}); + + my $storage_group_member_vg_uuid = $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_member_vg_uuid => $storage_group_member_vg_uuid }}); + + my $storage_group_member_uuid = $anvil->Database->insert_or_update_storage_group_members({ + debug => $debug, + storage_group_member_storage_group_uuid => $storage_group_uuid, + storage_group_member_host_uuid => $host_uuid, + storage_group_member_vg_uuid => $storage_group_member_vg_uuid, + }); + + $anvil->data->{ungrouped_vg_count}{$this_is}--; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "ungrouped_vg_count::${this_is}" => $anvil->data->{ungrouped_vg_count}{$this_is}, + }}); + } + + # Reload storage group data + $reload_storage_groups = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { reload_storage_groups => $reload_storage_groups }}); + } + } + + # If there's only one VG on each node that is ungrouped, group them even though they're not the same + # size. If DR also has only 1 VG ungrouped, it'll be added, too. + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "ungrouped_vg_count::node1" => $anvil->data->{ungrouped_vg_count}{node1}, + "ungrouped_vg_count::node2" => $anvil->data->{ungrouped_vg_count}{node2}, + "ungrouped_vg_count::dr1" => $anvil->data->{ungrouped_vg_count}{dr1}, + }}); + if (($anvil->data->{ungrouped_vg_count}{node1} == 1) && ($anvil->data->{ungrouped_vg_count}{node2} == 1)) + { + # We do! + my $storage_group_uuid = $anvil->Database->create_storage_group({ + debug => $debug, + storage_group_anvil_uuid => $anvil_uuid, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_uuid => $storage_group_uuid }}); + + my $hosts = [$node1_host_uuid, $node2_host_uuid]; + if ($anvil->data->{ungrouped_vg_count}{dr1} == 1) + { + push @{$hosts}, $dr1_host_uuid; + } + foreach my $host_uuid (@{$hosts}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }}); + + # I need to find the size of VG UUID without knowing it's size. + my $storage_group_member_vg_uuid = ""; + foreach my $scan_lvm_vg_size (sort {$a cmp $b} keys %{$anvil->data->{ungrouped_vgs}}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + scan_lvm_vg_size => $scan_lvm_vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $scan_lvm_vg_size}).")", + }}); + if ((exists $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}) && + ($anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid})) + { + # Found it. + $storage_group_member_vg_uuid = $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_member_vg_uuid => $storage_group_member_vg_uuid }}); + } + } + my $storage_group_member_uuid = $anvil->Database->insert_or_update_storage_group_members({ + debug => $debug, + storage_group_member_storage_group_uuid => $storage_group_uuid, + storage_group_member_host_uuid => $host_uuid, + storage_group_member_vg_uuid => $storage_group_member_vg_uuid, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_member_uuid => $storage_group_member_uuid }}); + + # Reload storage group data + $reload_storage_groups = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { reload_storage_groups => $reload_storage_groups }}); + } + } + + if ($reload_storage_groups) + { + $anvil->Database->get_storage_group_data({debug => $debug}); + } + + return(0); +} + + =head2 boot_server This uses pacemaker to boot a server. @@ -289,12 +553,65 @@ sub check_node_status return($anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{ready}); } +=head2 get_anvil_name + +This returns the C<< anvils >> -> C<< anvil_name >> for a given C<< anvil_uuid >>. If no C<< anvil_uuid >> is passed, a check is made to see if this host is in an Anvil! and, if so, the Anvil! name it's a member of is returned. + +If not match is found, a blank string is returned. + +Parameters; + +=head3 anvil_uuid (optional, default Cluster->get_anvil_uuid) + +This is the C<< anvil_uuid >> of the Anvil! whose name we're looking for. + +=cut +sub get_anvil_name +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Cluster->get_anvil_name()" }}); + + my $anvil_uuid = defined $parameter->{anvil_uuid} ? $parameter->{anvil_uuid} : $anvil->Get->anvil_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + anvil_uuid => $anvil_uuid, + }}); + + my $anvil_name = ""; + if (not $anvil_uuid) + { + $anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => $debug}); + } + if (not $anvil_uuid) + { + return($anvil_name); + } + + # Load the Anvil! data. + $anvil->Database->get_anvils({debug => $debug}); + if (exists $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}) + { + $anvil_name = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name}; + } + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_name => $anvil_name }}); + return($anvil_name); +} + =head2 get_anvil_uuid This returns the C<< anvils >> -> C<< anvil_uuid >> that a host belongs to. If the host is not found in any Anvil!, an empty string is returned. +Optionally, this method can be passed an C<< anvil_name >>. If so, the name is used to find the UUID. + Parameters; +=head3 anvil_name (optional) + +If set, this is used to look up the Anvil! UUID. + =head3 host_uuid (optional, default Get->host_uuid) This is the C<< host_uuid >> of the host who we're looking for Anvil! membership of. @@ -308,15 +625,29 @@ sub get_anvil_uuid my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Cluster->get_anvil_uuid()" }}); - my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : $anvil->Get->host_uuid; + my $anvil_name = defined $parameter->{anvil_name} ? $parameter->{anvil_name} : ""; + my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : $anvil->Get->host_uuid; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - host_uuid => $host_uuid, + anvil_name => $anvil_name, + host_uuid => $host_uuid, }}); # Load the Anvil! data. $anvil->Database->get_anvils({debug => $debug}); - my $member_anvil_uuid = ""; + if ($anvil_name) + { + # Convert to the UUID directly. + my $anvil_uuid = ""; + if (exists $anvil->data->{anvils}{anvil_name}{$anvil_name}) + { + $anvil_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid}; + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_uuid => $anvil_uuid }}); + return($anvil_uuid); + } + + my $member_anvil_uuid = ""; foreach my $anvil_uuid (keys %{$anvil->data->{anvils}{anvil_uuid}}) { my $anvil_name = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name}; diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index 31e1b115..a51d5f25 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -3758,6 +3758,12 @@ FROM "servers::server_uuid::${server_uuid}::server_updated_by_user" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_updated_by_user}, "servers::server_uuid::${server_uuid}::server_boot_time" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_boot_time}, }}); + + # Store the servers in a hash under each Anvil!, sortable. + $anvil->data->{servers}{anvil_uuid}{$server_anvil_uuid}{server_name}{$server_name}{server_uuid} = $server_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "servers::anvil_uuid::${server_anvil_uuid}::server_name::${server_name}::server_uuid" => $anvil->data->{servers}{anvil_uuid}{$server_anvil_uuid}{server_name}{$server_name}{server_uuid}, + }}); } return(0); diff --git a/Anvil/Tools/Get.pm b/Anvil/Tools/Get.pm index f0ca0783..457f3d77 100644 --- a/Anvil/Tools/Get.pm +++ b/Anvil/Tools/Get.pm @@ -385,202 +385,20 @@ WHERE "anvil_resources::${anvil_uuid}::has_dr" => $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr}, }}); - # Load hosts, Network bridge, and Storages group data + # Load hosts and network bridges $anvil->Database->get_hosts({debug => $debug}); $anvil->Database->get_bridges({debug => $debug}); - $anvil->Database->get_storage_group_data({debug => $debug}); + + # This both loads storage group data and assembles ungrouped VGs into storage groups, when possible. + $anvil->Cluster->assemble_storage_groups({ + debug => $debug, + anvil_uuid => $anvil_uuid, + }); # 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}{ram}{hardware} = 0; - - # Before we see how much disk space is available, look for ungrouped VGs and see if we can group them - # by matching identical sizes together. - foreach my $host_uuid ($node1_host_uuid, $node2_host_uuid, $dr1_host_uuid) - { - # If DR isn't defined, it'll be blank. - next if not $host_uuid; - my $this_is = "node1"; - if ($host_uuid eq $node2_host_uuid) { $this_is = "node2"; } - elsif ($host_uuid eq $dr1_host_uuid) { $this_is = "dr1"; } - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_is => $this_is }}); - - $anvil->data->{ungrouped_vg_count}{$this_is} = 0; - - my $query = " -SELECT - scan_lvm_vg_uuid, - scan_lvm_vg_name, - scan_lvm_vg_extent_size, - scan_lvm_vg_size, - scan_lvm_vg_free, - scan_lvm_vg_internal_uuid -FROM - scan_lvm_vgs -WHERE - scan_lvm_vg_host_uuid = ".$anvil->Database->quote($host_uuid)." -ORDER BY - scan_lvm_vg_size ASC; -;"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); - my $count = @{$results}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - results => $results, - count => $count, - }}); - - foreach my $row (@{$results}) - { - my $scan_lvm_vg_size = $row->[3]; - my $scan_lvm_vg_internal_uuid = $row->[5]; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - scan_lvm_vg_size => $scan_lvm_vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $scan_lvm_vg_size}).")", - scan_lvm_vg_internal_uuid => $scan_lvm_vg_internal_uuid, - }}); - - # Skip VGs that are in a group already. - if ((exists $anvil->data->{storage_groups}{vg_uuid}{$scan_lvm_vg_internal_uuid}) && - ($anvil->data->{storage_groups}{vg_uuid}{$scan_lvm_vg_internal_uuid}{storage_group_uuid})) - { - # Already in a group, we can skip it. We log this data for debugging reasons - # only. - my $storage_group_uuid = $anvil->data->{storage_groups}{vg_uuid}{$scan_lvm_vg_internal_uuid}{storage_group_uuid}; - my $group_name = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{group_name}; - my $storage_group_member_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$host_uuid}{vg_uuid}{$scan_lvm_vg_internal_uuid}{storage_group_member_uuid}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - anvil_uuid => $anvil_uuid, - host_uuid => $host_uuid, - storage_group_uuid => $storage_group_uuid, - scan_lvm_vg_internal_uuid => $scan_lvm_vg_internal_uuid, - storage_group_member_uuid => $storage_group_member_uuid, - }}); - next; - } - - $anvil->data->{ungrouped_vg_count}{$this_is}++; - $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_uuid} = $row->[0]; - $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_name} = $row->[1]; - $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_extent_size} = $row->[2]; - $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_size} = $row->[3]; - $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_free} = $row->[4]; - $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid} = $row->[5]; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "ungrouped_vg_count::${this_is}" => $anvil->data->{ungrouped_vg_count}{$this_is}, - "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::count" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{count}, - "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_uuid" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_uuid}, - "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_name" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_name}, - "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_extent_size" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_extent_size}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_extent_size}}).")", - "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_size" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_size}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_size}}).")", - "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_free" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_free}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_free}}).")", - "ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_internal_uuid" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid}, - }}); - } - } - - # Fing ungrouped VGs and see if we can pair them - my $reload_storage_groups = 0; - foreach my $scan_lvm_vg_size (sort {$a cmp $b} keys %{$anvil->data->{ungrouped_vgs}}) - { - # If there are two or three VGs, we can create a group. - my $count = keys %{$anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}}; - if (($count == 2) or ($count == 3)) - { - # Create the volume group ... group. First we need a group number - my $storage_group_uuid = $anvil->Database->insert_or_update_storage_groups({ - debug => $debug, - storage_group_anvil_uuid => $anvil_uuid, - }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_uuid => $storage_group_uuid }}); - - foreach my $host_uuid (keys %{$anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}}) - { - my $this_is = "node1"; - if ($host_uuid eq $node2_host_uuid) { $this_is = "node2"; } - elsif ($host_uuid eq $dr1_host_uuid) { $this_is = "dr1"; } - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_is => $this_is }}); - - my $storage_group_member_vg_uuid = $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_member_vg_uuid => $storage_group_member_vg_uuid }}); - - my $storage_group_member_uuid = $anvil->Database->insert_or_update_storage_group_members({ - debug => $debug, - storage_group_member_storage_group_uuid => $storage_group_uuid, - storage_group_member_host_uuid => $host_uuid, - storage_group_member_vg_uuid => $storage_group_member_vg_uuid, - }); - - $anvil->data->{ungrouped_vg_count}{$this_is}--; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "ungrouped_vg_count::${this_is}" => $anvil->data->{ungrouped_vg_count}{$this_is}, - }}); - } - - # Reload storage group data - $reload_storage_groups = 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { reload_storage_groups => $reload_storage_groups }}); - } - } - - # If there's only one VG on each node that is ungrouped, group them even though they're not the same - # size. If DR also has only 1 VG ungrouped, it'll be added, too. - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "ungrouped_vg_count::node1" => $anvil->data->{ungrouped_vg_count}{node1}, - "ungrouped_vg_count::node2" => $anvil->data->{ungrouped_vg_count}{node2}, - "ungrouped_vg_count::dr1" => $anvil->data->{ungrouped_vg_count}{dr1}, - }}); - if (($anvil->data->{ungrouped_vg_count}{node1} == 1) && ($anvil->data->{ungrouped_vg_count}{node2} == 1)) - { - # We do! - my $storage_group_uuid = $anvil->Database->create_storage_group({ - debug => $debug, - storage_group_anvil_uuid => $anvil_uuid, - }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_uuid => $storage_group_uuid }}); - - my $hosts = [$node1_host_uuid, $node2_host_uuid]; - if ($anvil->data->{ungrouped_vg_count}{dr1} == 1) - { - push @{$hosts}, $dr1_host_uuid; - } - foreach my $host_uuid (@{$hosts}) - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }}); - - # I need to find the size of VG UUID without knowing it's size. - my $storage_group_member_vg_uuid = ""; - foreach my $scan_lvm_vg_size (sort {$a cmp $b} keys %{$anvil->data->{ungrouped_vgs}}) - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - scan_lvm_vg_size => $scan_lvm_vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $scan_lvm_vg_size}).")", - }}); - if ((exists $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}) && - ($anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid})) - { - # Found it. - $storage_group_member_vg_uuid = $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_member_vg_uuid => $storage_group_member_vg_uuid }}); - } - } - my $storage_group_member_uuid = $anvil->Database->insert_or_update_storage_group_members({ - debug => $debug, - storage_group_member_storage_group_uuid => $storage_group_uuid, - storage_group_member_host_uuid => $host_uuid, - storage_group_member_vg_uuid => $storage_group_member_vg_uuid, - }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_member_uuid => $storage_group_member_uuid }}); - - # Reload storage group data - $reload_storage_groups = 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { reload_storage_groups => $reload_storage_groups }}); - } - } - - if ($reload_storage_groups) - { - $anvil->Database->get_storage_group_data({debug => $debug}); - } foreach my $host_uuid ($node1_host_uuid, $node2_host_uuid, $dr1_host_uuid) { @@ -691,18 +509,6 @@ WHERE }}); } } - - # Now read in the LVM VG data. - undef $query; - undef $results; - undef $count; - - # TODO: Left off here; We'll now look for unassigned VGs. Ones that aren't, if we find a - # matching size one on both nodes / DR, we'll group them automatically. If only one VG - # is unassigned on each node / dr host, they will be grouped as well. - # After this, we'll look at groups and track which has the least free space per group, - # ignoring DR for now as it's feasible a user builds a lesser-spec'ed DR for a subset - # of VMs } # Read in the amount of RAM allocated to servers and subtract it from the RAM available. @@ -809,6 +615,8 @@ ORDER BY This finds a list of bridges on the host. Bridges that are found are stored is ' +This method takes no parameters. + =cut sub bridges { diff --git a/Anvil/Tools/Job.pm b/Anvil/Tools/Job.pm index 621136e4..e1053fae 100644 --- a/Anvil/Tools/Job.pm +++ b/Anvil/Tools/Job.pm @@ -102,6 +102,12 @@ sub clear my $job_uuid = defined $parameter->{job_uuid} ? $parameter->{job_uuid} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { job_uuid => $job_uuid }}); + if ((not $job_uuid) && ($anvil->data->{switches}{'job-uuid'})) + { + $job_uuid = $anvil->data->{switches}{'job-uuid'}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { job_uuid => $job_uuid }}); + } + # Return if we don't have a program name. if ($job_uuid eq "") { diff --git a/Anvil/Tools/Storage.pm b/Anvil/Tools/Storage.pm index 232045f4..9c1c20aa 100644 --- a/Anvil/Tools/Storage.pm +++ b/Anvil/Tools/Storage.pm @@ -99,6 +99,7 @@ sub parent # Public methods # ############################################################################################################# + =head2 backup This will create a copy of the file under the C<< path::directories::backups >> directory with the datestamp as a suffix. The path is preserved under the backup directory. The path and file name are returned. diff --git a/share/words.xml b/share/words.xml index 5f430395..2678899c 100644 --- a/share/words.xml +++ b/share/words.xml @@ -1375,6 +1375,7 @@ About to try to download aproximately: [#!variable!packages!#] packages needed t # Please do edit or remove it. # scan_drbd_resource_uuid = #!variable!uuid!# + Preparing to provision a new server. Saved the mail server information successfully! diff --git a/tools/anvil-provision-server b/tools/anvil-provision-server new file mode 100755 index 00000000..b0837c50 --- /dev/null +++ b/tools/anvil-provision-server @@ -0,0 +1,354 @@ +#!/usr/bin/perl +# +# This provisions a new virtual machine server. It handles creating the logical volumes, DRBD resources, +# verifies the needed files are ready, creates the provision script, begins the installation, and adds the +# new server to pacemaker. +# +# Exit codes; +# 0 = Normal exit. + +use strict; +use warnings; +use Anvil::Tools; +require POSIX; +use Term::Cap; + +my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; +my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; +if (($running_directory =~ /^\./) && ($ENV{PWD})) +{ + $running_directory =~ s/^\./$ENV{PWD}/; +} + +# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete. +$| = 1; + +my $anvil = Anvil::Tools->new(); +$anvil->Log->level({set => 2}); +$anvil->Log->secure({set => 1}); +$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }}); + +# Read switches (target ([user@]host[:port]) and the file with the target's password. If the password is +# passed directly, it will be used. Otherwise, the password will be read from the database. +$anvil->data->{switches}{'anvil-uuid'} = ""; +$anvil->data->{switches}{cpu} = ""; +$anvil->data->{switches}{'job-uuid'} = ""; +$anvil->data->{switches}{name} = ""; +$anvil->Get->switches; +$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 'switches::anvil-uuid' => $anvil->data->{switches}{'anvil-uuid'}, + 'switches::cpu' => $anvil->data->{switches}{cpu}, + 'switches::job-uuid' => $anvil->data->{switches}{'job-uuid'}, + 'switches::name' => $anvil->data->{switches}{name}, +}}); + +$anvil->Database->connect(); +$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"}); +if (not $anvil->data->{sys}{database}{connections}) +{ + # No databases, update the job, sleep for a bit and then exit. The daemon will pick it up and try + # again after we exit. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0077"}); + sleep 10; + $anvil->nice_exit({exit_code => 1}); +} + +# If we don't have a job UUID, try to find one. +if (not $anvil->data->{switches}{'job-uuid'}) +{ + # Load the job data. + $anvil->data->{switches}{job_uuid} = $anvil->Job->get_job_uuid({program => $THIS_FILE}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::job-uuid" => $anvil->data->{switches}{'job-uuid'} }}); +} + +# If we still don't have a job-uuit, go into interactive mode. +if ($anvil->data->{switches}{'job-uuid'}) +{ + # Load the job data. + $anvil->Job->clear(); + $anvil->Job->get_job_details(); + $anvil->Job->update_progress({ + progress => 1, + message => "message_0190", + }); + + # Job data will be in $anvil->data->{jobs}{job_data} +} +else +{ + # Interactive! + interactive_question($anvil); + +} + +$anvil->nice_exit({exit_code => 0}); + + +############################################################################################################# +# Functions # +############################################################################################################# + +# Go through a series of questions to ask the user how they want to build their server. +sub interactive_question +{ + my ($anvil) = @_; + + # Do we know or can we find the Anvil! UUID? + $anvil->data->{new_server}{anvil_uuid} = $anvil->data->{switches}{'anvil-uuid'} ? $anvil->data->{switches}{'anvil-uuid'} : ""; + $anvil->data->{new_server}{anvil_name} = $anvil->data->{switches}{'anvil-name'} ? $anvil->data->{switches}{'anvil-name'} : ""; + + if ((not $anvil->data->{new_server}{anvil_uuid}) && (not $anvil->data->{new_server}{anvil_name})) + { + # Nothing given. Is this host a node, perhaps? + my $anvil_uuid = $anvil->Cluster->get_anvil_uuid(); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { anvil_uuid => $anvil_uuid }}); + + if ($anvil_uuid) + { + $anvil->data->{new_server}{anvil_uuid} = $anvil_uuid; + $anvil->data->{new_server}{anvil_name} = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_server::anvil_name" => $anvil->data->{new_server}{anvil_name}, + "new_server::anvil_uuid" => $anvil->data->{new_server}{anvil_uuid}, + }}); + } + } + elsif (not $anvil->data->{new_server}{anvil_uuid}) + { + $anvil->data->{new_server}{anvil_uuid} = $anvil->Cluster->get_anvil_uuid({anvil_name => $anvil->data->{new_server}{anvil_name}}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "new_server::anvil_uuid" => $anvil->data->{new_server}{anvil_uuid} }}); + } + elsif (not $anvil->data->{new_server}{anvil_name}) + { + $anvil->data->{new_server}{anvil_name} = $anvil->Cluster->get_anvil_name({anvil_uuid => $anvil->data->{new_server}{anvil_uuid}}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "new_server::anvil_name" => $anvil->data->{new_server}{anvil_name} }}); + } + + $anvil->data->{new_server}{name} = $anvil->data->{switches}{name} ? $anvil->data->{switches}{name} : ""; + + # If this is a node, load the anvil_uuid automatically. + + my $termios = new POSIX::Termios; + $termios->getattr; + my $ospeed = $termios->getospeed; + + my $terminal = Tgetent Term::Cap { TERM => undef, OSPEED => $ospeed }; + $terminal->Trequire(qw/ce ku kd/); + + interactive_ask_anvil_name($anvil, $terminal); + interactive_ask_server_name($anvil, $terminal); + interactive_ask_server_cpu($anvil, $terminal); + + + return(0); +} + +sub interactive_ask_anvil_name +{ + my ($anvil, $terminal) = @_; + + $anvil->Database->get_anvils(); + + my $retry = 0; + while(1) + { + my $default_anvil = $anvil->data->{new_server}{anvil_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { default_anvil => $default_anvil }}); + if (not $default_anvil) + { + my $known_anvils = keys %{$anvil->data->{anvils}{anvil_name}}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { known_anvils => $known_anvils }}); + if (not $known_anvils) + { + print "There are no known Anvil! systems at this time. Please setup an Anvil! and try again later.\n"; + $anvil->nice_exit({exit_code => 1}); + } + elsif ($known_anvils == 1) + { + foreach my $anvil_name (keys %{$anvil->data->{anvils}{anvil_name}}) + { + $default_anvil = $anvil_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { default_anvil => $default_anvil }}); + } + } + } + print $terminal->Tputs('cl'); + print "Provision a new server menu:\n"; + print "Anvil name: [".$default_anvil."]\n\n\n"; + + # Show all the current server names. + if ($retry) + { + print "* That was not a recognized Anvil! name. please try again.\n\n"; + } + print "-=] Existing Anvil! systems [=-\n"; + foreach my $anvil_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_name}}) + { + print "- ".$anvil_name.": - ".$anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_description}."\n"; + } + + print $terminal->Tgoto('cm', 0, 2); + my $answer = ; + chomp $answer; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }}); + + if ((not $answer) && ($default_anvil)) + { + $answer = $default_anvil; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }}); + } + + # Reload in case a new anvil! was saved while we waited. + $anvil->Database->get_anvils(); + if (exists $anvil->data->{anvils}{anvil_name}{$answer}) + { + # Valid. + $anvil->data->{new_server}{anvil_name} = $answer; + $anvil->data->{new_server}{anvil_uuid} = $anvil->Cluster->get_anvil_uuid({anvil_name => $answer}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_server::anvil_name" => $anvil->data->{new_server}{anvil_name}, + "new_server::anvil_uuid" => $anvil->data->{new_server}{anvil_uuid}, + }}); + + print "Loading available resources for: [".$anvil->data->{new_server}{anvil_name}."] (".$anvil->data->{new_server}{anvil_uuid}.")\n"; + $anvil->Get->available_resources({ + debug => 2, + anvil_uuid => $anvil->data->{new_server}{anvil_uuid}, + }); + + last; + } + else + { + $retry = 1; + } + } + + return(0); +} + +sub interactive_ask_server_name +{ + my ($anvil, $terminal) = @_; + + $anvil->Database->get_servers({debug => 2}); + + ### TODO: Figure out how many rows we have and break the server list into columns if too long. + my $retry = 0; + while(1) + { + my $default = ""; + if ($anvil->data->{switches}{name}) + { + $default = $anvil->data->{switches}{name}; + } + print $terminal->Tputs('cl'); + print "Provision a new server menu:\n"; + print "Anvil name: [".$anvil->data->{new_server}{anvil_name}."]\n"; + print "Server name: [".$anvil->data->{new_server}{name}."]\n\n\n"; + + # Show all the current server names. + if ($retry) + { + print "* Please enter a unique server name.\n\n"; + } + my $anvil_uuid = $anvil->data->{new_server}{anvil_uuid}; + print "-=] Existing Servers on the Anvil! [".$anvil->data->{new_server}{anvil_name}."] [=-\n"; + foreach my $server_name (sort {$a cmp $b} keys %{$anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}}) + { + print "- ".$server_name."\n"; + } + + print $terminal->Tgoto('cm', 0, 3); + my $answer = ; + chomp $answer; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }}); + + if ((not $answer) && ($default)) + { + $answer = $default; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }}); + } + + # Reload in case a new anvil! was saved while we waited. + $anvil->Database->get_servers(); + if (($answer) && (not exists $anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}{$answer})) + { + # Valid. + $anvil->data->{new_server}{name} = $answer; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_server::name" => $anvil->data->{new_server}{name}, + }}); + + last; + } + else + { + $retry = 1; + } + } + + return(0); +} + +sub interactive_ask_server_cpu +{ + my ($anvil, $terminal) = @_; + + my $retry = 0; + while(1) + { + my $anvil_uuid = $anvil->data->{new_server}{anvil_uuid}; + $anvil->Database->get_anvils(); + $anvil->Get->available_resources({anvil_uuid => $anvil_uuid}); + + my $default_cpu = $anvil->data->{switches}{cpu}; + if (not $default_cpu) + { + # Default to 2, unless only one core is available. + $default_cpu = $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads} == 1 ? 1 : 2; + } + + print $terminal->Tputs('cl'); + print "Provision a new server menu:\n"; + print "Anvil name: [".$anvil->data->{new_server}{anvil_name}."]\n"; + print "Server name: [".$anvil->data->{new_server}{name}."]\n"; + print "CPU Cores: . [".$default_cpu."]\n\n\n"; + + if ($retry) + { + print "* Please enter a number between 1 and ".$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}.".\n\n"; + } + print "-=] Available cores / threads: [".$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores}." / ".$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}."]\n"; + my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}; + my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}; + + print " - Node 1 CPU Model: [".$anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node1_host_uuid}{cpu}{model}."]\n"; + print " - Node 2 CPU Model: [".$anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node2_host_uuid}{cpu}{model}."]\n"; + + print $terminal->Tgoto('cm', 0, 4); + my $answer = ; + chomp $answer; + + if ($answer eq "") + { + $answer = $default_cpu; + } + if (($answer) && ($answer =~ /^\d+$/) && ($answer <= $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads})) + { + # Valid. + $anvil->data->{new_server}{cpu} = $answer; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_server::cpu" => $anvil->data->{new_server}{cpu}, + }}); + + last; + } + else + { + $retry = 1; + } + } + + return(0); +}