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);
+}