From 1a1fa7ce8867de346a34d0c26e633510895b0c9c Mon Sep 17 00:00:00 2001 From: Digimer Date: Mon, 28 Sep 2020 00:20:13 -0400 Subject: [PATCH] * Created Cluster->get_anvil_uuid() that returns the 'anvil_uuid' of a given 'host_uuid'. * Renamed the 'defitintions' table to 'server_definitions' to clarify the purpose, and made all the 'server' columns have then 'not null' constraint. * Created Database->insert_or_update_servers(), ->get_servers(), ->insert_or_update_server_definitions() and ->get_server_definitions(). * Updated scancore, anvil-daemon, and scan agents to not run unless they're run with root privs. * Got scan-server to update the servers / server_definition tables and the on-disk file when needed. Signed-off-by: Digimer --- Anvil/Tools/Cluster.pm | 55 ++++ Anvil/Tools/Database.pm | 342 ++++++++++++++++++-- Anvil/Tools/ScanCore.pm | 3 + notes | 145 +++++++++ scancore-agents/scan-cluster/scan-cluster | 15 +- scancore-agents/scan-hardware/scan-hardware | 15 +- scancore-agents/scan-server/scan-server | 199 +++++++++++- scancore-agents/scan-server/scan-server.xml | 15 +- share/anvil.sql | 62 ++-- share/words.xml | 29 ++ tools/anvil-daemon | 15 +- tools/scancore | 16 +- 12 files changed, 834 insertions(+), 77 deletions(-) diff --git a/Anvil/Tools/Cluster.pm b/Anvil/Tools/Cluster.pm index 941e1741..d3e5ede1 100644 --- a/Anvil/Tools/Cluster.pm +++ b/Anvil/Tools/Cluster.pm @@ -288,6 +288,61 @@ sub check_node_status return($anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{ready}); } +=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. + +Parameters; + +=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. + +=cut +sub get_anvil_uuid +{ + 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->check_node_status()" }}); + + 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, + }}); + + # Load the Anvil! data. + $anvil->Database->get_anvils({debug => $debug}); + 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}; + my $anvil_node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}; + my $anvil_node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}; + my $anvil_dr1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + anvil_name => $anvil_name, + 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 (($host_uuid eq $anvil_node1_host_uuid) or + ($host_uuid eq $anvil_node2_host_uuid) or + ($host_uuid eq $anvil_dr1_host_uuid)) + { + # Found ot! + $member_anvil_uuid = $anvil_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { member_anvil_uuid => $member_anvil_uuid }}); + last; + } + } + + return($member_anvil_uuid); +} + =head2 get_peers This method uses the local machine's host UUID and finds the host names of the cluster memebers. If this host is in a cluster and it is a node, the peer's short host name is returned. Otherwise, an empty string is returned. diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index a1fe6ac8..a967778c 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -8,8 +8,9 @@ use warnings; use DBI; use Scalar::Util qw(weaken isweak); use Data::Dumper; -use Time::HiRes qw(gettimeofday tv_interval); use Text::Diff; +use Time::HiRes qw(gettimeofday tv_interval); +use XML::LibXML; our $VERSION = "3.0.0"; my $THIS_FILE = "Database.pm"; @@ -58,6 +59,8 @@ my $THIS_FILE = "Database.pm"; # insert_or_update_oui # insert_or_update_power # insert_or_update_recipients +# insert_or_update_servers +# insert_or_update_server_definitions # insert_or_update_sessions # insert_or_update_ssh_keys # insert_or_update_states @@ -3184,13 +3187,13 @@ FROM $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_authentication} = $mail_server_authentication; $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_helo_domain} = $mail_server_helo_domain; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "mail_servers::mail_server::${mail_server_uuid}}::mail_server_address" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_address}, - "mail_servers::mail_server::${mail_server_uuid}}::mail_server_port" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_port}, - "mail_servers::mail_server::${mail_server_uuid}}::mail_server_username" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_username}, - "mail_servers::mail_server::${mail_server_uuid}}::mail_server_password" => $anvil->Log->is_secure($anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_password}), - "mail_servers::mail_server::${mail_server_uuid}}::mail_server_security" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_security}, - "mail_servers::mail_server::${mail_server_uuid}}::mail_server_authentication" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_authentication}, - "mail_servers::mail_server::${mail_server_uuid}}::mail_server_helo_domain" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_helo_domain}, + "mail_servers::mail_server::${mail_server_uuid}::mail_server_address" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_address}, + "mail_servers::mail_server::${mail_server_uuid}::mail_server_port" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_port}, + "mail_servers::mail_server::${mail_server_uuid}::mail_server_username" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_username}, + "mail_servers::mail_server::${mail_server_uuid}::mail_server_password" => $anvil->Log->is_secure($anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_password}), + "mail_servers::mail_server::${mail_server_uuid}::mail_server_security" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_security}, + "mail_servers::mail_server::${mail_server_uuid}::mail_server_authentication" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_authentication}, + "mail_servers::mail_server::${mail_server_uuid}::mail_server_helo_domain" => $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_helo_domain}, }}); # Make it easy to look up the mail server's UUID from the server address. @@ -3510,11 +3513,11 @@ WHERE $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_level} = $recipient_level; $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host} = $recipient_level; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "recipients::recipient_uuid::${recipient_uuid}}::recipient_name" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_name}, - "recipients::recipient_uuid::${recipient_uuid}}::recipient_email" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_email}, - "recipients::recipient_uuid::${recipient_uuid}}::recipient_language" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_language}, - "recipients::recipient_uuid::${recipient_uuid}}::recipient_level" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_level}, - "recipients::recipient_uuid::${recipient_uuid}}::level_on_host" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host}, + "recipients::recipient_uuid::${recipient_uuid}::recipient_name" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_name}, + "recipients::recipient_uuid::${recipient_uuid}::recipient_email" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_email}, + "recipients::recipient_uuid::${recipient_uuid}::recipient_language" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_language}, + "recipients::recipient_uuid::${recipient_uuid}::recipient_level" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_level}, + "recipients::recipient_uuid::${recipient_uuid}::level_on_host" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host}, }}); # Make it easy to look up the mail server's UUID from the server address. @@ -3538,7 +3541,7 @@ WHERE { $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host} = $notification_alert_level; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "recipients::recipient_uuid::${recipient_uuid}}::level_on_host" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host}, + "recipients::recipient_uuid::${recipient_uuid}::level_on_host" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host}, }}); last; } @@ -3582,7 +3585,7 @@ sub get_servers delete $anvil->data->{sys}{servers}{by_uuid}; delete $anvil->data->{sys}{servers}{by_name}; - my $query = " + my $query = " SELECT server_uuid, server_name, @@ -3602,18 +3605,12 @@ FROM ;"; $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 $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) - { - # I have a server_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 => "server_uuid", uuid => $server_uuid }}); - return(""); - } foreach my $row (@{$results}) { my $server_uuid = $row->[0]; @@ -3658,7 +3655,6 @@ FROM $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_file_uuid} = $server_post_migration_file_uuid; $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_arguments} = $server_post_migration_arguments; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "servers::server_uuid::${server_uuid}::" => $anvil->data->{servers}{server_uuid}{$server_uuid}{}, "servers::server_uuid::${server_uuid}::server_anvil_uuid" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid}, "servers::server_uuid::${server_uuid}::server_clean_stop" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_clean_stop}, "servers::server_uuid::${server_uuid}::server_start_after_server_uuid" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_after_server_uuid}, @@ -3677,6 +3673,96 @@ FROM } +=head2 get_server_definitions + +This loads all known server definition records from the database. + +Data is stored in two formats; + + server_definitions::server_definition_uuid::::server_definition_server_uuid + server_definitions::server_definition_uuid::::server_definition_xml + server_definitions::server_definition_uuid::::unix_modified_time + +And; + + server_definitions::server_definition_server_uuid::::$server_definition_uuid + server_definitions::server_definition_server_uuid::::server_definition_xml + server_definitions::server_definition_server_uuid::::unix_modified_time + +This method takes no parameters. + +=cut +sub get_server_definitions +{ + 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_server_definitions()" }}); + + # We're going to include the alert levels for this host based on overrides that might exist in the + # 'notifications' table. If the data hasn't already been loaded, we'll load it now. + if (not $anvil->data->{notifications}{notification_uuid}) + { + $anvil->Database->get_notifications({debug => $debug}); + } + + my $host_uuid = $anvil->Get->host_uuid(); + my $query = " +SELECT + server_definition_uuid, + server_definition_server_uuid, + server_definition_xml, + round(extract(epoch from modified_date)) AS mtime +FROM + server_definitions +;"; + $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 $server_definition_uuid = $row->[0]; + my $server_definition_server_uuid = $row->[1]; + my $server_definition_xml = $row->[2]; + my $unix_modified_time = $row->[3]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + server_definition_uuid => $server_definition_uuid, + server_definition_server_uuid => $server_definition_server_uuid, + server_definition_xml => $server_definition_xml, + unix_modified_time => $unix_modified_time, + }}); + + # Store the data + $anvil->data->{server_definitions}{server_definition_uuid}{$server_definition_uuid}{server_definition_server_uuid} = $server_definition_server_uuid; + $anvil->data->{server_definitions}{server_definition_uuid}{$server_definition_uuid}{server_definition_xml} = $server_definition_xml; + $anvil->data->{server_definitions}{server_definition_uuid}{$server_definition_uuid}{unix_modified_time} = $unix_modified_time; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "server_definitions::server_definition_uuid::${server_definition_uuid}::server_definition_server_uuid" => $anvil->data->{server_definitions}{server_definition_uuid}{$server_definition_uuid}{server_definition_server_uuid}, + "server_definitions::server_definition_uuid::${server_definition_uuid}::server_definition_xml" => $anvil->data->{server_definitions}{server_definition_uuid}{$server_definition_uuid}{server_definition_xml}, + "server_definitions::server_definition_uuid::${server_definition_uuid}::unix_modified_time" => $anvil->data->{server_definitions}{server_definition_uuid}{$server_definition_uuid}{unix_modified_time}, + }}); + + # Make it easy to locate records by 'server_uuid' as well. + $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_definition_server_uuid}{server_definition_uuid} = $server_definition_uuid; + $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_definition_server_uuid}{server_definition_xml} = $server_definition_xml; + $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_definition_server_uuid}{unix_modified_time} = $unix_modified_time; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "server_definitions::server_definition_server_uuid::${server_definition_server_uuid}::$server_definition_uuid" => $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_definition_server_uuid}{server_definition_uuid}, + "server_definitions::server_definition_server_uuid::${server_definition_server_uuid}::server_definition_xml" => $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_definition_server_uuid}{server_definition_xml}, + "server_definitions::server_definition_server_uuid::${server_definition_server_uuid}::unix_modified_time" => $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_definition_server_uuid}{unix_modified_time}, + }}); + } + + return(0); +} + + =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. @@ -9274,7 +9360,7 @@ sub insert_or_update_servers # Do we already know about this my $exists = 0; my $query = "SELECT COUNT(*) FROM servers WHERE server_uuid = ".$anvil->Database->quote($server_uuid).";"; - my $count = $anvil->Database->query({query => $function_query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + my $count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { count => $count }}); if ($count) { @@ -9305,7 +9391,7 @@ sub insert_or_update_servers my $old_server_state = $results->[0]->[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { old_server_state => $old_server_state }}); - if ($old_server_name ne "DELETED") + if ($old_server_state ne "DELETED") { my $query = " UPDATE @@ -9474,6 +9560,212 @@ WHERE } +=head2 insert_or_update_server_definitions + +This inserts or updates the C<< server_definitions >> table used to store (virtual) server XML definitions. + +Parameters; + +=head3 server_definition_uuid (optional) + +This is the server_definition UUID of a specific record to update. + +=head3 server_definition_server_uuid (required) + +This is the C<< servers >> -> C<< server_uuid >> of the server whose server_definition this belongs to. + +server_definition_xml (required) + +This is the server's XML definition file itself. + +=cut +sub insert_or_update_server_definitions +{ + 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->insert_or_update_server_definitions()" }}); + + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; + my $file = defined $parameter->{file} ? $parameter->{file} : ""; + my $line = defined $parameter->{line} ? $parameter->{line} : ""; + my $server_definition_uuid = defined $parameter->{server_definition_uuid} ? $parameter->{server_definition_uuid} : ""; + my $server_definition_server_uuid = defined $parameter->{server_definition_server_uuid} ? $parameter->{server_definition_server_uuid} : ""; + my $server_definition_xml = defined $parameter->{server_definition_xml} ? $parameter->{server_definition_xml} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + uuid => $uuid, + file => $file, + line => $line, + server_definition_uuid => $server_definition_uuid, + server_definition_server_uuid => $server_definition_server_uuid, + server_definition_xml => $server_definition_xml, + }}); + + if (not $server_definition_server_uuid) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_server_definitions()", parameter => "server_definition_server_uuid" }}); + return("!!error!!"); + } + if (not $anvil->Validate->uuid({uuid => $server_definition_server_uuid})) + { + # Bad UUID. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0130", variables => { method => "Database->insert_or_update_server_definitions()", parameter => "server_definition_server_uuid", uuid => $server_definition_server_uuid }}); + return("!!error!!"); + } + if (not $server_definition_xml) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_server_definitions()", parameter => "server_definition_xml" }}); + return("!!error!!"); + } + + # Make sure the server_definition_xml looks valid. + local $@; + my $dom = eval { XML::LibXML->load_xml(string => $server_definition_xml); }; + if ($@) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "warning_0066", variables => { + xml => $server_definition_xml, + error => $@, + }}); + return("!!error!!"); + } + + # Verify that I can read the UUID from the XML and verify that it matches the + # 'server_definition_server_uuid'. + my $server_name = $dom->findvalue('/domain/name'); + my $read_uuid = $dom->findvalue('/domain/uuid'); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + server_name => $server_name, + read_uuid => $read_uuid, + }}); + + if (not $read_uuid) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "warning_0067", variables => { xml => $server_definition_xml }}); + return("!!error!!"); + } + elsif ($read_uuid ne $server_definition_server_uuid) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "warning_0067", variables => { + passed_uuid => $server_definition_server_uuid, + read_uuid => $read_uuid, + xml => $server_definition_xml, + }}); + return("!!error!!"); + } + + # If we don't have a server_definition_uuid, look for one using the server_uuid. + if (not $server_definition_uuid) + { + my $query = " +SELECT + server_definition_uuid +FROM + server_definitions +WHERE + server_definition_server_uuid = ".$anvil->Database->quote($server_definition_server_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) + { + $server_definition_uuid = $results->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { server_definition_uuid => $server_definition_uuid }}); + } + } + + # UPDATE or INSERT. + if ($server_definition_uuid) + { + # Is there any difference? + my $query = " +SELECT + server_definition_server_uuid, + server_definition_xml +FROM + server_definitions +WHERE + server_definition_uuid = ".$anvil->Database->quote($server_definition_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, + }}); + foreach my $row (@{$results}) + { + my $old_server_definition_server_uuid = $row->[0]; + my $old_server_definition_xml = $row->[1]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + old_server_definition_server_uuid => $old_server_definition_server_uuid, + old_server_definition_xml => $old_server_definition_xml, + }}); + if (($old_server_definition_server_uuid ne $server_definition_server_uuid) or + ($old_server_definition_xml ne $server_definition_xml)) + { + # If the server_definition is what changed, log the diff. + if ($old_server_definition_xml ne $server_definition_xml) + { + my $difference = diff \$old_server_definition_xml, \$server_definition_xml, { STYLE => 'Unified' }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0556", variables => { + server_name => $server_name, + server_definition_server_uuid => $server_definition_server_uuid, + difference => $difference, + }}); + } + + # Save the changes. + my $query = " +UPDATE + server_definitions +SET + server_definition_xml = ".$anvil->Database->quote($server_definition_xml).", + server_definition_server_uuid = ".$anvil->Database->quote($server_definition_server_uuid).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + server_definition_uuid = ".$anvil->Database->quote($server_definition_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query =~ /passw/ ? $anvil->Log->is_secure($query) : $query }}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + } + } + } + else + { + $server_definition_uuid = $anvil->Get->uuid(); + my $query = " +INSERT INTO + server_definitions +( + server_definition_uuid, + server_definition_server_uuid, + server_definition_xml, + modified_date +) VALUES ( + ".$anvil->Database->quote($server_definition_uuid).", + ".$anvil->Database->quote($server_definition_server_uuid).", + ".$anvil->Database->quote($server_definition_xml).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +); +"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query =~ /passw/ ? $anvil->Log->is_secure($query) : $query }}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + } + + return($server_definition_uuid); +} + + =head2 insert_or_update_sessions This updates (or inserts) a record in the 'sessions' table. The C<< session_uuid >> referencing the database row will be returned. diff --git a/Anvil/Tools/ScanCore.pm b/Anvil/Tools/ScanCore.pm index 1977ce48..95a744e2 100755 --- a/Anvil/Tools/ScanCore.pm +++ b/Anvil/Tools/ScanCore.pm @@ -132,6 +132,9 @@ sub agent_startup return("!!error!!"); } + my $table_count = @{$tables}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { table_count => $table_count }}); + # It's possible that some agents don't have a database (or use core database tables only) if (@{$tables} > 0) { diff --git a/notes b/notes index 73875c0d..91ecaad1 100644 --- a/notes +++ b/notes @@ -264,6 +264,151 @@ CREATE TRIGGER trigger_temperature AFTER INSERT OR UPDATE ON temperature FOR EACH ROW EXECUTE PROCEDURE history_temperature(); +DROP FUNCTION history_definitions() CASCADE; +DROP TABLE history.definitions; +DROP TABLE definitions; +DROP FUNCTION history_servers() CASCADE; +DROP TABLE history.servers; +DROP TABLE servers; + +-- This stores servers made available to Anvil! systems and DR hosts. +CREATE TABLE servers ( + server_uuid uuid not null primary key, + server_name text not null, -- This is the server's name. It can change without re-uploading the server. + server_anvil_uuid uuid not null, -- This is the Anvil! system that the server lives on. It can move to another Anvil!, so this can change. + server_clean_stop boolean not null default FALSE, -- When set, the server was stopped by a user. The Anvil! will not start a server that has been cleanly stopped. + server_start_after_server_uuid uuid not null, -- This can be the server_uuid of another server. If set, this server will boot 'server_start_delay' seconds after the referenced server boots. A value of '00000000-0000-0000-0000-000000000000' will tell 'anvil-safe-start' to not boot the server at all. If a server is set not to start, any dependent servers will also stay off. + server_start_delay integer not null default 0, -- See above. + server_host_uuid uuid not null, -- This is the current hosts -> host_uuid for this server. If the server is off, this will be blank. + server_state text not null, -- This is the current state of this server. + server_live_migration boolean not null default TRUE, -- When false, servers will be stopped and then rebooted when a migration is requested. Also, when false, preventative migrations will not happen. + server_pre_migration_file_uuid uuid not null, -- This is set to the files -> file_uuid of a script to run BEFORE migrating a server. If the file isn't found or can't run, the script is ignored. + server_pre_migration_arguments text not null, -- These are arguments to pass to the pre-migration script + server_post_migration_file_uuid uuid not null, -- This is set to the files -> file_uuid of a script to run AFTER migrating a server. If the file isn't found or can't run, the script is ignored. + server_post_migration_arguments text not null, -- These are arguments to pass to the post-migration script + modified_date timestamp with time zone not null, + + FOREIGN KEY(server_anvil_uuid) REFERENCES anvils(anvil_uuid), + FOREIGN KEY(server_start_after_server_uuid) REFERENCES servers(server_uuid), + FOREIGN KEY(server_host_uuid) REFERENCES hosts(host_uuid), + FOREIGN KEY(server_pre_migration_file_uuid) REFERENCES files(file_uuid), + FOREIGN KEY(server_post_migration_file_uuid) REFERENCES files(file_uuid) +); +ALTER TABLE servers OWNER TO admin; + +CREATE TABLE history.servers ( + history_id bigserial, + server_uuid uuid, + server_name text, + server_anvil_uuid uuid, + server_clean_stop boolean, + server_start_after_server_uuid uuid, + server_start_delay integer, + server_host_uuid uuid, + server_state text, + server_live_migration boolean, + server_pre_migration_file_uuid uuid, + server_pre_migration_arguments text, + server_post_migration_file_uuid uuid, + server_post_migration_arguments text, + modified_date timestamp with time zone not null +); +ALTER TABLE history.servers OWNER TO admin; + +CREATE FUNCTION history_servers() RETURNS trigger +AS $$ +DECLARE + history_servers RECORD; +BEGIN + SELECT INTO history_servers * FROM servers WHERE server_uuid = new.server_uuid; + INSERT INTO history.servers + (server_uuid, + server_name, + server_anvil_uuid, + server_clean_stop, + server_start_after_server_uuid, + server_start_delay, + server_host_uuid, + server_state, + server_live_migration, + server_pre_migration_file_uuid, + server_pre_migration_arguments, + server_post_migration_file_uuid, + server_post_migration_arguments, + modified_date) + VALUES + (history_servers.server_uuid, + history_servers.server_name, + history_servers.server_clean_stop, + history_servers.server_start_after_server_uuid, + history_servers.server_start_delay, + history_servers.server_host_uuid, + history_servers.server_state, + history_servers.server_live_migration, + history_servers.server_pre_migration_file_uuid, + history_servers.server_pre_migration_arguments, + history_servers.server_post_migration_file_uuid, + history_servers.server_post_migration_arguments, + history_servers.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_servers() OWNER TO admin; + +CREATE TRIGGER trigger_servers + AFTER INSERT OR UPDATE ON servers + FOR EACH ROW EXECUTE PROCEDURE history_servers(); + + +-- This stores the XML definition for a server. Whenever a server_definition is found missing on a node or DR host, +-- it will be rewritten from here. If this copy changes, it will be updated on the hosts. +CREATE TABLE server_definitions ( + server_definition_uuid uuid not null primary key, + server_definition_server_uuid uuid not null, -- This is the servers -> server_uuid of the server + server_definition_xml text not null, -- This is the XML body. + modified_date timestamp with time zone not null, + + FOREIGN KEY(server_definition_server_uuid) REFERENCES servers(server_uuid) +); +ALTER TABLE server_definitions OWNER TO admin; + +CREATE TABLE history.server_definitions ( + history_id bigserial, + server_definition_uuid uuid, + server_definition_server_uuid uuid, + server_definition_xml text, + modified_date timestamp with time zone not null +); +ALTER TABLE history.server_definitions OWNER TO admin; + +CREATE FUNCTION history_server_definitions() RETURNS trigger +AS $$ +DECLARE + history_server_definitions RECORD; +BEGIN + SELECT INTO history_server_definitions * FROM server_definitions WHERE server_definition_uuid = new.server_definition_uuid; + INSERT INTO history.server_definitions + (server_definition_uuid, + server_definition_server_uuid, + server_definition_xml, + modified_date) + VALUES + (history_server_definitions.server_definition_uuid, + history_server_definitions.server_definition_server_uuid, + history_server_definitions.server_definition_xml, + history_server_definitions.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_server_definitions() OWNER TO admin; + +CREATE TRIGGER trigger_server_definitions + AFTER INSERT OR UPDATE ON server_definitions + FOR EACH ROW EXECUTE PROCEDURE history_server_definitions(); + + COMMIT; ============ diff --git a/scancore-agents/scan-cluster/scan-cluster b/scancore-agents/scan-cluster/scan-cluster index e06edb38..1e0694ba 100755 --- a/scancore-agents/scan-cluster/scan-cluster +++ b/scancore-agents/scan-cluster/scan-cluster @@ -9,7 +9,7 @@ # # Exit codes; # 0 = Normal exit. -# 1 = Startup failure (no DB, bad file read, etc) +# 1 = Startup failure (not running as root, no DB, bad file read, etc) # # TODO: # - @@ -23,6 +23,10 @@ use Data::Dumper; # Disable buffering $| = 1; +# Prevent a discrepency between UID/GID and EUID/EGID from throwing an error. +$< = $>; +$( = $); + my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; if (($running_directory =~ /^\./) && ($ENV{PWD})) @@ -34,6 +38,15 @@ my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1}); $anvil->Log->level({set => 2}); $anvil->Log->secure({set => 1}); +# Make sure we're running as 'root' +# $< == real UID, $> == effective UID +if (($< != 0) && ($> != 0)) +{ + # Not root + print $anvil->Words->string({key => "error_0005"})."\n"; + $anvil->nice_exit({exit_code => 1}); +} + $anvil->data->{scancore}{'scan-cluster'}{disable} = 0; $anvil->data->{switches}{force} = 0; diff --git a/scancore-agents/scan-hardware/scan-hardware b/scancore-agents/scan-hardware/scan-hardware index d1eac97a..4d55e389 100755 --- a/scancore-agents/scan-hardware/scan-hardware +++ b/scancore-agents/scan-hardware/scan-hardware @@ -6,7 +6,7 @@ # # Exit codes; # 0 = Normal exit. -# 1 = Startup failure (no DB, bad file read, etc) +# 1 = Startup failure (not running as root, no DB, bad file read, etc) # # TODO: # - @@ -20,6 +20,10 @@ use Data::Dumper; # Disable buffering $| = 1; +# Prevent a discrepency between UID/GID and EUID/EGID from throwing an error. +$< = $>; +$( = $); + my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; if (($running_directory =~ /^\./) && ($ENV{PWD})) @@ -31,6 +35,15 @@ my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1}); $anvil->Log->level({set => 2}); $anvil->Log->secure({set => 1}); +# Make sure we're running as 'root' +# $< == real UID, $> == effective UID +if (($< != 0) && ($> != 0)) +{ + # Not root + print $anvil->Words->string({key => "error_0005"})."\n"; + $anvil->nice_exit({exit_code => 1}); +} + # These are the threasholds for when to alert when swap is running out. $anvil->data->{scancore}{'scan-hardware'}{disable} = 0; $anvil->data->{scancore}{'scan-hardware'}{ram}{clear_threshold} = 134217728; # 128 MiB diff --git a/scancore-agents/scan-server/scan-server b/scancore-agents/scan-server/scan-server index e15ec83d..a5e34a33 100755 --- a/scancore-agents/scan-server/scan-server +++ b/scancore-agents/scan-server/scan-server @@ -8,7 +8,7 @@ # # Exit codes; # 0 = Normal exit. -# 1 = Startup failure (no DB, bad file read, etc) +# 1 = Startup failure (not running as root, no DB, bad file read, etc) # 2 = libvirtd is not running. # # TODO: @@ -19,10 +19,15 @@ use strict; use warnings; use Anvil::Tools; use Data::Dumper; +use Text::Diff; # Disable buffering $| = 1; +# Prevent a discrepency between UID/GID and EUID/EGID from throwing an error. +$< = $>; +$( = $); + my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; if (($running_directory =~ /^\./) && ($ENV{PWD})) @@ -34,6 +39,15 @@ my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1}); $anvil->Log->level({set => 2}); $anvil->Log->secure({set => 1}); +# Make sure we're running as 'root' +# $< == real UID, $> == effective UID +if (($< != 0) && ($> != 0)) +{ + # Not root + print $anvil->Words->string({key => "error_0005"})."\n"; + $anvil->nice_exit({exit_code => 1}); +} + $anvil->data->{scancore}{'scan-server'}{disable} = 0; $anvil->data->{switches}{force} = 0; @@ -50,7 +64,7 @@ if (($anvil->data->{scancore}{'scan-server'}{disable}) && (not $anvil->data->{sw } # This scan agent only uses core tables (server and definitions). -$anvil->data->{scancore}{'scan-server'}{tables} = [""]; +$anvil->data->{scancore}{'scan-server'}{tables} = []; # Handle start-up tasks my $problem = $anvil->ScanCore->agent_startup({ @@ -103,6 +117,10 @@ sub collect_data $anvil->nice_exit({exit_code => 2}); } + # Load data we know about + $anvil->Database->get_servers({debug => 2}); + $anvil->Database->get_server_definitions({debug => 2}); + my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." list --all", source => $THIS_FILE, line => __LINE__}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output, return_code => $return_code }}); foreach my $line (split/\n/, $output) @@ -112,8 +130,8 @@ sub collect_data if ($line =~ /^\d+ (.*?) (.*)$/) { - my $server = $1; - my $state = $2; + my $server = $1; + my $state = $2; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { server => $server, 'state' => $state, @@ -165,25 +183,174 @@ sub collect_data } my $server_uuid = $anvil->data->{server}{$target}{$server}{$source}{info}{uuid}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { server_uuid => $server_uuid }}); + my $server_name = $anvil->data->{server}{$target}{$server}{$source}{info}{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + server_uuid => $server_uuid, + server_name => $server_name, + }}); # See if this server is in the database. - my $query = "SELECT COUNT(*) FROM servers WHERE server_uuid = ".$anvil->Database->quote($server_uuid).";"; - my $count = $anvil->Database->query({query => $function_query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { count => $count }}); - if (not $count) + if (not exists $anvil->data->{servers}{server_uuid}{$server_uuid}) { - # Add it. + # Add it. I'll need my anvil_uuid first. + my $anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => 2}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { anvil_uuid => $anvil_uuid }}); + + my $got_server_uuid = $anvil->Database->insert_or_update_servers({ + debug => 2, + server_uuid => $server_uuid, + server_name => $server_name, + server_anvil_uuid => $anvil_uuid, + server_clean_stop => "", + server_start_after_server_uuid => "", + server_start_delay => 0, + server_host_uuid => $anvil->Get->host_uuid, + server_state => $anvil->data->{'scan-server'}{server}{$server}{'state'}, + server_live_migration => 1, + server_pre_migration_file_uuid => "", + server_pre_migration_arguments => "", + server_post_migration_file_uuid => "", + server_post_migration_arguments => "", + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { got_server_uuid => $got_server_uuid }}); + + if ((not $got_server_uuid) or ($got_server_uuid eq "!!error!!")) + { + # What? + next; + } + else + { + # Store the definition from memory + my $server_definition_uuid = $anvil->Database->insert_or_update_server_definitions({ + debug => 2, + server_definition_xml => $definition, + server_definition_server_uuid => $server_uuid, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_definition_uuid => $server_definition_uuid }}); + if ((not $server_definition_uuid) or ($server_definition_uuid ne "!!error!!")) + { + # What? + next; + } + } + + # Reload the servers. + $anvil->Database->get_servers({debug => 2}); + $anvil->Database->get_server_definitions({debug => 2}); } - ### TODO: left off here, look for the XML file on disk. If it's not found, write it. - my $xml_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server.".xml"; - if (-e $xml_file) + # If the defition in the database isn't the same as the definition in memory, update it. + if ($definition ne $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_uuid}{server_definition_xml}) { - $anvil->Storage->get_file_stats({ - debug => 2, - file_path => $xml_file, + # Register a notice-level alert. + my $difference = diff \$anvil->data->{server_definitions}{server_definition_server_uuid}{$server_uuid}{server_definition_xml}, \$definition, { STYLE => 'Unified' }; + my $variables = { + server => $server, + difference => $difference, + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0001", variables => $variables}); + $anvil->Alert->register({ + alert_level => "notice", + message => "scan_server_alert_0001", + message_variables => $variables, + set_by => $THIS_FILE, + }); + + # Update the definition in the database. + my $server_definition_uuid = $anvil->Database->insert_or_update_server_definitions({ + debug => 2, + server_definition_xml => $definition, + server_definition_server_uuid => $server_uuid, }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_definition_uuid => $server_definition_uuid }}); + if ((not $server_definition_uuid) or ($server_definition_uuid ne "!!error!!")) + { + # What? + next; + } + $anvil->Database->get_server_definitions({debug => 2}); + } + + ### NOTE: If the disk version differs from the in-memory or in-database copy, and the + ### age of the file on disk is newer than the age of the database record (which will + ### match the in-memory definition by this point), we'll leave the disk version alone. + ### This is done so that a manual edit of the definition file won't be immediately + ### overwritten by the definition in memory. + # If the file doesn't exist, we'll write it out. + # If it does exist, but differs from the version in memory, we'll write the file. + my $write_file = 0; + my $xml_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server.".xml"; + if (not -e $xml_file) + { + $write_file = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { write_file => $write_file }}); + } + else + { + my $on_disk_definition = $anvil->Storage->read_file({ + debug => 2, + file => $xml_file, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { on_disk_definition => $on_disk_definition }}); + if (($on_disk_definition ne "!!error!!") && ($on_disk_definition) && ($definition ne $on_disk_definition)) + { + $anvil->Storage->get_file_stats({ + debug => 2, + file_path => $xml_file, + }); + + my $current_time = time; + my $file_modified_time = $anvil->data->{file_stat}{$xml_file}{modified_time}; + my $database_modified_time = $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_uuid}{unix_modified_time}; + my $file_age = $current_time - $file_modified_time; + my $database_age = $current_time - $database_modified_time; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:current_time' => $current_time, + 's2:file_modified_time' => $file_modified_time, + 's3:database_modified_time' => $database_modified_time, + 's4:file_age' => $file_age, + 's5:database_age' => $database_age, + }}); + + # Compare the age of the on-disk file with the age of the database record. If + # the database age is newer, update the on-disk copy. + if ($file_age >= $database_age) + { + # Update the disk copy. + my $difference = diff \$anvil->data->{server_definitions}{server_definition_server_uuid}{$server_uuid}{server_definition_xml}, \$definition, { STYLE => 'Unified' }; + my $variables = { + server => $server, + difference => $difference, + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0002", variables => $variables}); + $anvil->Alert->register({ + alert_level => "notice", + message => "scan_server_alert_0002", + message_variables => $variables, + set_by => $THIS_FILE, + }); + + $write_file = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { write_file => $write_file }}); + } + } + } + + if ($write_file) + { + my $return = $anvil->Storage->write_file({ + debug => 3, + body => $definition, + file => $xml_file, + overwrite => 1, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'return' => $return }}); + if ($return) + { + # Something went wrong. + next; + } } } diff --git a/scancore-agents/scan-server/scan-server.xml b/scancore-agents/scan-server/scan-server.xml index 2c3ed1f8..701d2c95 100644 --- a/scancore-agents/scan-server/scan-server.xml +++ b/scancore-agents/scan-server/scan-server.xml @@ -14,7 +14,20 @@ NOTE: All string keys MUST be prefixed with the agent name! ie: 'scan_server_log - + +The definition file for the server: [#!variable!server!#] has changed relative to the database record. This is usually harmless. +The differences are: +==== +#!variable!difference!# +==== + + +The definition file for the server: [#!variable!server!#] has changed relative to disk record. This is usually harmless. +The differences are: +==== +#!variable!difference!# +==== + Starting: [#!variable!program!#]. diff --git a/share/anvil.sql b/share/anvil.sql index 3b4ef804..08685af3 100644 --- a/share/anvil.sql +++ b/share/anvil.sql @@ -1325,52 +1325,52 @@ CREATE TRIGGER trigger_servers FOR EACH ROW EXECUTE PROCEDURE history_servers(); --- This stores the XML definition for a server. Whenever a definition is found missing on a node or DR host, +-- This stores the XML definition for a server. Whenever a server_definition is found missing on a node or DR host, -- it will be rewritten from here. If this copy changes, it will be updated on the hosts. -CREATE TABLE definitions ( - definition_uuid uuid not null primary key, - definition_server_uuid uuid not null, -- This is the servers -> server_uuid of the server - definition_xml text not null, -- This is the XML body. - modified_date timestamp with time zone not null, +CREATE TABLE server_definitions ( + server_definition_uuid uuid not null primary key, + server_definition_server_uuid uuid not null, -- This is the servers -> server_uuid of the server + server_definition_xml text not null, -- This is the XML body. + modified_date timestamp with time zone not null, - FOREIGN KEY(definition_server_uuid) REFERENCES servers(server_uuid) + FOREIGN KEY(server_definition_server_uuid) REFERENCES servers(server_uuid) ); -ALTER TABLE definitions OWNER TO admin; - -CREATE TABLE history.definitions ( - history_id bigserial, - definition_uuid uuid, - definition_server_uuid uuid, - definition_xml text, - modified_date timestamp with time zone not null +ALTER TABLE server_definitions OWNER TO admin; + +CREATE TABLE history.server_definitions ( + history_id bigserial, + server_definition_uuid uuid, + server_definition_server_uuid uuid, + server_definition_xml text, + modified_date timestamp with time zone not null ); -ALTER TABLE history.definitions OWNER TO admin; +ALTER TABLE history.server_definitions OWNER TO admin; -CREATE FUNCTION history_definitions() RETURNS trigger +CREATE FUNCTION history_server_definitions() RETURNS trigger AS $$ DECLARE - history_definitions RECORD; + history_server_definitions RECORD; BEGIN - SELECT INTO history_definitions * FROM definitions WHERE definition_uuid = new.definition_uuid; - INSERT INTO history.definitions - (definition_uuid, - definition_server_uuid, - definition_xml, + SELECT INTO history_server_definitions * FROM server_definitions WHERE server_definition_uuid = new.server_definition_uuid; + INSERT INTO history.server_definitions + (server_definition_uuid, + server_definition_server_uuid, + server_definition_xml, modified_date) VALUES - (history_definitions.definition_uuid, - history_definitions.definition_server_uuid, - history_definitions.definition_xml, - history_definitions.modified_date); + (history_server_definitions.server_definition_uuid, + history_server_definitions.server_definition_server_uuid, + history_server_definitions.server_definition_xml, + history_server_definitions.modified_date); RETURN NULL; END; $$ LANGUAGE plpgsql; -ALTER FUNCTION history_definitions() OWNER TO admin; +ALTER FUNCTION history_server_definitions() OWNER TO admin; -CREATE TRIGGER trigger_definitions - AFTER INSERT OR UPDATE ON definitions - FOR EACH ROW EXECUTE PROCEDURE history_definitions(); +CREATE TRIGGER trigger_server_definitions + AFTER INSERT OR UPDATE ON server_definitions + FOR EACH ROW EXECUTE PROCEDURE history_server_definitions(); -- It stores a general list of OUI (Organizationally Unique Identifier) to allow lookup of MAC address to diff --git a/share/words.xml b/share/words.xml index cdc88100..031188d6 100644 --- a/share/words.xml +++ b/share/words.xml @@ -1058,6 +1058,11 @@ The file: [#!variable!file!#] needs to be updated. The difference is: The server: [#!variable!server!#] has booted! Waiting for the server: [#!variable!server!#] to shut down... The server: [#!variable!server!#] is now off. + The server: [#!variable!server!#] (#!variable!server_uuid!#) has a definition change: +==== +#!variable!difference!# +==== + The host name: [#!variable!target!#] does not resolve to an IP address. @@ -1857,6 +1862,30 @@ The error was: [ Warning ] - The server: [#!variable!server!#] was asked to be migrated to: [#!variable!requested_node!#], but the server is shutting down. Aborting. [ Warning ] - The server: [#!variable!server!#] was asked to be migrated to: [#!variable!requested_node!#], but the server is already in the middle of a migration. Aborting. + [ Warning ] - Failed to parse the XML: +======== +#!variable!xml!# +======== + +The error was: + +======== +#!variable!error!# +======== + + [ Warning ] - Failed to find the server's UUID from the definition XML: +======== +#!variable!xml!# +======== + + [ Warning ] - The server UUID read: from the definition XML doesn't match the passed-in server UUID. +Passed in UUID: [#!variable!passed_uuid!#] +Read UUID: .... [#!variable!read_uuid!#] + +======== +#!variable!xml!# +======== + diff --git a/tools/anvil-daemon b/tools/anvil-daemon index 9c85f39c..472cf5b7 100755 --- a/tools/anvil-daemon +++ b/tools/anvil-daemon @@ -5,7 +5,7 @@ # # Exit codes; # 0 = Normal exit or md5sum of this program changed and it exited to reload. -# 1 = +# 1 = Not running as root. # 2 = Unable to connect to any database, even after trying to initialize the local system. # # TODO: @@ -65,6 +65,10 @@ if (($running_directory =~ /^\./) && ($ENV{PWD})) # Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete. $| = 1; +# Prevent a discrepency between UID/GID and EUID/EGID from throwing an error. +$< = $>; +$( = $); + # NOTE: Setting 'log_level' and 'log_secure' here will get overridden in the main lopp. Use the Log methods # in the loop as well to override defaults in code. my $anvil = Anvil::Tools->new(); @@ -72,6 +76,15 @@ $anvil->Log->level({set => 2}); $anvil->Log->secure({set => 1}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }}); +# Make sure we're running as 'root' +# $< == real UID, $> == effective UID +if (($< != 0) && ($> != 0)) +{ + # Not root + print $anvil->Words->string({key => "error_0005"})."\n"; + $anvil->nice_exit({exit_code => 1}); +} + # Connect to the database(s). If we have no connections, we'll proceed anyway as one of the 'run_once' tasks # is to setup the database server. $anvil->Database->connect({debug => 3, check_if_configured => 1}); diff --git a/tools/scancore b/tools/scancore index 9dd384d6..1d6164cd 100755 --- a/tools/scancore +++ b/tools/scancore @@ -6,7 +6,8 @@ # # Exit codes; # 0 = Normal exit. -# 1 = No database connections available. +# 1 = Not running as root. +# 2 = # # TODO: # - Decide if it's worth having a separate ScanCore.log file or just feed into anvil.log. @@ -20,6 +21,10 @@ use Data::Dumper; # Disable buffering $| = 1; +# Prevent a discrepency between UID/GID and EUID/EGID from throwing an error. +$< = $>; +$( = $); + my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; if (($running_directory =~ /^\./) && ($ENV{PWD})) @@ -29,6 +34,15 @@ if (($running_directory =~ /^\./) && ($ENV{PWD})) my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1}); +# Make sure we're running as 'root' +# $< == real UID, $> == effective UID +if (($< != 0) && ($> != 0)) +{ + # Not root + print $anvil->Words->string({key => "error_0005"})."\n"; + $anvil->nice_exit({exit_code => 1}); +} + $anvil->data->{scancore} = { threshold => { warning_temperature => 5,