diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index f4837a43..295fee7b 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -32,6 +32,7 @@ my $THIS_FILE = "Database.pm"; # get_manifests # get_notifications # get_recipients +# get_ssh_keys # get_upses # initialize # insert_or_update_anvils @@ -293,6 +294,8 @@ sub archive_database This checks to see if 'sys::database::local_lock_active' is set. If it is, its age is checked and if the age is >50% of sys::database::locking_reap_age, it will renew the lock. +This method takes no parameters. + =cut sub check_lock_age { @@ -357,6 +360,8 @@ If the system is already configured, this method will do nothing, so it is safe If the method completes, C<< 0 >> is returned. If this method is called without C<< root >> access, it returns C<< 1 >> without doing anything. If there is a problem, C<< !!error!! >> is returned. +This method takes no parameters. + =cut sub configure_pgsql { @@ -1332,7 +1337,7 @@ sub connect $anvil->Database->mark_active({debug => $debug, set => 1}); # Sync the database, if needed. - $anvil->Database->resync_databases({debug => 3}); + $anvil->Database->resync_databases({debug => $debug}); # Add ourselves to the database, if needed. $anvil->Database->insert_or_update_hosts({debug => $debug}); @@ -1346,6 +1351,8 @@ sub connect This cleanly closes any open file handles to all connected databases and clears some internal database related variables. +This method takes no parameters. + =cut sub disconnect { @@ -1393,6 +1400,8 @@ sub disconnect This returns a list of users listening to alerts for a given host, along with their alert level. +This method takes no parameters. + =cut sub get_recipients { @@ -1402,11 +1411,6 @@ sub get_recipients 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_recipients()" }}); - 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, - }}); - ### TODO: Read in 'notifications' my $query = " SELECT @@ -1814,12 +1818,16 @@ Each anonymous hash is structured as: host_uuid => $host_uuid, host_name => $host_name, host_type => $host_type, + host_key => $host_key, + host_ipmi => $host_ipmi, modified_date => $modified_date, It also sets the variables; hosts::host_uuid::::host_name = ; hosts::host_uuid::::host_type = + hosts::host_uuid::::host_key = + hosts::host_uuid::::host_ipmi = hosts::host_uuid::::anvil_name = And to simplify look-ups by UUID or name; @@ -1829,6 +1837,8 @@ And to simplify look-ups by UUID or 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 >>. +This method takes no parameters. + =cut sub get_hosts { @@ -1838,6 +1848,11 @@ 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()" }}); + # Delete any data from past scans. + delete $anvil->data->{hosts}{host_uuid}; + delete $anvil->data->{sys}{hosts}{by_uuid}; + delete $anvil->data->{sys}{hosts}{by_name}; + # Load anvils. If a host is registered with an Anvil!, we'll note it. $anvil->Database->get_anvils({debug => $debug}); @@ -1846,6 +1861,8 @@ SELECT host_uuid, host_name, host_type, + host_key, + host_ipmi, modified_date FROM hosts @@ -1861,14 +1878,18 @@ FROM }}); foreach my $row (@{$results}) { - my $host_uuid = $row->[0]; - my $host_name = $row->[1]; - my $host_type = $row->[2]; - my $modified_date = $row->[3]; + my $host_uuid = $row->[0]; + my $host_name = $row->[1]; + my $host_type = defined $row->[2] ? $row->[2] : ""; + my $host_key = defined $row->[3] ? $row->[3] : ""; + my $host_ipmi = $row->[4]; + my $modified_date = $row->[5]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid, host_name => $host_name, host_type => $host_type, + host_key => $host_key, + host_ipmi => $host_ipmi, modified_date => $modified_date, }}); @@ -1883,16 +1904,22 @@ FROM host_uuid => $host_uuid, host_name => $host_name, host_type => $host_type, + host_key => $host_key, + host_ipmi => $host_ipmi, modified_date => $modified_date, }; # 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_key} = $host_key; + $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_ipmi} = $host_ipmi; $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_key" => $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_key}, + "hosts::host_uuid::${host_uuid}::host_ipmi" => $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_ipmi}, "hosts::host_uuid::${host_uuid}::anvil_name" => $anvil->data->{hosts}{host_uuid}{$host_uuid}{anvil_name}, }}); @@ -1915,6 +1942,8 @@ FROM This gathers up all the known information about all known hosts. +This method takes no parameters. + =cut sub get_hosts_info { @@ -2010,6 +2039,8 @@ Parameters; This is the C<< job_uuid >> of the job being retrieved. +This method takes no parameters. + =cut sub get_job_details { @@ -2132,6 +2163,8 @@ Jobs that reached 100% within this number of seconds ago will be included. If th This is the host that we're getting a list of jobs from. +This method takes no parameters. + =cut sub get_jobs { @@ -2270,6 +2303,8 @@ NOTE: This returns nothing if the local machine is not found as a configured dat # Get the local UUID my $local_uuid = $anvil->Database->get_local_uuid; +This method takes no parameters. + =cut sub get_local_uuid { @@ -2340,6 +2375,8 @@ sub get_local_uuid This gets the list of configured mail servers. +This method takes no parameters. + =cut sub get_mail_servers { @@ -2632,6 +2669,94 @@ FROM } +=head2 get_ssh_keys + +This loads all known user's SSH public keys and all known machine's public keys into the data hash. On success, this method returns C<< 0 >>. If any problems occur, C<< 1 >> is returned. + + ssh_keys::ssh_key_uuid::::ssh_key_host_uuid = + ssh_keys::ssh_key_uuid::::ssh_key_user_name = + ssh_keys::ssh_key_uuid::::ssh_key_public_key = + +And: + + ssh_keys::ssh_key_host_uuid::::ssh_key_uuid = + ssh_keys::ssh_key_host_uuid::::ssh_key_user_name = + ssh_keys::ssh_key_host_uuid::::ssh_key_public_key = + + +This method takes no parameters. + +=cut +sub get_ssh_keys +{ + 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()" }}); + + # Delete any data from past scans. + delete $anvil->data->{ssh_keys}{ssh_key_uuid}; + delete $anvil->data->{sys}{ssh_keys}{by_uuid}; + delete $anvil->data->{sys}{ssh_keys}{by_name}; + + my $query = " +SELECT + ssh_key_uuid, + ssh_key_host_uuid, + ssh_key_user_name, + ssh_key_public_key, + modified_date +FROM + ssh_keys +;"; + $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 $ssh_key_uuid = $row->[0]; + my $ssh_key_host_uuid = $row->[1]; + my $ssh_key_user_name = $row->[2]; + my $ssh_key_public_key = $row->[3]; + my $modified_date = $row->[4]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + ssh_key_uuid => $ssh_key_uuid, + ssh_key_host_uuid => $ssh_key_host_uuid, + ssh_key_user_name => $ssh_key_user_name, + ssh_key_public_key => $ssh_key_public_key, + modified_date => $modified_date, + }}); + + # Record the data in the hash, too. + $anvil->data->{ssh_keys}{ssh_key_uuid}{$ssh_key_uuid}{ssh_key_host_uuid} = $ssh_key_host_uuid; + $anvil->data->{ssh_keys}{ssh_key_uuid}{$ssh_key_uuid}{ssh_key_user_name} = $ssh_key_user_name; + $anvil->data->{ssh_keys}{ssh_key_uuid}{$ssh_key_uuid}{ssh_key_public_key} = $ssh_key_public_key; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "ssh_keys::ssh_key_uuid::${ssh_key_uuid}::ssh_key_host_uuid" => $anvil->data->{ssh_keys}{ssh_key_uuid}{$ssh_key_uuid}{ssh_key_host_uuid}, + "ssh_keys::ssh_key_uuid::${ssh_key_uuid}::ssh_key_user_name" => $anvil->data->{ssh_keys}{ssh_key_uuid}{$ssh_key_uuid}{ssh_key_user_name}, + "ssh_keys::ssh_key_uuid::${ssh_key_uuid}::ssh_key_public_key" => $anvil->data->{ssh_keys}{ssh_key_uuid}{$ssh_key_uuid}{ssh_key_public_key}, + }}); + + $anvil->data->{ssh_keys}{ssh_key_host_uuid}{$ssh_key_host_uuid}{ssh_key_uuid} = $ssh_key_uuid; + $anvil->data->{ssh_keys}{ssh_key_host_uuid}{$ssh_key_host_uuid}{ssh_key_user_name} = $ssh_key_user_name; + $anvil->data->{ssh_keys}{ssh_key_host_uuid}{$ssh_key_host_uuid}{ssh_key_public_key} = $ssh_key_public_key; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "ssh_keys::ssh_key_host_uuid::${ssh_key_host_uuid}::ssh_key_uuid" => $anvil->data->{ssh_keys}{ssh_key_host_uuid}{$ssh_key_host_uuid}{ssh_key_uuid}, + "ssh_keys::ssh_key_host_uuid::${ssh_key_host_uuid}::ssh_key_user_name" => $anvil->data->{ssh_keys}{ssh_key_host_uuid}{$ssh_key_host_uuid}{ssh_key_user_name}, + "ssh_keys::ssh_key_host_uuid::${ssh_key_host_uuid}::ssh_key_public_key" => $anvil->data->{ssh_keys}{ssh_key_host_uuid}{$ssh_key_host_uuid}{ssh_key_public_key}, + }}); + } + + return(0); +} + + =head2 get_upses This loads the known UPSes (uninterruptible power supplies) into the C<< anvil::data >> hash at: diff --git a/Anvil/Tools/System.pm b/Anvil/Tools/System.pm index 2c63526b..87787c1d 100644 --- a/Anvil/Tools/System.pm +++ b/Anvil/Tools/System.pm @@ -22,6 +22,7 @@ my $THIS_FILE = "System.pm"; # change_shell_user_password # check_daemon # check_if_configured +# check_ssh_keys # check_memory # check_storage # disable_daemon @@ -643,8 +644,9 @@ sub check_ssh_keys my $users = $anvil->Get->host_type eq "node" ? ["root", "admin", "hacluster"] : ["root", "admin"]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { users => \@{$users} }}); - # Get a list of machine host keys and user public keys from other machines. - #get_other_keys($anvil); + # Load the host keys and the SSH keys + $anvil->Database->get_hosts({debug => $debug}); + $anvil->Database->get_ssh_keys({debug => $debug}); # Users to check: foreach my $user (@{$users}) @@ -672,11 +674,6 @@ sub check_ssh_keys group => $user, mode => "0700", }); - if (not -e $ssh_directory) - { - # Failed ? - next; - } } my $ssh_private_key_file = $user_home."/.ssh/id_rsa"; @@ -753,10 +750,33 @@ sub check_ssh_keys $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { authorized_keys_file_body => $authorized_keys_file_body }}); } - ### Walk through each host we now know of. As we we do, loop through the old file body to see - ### if it exists. If it does, and the key has changed, update the line with the new key. If - ### it isn't found, add it. Once we check the old body for this entry, change the "old" body - ### to the new one, then repeat the process. + # Walk through the Striker dashboards we use. If we're a Node or DR host, walk through our + # peers as well. As we we do, loop through the old file body to see if it exists. If it does, + # and the key has changed, update the line with the new key. If it isn't found, add it. Once + # we check the old body for this entry, change the "old" body to the new one, then repeat the + # process. + my $trusted_host_uuids = []; + + if ($anvil->Get->host_type eq "striker") + { + # Add all known machines + } + else + { + # Add dashboard (using postgres connection info), the UUID is the host UUID. + foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{database}}) + { + # Periodically, autovivication causes and empty key to appear. + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { uuid => $uuid }}); + next if ((not $uuid) or (not $anvil->Validate->is_uuid({uuid => $uuid}))); + + push @{$trusted_host_uuids}, $uuid; + } + + # Now add our peers. + } + + my $peers = []; # Look at all the hosts I know about (other than myself) and see if any of the machine or # user keys either don't exist or have changed. diff --git a/share/words.xml b/share/words.xml index d643c2a1..7d3272d3 100644 --- a/share/words.xml +++ b/share/words.xml @@ -33,6 +33,7 @@ Author: Madison Kelly Anvil!]]> Node DR Host + Unknown Type There are not enough network interfaces on this machine. You have: [#!variable!interface_count!#] interface(s), and you need at least: [#!variable!required_interfaces_for_single!#] interfaces to connect to the requested networks (one for Back-Channel and one for each Internet-Facing network). @@ -188,6 +189,8 @@ The error was: 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. Unable to connect to any database, unable to read the job details. + The answer: [#!variable!answer!#] is invalid. Please try again. + The host UUID: [#!variable!host_uuid!#] was not found. Has it already been purged? Current Network Interfaces and States @@ -1126,6 +1129,14 @@ About to try to download aproximately: [#!variable!packages!#] packages needed t An isolated, VLAN'ed network used for all inter-machine communication in the #!string!brand_0006!#, as well as communication for foundation pack devices. An isolated, VLAN'ed network Used for storage replication traffic only. Connecting to the main site intranet. This is the network (or networks) that guest virtual servers will use to connect to all devices outside the #!string!brand_0006!# system. + Please select the host you want to purge from the database: + #!variable!key!#) #!variable!host_name!# - #!variable!type!# - #!variable!host_uuid!#) + Which machine do you want to purge from the database(s)? + Note: Be sure all databases are online! Otherwise, the purged records could return during the next resync! + Are you sure you want to purge: [#!variable!host_name!# (#!variable!host_uuid!#)]? + Confirmed by switch, proceeding with purge of: [#!variable!host_name!#]. + Thank you, proceeding. + The host: [#!variable!host_name!#] has been purged. Saved the mail server information successfully! diff --git a/tools/striker-purge-host b/tools/striker-purge-host new file mode 100755 index 00000000..57126fe5 --- /dev/null +++ b/tools/striker-purge-host @@ -0,0 +1,324 @@ +#!/usr/bin/perl +# +# This tool is meant to be run from the command line and lets a user purge all information about a given host +# from the databases. +# +# Exit codes; +# 0 = Normal exit. +# 1 = User's answer to which machine to purge was invalid. +# 2 = The passed-in (or selected) host UUID (no longer) exists. +# 3 = User did not confirm to proceed. +# 4 = Runaway loop detected. +# + +use strict; +use warnings; +use Anvil::Tools; +use Data::Dumper; +use Curses::UI; + +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({debug => 2}); +$anvil->Log->secure({set => 1}); +$anvil->Log->level({set => 2}); + +$anvil->Database->connect({debug => 3}); +$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"}); + +# First, ask the user to select a host. +$anvil->data->{switches}{'host-uuid'} = "" if not defined $anvil->data->{switches}{'host-uuid'}; +$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'switches::host-uuid' => $anvil->data->{switches}{'host-uuid'} }}); + +$anvil->Database->get_hosts(); + +# Ask the user to pick a host. +pick_host($anvil); + +# Ask the user to confirm. +confirm($anvil); + +# If we're alive, purge. +purge($anvil); + +$anvil->nice_exit({code => 0}); + +############################################################################################################# +# Functions # +############################################################################################################# + +sub purge +{ + my ($anvil) = @_; + + # Read in all constraints and create a hash of what depends on what, then delete anything referencing + # hosts without any further references. As each delete is done, delete it. Loop until all entries are + # gone. + + # Load all the known tables in the DB. + my $query = "SELECT schemaname||'.'||tablename AS table_name FROM pg_catalog.pg_tables WHERE schemaname = 'history' ORDER BY tablename ASC, schemaname ASC;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, 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 => 3, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $table_name = $row->[0]; + $anvil->data->{sql}{history_tables}{$table_name} = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sql::history_tables::${table_name}" => $anvil->data->{sql}{history_tables}{$table_name} }}); + } + undef $query; + undef $results; + undef $count; + + ### NOTE: Credit for this query goes to: + ### - https://stackoverflow.com/questions/1152260/postgres-sql-to-list-table-foreign-keys + $query = " +SELECT + tc.table_schema||'.'||tc.table_name AS table, + kcu.column_name, + ccu.table_schema||'.'||ccu.table_name AS foreign_table, + ccu.column_name AS foreign_column_name +FROM + information_schema.table_constraints AS tc + JOIN information_schema.key_column_usage AS kcu + ON tc.constraint_name = kcu.constraint_name + AND tc.table_schema = kcu.table_schema + JOIN information_schema.constraint_column_usage AS ccu + ON ccu.constraint_name = tc.constraint_name + AND ccu.table_schema = tc.table_schema +WHERE + tc.constraint_type = 'FOREIGN KEY' +AND + ccu.column_name = 'host_uuid' +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + + $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $table_name = $row->[0]; + my $column_name = $row->[1]; + my $foreign_table_name = $row->[2]; + my $foreign_column_name = $row->[3]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { + 's1:table_name' => $table_name, + 's2:column_name' => $column_name, + 's3:foreign_table_name' => $foreign_table_name, + 's4:foreign_column_name' => $foreign_column_name, + }}); + + if (not exists $anvil->data->{sql}{table}{$table_name}) + { + $anvil->data->{sql}{table}{$table_name}{column} = $column_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sql::table::${table_name}::column" => $anvil->data->{sql}{table}{$table_name}{column} }}); + } + if (not exists $anvil->data->{sql}{table}{$foreign_table_name}) + { + $anvil->data->{sql}{table}{$foreign_table_name}{column} = $foreign_column_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sql::table::${foreign_table_name}::column" => $anvil->data->{sql}{table}{$foreign_table_name}{column} }}); + } + $anvil->data->{sql}{table}{$foreign_table_name}{referenced_by}{$table_name} = 1; + } + + my $loops = 20; + my $loop = 0; + my $done = 0; + my $queries = []; + until($done) + { + $loop++; + foreach my $table_name (sort {$a cmp $b} keys %{$anvil->data->{sql}{table}}) + { + my $count = exists $anvil->data->{sql}{table}{$table_name}{referenced_by} ? keys %{$anvil->data->{sql}{table}{$table_name}{referenced_by}} : 0; + #print "-- Table: [".$table_name."] is referenced by: [".$count."] foreign table(s);\n"; + if (not $count) + { + # If there is a corresponding history table, delete it as well. + my $column = $anvil->data->{sql}{table}{$table_name}{column}; + my $history_table = $table_name; + $history_table =~ s/^public\./history\./; + if (exists $anvil->data->{sql}{history_tables}{$history_table}) + { + push @{$queries}, "DELETE FROM ".$history_table." WHERE ".$column." = ".$anvil->Database->quote($anvil->data->{switches}{'host-uuid'}).";"; + } + + # Now delete the public table entries. + push @{$queries}, "DELETE FROM ".$table_name." WHERE ".$column." = ".$anvil->Database->quote($anvil->data->{switches}{'host-uuid'}).";"; + + foreach my $foreign_table_name (sort {$a cmp $b} keys %{$anvil->data->{sql}{table}}) + { + if (exists $anvil->data->{sql}{table}{$foreign_table_name}{referenced_by}{$table_name}) + { + #print "-- Deleting: [".$foreign_table_name."] reference to: [".$table_name."]\n"; + delete $anvil->data->{sql}{table}{$foreign_table_name}{referenced_by}{$table_name}; + } + } + #print "-- Deleting hash entry for: [".$table_name."]\n"; + delete $anvil->data->{sql}{table}{$table_name}; + } + } + + my $remaining = keys %{$anvil->data->{sql}{table}}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { remaining => $remaining }}); + if (not $remaining) + { + $done = 1; + } + elsif ($loop > $loops) + { + print "Run-away loop detected, aborting!\n"; + $anvil->nice_exit({code => 4}); + } + } + + # Do the deed. + $anvil->Database->write({debug => 2, query => $queries, source => $THIS_FILE, line => __LINE__}); + my $host_uuid = $anvil->data->{switches}{'host-uuid'}; + print $anvil->Words->string({key => "message_0176", variables => { host_name => $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name} }})."\n"; + + return(0); +} + +sub confirm +{ + my ($anvil) = @_; + + # Normalize -y + $anvil->data->{switches}{'y'} = "" if not defined $anvil->data->{switches}{'y'}; + $anvil->data->{switches}{'yes'} = "" if not defined $anvil->data->{switches}{'yes'}; + $anvil->data->{switches}{'Y'} = "" if not defined $anvil->data->{switches}{'Y'}; + $anvil->data->{switches}{'Yes'} = "" if not defined $anvil->data->{switches}{'Yes'}; + $anvil->data->{switches}{'YES'} = "" if not defined $anvil->data->{switches}{'YES'}; + if (not $anvil->data->{switches}{'y'}) + { + if ($anvil->data->{switches}{'yes'}) { $anvil->data->{switches}{'y'} = $anvil->data->{switches}{'yes'}; } + elsif ($anvil->data->{switches}{'Y'}) { $anvil->data->{switches}{'y'} = $anvil->data->{switches}{'Y'}; } + elsif ($anvil->data->{switches}{'Yes'}) { $anvil->data->{switches}{'y'} = $anvil->data->{switches}{'Yes'}; } + elsif ($anvil->data->{switches}{'YES'}) { $anvil->data->{switches}{'y'} = $anvil->data->{switches}{'YES'}; } + } + + # Ask to confirm, is sane and not -y + my $host_uuid = $anvil->data->{switches}{'host-uuid'}; + if (not exists $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}) + { + print $anvil->Words->string({key => "error_0131", variables => { host_uuid => $host_uuid }})."\n\n"; + $anvil->nice_exit({code => 2}); + } + else + { + print $anvil->Words->string({key => "message_0172"})."\n\n"; + print $anvil->Words->string({key => "message_0173", variables => { + host_name => $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}, + host_uuid => $host_uuid, + }})."\n"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'switches::y' => $anvil->data->{switches}{'y'} }}); + if ($anvil->data->{switches}{'y'} eq "#!SET!#") + { + # Already confirmed. + print $anvil->Words->string({key => "message_0023"})."\n\n"; + return(0); + } + print $anvil->Words->string({key => "message_0021"})." "; + chomp(my $answer = ); + + if ($answer =~ /^y/i) + { + # Proceed. + print $anvil->Words->string({key => "message_0175"})."\n"; + } + else + { + # Abort. + print $anvil->Words->string({key => "message_0022"})."\n"; + $anvil->nice_exit({code => 3}); + } + } + + return(0); +} + +sub pick_host +{ + my ($anvil) = @_; + + return(0) if $anvil->data->{switches}{'host-uuid'}; + + # Get a list of hosts + my $i = 1; + my $select = {}; + my $host_length = 0; + my $type_length = 0; + foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{sys}{hosts}{by_name}}) + { + my $host_uuid = $anvil->data->{sys}{hosts}{by_name}{$host_name}; + my $host_type = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type}; + my $anvil_name = defined $anvil->data->{hosts}{host_uuid}{$host_uuid}{anvil_name} ? $anvil->data->{hosts}{host_uuid}{$host_uuid}{anvil_name} : ""; + my $say_type = $anvil->Words->string({key => "brand_0009"}); + if ($host_type eq "striker") { $say_type = $anvil->Words->string({key => "brand_0003"}); } + elsif ($host_type eq "node") { $say_type = $anvil->Words->string({key => "brand_0007"}); } + elsif ($host_type eq "dr") { $say_type = $anvil->Words->string({key => "brand_0008"}); } + + if (length($host_name) > $host_length) { $host_length = length($host_name); } + if (length($say_type) > $type_length) { $type_length = length($say_type); } + + # This is used to build the menu. + $select->{$i}{host_uuid} = $host_uuid; + $select->{$i}{host_name} = $host_name; + $select->{$i}{host_type} = $host_type; + $select->{$i}{say_type} = $say_type; + $select->{$i}{anvil_name} = $anvil_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { + "${i}::host_uuid" => $select->{$i}{host_uuid}, + "${i}::host_name" => $select->{$i}{host_name}, + "${i}::host_type" => $select->{$i}{host_type}, + "${i}::anvil_name" => $select->{$i}{anvil_name}, + }}); + $i++; + } + + my $pad = $i > 9 ? 2 : 1; + print $anvil->Words->string({key => "message_0169"})."\n\n"; + foreach my $i (sort {$a cmp $b} keys %{$select}) + { + print " ".$anvil->Words->string({key => "message_0170", variables => { + key => sprintf("%".$pad."s", $i), + type => sprintf("%-".$type_length."s", $select->{$i}{say_type}), + host_name => sprintf("%-".$host_length."s", $select->{$i}{host_name}), + host_uuid => $select->{$i}{host_uuid}, + }})."\n"; + } + print "\n".$anvil->Words->string({key => "message_0171"})." "; + + chomp(my $answer = ); + + if (not exists $select->{$answer}{host_name}) + { + print "\n".$anvil->Words->string({key => "error_0130", variables => { answer => $answer }})."\n\n"; + $anvil->nice_exit({code => 1}); + } + else + { + print "\n"; + $anvil->data->{switches}{'host-uuid'} = $select->{$answer}{host_uuid}; + } + + return(0); +} diff --git a/tools/test.pl b/tools/test.pl index a1c6a750..fff3b7e4 100755 --- a/tools/test.pl +++ b/tools/test.pl @@ -25,4 +25,4 @@ $anvil->Database->connect({debug => 3}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"}); print "DB Connections: [".$anvil->data->{sys}{database}{connections}."]\n"; -$anvil->System->check_ssh_keys({debug => 2}); +#$anvil->System->check_ssh_keys({debug => 2});