From e99bfac9be973c787098c8be0fe7d26ae64675cb Mon Sep 17 00:00:00 2001 From: Digimer Date: Mon, 25 May 2020 00:02:52 -0400 Subject: [PATCH] * Created Database->get_anvils() and ->insert_or_update_anvils(). * Updated the anvils database table to have columns for traching which hosts are used as node 1, node 2 and the DR host. * Updated Database->get_hosts() to check which, if any, Anvil! a given hosts is attached to. Signed-off-by: Digimer --- Anvil/Tools/Database.pm | 540 +++++++++++++++++++++++++++++++++- Anvil/Tools/Striker.pm | 11 + cgi-bin/striker | 296 +++++++++++++++---- html/skins/alteeve/anvil.html | 162 ++++++++++ share/anvil.sql | 35 ++- share/words.xml | 14 + 6 files changed, 976 insertions(+), 82 deletions(-) diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index ea1e52a5..1cc1d05f 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -20,6 +20,7 @@ my $THIS_FILE = "Database.pm"; # configure_pgsql # connect # disconnect +# get_anvils # get_fences # get_host_from_uuid # get_hosts @@ -1468,6 +1469,167 @@ FROM } +=head2 get_anvils + +This loads information about all known Anvil! systems as recorded in the C<< anvils >> table. + +Data is stored in two hashes, one sorted by C<< anvil_uuid >> and one by C<< anvil_name >>. While loading, any referenced nodes and DR hosts are stored for quick reference as well. Data is stored as: + + anvils::anvil_uuid::::anvil_name + anvils::anvil_uuid::::anvil_description + anvils::anvil_uuid::::anvil_password + anvils::anvil_uuid::::anvil_node1_host_uuid + anvils::anvil_uuid::::anvil_node2_host_uuid + anvils::anvil_uuid::::anvil_dr1_host_uuid + anvils::anvil_uuid::::modified_date + + anvils::anvil_name::::anvil_uuid + anvils::anvil_name::::anvil_description + anvils::anvil_name::::anvil_password + anvils::anvil_name::::anvil_node1_host_uuid + anvils::anvil_name::::anvil_node2_host_uuid + anvils::anvil_name::::anvil_dr1_host_uuid + anvils::anvil_name::::modified_date + +When a host UUID is stored for either node or the DR host, it will be stored at: + + anvils::host_uuid::::anvil_name + +Parameters; + +=head3 include_deleted (Optional, default 0) + +If set to C<< 1 >>, deleted last_rans are included when loading the data. When C<< 0 >> is set, the default, any C<< anvil_description >> set to C<< DELETED >> are ignored. + +=cut +sub get_anvils +{ + 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 => "Database->get_manifests()" }}); + + my $include_deleted = defined $parameter->{include_deleted} ? $parameter->{include_deleted} : 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + include_deleted => $include_deleted, + }}); + + if (exists $anvil->data->{manifests}) + { + delete $anvil->data->{manifests}; + } + + my $query = " +SELECT + anvil_uuid, + anvil_name, + anvil_description, + anvil_password, + anvil_node1_host_uuid, + anvil_node2_host_uuid, + anvil_dr1_host_uuid, + modified_date +FROM + anvils "; + if (not $include_deleted) + { + $query .= " +WHERE + anvil_description != 'DELETED'"; + } + $query .= " +;"; + $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 $anvil_uuid = $row->[0]; + my $anvil_name = $row->[1]; + my $anvil_description = $row->[2]; + my $anvil_password = $row->[3]; + my $anvil_node1_host_uuid = defined $row->[4] ? $row->[4] : ""; + my $anvil_node2_host_uuid = defined $row->[5] ? $row->[5] : ""; + my $anvil_dr1_host_uuid = defined $row->[6] ? $row->[6] : ""; + my $modified_date = $row->[5]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + anvil_uuid => $anvil_uuid, + anvil_name => $anvil_name, + anvil_description => $anvil_description, + anvil_password => $anvil->Log->is_secure($anvil_password), + anvil_node1_host_uuid => $anvil_node1_host_uuid, + anvil_node2_host_uuid => $anvil_node2_host_uuid, + anvil_dr1_host_uuid => $anvil_dr1_host_uuid, + modified_date => $modified_date, + }}); + + # Record the data in the hash, too. + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name} = $anvil_name; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_description} = $anvil_description; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_password} = $anvil_password; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid} = $anvil_node1_host_uuid; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid} = $anvil_node2_host_uuid; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid} = $anvil_dr1_host_uuid; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{modified_date} = $modified_date; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "anvils::anvil_uuid::${anvil_uuid}::anvil_name" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name}, + "anvils::anvil_uuid::${anvil_uuid}::anvil_description" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_description}, + "anvils::anvil_uuid::${anvil_uuid}::anvil_password" => $anvil->Log->is_secure($anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_password}), + "anvils::anvil_uuid::${anvil_uuid}::anvil_node1_host_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}, + "anvils::anvil_uuid::${anvil_uuid}::anvil_node2_host_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}, + "anvils::anvil_uuid::${anvil_uuid}::anvil_dr1_host_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid}, + "anvils::anvil_uuid::${anvil_uuid}::modified_date" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{modified_date}, + }}); + + $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid} = $anvil_uuid; + $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_description} = $anvil_description; + $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_password} = $anvil_password; + $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node1_host_uuid} = $anvil_node1_host_uuid; + $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node2_host_uuid} = $anvil_node2_host_uuid; + $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_dr1_host_uuid} = $anvil_dr1_host_uuid; + $anvil->data->{anvils}{anvil_name}{$anvil_name}{modified_date} = $modified_date; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "anvils::anvil_name::${anvil_name}::anvil_uuid" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid}, + "anvils::anvil_name::${anvil_name}::anvil_description" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_description}, + "anvils::anvil_name::${anvil_name}::anvil_password" => $anvil->Log->is_secure($anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_password}), + "anvils::anvil_name::${anvil_name}::anvil_node1_host_uuid" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node1_host_uuid}, + "anvils::anvil_name::${anvil_name}::anvil_node2_host_uuid" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node2_host_uuid}, + "anvils::anvil_name::${anvil_name}::anvil_dr1_host_uuid" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_dr1_host_uuid}, + "anvils::anvil_name::${anvil_name}::modified_date" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{modified_date}, + }}); + + if ($anvil_node1_host_uuid) + { + $anvil->data->{anvils}{host_uuid}{$anvil_node1_host_uuid}{anvil_name} = $anvil_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "anvils::host_uuid::${anvil_node1_host_uuid}::anvil_name" => $anvil->data->{anvils}{host_uuid}{$anvil_node1_host_uuid}{anvil_name}, + }}); + } + if ($anvil_node2_host_uuid) + { + $anvil->data->{anvils}{host_uuid}{$anvil_node2_host_uuid}{anvil_name} = $anvil_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "anvils::host_uuid::${anvil_node2_host_uuid}::anvil_name" => $anvil->data->{anvils}{host_uuid}{$anvil_node2_host_uuid}{anvil_name}, + }}); + } + if ($anvil_dr1_host_uuid) + { + $anvil->data->{anvils}{host_uuid}{$anvil_dr1_host_uuid}{anvil_name} = $anvil_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "anvils::host_uuid::${anvil_dr1_host_uuid}::anvil_name" => $anvil->data->{anvils}{host_uuid}{$anvil_dr1_host_uuid}{anvil_name}, + }}); + } + } + + return(0); +} + + =head2 get_fences This loads the known fence devices into the C<< anvil::data >> hash at: @@ -1654,7 +1816,18 @@ Each anonymous hash is structured as: host_type => $host_type, modified_date => $modified_date, -It also sets the variables C<< sys::hosts::by_uuid:: = >> and C<< sys::hosts::by_name:: = >> per host read, for quick reference. +It also sets the variables; + + hosts::host_uuid::::host_name = ; + hosts::host_uuid::::host_type = + hosts::host_uuid::::anvil_name = + +And to simplify look-ups by UUID or name; + + sys::hosts::by_uuid:: = + sys::hosts::by_name:: = + +To prevent some cases of recursion, C<< hosts::loaded >> is set on successful load, and if this is set, this method immediately returns with C<< 0 >>. =cut sub get_hosts @@ -1665,6 +1838,12 @@ sub get_hosts my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->get_hosts()" }}); + # This prevents some recursive loops, like when Database->insert_or_update_anvils() is called. + return(0) if $anvil->data->{hosts}{loaded}; + + # Load anvils. If a host is registered with an Anvil!, we'll note it. + $anvil->Database->get_anvils({debug => $debug}); + my $query = " SELECT host_uuid, @@ -1695,6 +1874,14 @@ FROM host_type => $host_type, modified_date => $modified_date, }}); + + my $anvil_name = ""; + if ((exists $anvil->data->{anvils}{host_uuid}{$host_uuid}) && ($anvil->data->{anvils}{host_uuid}{$host_uuid}{anvil_name})) + { + $anvil_name = $anvil->data->{anvils}{host_uuid}{$host_uuid}{anvil_name}; + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_name => $anvil_name }}); + push @{$return}, { host_uuid => $host_uuid, host_name => $host_name, @@ -1703,11 +1890,13 @@ FROM }; # Record the data in the hash, too. - $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name} = $host_name; - $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type} = $host_type; + $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name} = $host_name; + $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type} = $host_type; + $anvil->data->{hosts}{host_uuid}{$host_uuid}{anvil_name} = $anvil_name; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "hosts::host_uuid::${host_uuid}::host_name" => $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}, - "hosts::host_uuid::${host_uuid}::host_type" => $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type}, + "hosts::host_uuid::${host_uuid}::host_name" => $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}, + "hosts::host_uuid::${host_uuid}::host_type" => $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type}, + "hosts::host_uuid::${host_uuid}::anvil_name" => $anvil->data->{hosts}{host_uuid}{$host_uuid}{anvil_name}, }}); # Record the host_uuid in a hash so that the name can be easily retrieved. @@ -1719,6 +1908,8 @@ FROM }}); } + $anvil->data->{hosts}{loaded} = 1; + my $return_count = @{$return}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { return_count => $return_count }}); return($return); @@ -2678,9 +2869,326 @@ sub initialize =head2 insert_or_update_anvils + +This updates (or inserts) a record in the C<< anvils >> table. The C<< anvil_uuid >> referencing the database row will be returned. + +If there is an error, an empty string is returned. + +Parameters; + +=head3 uuid (optional) + +If set, only the corresponding database will be written to. + +=head3 file (optional) + +If set, this is the file name logged as the source of any INSERTs or UPDATEs. + +=head3 line (optional) + +If set, this is the file line number logged as the source of any INSERTs or UPDATEs. + +=head3 anvil_description (optional) + +This is a free-form description for this Anvil! system. If this is set to C<< DELETED >>, the Anvil! will be considered to be deleted and no longer used. + +=head3 anvil_dr1_host_uuid (optional) + +This is the C<< hosts >> -> C<< host_uuid >> of the machine that is used as the DR host. + +B<< Note >>: If set, there must be a matching C<< hosts >> -> C<< host_uuid >> in the database. + +=head3 anvil_name (required) + +This is the anvil's name. It is usually in the format C<< -anvil- >>. + +=head3 anvil_password (required) + +This is the password used for this Anvil! system. Specifically, it is used to set the IPMI BMC user and for C<< hacluster >> and C<< root >> system users. + +=head3 anvil_uuid (optional) + +If not passed, a check will be made to see if an existing entry is found for C<< anvil_name >>. If found, that entry will be updated. If not found, a new record will be inserted. + +=head3 anvil_node1_host_uuid (optional) + +This is the C<< hosts >> -> C<< host_uuid >> of the machine that is used as node 1. + +B<< Note >>: If set, there must be a matching C<< hosts >> -> C<< host_uuid >> in the database. + +=head3 anvil_node2_host_uuid (optional) + +This is the C<< hosts >> -> C<< host_uuid >> of the machine that is used as node 2. + +B<< Note >>: If set, there must be a matching C<< hosts >> -> C<< host_uuid >> in the database. + +=head3 delete (optional, default '0') + +If set to C<< 1 >>, C<< anvil_description >> will be set to C<< DELETED >>, indicating that the anvil has been deleted from the system. If set, only C<< anvil_uuid >> or C<< anvil_name >> is needed. + =cut sub insert_or_update_anvils { + 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 => "Database->initialize()" }}); + + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; + my $file = defined $parameter->{file} ? $parameter->{file} : ""; + my $line = defined $parameter->{line} ? $parameter->{line} : ""; + my $delete = defined $parameter->{'delete'} ? $parameter->{'delete'} : ""; + my $anvil_uuid = defined $parameter->{anvil_uuid} ? $parameter->{anvil_uuid} : ""; + my $anvil_description = defined $parameter->{anvil_description} ? $parameter->{anvil_description} : $anvil->data->{sys}{host_uuid}; + my $anvil_name = defined $parameter->{anvil_name} ? $parameter->{anvil_name} : ""; + my $anvil_password = defined $parameter->{anvil_password} ? $parameter->{anvil_password} : ""; + my $anvil_node1_host_uuid = defined $parameter->{anvil_node1_host_uuid} ? $parameter->{anvil_node1_host_uuid} : "NULL"; + my $anvil_node2_host_uuid = defined $parameter->{anvil_node2_host_uuid} ? $parameter->{anvil_node2_host_uuid} : "NULL"; + my $anvil_dr1_host_uuid = defined $parameter->{anvil_dr1_host_uuid} ? $parameter->{anvil_dr1_host_uuid} : "NULL"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + uuid => $uuid, + file => $file, + line => $line, + 'delete' => $delete, + anvil_uuid => $anvil_uuid, + anvil_description => $anvil_description, + anvil_name => $anvil_name, + anvil_password => $anvil_password, + anvil_node1_host_uuid => $anvil_node1_host_uuid, + anvil_node2_host_uuid => $anvil_node2_host_uuid, + anvil_dr1_host_uuid => $anvil_dr1_host_uuid, + }}); + + if (not $delete) + { + if (not $anvil_name) + { + # Throw an error and exit. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_anvils()", parameter => "anvil_name" }}); + return(""); + } + if (not $anvil_password) + { + # Throw an error and exit. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_anvils()", parameter => "anvil_password" }}); + return(""); + } + } + elsif ((not $anvil_name) && (not $anvil_uuid)) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0127", variables => { table => "anvils" }}); + return(""); + } + + # If we don't have a UUID, see if we can find one for the given anvil name. + if (not $anvil_uuid) + { + my $query = " +SELECT + anvil_uuid +FROM + anvils +WHERE + anvil_name = ".$anvil->Database->quote($anvil_name)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + + my $results = $anvil->Database->query({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + if ($count) + { + $anvil_uuid = $results->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_uuid => $anvil_uuid }}); + } + } + + # Make sure that, if any host_uuid's are passed, that they're valid. + $anvil->Database->get_hosts({debug => $debug}) if not $anvil->data->{hosts}{loaded}; + if (($anvil_node1_host_uuid) && (not $anvil->data->{hosts}{host_uuid}{$anvil_node1_host_uuid}{host_name})) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0128", variables => { uuid => $anvil_node1_host_uuid, column => "anvil_node1_host_uuid" }}); + return(""); + } + if (($anvil_node2_host_uuid) && (not $anvil->data->{hosts}{host_uuid}{$anvil_node2_host_uuid}{host_name})) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0128", variables => { uuid => $anvil_node2_host_uuid, column => "anvil_node2_host_uuid" }}); + return(""); + } + if (($anvil_dr1_host_uuid) && (not $anvil->data->{hosts}{host_uuid}{$anvil_dr1_host_uuid}{host_name})) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0128", variables => { uuid => $anvil_dr1_host_uuid, column => "anvil_dr1_host_uuid" }}); + return(""); + } + + if ($delete) + { + if (not $anvil_uuid) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_anvils()", parameter => "anvil_uuid" }}); + return(""); + } + else + { + # Delete it + my $query = "SELECT anvil_description 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({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + if ($count) + { + my $old_anvil_description = $results->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { old_anvil_description => $old_anvil_description }}); + + if ($old_anvil_description ne "DELETED") + { + my $query = " +UPDATE + anvils +SET + anvil_description = 'DELETED', + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + anvil_uuid = ".$anvil->Database->quote($anvil_uuid)." +;"; + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + } + return($anvil_uuid); + } + else + { + # Not found. + return(""); + } + } + } + + # NULL values can't be quoted + my $say_anvil_node1_host_uuid = $anvil_node1_host_uuid eq "NULL" ? "NULL" : $anvil->Database->quote($anvil_node1_host_uuid); + my $say_anvil_node2_host_uuid = $anvil_node2_host_uuid eq "NULL" ? "NULL" : $anvil->Database->quote($anvil_node2_host_uuid); + my $say_anvil_dr1_host_uuid = $anvil_dr1_host_uuid eq "NULL" ? "NULL" : $anvil->Database->quote($anvil_dr1_host_uuid); + + + # If I still don't have an anvil_uuid, we're INSERT'ing . + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_uuid => $anvil_uuid }}); + if (not $anvil_uuid) + { + # INSERT + $anvil_uuid = $anvil->Get->uuid(); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_uuid => $anvil_uuid }}); + + my $query = " +INSERT INTO + anvils +( + anvil_uuid, + anvil_name, + anvil_description, + anvil_password, + anvil_node1_host_uuid, + anvil_node2_host_uuid, + anvil_dr1_host_uuid, + modified_date +) VALUES ( + ".$anvil->Database->quote($anvil_uuid).", + ".$anvil->Database->quote($anvil_name).", + ".$anvil->Database->quote($anvil_description).", + ".$anvil->Database->quote($anvil_password).", + ".$say_anvil_node1_host_uuid.", + ".$say_anvil_node2_host_uuid.", + ".$say_anvil_dr1_host_uuid.", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +); +"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + } + else + { + # Query the rest of the values and see if anything changed. + my $query = " +SELECT + anvil_name, + anvil_description, + anvil_password, + 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({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + if (not $count) + { + # I have a anvil_uuid but no matching record. Probably an error. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0216", variables => { uuid_name => "anvil_uuid", uuid => $anvil_uuid }}); + return(""); + } + foreach my $row (@{$results}) + { + my $old_anvil_name = $row->[0]; + my $old_anvil_description = $row->[1]; + my $old_anvil_password = $row->[2]; + my $old_anvil_node1_host_uuid = defined $row->[3] ? $row->[3] : "NULL"; + my $old_anvil_node2_host_uuid = defined $row->[4] ? $row->[4] : "NULL"; + my $old_anvil_dr1_host_uuid = defined $row->[5] ? $row->[5] : "NULL"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + old_anvil_name => $old_anvil_name, + old_anvil_description => $old_anvil_description, + old_anvil_password => $old_anvil_password, + old_anvil_node1_host_uuid => $old_anvil_node1_host_uuid, + old_anvil_node2_host_uuid => $old_anvil_node2_host_uuid, + old_anvil_dr1_host_uuid => $old_anvil_dr1_host_uuid, + }}); + + # Anything change? + if (($old_anvil_name ne $anvil_name) or + ($old_anvil_description ne $anvil_description) or + ($old_anvil_password ne $anvil_password) or + ($old_anvil_node1_host_uuid ne $anvil_node1_host_uuid) or + ($old_anvil_node2_host_uuid ne $anvil_node2_host_uuid) or + ($old_anvil_dr1_host_uuid ne $anvil_dr1_host_uuid)) + { + # Something changed, save. + my $query = " +UPDATE + anvils +SET + anvil_name = ".$anvil->Database->quote($anvil_name).", + anvil_description = ".$anvil->Database->quote($anvil_description).", + anvil_password = ".$anvil->Database->quote($anvil_password).", + anvil_node1_host_uuid = ".$say_anvil_node1_host_uuid.", + anvil_node2_host_uuid = ".$say_anvil_node2_host_uuid.", + anvil_dr1_host_uuid = ".$say_anvil_dr1_host_uuid.", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + anvil_uuid = ".$anvil->Database->quote($anvil_uuid)." +"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + } + } + } + + return($anvil_uuid); } @@ -2724,7 +3232,7 @@ This is the unique identifier for the bridge. This is the MAC address of the bridge. -=head3 bridge_mto (optional) +=head3 bridge_mtu (optional) This is the MTU (maximum transfer unit, size in bytes) of the bridge. @@ -2779,6 +3287,11 @@ sub insert_or_update_bridges return(""); } } + elsif ((not $bridge_name) && (not $bridge_uuid)) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0127", variables => { table => "bridges" }}); + return(""); + } # If we don't have a UUID, see if we can find one for the given bridge server name. if (not $bridge_uuid) @@ -3140,6 +3653,11 @@ sub insert_or_update_bonds $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { bond_bridge_uuid => $bond_bridge_uuid }}); } } + elsif ((not $bond_name) && (not $bond_uuid)) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0127", variables => { table => "bonds" }}); + return(""); + } # If we don't have a UUID, see if we can find one for the given bond server name. if (not $bond_uuid) @@ -5722,6 +6240,11 @@ sub insert_or_update_manifests return(""); } } + elsif ((not $manifest_name) && (not $manifest_uuid)) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0127", variables => { table => "manifests" }}); + return(""); + } # If we don't have a network interface UUID, try to look one up using the MAC address if (not $manifest_uuid) @@ -6071,6 +6594,11 @@ sub insert_or_update_network_interfaces return(""); } } + elsif ((not $network_interface_name) && (not $network_interface_uuid)) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0127", variables => { table => "network_interfaces" }}); + return(""); + } # If we don't have a network interface UUID, try to look one up using the MAC address if (not $network_interface_uuid) diff --git a/Anvil/Tools/Striker.pm b/Anvil/Tools/Striker.pm index 96922659..e856feb2 100644 --- a/Anvil/Tools/Striker.pm +++ b/Anvil/Tools/Striker.pm @@ -770,6 +770,8 @@ The parsed manifest XML is stored as ( == node1, node2 or dr1): manifests::manifest_uuid::::parsed::name = manifests::manifest_uuid::::parsed::domain = + manifests::manifest_uuid::::parsed::prefix = + manifests::manifest_uuid::::parsed::sequence = manifests::manifest_uuid::::parsed::upses::::uuid = ups_uuid of named UPS> manifests::manifest_uuid::::parsed::fences::::uuid = fence_uuid of named fence device> manifests::manifest_uuid::::parsed::networks::dns = @@ -891,6 +893,15 @@ WHERE "manifests::manifest_uuid::${manifest_uuid}::parsed::name" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{name}, }}); + my ($prefix, $sequence) = ($anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{name} =~ /^(.*?)-anvil-(\d+)$/); + $sequence = sprintf("%02d", $sequence); + $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{prefix} = $prefix; + $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{sequence} = $sequence; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "manifests::manifest_uuid::${manifest_uuid}::parsed::prefix" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{prefix}, + "manifests::manifest_uuid::${manifest_uuid}::parsed::sequence" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{sequence}, + }}); + foreach my $hash_ref (@{$parsed_xml->{upses}{ups}}) { my $ups_name = $hash_ref->{name}; diff --git a/cgi-bin/striker b/cgi-bin/striker index 9a879881..f99f4aa0 100755 --- a/cgi-bin/striker +++ b/cgi-bin/striker @@ -1537,47 +1537,243 @@ sub process_anvil_menu return(0); } +# This handles all aspects of Install Manifests. +sub process_manifests +{ + my ($anvil) = @_; + + # Are we creating a new manifest? + $anvil->data->{cgi}{manifest_uuid}{value} = "" if not defined $anvil->data->{cgi}{manifest_uuid}{value}; + $anvil->data->{cgi}{confirm}{value} = "" if not defined $anvil->data->{cgi}{confirm}{alert}; + $anvil->data->{cgi}{run}{value} = "" if not defined $anvil->data->{cgi}{run}{value}; + $anvil->data->{cgi}{step}{value} = 1 if not defined $anvil->data->{cgi}{step}{value}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 'cgi::manifest_uuid::value' => $anvil->data->{cgi}{manifest_uuid}{value}, + }}); + + # Are we running a manifest, or creating/editing one? + if (($anvil->data->{cgi}{run}{value}) && ($anvil->Validate->is_uuid({uuid => $anvil->data->{cgi}{manifest_uuid}{value}}))) + { + run_manifest($anvil); + } + else + { + handle_manifest($anvil); + } + + return(0); +} + +# This is an extension to 'handle_manifests' that specifically handles running an install manifest. +sub run_manifest +{ + my ($anvil) = @_; + + my $manifest_uuid = $anvil->data->{cgi}{manifest_uuid}{value}; + my $problem = $anvil->Striker->load_manifest({ + debug => 2, + manifest_uuid => $manifest_uuid, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + manifest_uuid => $manifest_uuid, + problem => $problem, + }}); + + if ($problem) + { + # Report a problem and send the user back to the manifests page. + my $message = $anvil->Words->string({key => "warning_0046", variables => { uuid => $manifest_uuid }}); + $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $message }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "form::error_massage" => $anvil->data->{form}{error_massage} }}); + } + +=cut + manifests::manifest_uuid::::parsed::name = + manifests::manifest_uuid::::parsed::domain = + manifests::manifest_uuid::::parsed::prefix = + manifests::manifest_uuid::::parsed::sequence = + manifests::manifest_uuid::::parsed::upses::::uuid = ups_uuid of named UPS> + manifests::manifest_uuid::::parsed::fences::::uuid = fence_uuid of named fence device> + manifests::manifest_uuid::::parsed::networks::dns = + manifests::manifest_uuid::::parsed::networks::ntp = + manifests::manifest_uuid::::parsed::networks::mtu = + manifests::manifest_uuid::::parsed::networks::bcn_count = + manifests::manifest_uuid::::parsed::networks::sn_count = + manifests::manifest_uuid::::parsed::networks::ifn_count = + manifests::manifest_uuid::::parsed::networks::name::::network = + manifests::manifest_uuid::::parsed::networks::name::::subnet = + manifests::manifest_uuid::::parsed::networks::name::::gateway = + manifests::manifest_uuid::::parsed::machine::::name = + manifests::manifest_uuid::::parsed::machine::::ipmi_ip = + manifests::manifest_uuid::::parsed::machine::::fence::::port = <'port' name/number (see fence agent man page) + manifests::manifest_uuid::::parsed::machine::::ups::::used = <1 if powered by USB, 0 if not> + manifests::manifest_uuid::::parsed::machine::::network::::ip = +=cut + my $anvil_name = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{name}; + my $prefix = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{prefix}; + my $sequence = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{sequence}; + my $domain = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{domain}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + anvil_name => $anvil_name, + prefix => $prefix, + sequence => $sequence, + domain => $domain, + }}); + + my $node1_name = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{node1}{name}; + my $node2_name = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{node2}{name}; + my $dr1_name = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{dr1}{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + node1_name => $node1_name, + node2_name => $node2_name, + dr1_name => $dr1_name, + }}); + + # If confirmed, run! + $anvil->data->{cgi}{confirm}{value} = "" if not defined $anvil->data->{cgi}{confirm}{value}; + if ($anvil->data->{cgi}{confirm}{value}) + { + # Save the jobs! + } + else + { + # Ask the user to choose the targets and confirm the manifest settings. + $anvil->Database->get_hosts(); + my $nodes = []; + my $dr_hosts = []; + foreach my $host_uuid (keys %{$anvil->data->{hosts}{host_uuid}}) + { + my $host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}; + my $host_type = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type}; + my $host_anvil = ""; + if ((exists $anvil->data->{hosts}{host_uuid}{$host_uuid}) && ($anvil->data->{hosts}{host_uuid}{$host_uuid}{anvil_name} ne $anvil_name)) + { + $host_anvil = $anvil_name; + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + host_uuid => $host_uuid, + host_name => $host_name, + host_type => $host_type, + host_anvil => $host_anvil, + }}); + + # Does this host belong to another Anvil! already? + if (($host_anvil) && ($host_anvil ne $anvil_name)) + { + # yup, ignore it. + next; + } + + if ($host_type eq "node") + { + push @{$nodes}, $host_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_name => $host_name }}); + + if ((not defined $anvil->data->{cgi}{node1_host}{value}) && ($host_name eq $node1_name)) + { + $anvil->data->{cgi}{node1_host}{value} = $host_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::node1_host::value" => $anvil->data->{cgi}{node1_host}{value} }}); + } + elsif ((not defined $anvil->data->{cgi}{node2_host}{value}) && ($host_name eq $node2_name)) + { + $anvil->data->{cgi}{node2_host}{value} = $host_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::node2_host::value" => $anvil->data->{cgi}{node2_host}{value} }}); + } + } + elsif ($host_type eq "dr") + { + push @{$dr_hosts}, $host_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_name => $host_name }}); + if ((not defined $anvil->data->{cgi}{dr1_host}{value}) && ($host_name eq $dr1_name)) + { + $anvil->data->{cgi}{node1_host}{value} = $host_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::dr1_host::value" => $anvil->data->{cgi}{dr1_host}{value} }}); + } + } + } + + # We're going to need three selects; one for each node and one for DR. The DR is allowed to + # be unselected so it has an empty entry. + my $select_node1 = $anvil->Template->select_form({ + name => "node1_host", + options => $nodes, + blank => 0, + 'sort' => 1, + selected => $anvil->data->{cgi}{node1_host}{value}, + class => $anvil->data->{cgi}{node1_host}{alert} ? "input_alert" : "input_clear", + style => "", + }); + + my $select_node2 = $anvil->Template->select_form({ + name => "node2_host", + options => $nodes, + blank => 0, + 'sort' => 1, + selected => $anvil->data->{cgi}{node2_host}{value}, + class => $anvil->data->{cgi}{node2_host}{alert} ? "input_alert" : "input_clear", + style => "", + }); + + my $select_dr1 = $anvil->Template->select_form({ + name => "dr_host", + options => $dr_hosts, + blank => 1, + 'sort' => 1, + selected => $anvil->data->{cgi}{dr_host}{value}, + class => $anvil->data->{cgi}{dr_host}{alert} ? "input_alert" : "input_clear", + style => "", + }); + + $anvil->data->{form}{back_link} = "?anvil=true&task=create"; + $anvil->data->{form}{refresh_link} = "?anvil=true&task=manifests&run=true&manifest_uuid=".$anvil->data->{cgi}{manifest_uuid}{value}; + $anvil->data->{form}{body} = $anvil->Template->get({file => "anvil.html", name => "run-manifest", variables => { + select_node1 => $select_node1, + select_node2 => $select_node2, + select_dr1 => $select_dr1, + }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'form::body' => $anvil->data->{form}{body} }}); + } + + return(0); +} + # This handles creating or editing an Install Manifest. sub handle_manifest { my ($anvil) = @_; - $anvil->data->{cgi}{step}{value} = 1 if not defined $anvil->data->{cgi}{step}{value}; - $anvil->data->{cgi}{prefix}{value} = "" if not defined $anvil->data->{cgi}{prefix}{value}; - $anvil->data->{cgi}{prefix}{alert} = 0 if not defined $anvil->data->{cgi}{prefix}{alert}; - $anvil->data->{cgi}{domain}{value} = "" if not defined $anvil->data->{cgi}{domain}{value}; - $anvil->data->{cgi}{domain}{alert} = 0 if not defined $anvil->data->{cgi}{domain}{alert}; - $anvil->data->{cgi}{sequence}{value} = "" if not defined $anvil->data->{cgi}{sequence}{value}; - $anvil->data->{cgi}{sequence}{alert} = 0 if not defined $anvil->data->{cgi}{sequence}{alert}; - $anvil->data->{cgi}{bcn_count}{value} = 0 if not defined $anvil->data->{cgi}{bcn_count}{value}; - $anvil->data->{cgi}{bcn_count}{alert} = 0 if not defined $anvil->data->{cgi}{bcn_count}{alert}; - $anvil->data->{cgi}{sn_count}{value} = 0 if not defined $anvil->data->{cgi}{sn_count}{value}; - $anvil->data->{cgi}{sn_count}{alert} = 0 if not defined $anvil->data->{cgi}{sn_count}{alert}; - $anvil->data->{cgi}{ifn_count}{value} = 0 if not defined $anvil->data->{cgi}{ifn_count}{value}; - $anvil->data->{cgi}{ifn_count}{alert} = 0 if not defined $anvil->data->{cgi}{ifn_count}{alert}; - $anvil->data->{cgi}{dns}{value} = "8.8.8.8,8.8.4.4" if not defined $anvil->data->{cgi}{dns}{value}; - $anvil->data->{cgi}{dns}{alert} = 0 if not defined $anvil->data->{cgi}{dns}{alert}; - $anvil->data->{cgi}{ntp}{value} = "" if not defined $anvil->data->{cgi}{ntp}{value}; - $anvil->data->{cgi}{ntp}{alert} = 0 if not defined $anvil->data->{cgi}{ntp}{alert}; - $anvil->data->{cgi}{mtu}{value} = 1500 if not defined $anvil->data->{cgi}{mtu}{value}; - $anvil->data->{cgi}{mtu}{alert} = 0 if not defined $anvil->data->{cgi}{mtu}{alert}; - $anvil->data->{cgi}{'delete'}{value} = "" if not defined $anvil->data->{cgi}{'delete'}{alert}; - $anvil->data->{cgi}{manifest_uuid}{value} = "" if not defined $anvil->data->{cgi}{manifest_uuid}{alert}; - $anvil->data->{cgi}{confirm}{value} = "" if not defined $anvil->data->{cgi}{confirm}{alert}; + $anvil->data->{cgi}{step}{value} = 1 if not defined $anvil->data->{cgi}{step}{value}; + $anvil->data->{cgi}{prefix}{value} = "" if not defined $anvil->data->{cgi}{prefix}{value}; + $anvil->data->{cgi}{prefix}{alert} = 0 if not defined $anvil->data->{cgi}{prefix}{alert}; + $anvil->data->{cgi}{domain}{value} = "" if not defined $anvil->data->{cgi}{domain}{value}; + $anvil->data->{cgi}{domain}{alert} = 0 if not defined $anvil->data->{cgi}{domain}{alert}; + $anvil->data->{cgi}{sequence}{value} = "" if not defined $anvil->data->{cgi}{sequence}{value}; + $anvil->data->{cgi}{sequence}{alert} = 0 if not defined $anvil->data->{cgi}{sequence}{alert}; + $anvil->data->{cgi}{bcn_count}{value} = 0 if not defined $anvil->data->{cgi}{bcn_count}{value}; + $anvil->data->{cgi}{bcn_count}{alert} = 0 if not defined $anvil->data->{cgi}{bcn_count}{alert}; + $anvil->data->{cgi}{sn_count}{value} = 0 if not defined $anvil->data->{cgi}{sn_count}{value}; + $anvil->data->{cgi}{sn_count}{alert} = 0 if not defined $anvil->data->{cgi}{sn_count}{alert}; + $anvil->data->{cgi}{ifn_count}{value} = 0 if not defined $anvil->data->{cgi}{ifn_count}{value}; + $anvil->data->{cgi}{ifn_count}{alert} = 0 if not defined $anvil->data->{cgi}{ifn_count}{alert}; + $anvil->data->{cgi}{dns}{value} = "8.8.8.8,8.8.4.4" if not defined $anvil->data->{cgi}{dns}{value}; + $anvil->data->{cgi}{dns}{alert} = 0 if not defined $anvil->data->{cgi}{dns}{alert}; + $anvil->data->{cgi}{ntp}{value} = "" if not defined $anvil->data->{cgi}{ntp}{value}; + $anvil->data->{cgi}{ntp}{alert} = 0 if not defined $anvil->data->{cgi}{ntp}{alert}; + $anvil->data->{cgi}{mtu}{value} = 1500 if not defined $anvil->data->{cgi}{mtu}{value}; + $anvil->data->{cgi}{mtu}{alert} = 0 if not defined $anvil->data->{cgi}{mtu}{alert}; + $anvil->data->{cgi}{'delete'}{value} = "" if not defined $anvil->data->{cgi}{'delete'}{alert}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - "cgi::step::value" => $anvil->data->{cgi}{step}{value}, - "cgi::prefix::value" => $anvil->data->{cgi}{prefix}{value}, - "cgi::domain::value" => $anvil->data->{cgi}{domain}{value}, - "cgi::sequence::value" => $anvil->data->{cgi}{sequence}{value}, - "cgi::bcn_count::value" => $anvil->data->{cgi}{bcn_count}{value}, - "cgi::sn_count::value" => $anvil->data->{cgi}{sn_count}{value}, - "cgi::ifn_count::value" => $anvil->data->{cgi}{ifn_count}{value}, - "cgi::dns::value" => $anvil->data->{cgi}{dns}{value}, - "cgi::ntp::value" => $anvil->data->{cgi}{ntp}{value}, - "cgi::mtu::value" => $anvil->data->{cgi}{mtu}{value}, - "cgi::delete::value" => $anvil->data->{cgi}{'delete'}{value}, - "cgi::manifest_uuid::value" => $anvil->data->{cgi}{manifest_uuid}{value}, - "cgi::confirm::value" => $anvil->data->{cgi}{confirm}{value}, + "cgi::step::value" => $anvil->data->{cgi}{step}{value}, + "cgi::prefix::value" => $anvil->data->{cgi}{prefix}{value}, + "cgi::domain::value" => $anvil->data->{cgi}{domain}{value}, + "cgi::sequence::value" => $anvil->data->{cgi}{sequence}{value}, + "cgi::bcn_count::value" => $anvil->data->{cgi}{bcn_count}{value}, + "cgi::sn_count::value" => $anvil->data->{cgi}{sn_count}{value}, + "cgi::ifn_count::value" => $anvil->data->{cgi}{ifn_count}{value}, + "cgi::dns::value" => $anvil->data->{cgi}{dns}{value}, + "cgi::ntp::value" => $anvil->data->{cgi}{ntp}{value}, + "cgi::mtu::value" => $anvil->data->{cgi}{mtu}{value}, + "cgi::delete::value" => $anvil->data->{cgi}{'delete'}{value}, }}); # Are we deleting a manifest? @@ -2955,36 +3151,6 @@ sub process_create return(0); } -# This handles all aspects of Install Manifests. -sub process_manifests -{ - my ($anvil) = @_; - - # Are we creating a new manifest? - $anvil->data->{cgi}{manifest_uuid}{value} = "" if not defined $anvil->data->{cgi}{manifest_uuid}{value}; - $anvil->data->{cgi}{run}{value} = "" if not defined $anvil->data->{cgi}{run}{value}; - $anvil->data->{cgi}{step}{value} = 1 if not defined $anvil->data->{cgi}{step}{value}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - 'cgi::manifest_uuid::value' => $anvil->data->{cgi}{manifest_uuid}{value}, - }}); - - # Are we running a manifest, or creating/editing one? - if ($anvil->data->{cgi}{run}{value}) - { - #run_manifest($anvil); - } - elsif ($anvil->data->{cgi}{manifest_uuid}{value}) - { - handle_manifest($anvil); - } - else - { - # Uhhhh... - } - - return(0); -} - # This allows the user to tell us about upses. sub process_upses { diff --git a/html/skins/alteeve/anvil.html b/html/skins/alteeve/anvil.html index 2d39168b..5095efc2 100644 --- a/html/skins/alteeve/anvil.html +++ b/html/skins/alteeve/anvil.html @@ -79,6 +79,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+   +
+ #!string!header_0028!# +
+   +
+ #!string!striker_0268!# +
+ + #!string!striker_0269!# +
+   +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+   + + + #!string!striker_0255!# + +   + + + #!string!striker_0256!# + +   + + + #!string!striker_0257!# +
+ + #!string!header_0029!#: + + #!variable!select_node1!# + +   + + #!variable!select_node2!# + +   + + #!variable!select_dr1!# +
+   +
+ + + +
+
+
+   +
+ + + + + + + + + + + + + + + + + + + + + +
+ + #!string!striker_0228!#   + + #!string!striker_0246!# +
+ + #!string!striker_0007!#   + + #!string!striker_0247!# +
+ + #!string!striker_0229!#   + + #!string!striker_0244!# +
+ + #!string!striker_0230!# + + #!string!striker_0245!# +
+ + #!string!striker_0051!# + + #!string!striker_0259!# +
+
+ + diff --git a/share/anvil.sql b/share/anvil.sql index 94b80510..8f8fa3ec 100644 --- a/share/anvil.sql +++ b/share/anvil.sql @@ -330,21 +330,31 @@ CREATE TRIGGER trigger_sessions -- This stores information about Anvil! systems. CREATE TABLE anvils ( - anvil_uuid uuid not null primary key, - anvil_name text not null, - anvil_description text not null, -- This is a short, one-line (usually) description of this particular Anvil!. It is displayed in the Anvil! selection list. - anvil_password text not null, -- This is the 'hacluster' user password. It is also used to access nodes that don't have a specific password set. - modified_date timestamp with time zone not null + anvil_uuid uuid not null primary key, + anvil_name text not null, + anvil_description text not null, -- This is a short, one-line (usually) description of this particular Anvil!. It is displayed in the Anvil! selection list. + anvil_password text not null, -- This is the 'hacluster' user password. It is also used to access nodes that don't have a specific password set. + anvil_node1_host_uuid uuid, -- This is the host_uuid of the machine that is used as node 1. + anvil_node2_host_uuid uuid, -- This is the host_uuid of the machine that is used as node 2. + anvil_dr1_host_uuid uuid, -- This is the host_uuid of the machine that is used as DR host. + modified_date timestamp with time zone not null, + + FOREIGN KEY(anvil_node1_host_uuid) REFERENCES hosts(host_uuid), + FOREIGN KEY(anvil_node2_host_uuid) REFERENCES hosts(host_uuid), + FOREIGN KEY(anvil_dr1_host_uuid) REFERENCES hosts(host_uuid) ); ALTER TABLE anvils OWNER TO admin; CREATE TABLE history.anvils ( - history_id bigserial, - anvil_uuid uuid, - anvil_name text, - anvil_description text, - anvil_password text, - modified_date timestamp with time zone not null + history_id bigserial, + anvil_uuid uuid, + anvil_name text, + anvil_description text, + anvil_password text, + anvil_node1_host_uuid uuid, + anvil_node2_host_uuid uuid, + anvil_dr1_host_uuid uuid, + modified_date timestamp with time zone not null ); ALTER TABLE history.anvils OWNER TO admin; @@ -359,6 +369,9 @@ BEGIN anvil_name, anvil_description, anvil_password, + anvil_node1_host_uuid, + anvil_node2_host_uuid, + anvil_dr1_host_uuid, modified_date) VALUES (history_anvils.anvil_uuid, diff --git a/share/words.xml b/share/words.xml index 8e3c9c30..bb69d52c 100644 --- a/share/words.xml +++ b/share/words.xml @@ -185,6 +185,8 @@ The error was: The IP address: [#!variable!ip!#] does not appear to be within any of the configured networks. The IPv4 address assigned to the IPMI interface on: [#!variable!network!#] is invalid. The IP address: [#!variable!ip!#] does not appear to be in the network: [#!variable!network!#]. + I was asked to delete and entry from: [#!variable!table!#] but neither the name or UUID was passed. + The host UUID: [#!variable!uuid!#] was set as the value for: [#!variable!column!#], but that host doesn't appear to exist. Current Network Interfaces and States @@ -215,6 +217,7 @@ The error was: Host Name Brand Install Manifest + Machine Configure Network @@ -1387,6 +1390,16 @@ If you are comfortable that the target has changed for a known reason, you can s If the machine is powered by a given UPS, click to check the corresponding box. This information will be used in power loss events to decide what machine should host servers, which should be powered off during load-shed conditions and when to gracefully power off entirely. If your machine has an IPMI BMC, (iDRAC, iLO, iRMC, etc), then you can enter the IP to give it here. Further details will be collected when the manifest runs. Leave blank if the machine doesn't have IPMI. #!variable!network!#].]]> + + +
+
    +
  • A node that is found to be running a server will NOT be changed, except to configure passwordless SSH to the peer node and/or DR host. As such, it is safe to run this manifest when adding a rebuilt node or adding a DR host to a live #!string!brand_0006!# system.
  • +
  • Adding a disaster recovery (DR) host is optional. You can add one later if you don't have one now.
  • +
  • If there are no servers on either node (as it a new #!string!brand_0006!# build), the OSes will be updated. Otherwise, they won't be updated. If the kernel is updated, or the network reconfigured, the node will be rebooted.
  • +
+ ]]>
#!variable!number!#/sec @@ -1555,6 +1568,7 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st [ Warning ] - The install manifest with the UUID: [#!variable!uuid!#] was not found. [ Warning ] - The install manifest: [#!variable!name!#] with the UUID: [#!variable!uuid!#] has already been deleted. [ Warning ] - The install manifest: [#!variable!name!#] with the UUID: [#!variable!uuid!#] was NOT deleted. The reason may be in the: [#!data!path::log::main!#] log file on this host. + [ Warning ] - The Install Manifest with the UUID: [#!variable!uuid!#] was not found.