diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm index ac9f7d73..92f10e3a 100644 --- a/Anvil/Tools.pm +++ b/Anvil/Tools.pm @@ -51,6 +51,7 @@ use Anvil::Tools::Job; use Anvil::Tools::Log; use Anvil::Tools::Network; use Anvil::Tools::Remote; +use Anvil::Tools::ScanCore; use Anvil::Tools::Server; use Anvil::Tools::Striker; use Anvil::Tools::Storage; @@ -134,6 +135,7 @@ sub new JOB => Anvil::Tools::Job->new(), NETWORK => Anvil::Tools::Network->new(), REMOTE => Anvil::Tools::Remote->new(), + SCANCORE => Anvil::Tools::ScanCore->new(), SERVER => Anvil::Tools::Server->new(), STRIKER => Anvil::Tools::Striker->new(), STORAGE => Anvil::Tools::Storage->new(), @@ -177,6 +179,7 @@ sub new $anvil->Job->parent($anvil); $anvil->Network->parent($anvil); $anvil->Remote->parent($anvil); + $anvil->ScanCore->parent($anvil); $anvil->Server->parent($anvil); $anvil->Striker->parent($anvil); $anvil->Storage->parent($anvil); @@ -592,6 +595,18 @@ sub Remote return ($self->{HANDLE}{REMOTE}); } +=head2 ScanCore + +Access the C methods via 'C<< $anvil->ScanCore->method >>'. + +=cut +sub ScanCore +{ + my $self = shift; + + return ($self->{HANDLE}{SCANCORE}); +} + =head2 Server Access the C methods via 'C<< $anvil->Server->method >>'. @@ -897,6 +912,9 @@ sub _set_defaults # grep 'CREATE TABLE' share/anvil.sql | grep -v history. | awk '{print $3}' core_tables => [ "hosts", # Always has to be first. + "health", + "power", + "temperature", "ssh_keys", "users", "host_variable", @@ -1184,6 +1202,7 @@ sub _set_paths ifdown => "/sbin/ifdown", ifup => "/sbin/ifup", ip => "/usr/sbin/ip", + 'ipmi-oem' => "/usr/sbin/ipmi-oem", ipmitool => "/usr/bin/ipmitool", 'iptables-save' => "/usr/sbin/iptables-save", journalctl => "/usr/bin/journalctl", @@ -1257,6 +1276,8 @@ sub _set_paths alert => "/var/log/anvil.alert.log", }, proc => { + cpuinfo => "/proc/cpuinfo", + meminfo => "/proc/meminfo", uptime => "/proc/uptime", }, secure => { diff --git a/Anvil/Tools/Alert.pm b/Anvil/Tools/Alert.pm index d1053652..053a1d75 100644 --- a/Anvil/Tools/Alert.pm +++ b/Anvil/Tools/Alert.pm @@ -166,7 +166,7 @@ SELECT FROM alert_sent WHERE - alert_sent_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." + alert_sent_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." AND alert_set_by = ".$anvil->Database->quote($set_by)." AND @@ -198,7 +198,7 @@ SELECT FROM hosts WHERE - host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." + host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); @@ -236,7 +236,7 @@ INSERT INTO modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid).", - ".$anvil->Database->quote($anvil->data->{sys}{host_uuid}).", + ".$anvil->Database->quote($anvil->Get->host_uuid).", ".$anvil->Database->quote($set_by).", ".$anvil->Database->quote($record_locator).", ".$anvil->Database->quote($name).", @@ -317,6 +317,12 @@ This is the message body of the alert. It is expected to be in the format C<< >. Example with two variables; C<< foo_0002,!!bar!abc!!,!!baz!123!! >>. +B<< Note >>: See C<< message_variables >> for an alternate method of passing variables + +=head3 message_variables (optional) + +This can be set as a hash reference containing key / variable pairs to inject into the message key. the C<< variable => value >> pairs will be appended to the C<< message >> key automatically. This is meant to simplify when an alert is also being longed, or when a large number of variables are being injected into the string. + =head3 set_by (required) This is the name of the program that registered this alert. Usually this is simply the caller's C<< $THIS_FILE >> or C<< $0 >> variable. @@ -357,21 +363,23 @@ sub register my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Alert->register()" }}); - my $alert_level = defined $parameter->{alert_level} ? $parameter->{alert_level} : 0; - my $clear_alert = defined $parameter->{clear_alert} ? $parameter->{clear_alert} : 0; - my $message = defined $parameter->{message} ? $parameter->{message} : ""; - my $set_by = defined $parameter->{set_by} ? $parameter->{set_by} : ""; - my $show_header = defined $parameter->{show_header} ? $parameter->{show_header} : 1; - my $sort_position = defined $parameter->{sort_position} ? $parameter->{sort_position} : 9999; - my $title = defined $parameter->{title} ? $parameter->{title} : ""; + my $alert_level = defined $parameter->{alert_level} ? $parameter->{alert_level} : 0; + my $clear_alert = defined $parameter->{clear_alert} ? $parameter->{clear_alert} : 0; + my $message = defined $parameter->{message} ? $parameter->{message} : ""; + my $message_variables = defined $parameter->{message_variables} ? $parameter->{message_variables} : "", + my $set_by = defined $parameter->{set_by} ? $parameter->{set_by} : ""; + my $show_header = defined $parameter->{show_header} ? $parameter->{show_header} : 1; + my $sort_position = defined $parameter->{sort_position} ? $parameter->{sort_position} : 9999; + my $title = defined $parameter->{title} ? $parameter->{title} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - show_header => $show_header, - clear_alert => $clear_alert, - alert_level => $alert_level, - message => $message, - set_by => $set_by, - sort_position => $sort_position, - title => $title, + show_header => $show_header, + clear_alert => $clear_alert, + alert_level => $alert_level, + message => $message, + message_variables => ref($message_variables), + set_by => $set_by, + sort_position => $sort_position, + title => $title, }}); # Missing parameters? @@ -391,6 +399,15 @@ sub register return("!!error!!"); } + if (ref($message_variables) eq "HASH") + { + foreach my $variable (sort {$a cmp $b} keys %{$message_variables}) + { + my $value = defined $message_variables->{$variable} ? $message_variables->{$variable} : "undefined:".$variable; + $message .= ",!!".$variable."!".$value."!!"; + } + } + # If the alert level was a string, convert it to the numerical version. Also check that we've got a # sane alert level at all. if (lc($alert_level) eq "critical") diff --git a/Anvil/Tools/Convert.pm b/Anvil/Tools/Convert.pm index 6e96bd4d..950e71bd 100644 --- a/Anvil/Tools/Convert.pm +++ b/Anvil/Tools/Convert.pm @@ -182,7 +182,7 @@ This is the number of bytes that will be converted. This can be a signed integer =head3 unit (optional) -This is a letter +This is a letter that allows the caller to request the returned value be in a given unit, rather than the closest unit for the given value. =cut sub bytes_to_human_readable @@ -194,36 +194,36 @@ sub bytes_to_human_readable $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Convert->bytes_to_human_readable()" }}); # Now see if the user passed the values in a hash reference or directly. - my $size = defined $parameter->{'bytes'} ? $parameter->{'bytes'} : 0; + my $bytes = defined $parameter->{'bytes'} ? $parameter->{'bytes'} : 0; my $unit = defined $parameter->{unit} ? uc($parameter->{unit}) : ""; my $base2 = defined $parameter->{base2} ? $parameter->{base2} : $anvil->data->{sys}{use_base2}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - size => $size, - unit => $unit, - base2 => $base2, + base2 => $base2, + 'bytes' => $bytes, + unit => $unit, }}); # Expand exponential numbers. - if ($size =~ /(\d+)e\+(\d+)/) + if ($bytes =~ /(\d+)e\+(\d+)/) { - my $base = $1; - my $exp = $2; - $size = $base; + my $base = $1; + my $exp = $2; + $bytes = $base; for (1..$exp) { - $size .= "0"; + $bytes .= "0"; } } # Setup my variables. my $suffix = ""; - my $human_readable_size = $size; + my $human_readable_size = $bytes; # Store and strip the sign my $sign = ""; if ($human_readable_size =~ /^-/) { - $sign = "-"; + $sign = "-"; $human_readable_size =~ s/^-//; } $human_readable_size =~ s/,//g; @@ -240,6 +240,8 @@ sub bytes_to_human_readable return ("!!error!!"); } + ### TODO: We process the bytes here, but maybe we shouldn't so that when this goes into an alert, it + ### can be translated later. # Do the math. if ($base2) { diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index 875d3797..81d70a6c 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -16,6 +16,7 @@ my $THIS_FILE = "Database.pm"; ### Methods; # archive_database +# check_condition_age # check_lock_age # check_for_schema # configure_pgsql @@ -44,7 +45,7 @@ my $THIS_FILE = "Database.pm"; # insert_or_update_fences # insert_or_update_file_locations # insert_or_update_files -# insert_or_update_ssh_keys +# insert_or_update_health # insert_or_update_hosts # insert_or_update_ip_addresses # insert_or_update_jobs @@ -54,9 +55,12 @@ my $THIS_FILE = "Database.pm"; # insert_or_update_notifications # insert_or_update_mac_to_ip # insert_or_update_oui +# insert_or_update_power # insert_or_update_recipients # insert_or_update_sessions +# insert_or_update_ssh_keys # insert_or_update_states +# insert_or_update_temperature # insert_or_update_upses # insert_or_update_users # insert_or_update_variables @@ -293,6 +297,121 @@ sub archive_database } +=head2 check_condition_age + +This checks to see how long ago a given condition (variable, really) has been set. This is generally used when a program, often a scan agent, wants to wait to see if a given state persists before sending an alert and/or taking an action. + +A common example is seeing how long power has been lost, if a lost sensor is going to return, etc. + +The age of the condition is returned, in seconds. If there is a problem, C<< !!error!! >> is returned. + +Parameters; + +=head3 clear (optional) + +When set to C<< 1 >>, if the condition exists, it is cleared. If the condition does not exist, nothing happens. + +=head3 name (required) + +This is the name of the condition being set. It's a free-form string, but generally in a format like C<< :: >>. + +=head3 host_uuid (optional) + +If a condition is host-specific, this can be set to the caller's C<< host_uuid >>. Generally this is needed, save for conditions related to hosted servers that are not host-bound. + +=cut +sub check_condition_age +{ + 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->check_condition_age()" }}); + + my $clear = defined $parameter->{clear} ? $parameter->{clear} : 0; + my $name = defined $parameter->{name} ? $parameter->{name} : ""; + my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : "NULL"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + clear => $clear, + name => $name, + host_uuid => $host_uuid, + }}); + + if (not $name) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->check_condition_age()", parameter => "name" }}); + return("!!error!!"); + } + + my $age = 0; + my $source_table = $host_uuid ? "hosts" : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { source_table => $source_table }}); + + # See if this variable has been set yet. + my ($variable_value, $variable_uuid, $epoch_modified_date, $modified_date) = $anvil->Database->read_variable({ + variable_name => $name, + variable_source_table => $source_table, + variable_source_uuid => $host_uuid, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + variable_value => $variable_value, + variable_uuid => $variable_uuid, + epoch_modified_date => $epoch_modified_date, + modified_date => $modified_date, + }}); + if ($variable_uuid) + { + # Are we clearing? + if ($clear) + { + # Yup + $variable_uuid = $anvil->Database->insert_or_update_variables({ + debug => $debug, + variable_uuid => $variable_uuid, + variable_value => "clear", + update_value_only => 1, + }); + } + + # if the value was 'clear', change it to 'set'. + if ($variable_value eq "clear") + { + # Set it. + $variable_uuid = $anvil->Database->insert_or_update_variables({ + debug => $debug, + variable_uuid => $variable_uuid, + variable_value => "set", + update_value_only => 1, + }); + } + else + { + # How old is it? + $age = time - $epoch_modified_date; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { age => $age }}); + return($age); + } + } + elsif (not $clear) + { + # New, set it. + my $variable_uuid = $anvil->Database->insert_or_update_variables({ + debug => $debug, + variable_name => $name, + variable_value => "set", + variable_default => "set", + variable_description => "striker_0278", + variable_section => "conditions", + variable_source_uuid => $host_uuid, + variable_source_table => $source_table, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { variable_uuid => $variable_uuid }}); + } + + return($age); +} + + =head2 check_lock_age 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. @@ -355,6 +474,140 @@ sub check_lock_age } +=head2 check_agent_data + +This method is designed to be used by ScanCore scan agents. It does two main tasks; Verifies that the agent's SQL schema is loaded in all databases and handles resync'ing their data when necessary. + +B<< Note >>: This method calls C<< Database->check_for_schema >>, so calling it before this method is generally not required. + +Parameters; + +=head3 agent (required) + +This is the name of the calling scan agent. The name is used to find the schema file under C<< //.sql >>. + +=cut +sub check_agent_data +{ + 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->check_agent_data()" }}); + + my $agent = defined $parameter->{agent} ? $parameter->{agent} : ""; + my $tables = defined $parameter->{tables} ? $parameter->{tables} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + agent => $agent, + tables => $tables, + }}); + + if (not $agent) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->check_agent_data()", parameter => "agent" }}); + return("!!error!!"); + } + + my $schema_file = $anvil->data->{path}{directories}{scan_agents}."/".$agent."/".$agent.".sql"; + my $loaded = $anvil->Database->check_for_schema({ + debug => 2, + file => $schema_file, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + loaded => $loaded, + schema_file => $schema_file, + }}); + if ($loaded) + { + if ($loaded eq "!!error!!") + { + # Something went wrong. + my $changed = $anvil->Alert->check_alert_sent({ + debug => 2, + record_locator => "schema_load_failure", + set_by => $agent, + type => "set", + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Log and register an alert. This should never happen, so we set it as a + # warning level alert. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0181", variables => { + agent_name => $agent, + file => $schema_file, + }}); + $anvil->Alert->register({ + debug => 2, + alert_level => "warning", + message => "message_0181,!!agent_name!".$agent."!!,!!file!".$schema_file."!!", + set_by => $agent, + }); + } + } + elsif (ref($loaded) eq "ARRAY") + { + # If there was an alert, clear it. + my $changed = $anvil->Alert->check_alert_sent({ + debug => 2, + record_locator => "schema_load_failure", + set_by => $agent, + type => "clear", + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Register an alert cleared message. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0182", variables => { + agent_name => $agent, + file => $schema_file, + + }}); + $anvil->Alert->register({ + debug => 2, + alert_level => "warning", + clear_alert => 1, + message => "message_0182,!!agent_name!".$agent."!!,!!file!".$schema_file."!!", + set_by => $agent, + }); + } + + # Log which databses we loaded our schema into. + foreach my $uuid (@{$loaded}) + { + my $host_name = $anvil->Database->get_host_from_uuid({short => 1, host_uuid => $uuid}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "message_0183", variables => { + agent_name => $agent, + host_name => $host_name, + + }}); + } + } + } + + # Now check to see if a resync is required... + if ($anvil->data->{sys}{database}{connections} > 1) + { + # The source is the agent + $anvil->Database->_find_behind_databases({ + debug => $debug, + source => $agent, + }); + } + + # Hold if a lock has been requested. + $anvil->Database->locking({debug => $debug}); + + # Mark that we're not active. + $anvil->Database->mark_active({debug => $debug, set => 1}); + + # Sync the database, if needed. + $anvil->Database->resync_databases({debug => $debug}); + + return(0); +} + + =head2 check_for_schema This reads in a SQL schema file and checks if the first table seen exists in the database. If it isn't, the schema file is loaded into the database main. @@ -886,7 +1139,7 @@ If set to C<< 1 >>, no attempt to ping a target before connection will happen, e =head3 source (optional) -The C<< source >> parameter is used to check the special C<< updated >> table one all connected databases to see when that source (program name, usually) last updated a given database. If the date stamp is the same on all connected databases, nothing further happens. If one of the databases differ, however, a resync will be requested. +The C<< source >> parameter is used to check the special C<< updated >> table on all connected databases to see when that source (program name, usually) last updated a given database. If the date stamp is the same on all connected databases, nothing further happens. If one of the databases differ, however, a resync will be requested. If not defined, the core database will be checked. @@ -4793,7 +5046,7 @@ WHERE ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - my $results = $anvil->Database->query({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + 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, @@ -4826,7 +5079,7 @@ WHERE fence_uuid = ".$anvil->Database->quote($fence_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, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } } } @@ -4852,7 +5105,7 @@ INSERT INTO ); "; $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, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } return($fence_uuid); @@ -5332,93 +5585,93 @@ WHERE } -=head2 insert_or_update_ssh_keys - -This updates (or inserts) a record in the 'ssh_keys' table. The C<< ssh_key_uuid >> UUID will be returned. - -If there is an error, an empty string is returned. +=head2 insert_or_update_health -Parameters; +This inserts or updates a value in the special c<< health >> table. -=head3 uuid (optional) +This stores weighted health of nodes. Agents can set one or more health values. After a scan sweep completes, ScanCore will sum these weights and the node with the B<< highest >> value is considered the B<< least >> healthy and any servers on it will be migrated to the peer. -If set, only the corresponding database will be written to. +If there is a problem, an empty string is returned. Otherwise, the C<< health_uuid >> is returned. -=head3 file (optional) +parameters; -If set, this is the file name logged as the source of any INSERTs or UPDATEs. +=head3 health_uuid (optional) -=head3 line (optional) +Is passed, the specific entry will be updated. -If set, this is the file line number logged as the source of any INSERTs or UPDATEs. +=head3 health_host_uuid (optional, default Get->host_uuid) -=head3 ssh_key_host_uuid (optional, default is Get->host_uuid) +This is the host registering the health score. -This is the host that the corresponding user and key belong to. +=head3 health_agent_name (required) -=head3 ssh_key_public_key (required) +This is the scan agent (or program name) setting this score. -This is the B<> key for the user, the full line stored in the user's C<< ~/.ssh/id_rsa.pub >> file. +=head3 health_source_name (required) -=head3 ssh_key_user_name (required) +This is a decriptive name of the problem causing the health score to be set. -This is the name of the user that the public key belongs to. +=head3 health_source_weight (optional, default '1') -=head3 ssh_key_uuid (optional) +This is a whole number (0, 1, 2, ...) indicating the weight of the problem. The higher this number is, the more likely hosted server will be migrated to the peer. -This is the specific record to update. If not provides, a search will be made to find a matching entry. If found, the record will be updated if one of the values has changed. If not, a new record will be inserted. +B<< Note >>: A weight of C<< 0 >> is equal to the entry being deleted as it won't be factored into any health related decisions. =cut -sub insert_or_update_ssh_keys +sub insert_or_update_health { 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_ssh_keys()" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->insert_or_update_health()" }}); - my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; - my $file = defined $parameter->{file} ? $parameter->{file} : ""; - my $line = defined $parameter->{line} ? $parameter->{line} : ""; - my $ssh_key_host_uuid = defined $parameter->{ssh_key_host_uuid} ? $parameter->{ssh_key_host_uuid} : $anvil->Get->host_uuid; - my $ssh_key_public_key = defined $parameter->{ssh_key_public_key} ? $parameter->{ssh_key_public_key} : ""; - my $ssh_key_user_name = defined $parameter->{ssh_key_user_name} ? $parameter->{ssh_key_user_name} : ""; - my $ssh_key_uuid = defined $parameter->{ssh_key_uuid} ? $parameter->{ssh_key_uuid} : ""; + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; + my $file = defined $parameter->{file} ? $parameter->{file} : ""; + my $line = defined $parameter->{line} ? $parameter->{line} : ""; + my $health_uuid = defined $parameter->{health_uuid} ? $parameter->{health_uuid} : ""; + my $health_host_uuid = defined $parameter->{health_host_uuid} ? $parameter->{health_host_uuid} : $anvil->Get->host_uuid; + my $health_agent_name = defined $parameter->{health_agent_name} ? $parameter->{health_agent_name} : ""; + my $health_source_name = defined $parameter->{health_source_name} ? $parameter->{health_source_name} : ""; + my $health_source_weight = defined $parameter->{health_source_weight} ? $parameter->{health_source_weight} : 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - uuid => $uuid, - file => $file, - line => $line, - ssh_key_host_uuid => $ssh_key_host_uuid, - ssh_key_public_key => $ssh_key_public_key, - ssh_key_user_name => $ssh_key_user_name, - ssh_key_uuid => $ssh_key_uuid, + uuid => $uuid, + file => $file, + line => $line, + health_uuid => $health_uuid, + health_host_uuid => $health_host_uuid, + health_agent_name => $health_agent_name, + health_source_name => $health_source_name, + health_source_weight => $health_source_weight, }}); - if (not $ssh_key_public_key) + if (not $health_agent_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_ssh_keys()", parameter => "ssh_key_public_key" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_health()", parameter => "health_agent_name" }}); return(""); } - if (not $ssh_key_user_name) + if (not $health_source_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_ssh_keys()", parameter => "ssh_key_user_name" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_health()", parameter => "health_source_name" }}); return(""); } - # If we don't have a UUID, see if we can find one for the given user and host. - if (not $ssh_key_uuid) + # If we don't have a health UUID, see if we can find one. + if (not $health_uuid) { my $query = " SELECT - ssh_key_uuid + health_uuid FROM - ssh_keys + health WHERE - ssh_key_user_name = ".$anvil->Database->quote($ssh_key_user_name)." + health_host_uuid = ".$anvil->Database->quote($health_host_uuid)." AND - ssh_key_host_uuid = ".$anvil->Database->quote($ssh_key_host_uuid)." + health_agent_name = ".$anvil->Database->quote($health_agent_name)." +AND + health_source_name = ".$anvil->Database->quote($health_source_name)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); @@ -5430,52 +5683,25 @@ AND }}); if ($count) { - $ssh_key_uuid = $results->[0]->[0]; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ssh_key_uuid => $ssh_key_uuid }}); + $health_uuid = $results->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { health_uuid => $health_uuid }}); } } - # If I still don't have an ssh_key_uuid, we're INSERT'ing . - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ssh_key_uuid => $ssh_key_uuid }}); - if (not $ssh_key_uuid) - { - # INSERT - $ssh_key_uuid = $anvil->Get->uuid(); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ssh_key_uuid => $ssh_key_uuid }}); - - my $query = " -INSERT INTO - ssh_keys -( - ssh_key_uuid, - ssh_key_host_uuid, - ssh_key_public_key, - ssh_key_user_name, - modified_date -) VALUES ( - ".$anvil->Database->quote($ssh_key_uuid).", - ".$anvil->Database->quote($ssh_key_host_uuid).", - ".$anvil->Database->quote($ssh_key_public_key).", - ".$anvil->Database->quote($ssh_key_user_name).", - ".$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 + # If we have a health UUID now, look up the previous value and see if it has changed. If not, INSERT + # a new entry. + if ($health_uuid) { - # Query the rest of the values and see if anything changed. my $query = " SELECT - ssh_key_host_uuid, - ssh_key_public_key, - ssh_key_user_name + health_host_uuid, + health_agent_name, + health_source_name, + health_source_weight FROM - ssh_keys + health WHERE - ssh_key_uuid = ".$anvil->Database->quote($ssh_key_uuid)." -;"; + health_uuid = ".$anvil->Database->quote($health_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__}); @@ -5486,46 +5712,71 @@ WHERE }}); if (not $count) { - # I have a ssh_key_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 => "ssh_key_uuid", uuid => $ssh_key_uuid }}); + # What? + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0216", variables => { uuid_name => "health_uuid", uuid => $health_uuid }}); return(""); } - foreach my $row (@{$results}) + my $old_health_host_uuid = $results->[0]->[0]; + my $old_health_agent_name = $results->[0]->[1]; + my $old_health_source_name = $results->[0]->[2]; + my $old_health_source_weight = $results->[0]->[3]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + old_health_host_uuid => $old_health_host_uuid, + old_health_agent_name => $old_health_agent_name, + old_health_source_name => $old_health_source_name, + old_health_source_weight => $old_health_source_weight, + }}); + + if (($old_health_host_uuid ne $health_host_uuid) or + ($old_health_agent_name ne $health_agent_name) or + ($old_health_source_name ne $health_source_name) or + ($old_health_source_weight ne $health_source_weight)) { - my $old_ssh_key_host_uuid = $row->[0]; - my $old_ssh_key_public_key = $row->[1]; - my $old_ssh_key_user_name = $row->[2]; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - old_ssh_key_host_uuid => $old_ssh_key_host_uuid, - old_ssh_key_public_key => $old_ssh_key_public_key, - old_ssh_key_user_name => $old_ssh_key_user_name, - }}); - - # Anything change? - if (($old_ssh_key_host_uuid ne $ssh_key_host_uuid) or - ($old_ssh_key_public_key ne $ssh_key_public_key) or - ($old_ssh_key_user_name ne $ssh_key_user_name)) - { - # Something changed, save. - my $query = " + # Update. + my $query = " UPDATE - ssh_keys + health SET - ssh_key_host_uuid = ".$anvil->Database->quote($ssh_key_host_uuid).", - ssh_key_public_key = ".$anvil->Database->quote($ssh_key_public_key).", - ssh_key_user_name = ".$anvil->Database->quote($ssh_key_user_name).", - modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." -WHERE - ssh_key_uuid = ".$anvil->Database->quote($ssh_key_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__}); - } - } - } + health_host_uuid = ".$anvil->Database->quote($health_host_uuid).", + health_agent_name = ".$anvil->Database->quote($health_agent_name).", + health_source_name = ".$anvil->Database->quote($health_source_name).", + health_source_weight = ".$anvil->Database->quote($health_source_weight).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + health_uuid = ".$anvil->Database->quote($health_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__}); + } + } + else + { + # INSERT + $health_uuid = $anvil->Get->uuid(); + my $query = " +INSERT INTO + health +( + health_uuid, + health_host_uuid, + health_agent_name, + health_source_name, + health_source_weight, + modified_date +) VALUES ( + ".$anvil->Database->quote($health_uuid).", + ".$anvil->Database->quote($health_host_uuid).", + ".$anvil->Database->quote($health_agent_name).", + ".$anvil->Database->quote($health_source_name).", + ".$anvil->Database->quote($health_source_weight).", + ".$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__}); + } - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ssh_key_uuid => $ssh_key_uuid }}); - return($ssh_key_uuid); + return($health_uuid); } @@ -5653,7 +5904,7 @@ WHERE ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - my $results = $anvil->Database->query({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + 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, @@ -5695,7 +5946,7 @@ INSERT INTO ); "; $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, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } elsif (($old_host_name ne $host_name) or ($old_host_type ne $host_type) or @@ -5715,7 +5966,7 @@ WHERE host_uuid = ".$anvil->Database->quote($host_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, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0126", variables => { method => "Database->insert_or_update_hosts()" }}); @@ -7031,7 +7282,7 @@ WHERE ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - my $results = $anvil->Database->query({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + 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, @@ -7119,7 +7370,7 @@ WHERE "; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - my $results = $anvil->Database->query({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + 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, @@ -7172,7 +7423,7 @@ WHERE manifest_uuid = ".$anvil->Database->quote($manifest_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - $anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } } } @@ -7202,7 +7453,7 @@ INSERT INTO ); "; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - $anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0126", variables => { method => "Database->insert_or_update_network_interfaces()" }}); @@ -7477,7 +7728,7 @@ WHERE "; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - my $results = $anvil->Database->query({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + 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, @@ -7542,7 +7793,7 @@ WHERE ;"; $query =~ s/'NULL'/NULL/g; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - $anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } return($network_interface_uuid); } @@ -7583,7 +7834,7 @@ WHERE ;"; $query =~ s/'NULL'/NULL/g; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - $anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } } } @@ -7628,7 +7879,7 @@ INSERT INTO "; $query =~ s/'NULL'/NULL/g; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - $anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0126", variables => { method => "Database->insert_or_update_network_interfaces()" }}); @@ -7825,7 +8076,7 @@ INSERT INTO ); "; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - $anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } return($notification_uuid) @@ -8042,7 +8293,7 @@ INSERT INTO "; $query =~ s/'NULL'/NULL/g; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - $anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } return($mac_to_ip_uuid); @@ -8233,13 +8484,214 @@ INSERT INTO "; $query =~ s/'NULL'/NULL/g; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - $anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } return($oui_uuid); } +=head2 insert_or_update_power + +This inserts or updates a value in the special c<< power >> table. + +This stores the most recent view of UPSes, and can be updated by any node that is able to talk to the UPS. + +If there is a problem, an empty string is returned. Otherwise, the C<< power_uuid >> is returned. + +parameters; + +=head3 power_uuid (optional) + +Is passed, the specific entry will be updated. + +=head3 power_ups_uuid (required) + +This is the UPS UUID (C<< upses -> ups_uuid >> of this UPS. This is required to track that status of the UPSes powering nodes. + +=head3 power_on_battery (optional, default '0') + +This is used to determine when load shedding and emergency shut down actions should be taken. When set to C<< 1 >>, the UPS is considered to be drawing down it's batteries. If both/all UPSes powering a node are on batteries, load shedding will occur after a set delay. + +=head3 power_seconds_left (required) + +This is the estimated hold up time, in seconds, for the UPS. Of course, this estimate will fluctuate will actual load. + +=head3 power_charge_percentage (required) + +This is the percentage charge of the UPS batteries. Used to determine when the dashboard should boot the node after main power returns following a power loss event. + +=cut +sub insert_or_update_power +{ + 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_power()" }}); + + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; + my $file = defined $parameter->{file} ? $parameter->{file} : ""; + my $line = defined $parameter->{line} ? $parameter->{line} : ""; + my $power_uuid = defined $parameter->{power_uuid} ? $parameter->{power_uuid} : ""; + my $power_ups_uuid = defined $parameter->{power_ups_uuid} ? $parameter->{power_ups_uuid} : 1; + my $power_on_battery = defined $parameter->{power_on_battery} ? $parameter->{power_on_battery} : 0; + my $power_seconds_left = defined $parameter->{power_seconds_left} ? $parameter->{power_seconds_left} : ""; + my $power_charge_percentage = defined $parameter->{power_charge_percentage} ? $parameter->{power_charge_percentage} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + uuid => $uuid, + file => $file, + line => $line, + power_uuid => $power_uuid, + power_ups_uuid => $power_ups_uuid, + power_on_battery => $power_on_battery, + power_seconds_left => $power_seconds_left, + power_charge_percentage => $power_charge_percentage, + }}); + + if ($power_ups_uuid) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_power()", parameter => "power_ups_uuid" }}); + return(""); + } + if ($power_seconds_left eq "") + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_power()", parameter => "power_seconds_left" }}); + return(""); + } + if ($power_charge_percentage eq "") + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_power()", parameter => "power_charge_percentage" }}); + return(""); + } + + # Convert the passed in "on battery" value to TRUE/FALSE + $power_on_battery = $power_on_battery ? "TRUE" : "FALSE"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { power_on_battery => $power_on_battery }}); + + # If we don't have a power UUID, see if we can find one. + if (not $power_uuid) + { + my $query = " +SELECT + power_uuid +FROM + power +WHERE + power_ups_uuid = ".$anvil->Database->quote($power_ups_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) + { + $power_uuid = $results->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { power_uuid => $power_uuid }}); + } + } + + # If we have a power UUID now, look up the previous value and see if it has changed. If not, INSERT + # a new entry. + if ($power_uuid) + { + my $query = " +SELECT + power_record_locator, + power_ups_uuid, + power_on_battery, + power_seconds_left, + power_charge_percentage +FROM + power +WHERE + power_uuid = ".$anvil->Database->quote($power_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) + { + # What? + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0216", variables => { uuid_name => "power_uuid", uuid => $power_uuid }}); + return(""); + } + my $old_power_ups_uuid = $results->[0]->[0]; + my $old_power_on_battery = $results->[0]->[1]; + my $old_power_seconds_left = $results->[0]->[2]; + my $old_power_charge_percentage = $results->[0]->[3]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + old_power_ups_uuid => $old_power_ups_uuid, + old_power_on_battery => $old_power_on_battery, + old_power_seconds_left => $old_power_seconds_left, + old_power_charge_percentage => $power_charge_percentage, + }}); + + # Convert the read-in "on battery" value to TRUE/FALSE + $old_power_on_battery = $power_on_battery ? "TRUE" : "FALSE"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { old_power_on_battery => $old_power_on_battery }}); + + if (($old_power_ups_uuid ne $power_ups_uuid) or + ($old_power_on_battery ne $power_on_battery) or + ($old_power_seconds_left ne $power_seconds_left) or + ($old_power_charge_percentage ne $power_charge_percentage)) + { + # Update. + my $query = " +UPDATE + power +SET + power_ups_uuid = ".$anvil->Database->quote($power_ups_uuid).", + power_on_battery = ".$anvil->Database->quote($power_on_battery).", + power_seconds_left = ".$anvil->Database->quote($power_seconds_left).", + power_charge_percentage = ".$anvil->Database->quote($power_charge_percentage).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + power_uuid = ".$anvil->Database->quote($power_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__}); + } + } + else + { + # INSERT + $power_uuid = $anvil->Get->uuid(); + my $query = " +INSERT INTO + power +( + power_uuid, + power_ups_uuid, + power_on_battery, + power_seconds_left, + power_charge_percentage, + modified_date +) VALUES ( + ".$anvil->Database->quote($power_uuid).", + ".$anvil->Database->quote($power_ups_uuid).", + ".$anvil->Database->quote($power_on_battery).", + ".$anvil->Database->quote($power_seconds_left).", + ".$anvil->Database->quote($power_charge_percentage).", + ".$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__}); + } + + return($power_uuid); +} + + =head2 insert_or_update_recipients This updates (or inserts) a record in the 'recipients' table. The C<< recipient_uuid >> referencing the database row will be returned. @@ -8712,16 +9164,215 @@ INSERT INTO "; $query =~ s/'NULL'/NULL/g; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - $anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } return($session_uuid); } +=head2 insert_or_update_ssh_keys + +This updates (or inserts) a record in the 'ssh_keys' table. The C<< ssh_key_uuid >> UUID 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 ssh_key_host_uuid (optional, default is Get->host_uuid) + +This is the host that the corresponding user and key belong to. + +=head3 ssh_key_public_key (required) + +This is the B<> key for the user, the full line stored in the user's C<< ~/.ssh/id_rsa.pub >> file. + +=head3 ssh_key_user_name (required) + +This is the name of the user that the public key belongs to. + +=head3 ssh_key_uuid (optional) + +This is the specific record to update. If not provides, a search will be made to find a matching entry. If found, the record will be updated if one of the values has changed. If not, a new record will be inserted. + +=cut +sub insert_or_update_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->insert_or_update_ssh_keys()" }}); + + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; + my $file = defined $parameter->{file} ? $parameter->{file} : ""; + my $line = defined $parameter->{line} ? $parameter->{line} : ""; + my $ssh_key_host_uuid = defined $parameter->{ssh_key_host_uuid} ? $parameter->{ssh_key_host_uuid} : $anvil->Get->host_uuid; + my $ssh_key_public_key = defined $parameter->{ssh_key_public_key} ? $parameter->{ssh_key_public_key} : ""; + my $ssh_key_user_name = defined $parameter->{ssh_key_user_name} ? $parameter->{ssh_key_user_name} : ""; + my $ssh_key_uuid = defined $parameter->{ssh_key_uuid} ? $parameter->{ssh_key_uuid} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + uuid => $uuid, + file => $file, + line => $line, + ssh_key_host_uuid => $ssh_key_host_uuid, + ssh_key_public_key => $ssh_key_public_key, + ssh_key_user_name => $ssh_key_user_name, + ssh_key_uuid => $ssh_key_uuid, + }}); + + if (not $ssh_key_public_key) + { + # 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_ssh_keys()", parameter => "ssh_key_public_key" }}); + return(""); + } + if (not $ssh_key_user_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_ssh_keys()", parameter => "ssh_key_user_name" }}); + return(""); + } + + # If we don't have a UUID, see if we can find one for the given user and host. + if (not $ssh_key_uuid) + { + my $query = " +SELECT + ssh_key_uuid +FROM + ssh_keys +WHERE + ssh_key_user_name = ".$anvil->Database->quote($ssh_key_user_name)." +AND + ssh_key_host_uuid = ".$anvil->Database->quote($ssh_key_host_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) + { + $ssh_key_uuid = $results->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ssh_key_uuid => $ssh_key_uuid }}); + } + } + + # If I still don't have an ssh_key_uuid, we're INSERT'ing . + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ssh_key_uuid => $ssh_key_uuid }}); + if (not $ssh_key_uuid) + { + # INSERT + $ssh_key_uuid = $anvil->Get->uuid(); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ssh_key_uuid => $ssh_key_uuid }}); + + my $query = " +INSERT INTO + ssh_keys +( + ssh_key_uuid, + ssh_key_host_uuid, + ssh_key_public_key, + ssh_key_user_name, + modified_date +) VALUES ( + ".$anvil->Database->quote($ssh_key_uuid).", + ".$anvil->Database->quote($ssh_key_host_uuid).", + ".$anvil->Database->quote($ssh_key_public_key).", + ".$anvil->Database->quote($ssh_key_user_name).", + ".$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 + ssh_key_host_uuid, + ssh_key_public_key, + ssh_key_user_name +FROM + ssh_keys +WHERE + ssh_key_uuid = ".$anvil->Database->quote($ssh_key_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 ssh_key_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 => "ssh_key_uuid", uuid => $ssh_key_uuid }}); + return(""); + } + foreach my $row (@{$results}) + { + my $old_ssh_key_host_uuid = $row->[0]; + my $old_ssh_key_public_key = $row->[1]; + my $old_ssh_key_user_name = $row->[2]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + old_ssh_key_host_uuid => $old_ssh_key_host_uuid, + old_ssh_key_public_key => $old_ssh_key_public_key, + old_ssh_key_user_name => $old_ssh_key_user_name, + }}); + + # Anything change? + if (($old_ssh_key_host_uuid ne $ssh_key_host_uuid) or + ($old_ssh_key_public_key ne $ssh_key_public_key) or + ($old_ssh_key_user_name ne $ssh_key_user_name)) + { + # Something changed, save. + my $query = " +UPDATE + ssh_keys +SET + ssh_key_host_uuid = ".$anvil->Database->quote($ssh_key_host_uuid).", + ssh_key_public_key = ".$anvil->Database->quote($ssh_key_public_key).", + ssh_key_user_name = ".$anvil->Database->quote($ssh_key_user_name).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + ssh_key_uuid = ".$anvil->Database->quote($ssh_key_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__}); + } + } + } + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ssh_key_uuid => $ssh_key_uuid }}); + return($ssh_key_uuid); +} + + =head2 insert_or_update_states -This updates (or inserts) a record in the 'states' table. The C<< state_uuid >> referencing the database row will be returned. +This updates (or inserts) a record in the 'states' table. The C<< state_uuid >> referencing the database row will be returned. This table is meant to be used for transient information (ie: server is migrating, condition age, etc). + +B<< Note >>: No history is tracked on this table and it is excluded from resync operations. Think of this table as a scratch disk of sorts. If there is an error, an empty string is returned. @@ -8946,6 +9597,297 @@ WHERE } +=head2 insert_or_update_temperature + +This inserts or updates a value in the special c<< temperature >> table. + +This stores weighted temperature of nodes. Agents can set one or more temperature values. After a scan sweep completes, ScanCore will sum these weights and the node with the B<< highest >> value is considered the B<< least >> temperaturey and any servers on it will be migrated to the peer. + +If there is a problem, an empty string is returned. Otherwise, the C<< temperature_uuid >> is returned. + +parameters; + +=head3 temperature_uuid (optional) + +Is passed, the specific entry will be updated. + +=head3 temperature_host_uuid (optional, default Get->host_uuid) + +This is the host B<< recording >> the temperature. It is B<< not >> the host that the sensor is read from (though of course they can be the same). This is an important distinction as, for example, Striker dashboards will monitor available temperature sensors of nodes to tell when it is safe to boot them up, after a thermal event caused a shutdown, + +=head3 temperature_agent_name (required) + +This is the scan agent (or program name) setting this score. + +=head3 temperature_sensor_host (required) + +This is the host (uuid) that the sensor was read from. This is important as ScanCore on a striker will read available thermal data from a node using it's IPMI data. + +=head3 temperature_sensor_name (required) + +This is the name of the free-form, descriptive name of the sensor reporting the temperature. + +=head3 temperature_value_c (required) + +This is the actual temperature being recorded, in celsius. The value can be a signed decimal value. + +=head3 temperature_state (optional, default 'ok') + +This is a string represnting the state of the sensor. Valid values are C<< ok >>, C<< warning >>, and C<< critical >>. + +When a sensor is in C<< warning >>, it's value is added up (along with C<< critical >> sensors) to derive a score. If that score is equal to over greater than C<< scancore::threshold::warning_temperature >> (default C<< 5 >>), servers will be migrated over to the peer, B<< if >> the peer's temperature score is below this threshold. + +When a sensor is in C<< critical >>, it's score is summed up along with other C<< critical >> temperatures (C<< warnings >> are not factored). If the total score is equal to or greater than C<< scancore::threshold::warning_critical >>, (default C<< 5 >>), the node will enter emergency shutdown. + +=head3 temperature_is (optional, default 'nominal) + +This indicate if the temperature 'nominal', 'high' or 'low'. This distinction is used when calculating if C<< scancore::threshold::warning_temperature >> or C<< scancore::threshold::warning_critical >> has been passed. Temperatures that are in a C<< warning >> or C<< critical >> state will be evaluated as groups. That is, if through some weird case some temperatures were reading high and others were reading cold, decisions about threashold would be calculated separately for the over temp and again for the under temp values. + +=head3 temperature_weight (optional, default 1) + +This sets the weight of the temperature sensor. This allows a given sensor to have more or less influence of the derived score used to see if C<< scancore::threshold::warning_temperature >> or C<< scancore::threshold::warning_critical >> has been exceeded. + +=cut +sub insert_or_update_temperature +{ + 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_temperature()" }}); + + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; + my $file = defined $parameter->{file} ? $parameter->{file} : ""; + my $line = defined $parameter->{line} ? $parameter->{line} : ""; + my $temperature_uuid = defined $parameter->{temperature_uuid} ? $parameter->{temperature_uuid} : ""; + my $temperature_host_uuid = defined $parameter->{temperature_host_uuid} ? $parameter->{temperature_host_uuid} : $anvil->Get->host_uuid; + my $temperature_agent_name = defined $parameter->{temperature_agent_name} ? $parameter->{temperature_agent_name} : ""; + my $temperature_sensor_host = defined $parameter->{temperature_sensor_host} ? $parameter->{temperature_sensor_host} : ""; + my $temperature_sensor_name = defined $parameter->{temperature_sensor_name} ? $parameter->{temperature_sensor_name} : ""; + my $temperature_value_c = defined $parameter->{temperature_value_c} ? $parameter->{temperature_value_c} : ""; + my $temperature_state = defined $parameter->{temperature_state} ? $parameter->{temperature_state} : "ok"; + my $temperature_is = defined $parameter->{temperature_is} ? $parameter->{temperature_is} : "nominal"; + my $temperature_weight = defined $parameter->{temperature_weight} ? $parameter->{temperature_weight} : 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + uuid => $uuid, + file => $file, + line => $line, + temperature_uuid => $temperature_uuid, + temperature_host_uuid => $temperature_host_uuid, + temperature_agent_name => $temperature_agent_name, + temperature_sensor_host => $temperature_sensor_host, + temperature_sensor_name => $temperature_sensor_name, + temperature_value_c => $temperature_value_c, + temperature_state => $temperature_state, + temperature_is => $temperature_is, + temperature_weight => $temperature_weight, + }}); + + # Pointy end up? + if ($temperature_state eq "norminal") + { + $temperature_state = "nominal"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { temperature_state => $temperature_state }}); + } + + if (not $temperature_agent_name) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_temperature()", parameter => "temperature_agent_name" }}); + return(""); + } + if (not $temperature_sensor_host) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_temperature()", parameter => "temperature_sensor_host" }}); + return(""); + } + if (not $temperature_sensor_name) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_temperature()", parameter => "temperature_sensor_name" }}); + return(""); + } + if ($temperature_value_c eq "") + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_temperature()", parameter => "temperature_value_c" }}); + return(""); + } + if (not $temperature_state) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_temperature()", parameter => "temperature_state" }}); + return(""); + } + elsif (($temperature_state ne "ok") && ($temperature_state ne "warning") && ($temperature_state ne "critical")) + { + # Invalid value. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0546", variables => { temperature_state => $temperature_state }}); + return(""); + } + if (not $temperature_is) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_temperature()", parameter => "temperature_is" }}); + return(""); + } + elsif (($temperature_is ne "nominal") && ($temperature_is ne "warning") && ($temperature_is ne "critical")) + { + # Invalid value. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0547", variables => { temperature_is => $temperature_is }}); + return(""); + } + if ($temperature_weight eq "") + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_temperature()", parameter => "temperature_weight" }}); + return(""); + } + + # If we don't have a temperature UUID, see if we can find one. + if (not $temperature_uuid) + { + my $query = " +SELECT + temperature_uuid +FROM + temperature +WHERE + temperature_host_uuid = ".$anvil->Database->quote($temperature_host_uuid)." +AND + temperature_sensor_host = ".$anvil->Database->quote($temperature_sensor_host)." +AND + temperature_agent_name = ".$anvil->Database->quote($temperature_agent_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) + { + $temperature_uuid = $results->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { temperature_uuid => $temperature_uuid }}); + } + } + + # If we have a temperature UUID now, look up the previous value and see if it has changed. If not, INSERT + # a new entry. + if ($temperature_uuid) + { + my $query = " +SELECT + temperature_host_uuid, + temperature_agent_name, + temperature_sensor_host, + temperature_sensor_name, + temperature_value_c, + temperature_state, + temperature_is, + temperature_weight +FROM + temperature +WHERE + temperature_uuid = ".$anvil->Database->quote($temperature_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) + { + # What? + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0216", variables => { uuid_name => "temperature_uuid", uuid => $temperature_uuid }}); + return(""); + } + my $old_temperature_host_uuid = $results->[0]->[0]; + my $old_temperature_agent_name = $results->[0]->[1]; + my $old_temperature_sensor_host = $results->[0]->[2]; + my $old_temperature_sensor_name = $results->[0]->[3]; + my $old_temperature_value_c = $results->[0]->[4]; + my $old_temperature_state = $results->[0]->[5]; + my $old_temperature_is = $results->[0]->[6]; + my $old_temperature_weight = $results->[0]->[7]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + old_temperature_host_uuid => $old_temperature_host_uuid, + old_temperature_agent_name => $old_temperature_agent_name, + old_temperature_sensor_host => $old_temperature_sensor_host, + old_temperature_sensor_name => $old_temperature_sensor_name, + old_temperature_value_c => $old_temperature_value_c, + old_temperature_state => $old_temperature_state, + old_temperature_is => $old_temperature_is, + old_temperature_weight => $old_temperature_weight, + }}); + + if (($old_temperature_host_uuid ne $temperature_host_uuid) or + ($old_temperature_agent_name ne $temperature_agent_name) or + ($old_temperature_sensor_host ne $temperature_sensor_host) or + ($old_temperature_sensor_name ne $temperature_sensor_name) or + ($old_temperature_value_c ne $temperature_value_c) or + ($old_temperature_state ne $temperature_state) or + ($old_temperature_is ne $temperature_is) or + ($old_temperature_weight ne $temperature_weight)) + { + # Update. + my $query = " +UPDATE + temperature +SET + temperature_host_uuid = ".$anvil->Database->quote($temperature_host_uuid).", + temperature_agent_name = ".$anvil->Database->quote($temperature_agent_name).", + temperature_sensor_host = ".$anvil->Database->quote($temperature_sensor_host).", + temperature_sensor_name = ".$anvil->Database->quote($temperature_sensor_name).", + temperature_value_c = ".$anvil->Database->quote($temperature_value_c).", + temperature_state = ".$anvil->Database->quote($temperature_state).", + temperature_is = ".$anvil->Database->quote($temperature_is).", + temperature_weight = ".$anvil->Database->quote($temperature_weight).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + temperature_uuid = ".$anvil->Database->quote($temperature_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__}); + } + } + else + { + # INSERT + $temperature_uuid = $anvil->Get->uuid(); + my $query = " +INSERT INTO + temperature +( + temperature_uuid, + temperature_host_uuid, + temperature_agent_name, + temperature_sensor_host, + temperature_sensor_name, + temperature_value_c, + temperature_state, + temperature_is, + temperature_weight, + modified_date +) VALUES ( + ".$anvil->Database->quote($temperature_uuid).", + ".$anvil->Database->quote($temperature_host_uuid).", + ".$anvil->Database->quote($temperature_agent_name).", + ".$anvil->Database->quote($temperature_sensor_host).", + ".$anvil->Database->quote($temperature_sensor_name).", + ".$anvil->Database->quote($temperature_value_c).", + ".$anvil->Database->quote($temperature_state).", + ".$anvil->Database->quote($temperature_is).", + ".$anvil->Database->quote($temperature_weight).", + ".$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__}); + } + + return($temperature_uuid); +} + + =head2 insert_or_update_upses This updates (or inserts) a record in the 'upses' table. The C<< ups_uuid >> UUID will be returned. @@ -9081,7 +10023,7 @@ WHERE ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - my $results = $anvil->Database->query({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + 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, @@ -9114,7 +10056,7 @@ WHERE ups_uuid = ".$anvil->Database->quote($ups_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, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } } } @@ -9140,7 +10082,7 @@ INSERT INTO ); "; $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, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); + $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } return($ups_uuid); diff --git a/Anvil/Tools/ScanCore.pm b/Anvil/Tools/ScanCore.pm new file mode 100755 index 00000000..a1abc2af --- /dev/null +++ b/Anvil/Tools/ScanCore.pm @@ -0,0 +1,177 @@ +package Anvil::Tools::ScanCore; +# +# This module contains methods used to handle message processing related to support of multi-lingual use. +# + +use strict; +use warnings; +use Data::Dumper; +use Scalar::Util qw(weaken isweak); +use Data::Dumper; +use Time::HiRes qw(gettimeofday tv_interval); +use Text::Diff; + +our $VERSION = "3.0.0"; +my $THIS_FILE = "ScanCore.pm"; + +### Methods; +# agent_startup + +=pod + +=encoding utf8 + +=head1 NAME + +Anvil::Tools::ScanCore + +Provides all methods related to ScanCore and scan agents. + +=head1 SYNOPSIS + + use Anvil::Tools; + + # Get a common object handle on all Anvil::Tools modules. + my $anvil = Anvil::Tools->new(); + + # Access to methods using '$anvil->ScanCore->X'. + # + # Example using 'agent_startup()'; + my $foo_path = $anvil->ScanCore->read({file => $anvil->data->{path}{words}{'anvil.xml'}}); + +=head1 METHODS + +Methods in this module; + +=cut +sub new +{ + my $class = shift; + my $self = {}; + + bless $self, $class; + + return ($self); +} + +# Get a handle on the Anvil::Tools object. I know that technically that is a sibling module, but it makes more +# sense in this case to think of it as a parent. +sub parent +{ + my $self = shift; + my $parent = shift; + + $self->{HANDLE}{TOOLS} = $parent if $parent; + + # Defend against memory leads. See Scalar::Util'. + if (not isweak($self->{HANDLE}{TOOLS})) + { + weaken($self->{HANDLE}{TOOLS}); + } + + return ($self->{HANDLE}{TOOLS}); +} + + +############################################################################################################# +# Public methods # +############################################################################################################# + + +# =head3 +# +# Private Functions; +# +# =cut + +############################################################################################################# +# Private functions # +############################################################################################################# + + +=head2 agent_startup + +This method handles connecting to the databases, loading the agent's schema, resync'ing database tables if needed and reading in the words files. + +If there is a problem, this method exits with C<< 1 >>. Otherwise, it exits with C<< 0 >>. + +Parameters; + +=head3 agent (required) + +This is the name of the scan agent. Usually this can be set as C<< $THIS_FILE >>. + +=head3 tables (required) + +This is an array reference of database tables to check when resync'ing. It is important that the tables are sorted in the order they need to be resync'ed in. (tables with primary keys before their foreign key tables). + +=cut +sub agent_startup +{ + 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 => "ScanCore->agent_startup()" }}); + + my $agent = defined $parameter->{agent} ? $parameter->{agent} : ""; + my $tables = defined $parameter->{tables} ? $parameter->{tables} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + agent => $agent, + tables => $tables, + }}); + + if (not $agent) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "ScanCore->agent_startup()", parameter => "agent" }}); + return("!!error!!"); + } + if ((not $tables) or (ref($tables) ne "ARRAY") or (@{$tables} == 0)) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "ScanCore->agent_startup()", parameter => "tables" }}); + return("!!error!!"); + } + + # Append our tables + foreach my $table (@{$tables}) + { + push @{$anvil->data->{sys}{database}{check_tables}}, $table; + } + + # Connect to DBs. + $anvil->Database->connect({debug => ($debug + 1)}); + $anvil->Log->entry({source => $agent, line => __LINE__, level => $debug, secure => 0, key => "log_0132"}); + if (not $anvil->data->{sys}{database}{connections}) + { + # No databases, exit. + $anvil->Log->entry({source => $agent, line => __LINE__, 'print' => 1, level => 0, secure => 0, key => "error_0003"}); + return(1); + } + + # Make sure our schema is loaded. + $anvil->Database->check_agent_data({ + debug => $debug, + agent => $agent, + }); + + # Read in our word strings. + my $words_file = $anvil->data->{path}{directories}{scan_agents}."/".$agent."/".$agent.".xml"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { words_file => $words_file }}); + + my $problem = $anvil->Words->read({ + debug => ($debug + 1), + file => $words_file, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); + + if ($problem) + { + # Something went wrong loading the file. + return(1); + } + + return(0); + +} + +1; diff --git a/notes b/notes index 24baed68..249a6af1 100644 --- a/notes +++ b/notes @@ -75,6 +75,194 @@ ALTER FUNCTION history_alerts() OWNER TO admin; CREATE TRIGGER trigger_alerts AFTER INSERT OR UPDATE ON alerts FOR EACH ROW EXECUTE PROCEDURE history_alerts(); + + -- This stores weighted health of nodes. Agents can set one or more health values. After a scan sweep +-- completes, ScanCore will sum these weights and the node with the *highest* value is considered the +-- *least* healthy and any servers on it will be migrated to the peer. +CREATE TABLE health ( + health_uuid uuid primary key, + health_host_uuid uuid not null, -- The name of the node or dashboard that this health came from. + health_agent_name text not null, -- This is the scan agent (or program name) setting this score. + health_source_name text not null, -- This is the name of the problem, as set by the agent. + health_source_weight numeric not null, -- This is the numerical weight of this alert. The higher this value, the more severe the health issue is + modified_date timestamp with time zone not null, + + FOREIGN KEY(health_host_uuid) REFERENCES hosts(host_uuid) +); +ALTER TABLE health OWNER TO admin; + +CREATE TABLE history.health ( + history_id bigserial, + health_uuid uuid not null, + health_host_uuid uuid not null, + health_agent_name text not null, + health_source_name text not null, + health_source_weight numeric not null, + modified_date timestamp with time zone not null +); +ALTER TABLE history.health OWNER TO admin; + +CREATE FUNCTION history_health() RETURNS trigger +AS $$ +DECLARE + history_health RECORD; +BEGIN + SELECT INTO history_health * FROM health WHERE health_uuid = new.health_uuid; + INSERT INTO history.health + (health_uuid, + health_host_uuid, + health_agent_name, + health_source_name, + health_source_weight, + modified_date) + VALUES + (history_health.health_uuid, + history_health.health_host_uuid, + history_health.health_agent_name, + history_health.health_source_name, + history_health.health_source_weight, + history_health.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_health() OWNER TO admin; + +CREATE TRIGGER trigger_health + AFTER INSERT OR UPDATE ON health + FOR EACH ROW EXECUTE PROCEDURE history_health(); + +CREATE TABLE power ( + power_uuid uuid primary key, + power_host_uuid uuid not null, -- The name of the node or dashboard that this power came from. + power_ups_uuid uuid not null, -- This is the 'upses' -> 'ups_uuid' of the UPS. This is used to map what UPSes are powering a given node. + power_agent_name text not null, -- This is the name of the scan agent that wrote a given entry + power_on_battery boolean not null, -- TRUE == use "time_remaining" to determine if graceful power off is needed. FALSE == power loss NOT imminent, do not power off node. + power_seconds_left numeric, -- Should always be set, but not required *EXCEPT* when 'power_on_battery' is TRUE. + power_charge_percentage numeric, -- Percentage charge in the UPS. Used to determine when the dashboard should boot the node after AC restore + modified_date timestamp with time zone not null, + + FOREIGN KEY(power_host_uuid) REFERENCES hosts(host_uuid), + FOREIGN KEY(power_ups_uuid) REFERENCES upses(ups_uuid) +); +ALTER TABLE power OWNER TO admin; + +CREATE TABLE history.power ( + history_id bigserial, + power_uuid uuid, + power_host_uuid uuid, + power_ups_uuid uuid, + power_agent_name text, + power_on_battery boolean, + power_seconds_left numeric, + power_charge_percentage numeric, + modified_date timestamp with time zone not null +); +ALTER TABLE history.power OWNER TO admin; + +CREATE FUNCTION history_power() RETURNS trigger +AS $$ +DECLARE + history_power RECORD; +BEGIN + SELECT INTO history_power * FROM power WHERE power_uuid = new.power_uuid; + INSERT INTO history.power + (power_uuid, + power_host_uuid, + power_ups_uuid, + power_agent_name, + power_on_battery, + power_seconds_left, + power_charge_percentage, + modified_date) + VALUES + (history_power.power_uuid, + history_power.power_host_uuid, + history_power.power_ups_uuid, + history_power.power_agent_name, + history_power.power_on_battery, + history_power.power_seconds_left, + history_power.power_charge_percentage, + history_power.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_power() OWNER TO admin; + +CREATE TRIGGER trigger_power + AFTER INSERT OR UPDATE ON power + FOR EACH ROW EXECUTE PROCEDURE history_power(); + +-- This stores temperature information for a given host. ScanCore checks this data to decice if action needs +-- to be taken during a thermal event. On nodes, this is used to decide if a node should be shed or if an +-- Anvil! needs to be stopped entirely. On dashboards, this is used to check if/when it is safe to restart a +-- node that shut down because of a thermal event. +CREATE TABLE temperature ( + temperature_uuid uuid primary key, + temperature_host_uuid uuid not null, -- The name of the node or dashboard that this temperature came from. + temperature_agent_name text not null, -- This is the name of the agent that set the alert + temperature_sensor_host text not null, -- This is the host (uuid) that the sensor was read from. This is important as ScanCore on a striker will read available thermal data from a node using it's IPMI data. + temperature_sensor_name text not null, -- This is the name of the sensor reporting the temperature + temperature_celsius numeric not null, -- This is the actual temperature, in celcius of course. + temperature_state text not null, -- This is a string represnting the state of the sensor. Valid values are 'ok', 'warning', and 'critical' + temperature_is text not null, -- This indicate if the temperature 'nominal', 'high' or 'low'. + modified_date timestamp with time zone not null, + + FOREIGN KEY(temperature_host_uuid) REFERENCES hosts(host_uuid) +); +ALTER TABLE temperature OWNER TO admin; + +CREATE TABLE history.temperature ( + history_id bigserial, + temperature_uuid uuid not null, + temperature_host_uuid uuid not null, + temperature_agent_name text not null, + temperature_sensor_host text not null, + temperature_sensor_name text not null, + temperature_celsius numeric not null, + temperature_state text not null, + temperature_is text not null, + modified_date timestamp with time zone not null +); +ALTER TABLE history.temperature OWNER TO admin; + +CREATE FUNCTION history_temperature() RETURNS trigger +AS $$ +DECLARE + history_temperature RECORD; +BEGIN + SELECT INTO history_temperature * FROM temperature WHERE temperature_uuid = new.temperature_uuid; + INSERT INTO history.temperature + (temperature_uuid, + temperature_host_uuid, + temperature_agent_name, + temperature_sensor_host, + temperature_sensor_name, + temperature_celsius, + temperature_state, + temperature_is, + modified_date) + VALUES + (history_temperature.temperature_uuid, + history_temperature.temperature_host_uuid, + history_temperature.temperature_agent_name, + history_temperature.temperature_sensor_host, + history_temperature.temperature_sensor_name, + history_temperature.temperature_celsius, + history_temperature.temperature_state, + history_temperature.temperature_is, + history_temperature.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_temperature() OWNER TO admin; + +CREATE TRIGGER trigger_temperature + AFTER INSERT OR UPDATE ON temperature + FOR EACH ROW EXECUTE PROCEDURE history_temperature(); + COMMIT; ============ diff --git a/rpm/SPECS/anvil.spec b/rpm/SPECS/anvil.spec index 43fca5f7..99035a5c 100644 --- a/rpm/SPECS/anvil.spec +++ b/rpm/SPECS/anvil.spec @@ -38,6 +38,7 @@ Requires: expect Requires: fence-agents-all Requires: fence-agents-virsh Requires: firewalld +Requires: freeipmi Requires: gpm Requires: hdparm Requires: htop diff --git a/scancore-agents/scan-hardware/scan-hardware b/scancore-agents/scan-hardware/scan-hardware index 1077b5d6..99deb8cf 100755 --- a/scancore-agents/scan-hardware/scan-hardware +++ b/scancore-agents/scan-hardware/scan-hardware @@ -1,12 +1,12 @@ #!/usr/bin/perl # -# This scans the hardware, like RAM modules, CSS LED status, CPU information, etc. +# This scans the scan_hardware, like RAM modules, CSS LED status, CPU information, etc. # # Examples; # # Exit codes; # 0 = Normal exit. -# 1 = No database connections available. +# 1 = Startup failure (no DB, bad file read, etc) # # TODO: # - Decide if it's worth having a separate ScanCore.log file or just feed into anvil.log. @@ -30,25 +30,41 @@ my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1}); $anvil->Log->level({set => 2}); $anvil->Log->secure({set => 1}); +# These are the threasholds for when to alert when swap is running out. +$anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold} = 75; +$anvil->data->{scancore}{'scan-hardware'}{swap}{clear_threshold} = 25; +$anvil->data->{scancore}{'scan-hardware'}{ram}{high_threshold} = 1073741824; # 1 GiB +$anvil->data->{scancore}{'scan-hardware'}{ram}{clear_threshold} = 134217728; # 128 MiB +$anvil->data->{scancore}{'scan-hardware'}{score}{less_ram} = 5; + $anvil->Storage->read_config(); -$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0115", variables => { program => $THIS_FILE }}); +$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0115", variables => { program => $THIS_FILE }}); # Read switches $anvil->Get->switches; -# Connect to DBs. -$anvil->Database->connect; -$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"}); -if (not $anvil->data->{sys}{database}{connections}) +# Handle start-up tasks +my $problem = $anvil->ScanCore->agent_startup({ + debug => 2, + agent => $THIS_FILE, + tables => ["scan_hardware", "scan_hardware_ram_modules"], +}); +if ($problem) { - # No databases, exit. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, secure => 0, key => "error_0003"}); $anvil->nice_exit({exit_code => 1}); } -# Make sure our schema is loaded. -check_database($anvil); +# Read the data. +collect_data($anvil); + +# Load stored data. +read_last_scan($anvil); + +# Look for changes. +find_changes($anvil); +# Finally, process health weights. +process_health($anvil) $anvil->nice_exit({exit_code => 0}); @@ -57,86 +73,1483 @@ $anvil->nice_exit({exit_code => 0}); # Functions # ############################################################################################################# -sub check_database +# This reads in data about the CPU +sub collect_cpu_data { my ($anvil) = @_; + + my $total_cores = 0; + my $total_threads = 0; + my $cores = 0; + my $threads = 0; + my $in_cpu = ""; - my $schema_file = $anvil->data->{path}{directories}{scan_agents}."/".$THIS_FILE."/".$THIS_FILE.".sql"; - my $loaded = $anvil->Database->check_for_schema({ - debug => 2, - file => $schema_file, - }); + my ($output, $return_code) = $anvil->System->call({level => 2, shell_call => $anvil->data->{path}{exe}{dmidecode}." --type processor", source => $THIS_FILE, line => __LINE__}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }}); + foreach my $line (split/\n/, $output) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); + + if ($line =~ /Socket Designation: (.*+)$/) + { + $in_cpu = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_cpu => $in_cpu }}); + } + elsif (not $line) + { + $in_cpu = ""; + $cores = 0; + $threads = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + in_cpu => $in_cpu, + cores => $cores, + threads => $threads, + }}); + } + next if $in_cpu eq ""; + + if ($line =~ /Core Count: (\d+)$/) + { + $cores = $1; + $total_cores += $cores; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + cores => $cores, + total_cores => $total_cores, + }}); + } + if ($line =~ /Thread Count: (\d+)$/) + { + $threads = $1; + $total_threads += $threads; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + threads => $threads, + total_threads => $total_threads, + }}); + } + } + close $file_handle; + + # Read in /proc/cpuinfo. + my $flags = ""; + my $flag_mismatch = 0; + my $bugs = ""; + my $bugs_mismatch = 0; + my $model = ""; + my $model_mismatch = 0; + my $cpu_info = $anvil->Storage->read_file({level => 2, file => $anvil->data->{path}{proc}{cpuinfo}}); + foreach my $line (split/,/, $cpu_info) + { + $line = $anvil->Words->clean_spaces({string => $line}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); + + if ($line =~ /^flags: (.*?)$/) + { + my $these_flags = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { these_flags => $these_flags }}); + if (not $flags) + { + $flags = $these_flags; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { flags => $flags }}); + } + elsif ($flags ne $these_flags) + { + # This should never happen. + $flag_mismatch = 1; + my $changed = $anvil->Alert->check_alert_sent({ + debug => 2, + record_locator => "scan_hardware::cpu_flags_mismatch", + set_by => $THIS_FILE, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Register an alert. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0001", variables => { + these_flags => $these_flags, + flags => $flags, + }}); + $anvil->Alert->register({ + alert_level => "notice", + message => "scan_hardware_alert_0001,!!these_flags!".$these_flags."!!,!!flags!".$flags."!!", + set_by => $THIS_FILE, + }); + } + } + } + if ($line =~ /^bugs: (.*?)$/) + { + my $these_bugs = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { these_bugs => $these_bugs }}); + if (not $bugs) + { + $bugs = $these_bugs; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bugs => $bugs }}); + } + elsif ($bugs ne $these_bugs) + { + # This should never happen... + $bugs_mismatch = 1; + my $changed = $anvil->Alert->check_alert_sent({ + debug => 2, + record_locator => "scan_hardware::cpu_bugs_mismatch", + set_by => $THIS_FILE, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Register an alert. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0003", variables => { + these_bugs => $these_bugs, + bugs => $bugs, + }}); + $anvil->Alert->register({ + alert_level => "notice", + message => "scan_hardware_alert_0003,!!these_bugs!".$these_bugs."!!,!!bugs!".$bugs."!!", + set_by => $THIS_FILE, + }); + } + } + } + if ($line =~ /^model name: (.*?)$/) + { + my $this_model = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { this_model => $this_model }}); + if (not $model) + { + $model = $this_model; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { model => $model }}); + } + elsif ($model ne $this_model) + { + # This should never happen... + $model_mismatch = 1; + my $changed = $anvil->Alert->check_alert_sent({ + debug => 2, + record_locator => "scan_hardware::cpu_model_mismatch", + set_by => $THIS_FILE, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Register an alert. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0005", variables => { + this_model => $this_model, + model => $model, + }}); + $anvil->Alert->register({ + alert_level => "notice", + message => "scan_hardware_alert_0005,!!this_model!".$this_model."!!,!!model!".$model."!!", + set_by => $THIS_FILE, + }); + } + } + } + } + + # In the off chance that there was a flags, bugs or name mismatch before, clear it now if the issue is resolved. + if (not $flags_mismatch) + { + my $changed = $anvil->Alert->check_alert_sent({ + debug => 2, + record_locator => "scan_hardware::cpu_flags_mismatch", + set_by => $THIS_FILE, + type => "clear", + }); + if ($changed) + { + # Register an alert-cleared event. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0002"}); + $anvil->Alert->register({ + alert_level => "notice", + clear => 1, + message => "scan_hardware_alert_0002", + set_by => $THIS_FILE, + }); + } + } + if (not $bugs_mismatch) + { + my $changed = $anvil->Alert->check_alert_sent({ + debug => 2, + record_locator => "scan_hardware::cpu_bugs_mismatch", + set_by => $THIS_FILE, + type => "clear", + }); + if ($changed) + { + # Register an alert-cleared event. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0004"}); + $anvil->Alert->register({ + alert_level => "notice", + clear => 1, + message => "scan_hardware_alert_0004", + set_by => $THIS_FILE, + }); + } + } + if (not $model_mismatch) + { + my $changed = $anvil->Alert->check_alert_sent({ + debug => 2, + record_locator => "scan_hardware::cpu_model_mismatch", + set_by => $THIS_FILE, + type => "clear", + }); + if ($changed) + { + # Register an alert-cleared event. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0006"}); + $anvil->Alert->register({ + alert_level => "notice", + clear => 1, + message => "scan_hardware_alert_0006", + set_by => $THIS_FILE, + }); + } + } + + # Record what we found. + $anvil->data->{summary}{cpu}{model} = $model; + $anvil->data->{summary}{cpu}{cores} = $total_cores; + $anvil->data->{summary}{cpu}{threads} = $total_threads; + $anvil->data->{summary}{cpu}{bugs} = $bugs; + $anvil->data->{summary}{cpu}{flags} = $flags; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - loaded => $loaded, - schema_file => $schema_file, + "summary::cpu::model" => $anvil->data->{summary}{cpu}{model}, + "summary::cpu::cores" => $anvil->data->{summary}{cpu}{cores}, + "summary::cpu::threads" => $anvil->data->{summary}{cpu}{threads}, + "summary::cpu::bugs" => $anvil->data->{summary}{cpu}{bugs}, + "summary::cpu::flags" => $anvil->data->{summary}{cpu}{flags}, }}); - if ($loaded) + + return(0); +} + +# This reads in all the data we can find on the local system +sub collect_data +{ + my ($anvil) = @_; + + # Collect CPU info. + collect_cpu_data($anvil); + + # Collect RAM data. + collect_ram_data($anvil); + + # If this is a machine with IPMI, see if we can gather more info. + collect_led_states($anvil); + + return(0); +} + +# If this is a machine with IPMI, see if we can gather more info. +sub collect_led_states +{ + my ($anvil) = @_; + + # I need to know what kind of machine I am. + my $manufacturer = ""; + my $id_led = "unknown"; + my $css_led = "unknown"; + my $error_led = "unknown"; + + my ($output, $return_code) = $anvil->System->call({level => 2, shell_call => $anvil->data->{path}{exe}{dmidecode}." --string system-manufacturer", source => $THIS_FILE, line => __LINE__}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }}); + foreach my $line (split/\n/, $output) { - if ($loaded eq "!!error!!") + $manufacturer = lc($line); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { manufacturer => $manufacturer }}); + } + + if ($manufacturer eq "fujitsu") + { + my ($output, $return_code) = $anvil->System->call({level => 2, shell_call => $anvil->data->{path}{exe}{'ipmi-oem'}." Fujitsu get-system-status", source => $THIS_FILE, line => __LINE__}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }}); + foreach my $line (split/\n/, $output) + { + $line = $anvil->Words->clean_spaces({string => $line}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); + + if ($line =~ /Identify LED: (.*)$/) + { + $id_led = lc($1); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { id_led => $id_led }}); + } + if ($line =~ /CSS LED: (.*)$/) + { + $css_led = lc($1); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { css_led => $css_led }}); + } + if ($line =~ /Global Error LED: (.*)$/) + { + $error_led = lc($1); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { error_led => $error_led }}); + } + } + } + elsif ($manufacturer eq "dell") + { + ### TODO: When we get a dell, figure this out... + ### TODO: There are a lot of useful bits here, including power load/headroom. Excellent data for the UI later. + #my ($output, $return_code) = $anvil->System->call({level => 2, shell_call => $anvil->data->{path}{exe}{'ipmi-oem'}." Dell get-system-status", source => $THIS_FILE, line => __LINE__}); + #$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }}); + #foreach my $line (split/\n/, $output) + #{ + # $line = $anvil->Words->clean_spaces({string => $line}); + # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); + #} + } + + # Record. + $anvil->data->{summary}{led}{id_led} = $id_led; + $anvil->data->{summary}{led}{css_led} = $css_led; + $anvil->data->{summary}{led}{error_led} = $error_led; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "summary::led::id_led" => $anvil->data->{summary}{led}{id_led}, + "summary::led::css_led" => $anvil->data->{summary}{led}{css_led}, + "summary::led::error_led" => $anvil->data->{summary}{led}{error_led}, + }}); + + return(0); +} + +# This reads in data about the RAM +sub collect_ram_data +{ + my ($anvil) = @_; + + my $total_size = 0; + my $size = ""; + my $locator = ""; + my $manufacturer = ""; + my $part_number = ""; + my $serial_number = ""; + + my ($output, $return_code) = $anvil->System->call({level => 2, shell_call => $anvil->data->{path}{exe}{dmidecode}." --type memory", source => $THIS_FILE, line => __LINE__}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }}); + foreach my $line (split/\n/, $output) + { + $line = $anvil->Words->clean_spaces({string => $line}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); + + if ($line =~ /^Locator: (.*?)$/) + { + $locator = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { locator => $locator }}); + } + if ($line =~ /^Size: (.*?)$/) { - # Something went wrong. - my $changed = $anvil->Alert->check_alert_sent({ - debug => 2, - record_locator => "schema_load_failure", - set_by => $THIS_FILE, - type => "set", + $size = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { size => $size }}); + + # If the "size" is "no module installed", we're done here. + if ($size !~ /^\d/) + { + $locator = ""; + $size = ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + locator => $locator, + size => $size, + }}); + next; + } + + # THis reports in 'MB' but it's really 'MiB'. + $size = $anvil->Convert->human_readable_to_bytes({ + base2 => 1, + size => $size, }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); - if ($changed) - { - # Log and register an alert. This should never happen, so we set it as a - # warning level alert. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0181", variables => { - agent_name => $THIS_FILE, - file => $schema_file, + $total_size += $size; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + locator => $locator, + total_size => $total_size, + }}); + } + if ($line =~ /^Manufacturer: (.*)$/) + { + $manufacturer = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { manufacturer => $manufacturer }}); + } + if ($line =~ /^Part Number: (.*)$/) + { + $part_number = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { part_number => $part_number }}); + } + if ($line =~ /^Serial Number: (.*)$/) + { + $serial_number = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { serial_number => $serial_number }}); + } + next if not $locator; + if (not $line) + { + if ($size) + { + $anvil->data->{ram}{dmi}{locator}{$locator}{size} = $size; + $anvil->data->{ram}{dmi}{locator}{$locator}{manufacturer} = $manufacturer; + $anvil->data->{ram}{dmi}{locator}{$locator}{part_number} = $part_number; + $anvil->data->{ram}{dmi}{locator}{$locator}{serial_number} = $serial_number; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ram::dmi::locator::${locator}::size" => $anvil->Convert->add_commas({number => $anvil->data->{ram}{dmi}{locator}{$locator}{size}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{ram}{dmi}{locator}{$locator}{size}}).")", + "ram::dmi::locator::${locator}::manufacturer" => $anvil->data->{ram}{dmi}{locator}{$locator}{manufacturer}, + "ram::dmi::locator::${locator}::part_number" => $anvil->data->{ram}{dmi}{locator}{$locator}{part_number}, + "ram::dmi::locator::${locator}::serial_number" => $anvil->data->{ram}{dmi}{locator}{$locator}{serial_number}, }}); - $anvil->Alert->register({ - debug => 2, - alert_level => "warning", - message => "message_0181,!!agent_name!".$THIS_FILE."!!,!!file!".$schema_file."!!", - set_by => $THIS_FILE, - }); } + + $size = ""; + $locator = ""; + $manufacturer = ""; + $part_number = ""; + $serial_number = ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "size" => $size, + "locator" => $locator, + "manufacturer" => $manufacturer, + "part_number" => $part_number, + "serial_number" => $serial_number, + }}); } - elsif (ref($loaded) eq "ARRAY") + } + + my $cpu_info = $anvil->Storage->read_file({level => 2, file => $anvil->data->{path}{proc}{meminfo}}); + foreach my $line (split/,/, $cpu_info) + { + $line = $anvil->Words->clean_spaces({string => $line}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); + + if ($line =~ /^(.*?):\s+(\d+.*?)$/) { - # If there was an alert, clear it. - my $changed = $anvil->Alert->check_alert_sent({ - debug => 2, - record_locator => "schema_load_failure", - set_by => $THIS_FILE, - type => "clear", + my $variable = $1; + my $size = $2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + variable => $variable, + size => $size, + }}); + + # We care about a few variables only. + my $say_variable = ""; + if ($variable eq "MemTotal") + { + $say_variable = "memory_total"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_variable => $say_variable }}); + } + if ($variable eq "MemFree") + { + $say_variable = "memory_free"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_variable => $say_variable }}); + } + if ($variable eq "SwapTotal") + { + $say_variable = "swap_total"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_variable => $say_variable }}); + } + if ($variable eq "SwapFree") + { + $say_variable = "swap_free"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_variable => $say_variable }}); + } + next if not $say_variable; + + # This reports sizes as 'kB', but it's really base2. + $size = $anvil->Convert->human_readable_to_bytes({ + base2 => 1, + size => $size, }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); - if ($changed) + + $anvil->data->{summary}{ram}{proc}{$say_variable} = $size; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "summary::ram::proc::${say_variable}" => $anvil->Convert->add_commas({number => $anvil->data->{summary}{ram}{proc}{$say_variable}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{summary}{ram}{proc}{$say_variable}}).")", + }}); + } + } + + $anvil->data->{summary}{ram}{size} = $total_size; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "summary::ram::size" => $anvil->Convert->add_commas({number => $anvil->data->{summary}{ram}{size}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{summary}{ram}{size}}).")", + }}); + + return(0); +} + +# This reads in the last scan data from one of the databases and compares it against the just-read data. If +# anything changed, register an alert. +sub find_changes +{ + my ($anvil) = @_; + + # Walk through what I know. First the main info, then the RAM modules below. + my $new_scan_hardware_cpu_model = $anvil->data->{summary}{cpu}{model}; + my $new_scan_hardware_cpu_cores = $anvil->data->{summary}{cpu}{cores}; + my $new_scan_hardware_cpu_threads = $anvil->data->{summary}{cpu}{threads}; + my $new_scan_hardware_cpu_bugs = $anvil->data->{summary}{cpu}{bugs}; + my $new_scan_hardware_cpu_flags = $anvil->data->{summary}{cpu}{flags}; + my $new_scan_hardware_led_id = $anvil->data->{summary}{led}{id_led}; + my $new_scan_hardware_led_css = $anvil->data->{summary}{led}{css_led}; + my $new_scan_hardware_led_error = $anvil->data->{summary}{led}{error_led}; + my $new_scan_hardware_ram_total = $anvil->data->{summary}{ram}{size}; # This is from counting the actual module capacity + my $new_scan_hardware_memory_total = $anvil->data->{summary}{ram}{proc}{memory_total}; # This is from /proc/meminfo and subtracts RAM used by shared video, etc. + my $new_scan_hardware_memory_free = $anvil->data->{summary}{ram}{proc}{memory_free}; + my $new_scan_hardware_swap_total = $anvil->data->{summary}{ram}{proc}{swap_total}; + my $new_scan_hardware_swap_free = $anvil->data->{summary}{ram}{proc}{swap_free}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_hardware_cpu_model" => $new_scan_hardware_cpu_model, + "new_scan_hardware_cpu_cores" => $new_scan_hardware_cpu_cores, + "new_scan_hardware_cpu_threads" => $new_scan_hardware_cpu_threads, + "new_scan_hardware_cpu_bugs" => $new_scan_hardware_cpu_bugs, + "new_scan_hardware_cpu_flags" => $new_scan_hardware_cpu_flags, + "new_scan_hardware_led_id" => $new_scan_hardware_led_id, + "new_scan_hardware_led_css" => $new_scan_hardware_led_css, + "new_scan_hardware_ram_total" => $new_scan_hardware_ram_total, + "new_scan_hardware_led_error" => $new_scan_hardware_led_error, + "new_scan_hardware_memory_total" => $anvil->Convert->add_commas({number => $new_scan_hardware_memory_total})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_total}).")", + "new_scan_hardware_memory_free" => $anvil->Convert->add_commas({number => $new_scan_hardware_memory_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_free}).")", + "new_scan_hardware_swap_total" => $anvil->Convert->add_commas({number => $new_scan_hardware_swap_total})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total}).")", + "new_scan_hardware_swap_free" => $anvil->Convert->add_commas({number => $new_scan_hardware_swap_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_free}).")", + }}); + + # The LED status needs to be translated. + my $say_new_scan_hardware_led_id = "#!string!scan_hardware_message_0001!#"; + if ($new_scan_hardware_led_id eq "on") + { + $say_new_scan_hardware_led_id = "#!string!scan_hardware_message_0002!#"; + } + elsif ($new_scan_hardware_led_id eq "off") + { + $say_new_scan_hardware_led_id = "#!string!scan_hardware_message_0003!#"; + } + my $say_new_scan_hardware_led_css = "#!string!scan_hardware_message_0001!#"; + if ($new_scan_hardware_led_css eq "on") + { + $say_new_scan_hardware_led_css = "#!string!scan_hardware_message_0002!#"; + } + elsif ($new_scan_hardware_led_css eq "off") + { + $say_new_scan_hardware_led_css = "#!string!scan_hardware_message_0003!#"; + } + my $say_new_scan_hardware_led_error = "#!string!scan_hardware_message_0001!#"; + if ($new_scan_hardware_led_error eq "on") + { + $say_new_scan_hardware_led_error = "#!string!scan_hardware_message_0002!#"; + } + elsif ($new_scan_hardware_led_error eq "off") + { + $say_new_scan_hardware_led_error = "#!string!scan_hardware_message_0003!#"; + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + say_new_scan_hardware_led_id => $say_new_scan_hardware_led_id, + say_new_scan_hardware_led_css => $say_new_scan_hardware_led_css, + say_new_scan_hardware_led_error => $say_new_scan_hardware_led_error, + }}); + + # INSERT or UPDATE? + if ((exists $anvil->data->{sql}{scan_hardware_uuid}) && ($anvil->data->{sql}{scan_hardware_uuid})) + { + # Look for changed. + my $scan_hardware_uuid = $anvil->data->{sql}{scan_hardware_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_hardware_uuid => $scan_hardware_uuid }}); + + my $old_scan_hardware_cpu_model = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_model}; + my $old_scan_hardware_cpu_cores = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_cores}; + my $old_scan_hardware_cpu_threads = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_threads}; + my $old_scan_hardware_cpu_bugs = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_bugs}; + my $old_scan_hardware_cpu_flags = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_flags}; + my $old_scan_hardware_ram_total = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_ram_total}; + my $old_scan_hardware_memory_total = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_memory_total}; + my $old_scan_hardware_memory_free = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_memory_free}; + my $old_scan_hardware_swap_total = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_swap_total}; + my $old_scan_hardware_swap_free = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_swap_free}; + my $old_scan_hardware_led_id = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_id}; + my $old_scan_hardware_led_css = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_css}; + my $old_scan_hardware_led_error = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_error}; + my $old_scan_hardware_modified_date = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_modified_date}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + old_scan_hardware_cpu_model => $old_scan_hardware_cpu_model, + old_scan_hardware_cpu_cores => $old_scan_hardware_cpu_cores, + old_scan_hardware_cpu_threads => $old_scan_hardware_cpu_threads, + old_scan_hardware_cpu_bugs => $old_scan_hardware_cpu_bugs, + old_scan_hardware_cpu_flags => $old_scan_hardware_cpu_flags, + old_scan_hardware_ram_total => $old_scan_hardware_ram_total, + old_scan_hardware_memory_total => $old_scan_hardware_memory_total, + old_scan_hardware_memory_free => $old_scan_hardware_memory_free, + old_scan_hardware_swap_total => $old_scan_hardware_swap_total, + old_scan_hardware_swap_free => $old_scan_hardware_swap_free, + old_scan_hardware_led_id => $old_scan_hardware_led_id, + old_scan_hardware_led_css => $old_scan_hardware_led_css, + old_scan_hardware_led_error => $old_scan_hardware_led_error, + old_scan_hardware_modified_date => $old_scan_hardware_modified_date, + }}); + + my $update = 0; + if ($new_scan_hardware_cpu_model ne $old_scan_hardware_cpu_model) + { + # CPU model changed, probably from a CPU upgrade or firmware flash, so just a notice level alert. + $update = 1; + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0007,!!new!".$new_scan_hardware_cpu_model."!!,!!old!".$old_scan_hardware_cpu_model."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0007", variables => { new => $new_scan_hardware_cpu_model, old => $old_scan_hardware_cpu_model }}); + } + if ($new_scan_hardware_cpu_bugs ne $old_scan_hardware_cpu_bugs) + { + # This is probably from a CPU upgrade or firward flash + $update = 1; + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0008,!!new!".$new_scan_hardware_cpu_bugs."!!,!!old!".$old_scan_hardware_cpu_bugs."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0008", variables => { new => $new_scan_hardware_cpu_bugs, old => $old_scan_hardware_cpu_bugs }}); + } + if ($new_scan_hardware_cpu_flags ne $old_scan_hardware_cpu_flags) + { + # This is probably from a CPU upgrade or firward flash + $update = 1; + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0009,!!new!".$new_scan_hardware_cpu_flags."!!,!!old!".$old_scan_hardware_cpu_flags."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0009", variables => { new => $new_scan_hardware_cpu_flags, old => $old_scan_hardware_cpu_flags }}); + } + if ($new_scan_hardware_cpu_cores ne $old_scan_hardware_cpu_cores) + { + # This is probably from a CPU upgrade + $update = 1; + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0010,!!new!".$new_scan_hardware_cpu_cores."!!,!!old!".$old_scan_hardware_cpu_cores."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0010", variables => { new => $new_scan_hardware_cpu_cores, old => $old_scan_hardware_cpu_cores }}); + } + if ($new_scan_hardware_cpu_threads ne $old_scan_hardware_cpu_threads) + { + # This is probably from a CPU upgrade + $update = 1; + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0011,!!new!".$new_scan_hardware_cpu_threads."!!,!!old!".$old_scan_hardware_cpu_threads."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0011", variables => { new => $new_scan_hardware_cpu_threads, old => $old_scan_hardware_cpu_threads }}); + } + if ($new_scan_hardware_ram_total ne $old_scan_hardware_ram_total) + { + # RAM (from dmidecode) changed could be a hardware upgrade, or it could be from a + # failed modules. As such, we set this to 'warning'. + $update = 1; + my $say_new_ram = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)"; + my $say_old_ram = $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)"; + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0012,!!new!".$say_new_ram."!!,!!old!".$say_old_ram."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0012", variables => { new => $say_new_ram, old => $say_old_ram}}); + } + if ($new_scan_hardware_memory_total ne $old_scan_hardware_memory_total) + { + # Memory (/proc/meminfo) changed could be a hardware upgrade, or it could be from a + # failed modules. As such, we set this to 'warning'. + $update = 1; + my $say_new_memory = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_memory_total})." #!string!scan_hardware_unit_0001!#)"; + my $say_old_memory = $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_memory_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_memory_total})." #!string!scan_hardware_unit_0001!#)"; + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0013,!!new!".$say_new_memory."!!,!!old!".$say_old_memory."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0013", variables => { new => $say_new_memory, old => $say_old_memory}}); + } + if ($new_scan_hardware_swap_total ne $old_scan_hardware_swap_total) + { + # Memory (/proc/meminfo) changed could be a hardware upgrade, or it could be from a + # failed modules. As such, we set this to 'warning'. + $update = 1; + my $say_new_swap = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_swap_total})." #!string!scan_hardware_unit_0001!#)"; + my $say_old_swap = $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_swap_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_swap_total})." #!string!scan_hardware_unit_0001!#)"; + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0014,!!new!".$say_new_swap."!!,!!old!".$say_old_swap."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0014", variables => { new => $say_new_swap, old => $say_old_swap}}); + } + if ($new_scan_hardware_led_id ne $old_scan_hardware_led_id) + { + $update = 1; + my $say_old_scan_hardware_led_id = "#!string!scan_hardware_message_0001!#"; + if ($old_scan_hardware_led_id eq "on") { - # Register an alert cleared message. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0182", variables => { - agent_name => $THIS_FILE, - file => $schema_file, - - }}); - $anvil->Alert->register({ - debug => 2, - alert_level => "warning", - clear_alert => 1, - message => "message_0182,!!agent_name!".$THIS_FILE."!!,!!file!".$schema_file."!!", - set_by => $THIS_FILE, + $say_old_scan_hardware_led_id = "#!string!scan_hardware_message_0002!#"; + } + elsif ($old_scan_hardware_led_id eq "off") + { + $say_old_scan_hardware_led_id = "#!string!scan_hardware_message_0003!#"; + } + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0015,!!new!".$say_new_scan_hardware_led_id."!!,!!old!".$say_old_scan_hardware_led_id."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0015", variables => { new => $say_new_scan_hardware_led_id, old => $say_old_scan_hardware_led_id}}); + } + if ($new_scan_hardware_led_css ne $old_scan_hardware_led_css) + { + # The CSS LED is an error LED, so it changing deserves to be a 'warning' level alert. + $update = 1; + my $say_old_scan_hardware_led_css = "#!string!scan_hardware_message_0001!#"; + if ($old_scan_hardware_led_css eq "on") + { + $say_old_scan_hardware_led_css = "#!string!scan_hardware_message_0002!#"; + } + elsif ($old_scan_hardware_led_css eq "off") + { + $say_old_scan_hardware_led_css = "#!string!scan_hardware_message_0003!#"; + } + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0016,!!new!".$say_new_scan_hardware_led_css."!!,!!old!".$say_old_scan_hardware_led_css."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0016", variables => { new => $say_new_scan_hardware_led_css, old => $say_old_scan_hardware_led_css}}); + } + if ($new_scan_hardware_led_error ne $old_scan_hardware_led_error) + { + # The Hardware Error LED deserves to be a 'warning' level alert. + $update = 1; + my $say_old_scan_hardware_led_error = "#!string!scan_hardware_message_0001!#"; + if ($old_scan_hardware_led_error eq "on") + { + $say_old_scan_hardware_led_error = "#!string!scan_hardware_message_0002!#"; + } + elsif ($old_scan_hardware_led_error eq "off") + { + $say_old_scan_hardware_led_error = "#!string!scan_hardware_message_0003!#"; + } + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0017,!!new!".$say_new_scan_hardware_led_error."!!,!!old!".$say_old_scan_hardware_led_error."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0017", variables => { new => $say_new_scan_hardware_led_error, old => $say_old_scan_hardware_led_error}}); + } + if ($new_scan_hardware_memory_free ne $old_scan_hardware_memory_free) + { + # This always changes, so it's an info-level alert + $update = 1; + my $say_new_scan_hardware_memory_free = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_free})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_memory_free})." #!string!scan_hardware_unit_0001!#)" + my $say_old_scan_hardware_memory_free = $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_memory_free})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_memory_free})." #!string!scan_hardware_unit_0001!#)" + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "info", message => "scan_hardware_alert_0018,!!new!".$say_new_scan_hardware_memory_free."!!,!!old!".$say_old_scan_hardware_memory_free."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0018", variables => { new => $say_new_scan_hardware_memory_free, old => $say_old_scan_hardware_memory_free}}); + } + if ($new_scan_hardware_swap_free ne $old_scan_hardware_swap_free) + { + $update = 1; + my $say_new_scan_hardware_swap_free = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_free})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_swap_free})." #!string!scan_hardware_unit_0001!#)" + my $say_old_scan_hardware_swap_free = $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_swap_free})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_swap_free})." #!string!scan_hardware_unit_0001!#)" + $anvil->Alert->register({set_by => $THIS_FILE, alert_level => "info", message => "scan_hardware_alert_0019,!!new!".$say_new_scan_hardware_swap_free."!!,!!old!".$say_old_scan_hardware_swap_free."!!"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0019", variables => { new => $say_new_scan_hardware_swap_free, old => $say_old_scan_hardware_swap_free}}); + + my $new_swap_bytes_used = $new_scan_hardware_swap_total - $new_scan_hardware_swap_free; + my $old_swap_bytes_used = $old_scan_hardware_swap_total - $old_scan_hardware_swap_free; + my $new_swap_percent_used = $anvil->Math->round({number => (($new_swap_bytes_used / $new_scan_hardware_swap_total) * 100)}); + my $old_swap_percent_used = $anvil->Math->round({number => (($old_swap_bytes_used / $old_scan_hardware_swap_total) * 100)}); + my $swap_percent_high = $anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold} =~ /^\d+/ ? $anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold} : 75; + my $swap_percent_low = $anvil->data->{scancore}{'scan-hardware'}{swap}{clear_threshold} =~ /^\d+/ ? $anvil->data->{scancore}{'scan-hardware'}{swap}{clear_threshold} : 25; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:new_swap_bytes_used' => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_swap_bytes_used})." (".$anvil->Convert->add_commas({number => $new_swap_bytes_used})." #!string!scan_hardware_unit_0001!#)", + 's2:old_swap_bytes_used' => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_swap_bytes_used})." (".$anvil->Convert->add_commas({number => $old_swap_bytes_used})." #!string!scan_hardware_unit_0001!#)", + 's3:new_swap_percent_used' => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_swap_percent_used})." (".$anvil->Convert->add_commas({number => $new_swap_percent_used})." #!string!scan_hardware_unit_0001!#)", + 's4:old_swap_percent_used' => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_swap_percent_used})." (".$anvil->Convert->add_commas({number => $old_swap_percent_used})." #!string!scan_hardware_unit_0001!#)", + 's5:swap_percent_high' => $anvil->Convert->bytes_to_human_readable({'bytes' => $swap_percent_high})." (".$anvil->Convert->add_commas({number => $swap_percent_high})." #!string!scan_hardware_unit_0001!#)", + 's6:swap_percent_low' => $anvil->Convert->bytes_to_human_readable({'bytes' => $swap_percent_low})." (".$anvil->Convert->add_commas({number => $swap_percent_low})." #!string!scan_hardware_unit_0001!#)", + }}); + + # Check if swap has gone over the high threshold or dropped below the clear + # threashold. + if ($new_scan_hardware_swap_free > $anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold}) + { + # It's high + my $changed = $anvil->Alert->check_alert_sent({ + debug => 2, + record_locator => "scan_hardware::high_swap", + set_by => $agent, + type => "set", + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Log and register an alert. + my $say_used = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_swap_bytes_used}); + my $say_swap = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total}); + my $variables = { + say_used => $say_used, + say_swap => $say_swap, + swap_percent => $new_swap_percent_used, + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "scan_hardware_alert_0020", variables => $variables}); + $anvil->Alert->register({ + debug => 2, + alert_level => "warning", + message => "scan_hardware_alert_0020", + message_variables => $variables, + set_by => $agent, + }); + } + } + elsif ($new_scan_hardware_swap_free < $anvil->data->{scancore}{'scan-hardware'}{swap}{clear_threshold}) + { + # It's low + my $changed = $anvil->Alert->check_alert_sent({ + debug => 2, + record_locator => "scan_hardware::high_swap", + set_by => $agent, + type => "clear", }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Log and register that the alert has cleared. + my $say_used = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_swap_bytes_used}); + my $say_swap = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "scan_hardware_alert_0021", variables => { + say_used => $say_used, + say_swap => $say_swap, + swap_percent => $new_swap_percent_used, + }}); + $anvil->Alert->register({ + debug => 2, + alert_level => "warning", + message => "scan_hardware_alert_0021,!!say_used!".$say_used."!!,!!say_swap!".$say_swap."!!,!!swap_percent!".$new_swap_percent_used."!!", + set_by => $agent, + }); + } } + } + + # Update? + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); + if ($update) + { + # Yup! + my $query = " +UPDATE + scan_hardware +SET + scan_hardware_cpu_model = ".$anvil->Database->quote($new_scan_hardware_cpu_model).", + scan_hardware_cpu_cores = ".$anvil->Database->quote($new_scan_hardware_cpu_cores).", + scan_hardware_cpu_threads = ".$anvil->Database->quote($new_scan_hardware_cpu_threads).", + scan_hardware_cpu_bugs = ".$anvil->Database->quote($new_scan_hardware_cpu_bugs).", + scan_hardware_cpu_flags = ".$anvil->Database->quote($new_scan_hardware_cpu_flags).", + scan_hardware_ram_total = ".$anvil->Database->quote($new_scan_hardware_ram_total).", + scan_hardware_memory_total = ".$anvil->Database->quote($new_scan_hardware_memory_total).", + scan_hardware_memory_free = ".$anvil->Database->quote($new_scan_hardware_memory_free).", + scan_hardware_swap_total = ".$anvil->Database->quote($new_scan_hardware_swap_total).", + scan_hardware_swap_free = ".$anvil->Database->quote($new_scan_hardware_swap_free).", + scan_hardware_led_id = ".$anvil->Database->quote($new_scan_hardware_led_id).", + scan_hardware_led_css = ".$anvil->Database->quote($new_scan_hardware_led_css).", + scan_hardware_led_error = ".$anvil->Database->quote($new_scan_hardware_led_error).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_hardware_uuid = ".$anvil->Database->quote($scan_hardware_uuid)." +;"; - # Log which databses we loaded our schema into. - foreach my $uuid (@{$loaded}) + # Now record the query in the array + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + } + } + else + { + # New record, send an alert telling that we've found data. + my $message_variables = { + total_cores => $new_scan_hardware_cpu_cores, + total_threads => $new_scan_hardware_cpu_threads, + cpu_bugs => $new_scan_hardware_cpu_bugs, + cpu_flags => $new_scan_hardware_cpu_flags, + id_led => $say_new_scan_hardware_led_id, + css_led => $say_new_scan_hardware_led_css, + error_led => $say_new_scan_hardware_led_error, + ram_total_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)", + ram_memory_total => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_memory_total})." #!string!scan_hardware_unit_0001!#)", + ram_memory_free => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_free})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_memory_free})." #!string!scan_hardware_unit_0001!#)", + ram_swap_total => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_swap_total})." #!string!scan_hardware_unit_0001!#)", + ram_swap_free => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_free})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_swap_free})." #!string!scan_hardware_unit_0001!#)", + }; + $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_hardware_alert_0022", message_variables => $message_variables, set_by => $agent }); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0019", variables => $message_variables}); + + # INSERT + my $scan_hardware_uuid = $anvil->Get->uuid(); + my $query = " +INSERT INTO + scan_hardware +( + scan_hardware_uuid, + scan_hardware_host_uuid, + scan_hardware_cpu_model, + scan_hardware_cpu_cores, + scan_hardware_cpu_threads, + scan_hardware_cpu_bugs, + scan_hardware_cpu_flags, + scan_hardware_ram_total, + scan_hardware_memory_total, + scan_hardware_memory_free, + scan_hardware_swap_total, + scan_hardware_swap_free, + scan_hardware_led_id, + scan_hardware_led_css, + scan_hardware_led_error, + modified_date +) VALUES ( + ".$anvil->Database->quote($scan_hardware_uuid).", + ".$anvil->Database->quote($anvil->Get->host_uuid).", + ".$anvil->Database->quote($new_scan_hardware_cpu_model).", + ".$anvil->Database->quote($new_scan_hardware_cpu_cores).", + ".$anvil->Database->quote($new_scan_hardware_cpu_threads).", + ".$anvil->Database->quote($new_scan_hardware_cpu_bugs).", + ".$anvil->Database->quote($new_scan_hardware_cpu_flags).", + ".$anvil->Database->quote($new_scan_hardware_ram_total).", + ".$anvil->Database->quote($new_scan_hardware_memory_total).", + ".$anvil->Database->quote($new_scan_hardware_memory_free).", + ".$anvil->Database->quote($new_scan_hardware_swap_total).", + ".$anvil->Database->quote($new_scan_hardware_swap_free).", + ".$anvil->Database->quote($new_scan_hardware_led_id).", + ".$anvil->Database->quote($new_scan_hardware_led_css).", + ".$anvil->Database->quote($new_scan_hardware_led_css).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + # Now record the query in the array + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + } + + ### TODO: Left off here. + # Now the RAM modules. + foreach my $hardware_ram_module_locator (sort {$a cmp $b} keys %{$anvil->data->{ram}{dmi}{locator}}) + { + my $new_scan_hardware_ram_module_size = $anvil->data->{ram}{dmi}{locator}{$hardware_ram_module_locator}{size}; + my $new_scan_hardware_ram_module_manufacturer = $anvil->data->{ram}{dmi}{locator}{$hardware_ram_module_locator}{manufacturer}; + my $new_scan_hardware_ram_module_model = $anvil->data->{ram}{dmi}{locator}{$hardware_ram_module_locator}{part_number}; + my $new_scan_hardware_ram_module_serial_number = $anvil->data->{ram}{dmi}{locator}{$hardware_ram_module_locator}{serial_number}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + hardware_ram_module_locator => $hardware_ram_module_locator, + new_scan_hardware_ram_module_size => $anvil->Convert->add_commas({number => $new_scan_hardware_ram_module_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_module_size}).")", + new_scan_hardware_ram_module_manufacturer => $new_scan_hardware_ram_module_manufacturer, + new_scan_hardware_ram_module_model => $new_scan_hardware_ram_module_model, + new_scan_hardware_ram_module_serial_number => $new_scan_hardware_ram_module_serial_number, + }}); + + if ((exists $anvil->data->{sql}{scan_hardware_ram_module_uuid}{$hardware_ram_module_locator}) && ($anvil->data->{sql}{scan_hardware_ram_module_uuid}{$hardware_ram_module_locator})) + { + # We've seen this module before, look for changes. + my $scan_hardware_ram_module_uuid = $anvil->data->{sql}{scan_hardware_ram_module_uuid}{$hardware_ram_module_locator}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::hardware_ram_module_uuid::${scan_hardware_ram_module_locator}" => $anvil->data->{sql}{scan_hardware_ram_module_uuid}{$hardware_ram_module_locator}, + }}); + + my $old_scan_hardware_ram_module_size = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_size}; + my $old_scan_hardware_ram_module_manufacturer = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_manufacturer}; + my $old_scan_hardware_ram_module_model = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_model}; + my $old_scan_hardware_ram_module_serial_number = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_serial_number}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + old_scan_hardware_ram_module_size => $anvil->Convert->add_commas({number => $old_scan_hardware_ram_module_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_module_size}).")", + old_scan_hardware_ram_module_manufacturer => $old_scan_hardware_ram_module_manufacturer, + old_scan_hardware_ram_module_model => $old_scan_hardware_ram_module_model, + old_scan_hardware_ram_module_serial_number => $old_scan_hardware_ram_module_serial_number, + }}); + + # Delete the entry so we know we've processed this existing module. + delete $anvil->data->{sql}{scan_hardware_ram_module_uuid}{$hardware_ram_module_locator}; + delete $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}; + + ### We check all at once for a single alert as it's easy to see what has changed. + # Looks for changes. + if (($new_scan_hardware_ram_module_size ne $old_scan_hardware_ram_module_size) or + ($new_scan_hardware_ram_module_manufacturer ne $old_scan_hardware_ram_module_manufacturer) or + ($old_scan_hardware_ram_module_model ne $old_scan_hardware_ram_module_model) or + ($new_scan_hardware_ram_module_serial_number ne $old_scan_hardware_ram_module_serial_number)) + { + # This shouldn't change, but maybe the RAM was replaced or upgraded? It could + # be that a vanished module has returned? + if (($old_scan_hardware_ram_module_manufacturer eq "VANISHED") && ($new_scan_hardware_ram_module_manufacturer ne "VANISHED")) + { + # A vanished module has returned. + my $variables = { + locator => $hardware_ram_module_locator, + old_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_module_size})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_module_size})." #!string!scan_hardware_unit_0001!#)", + old_manufacturer => $old_scan_hardware_ram_module_manufacturer, + old_model => $old_scan_hardware_ram_module_model, + old_serial_number => $old_scan_hardware_ram_module_serial_number, + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "scan_hardware_alert_0023", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "warning", message => "scan_hardware_alert_0023", message_variables => $variables, set_by => $agent}); + } + else + { + # Something else changed. + my $variables = { + locator => $hardware_ram_module_locator, + new_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_module_size})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_module_size})." #!string!scan_hardware_unit_0001!#)", + new_manufacturer => $new_scan_hardware_ram_module_manufacturer, + new_model => $new_scan_hardware_ram_module_model, + new_serial_number => $new_scan_hardware_ram_module_serial_number, + old_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_module_size})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_module_size})." #!string!scan_hardware_unit_0001!#)", + old_manufacturer => $old_scan_hardware_ram_module_manufacturer, + old_model => $old_scan_hardware_ram_module_model, + old_serial_number => $old_scan_hardware_ram_module_serial_number, + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "scan_hardware_alert_0024", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "warning", message => "scan_hardware_alert_0024", message_variables => $variables, set_by => $agent}); + } + + my $query = " +UPDATE + scan_hardware_ram_modules +SET + scan_hardware_ram_module_locator = ".$anvil->Database->quote($hardware_ram_module_locator).", + scan_hardware_ram_module_size = ".$anvil->Database->quote($new_scan_hardware_ram_module_size).", + scan_hardware_ram_module_manufacturer = ".$anvil->Database->quote($new_scan_hardware_ram_module_manufacturer).", + scan_hardware_ram_module_model = ".$anvil->Database->quote($new_scan_hardware_ram_module_model).", + scan_hardware_ram_module_serial_number = ".$anvil->Database->quote($new_scan_hardware_ram_module_serial_number).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_hardware_ram_module_uuid = ".$anvil->Database->quote($scan_hardware_ram_module_uuid)." +;"; + # Now record the query in the array + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + } + } + else + { + # Send an alert telling the user that we've found a new module. + my $variables = { + locator => $hardware_ram_module_locator, + size => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_module_size})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_module_size})." #!string!scan_hardware_unit_0001!#)", + manufacturer => $new_scan_hardware_ram_module_manufacturer, + model => $new_scan_hardware_ram_module_model, + serial_number => $new_scan_hardware_ram_module_serial_number, + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "scan_hardware_alert_0025", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_hardware_alert_0025", message_variables => $variables, set_by => $agent}); + + # INSERT + my $scan_hardware_ram_module_uuid = $anvil->Get->uuid(); + my $query = " +INSERT INTO + scan_hardware_ram_modules +( + scan_hardware_ram_module_host_uuid, + scan_hardware_ram_module_uuid, + scan_hardware_ram_module_locator, + scan_hardware_ram_module_size, + scan_hardware_ram_module_manufacturer, + scan_hardware_ram_module_model, + scan_hardware_ram_module_serial_number, + modified_date +) VALUES ( + ".$anvil->Database->quote($anvil->Get->host_uuid).", + ".$anvil->Database->quote($scan_hardware_ram_module_uuid).", + ".$anvil->Database->quote($hardware_ram_module_locator).", + ".$anvil->Database->quote($new_scan_hardware_ram_module_size).", + ".$anvil->Database->quote($new_scan_hardware_ram_module_manufacturer).", + ".$anvil->Database->quote($new_scan_hardware_ram_module_model).", + ".$anvil->Database->quote($new_scan_hardware_ram_module_serial_number).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + # Now record the query in the array + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + } + } + + # Now look for left over modules we found in the database but not on the local system. + foreach my $hardware_ram_module_locator (keys %{$anvil->data->{sql}{scan_hardware_ram_module_uuid}}) + { + # Module vanished! + my $scan_hardware_ram_module_uuid = $anvil->data->{sql}{scan_hardware_ram_module_uuid}{$hardware_ram_module_locator}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::hardware_ram_module_uuid::${scan_hardware_ram_module_locator}" => $anvil->data->{sql}{scan_hardware_ram_module_uuid}{$hardware_ram_module_locator} + }}); + + my $old_scan_hardware_ram_module_size = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_size}; + my $old_scan_hardware_ram_module_manufacturer = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_manufacturer}; + my $old_scan_hardware_ram_module_model = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_model}; + my $old_scan_hardware_ram_module_serial_number = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_serial_number}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + old_scan_hardware_ram_module_size => $old_scan_hardware_ram_module_size, + old_scan_hardware_ram_module_manufacturer => $old_scan_hardware_ram_module_manufacturer, + old_scan_hardware_ram_module_model => $old_scan_hardware_ram_module_model, + old_scan_hardware_ram_module_serial_number => $old_scan_hardware_ram_module_serial_number, + }}); + + # Delete the entry so we know we've processed this existing module. + delete $anvil->data->{sql}{scan_hardware_ram_module_uuid}{$hardware_ram_module_locator}; + delete $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}; + + my $variables = { + locator => $hardware_ram_module_locator, + old_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_module_size})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_module_size})." #!string!scan_hardware_unit_0001!#)", + old_manufacturer => $old_scan_hardware_ram_module_manufacturer, + old_model => $old_scan_hardware_ram_module_model, + old_serial_number => $old_scan_hardware_ram_module_serial_number, + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "scan_hardware_alert_0026", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "warning", message => "scan_hardware_alert_0026", message_variables => $variables, set_by => $agent}); + + my $query = " +UPDATE + scan_hardware_ram_modules +SET + scan_hardware_ram_module_manufacturer = 'VANISHED', + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_hardware_ram_module_uuid = ".$anvil->Database->quote($scan_hardware_ram_module_uuid)." +;"; + # Now record the query in the array + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + } + + return(0); +} + +# Here we see if our peer has more or less RAM. If we have less, we'll mark our health as degraded. +sub process_health +{ + my ($anvil) = @_; + + # Only do this on nodes. + my $host_type = $anvil->Get->host_type(); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }}); + return(0) if $host_type ne "node"; + + # Find the host_uuid for the peer's host name and UUID. + $anvil->Cluster->get_peers({debug => 2}); + my $peer_is = $anvil->data->{sys}{anvil}{peer_is}; + my $peer_host_name = $anvil->data->{sys}{anvil}{$peer_is}{host_name}; + my $peer_host_uuid = $anvil->data->{sys}{anvil}{$peer_is}{host_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + peer_host_name => $peer_host_name, + peer_host_uuid => $peer_host_uuid, + }}); + + # How much RAM is on the other node? + my $query = "SELECT scan_hardware_ram_total FROM scan_hardware WHERE scan_hardware_host_uuid = ".$anvil->Database->quote($peer_host_uuid).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + + my $peer_ram_total = $anvil->Database->query({level => 2, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + $peer_ram_total = 0 if not defined $peer_ram_total; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_ram_total => $peer_ram_total }}); + + # Do we know the peer's RAM? + my $clear_alerts = 1; + if ($peer_ram_total) + { + # We don't want to freak out unless the difference is at least 1GiB + my $hardware_ram_total = $an->data->{summary}{ram}{size}; + my $difference = $peer_ram_total - $hardware_ram_total; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + hardware_ram_total => $an->Readable->comma($hardware_ram_total)." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $hardware_ram_total}).")", + difference => $an->Readable->comma($difference)." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $difference}).")", + }}); + + # greater than 1 GiB (default) or less than 128 MiB (default) + if (($peer_ram_total > $hardware_ram_total) && ($difference > $anvil->data->{scancore}{'scan-hardware'}{ram}{high_threshold})) + { + # Mark us as having a fairly major health issue if this has been the case for more + # than five minutes. + my $age = $anvil->Database->check_condition_age({ + debug => 2, + name => "scan-hardware::less_ram_than_peer", + host_uuid => $anvil->Get->host_uuid, + }); + + if ($age > 300) { - my $host_name = $anvil->Database->get_host_from_uuid({short => 1, host_uuid => $uuid}); - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "message_0183", variables => { - agent_name => $THIS_FILE, - host_name => $host_name, + my $changed = $anvil->Alert->check_alert_sent({ + debug => 2, + record_locator => "scan_hardware::less_ram_than_peer", + set_by => $THIS_FILE, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Register an alert. + my $variables = { + local_ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $hardware_ram_total})." (".$an->Readable->comma($hardware_ram_total)." #!string!suffix_0009!#)", + peer_ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $peer_ram_total})." (".$an->Readable->comma($peer_ram_total)." #!string!suffix_0009!#)", + difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$an->Readable->comma($difference)." #!string!suffix_0009!#)", + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0027", variables => $variables}); + $anvil->Alert->register({alert_level => "warning", message => "scan_hardware_alert_0027", message_variables => $variables, set_by => $THIS_FILE}); + } + + + } + $clear_alerts = 0; + + my ($health_uuid) = $anvil->Database->insert_or_update_health({ + debug => 2, + health_agent_name => $THIS_FILE, + health_source_name => "less_ram_than_peer", + health_source_weight => $anvil->data->{scancore}{'scan-hardware'}{score}{less_ram}, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { health_uuid => $health_uuid }}); + } + elsif (($peer_ram_total == $hardware_ram_total) or ($difference < $anvil->data->{scancore}{'scan-hardware'}{ram}{clear_threshold})) + { + ### TODO: Left off here. +=cut + # If there was a difference before, clear it. + my $clear = 0; + my $set = $an->Alert->check_alert_sent({ + type => "clear", # This is not the alert level, 'warning' == trouble, 'clear' == ok now. + alert_sent_by => $THIS_FILE, + alert_record_locator => "na", + alert_name => "less_ram_than_peer", + modified_date => $an->data->{sys}{db_timestamp}, + }); + $an->Log->entry({log_level => 2, message_key => "an_variables_0001", message_variables => { + name1 => "set", value1 => $set, + }, file => $THIS_FILE, line => __LINE__}); + if (not $set) + { + # There's a bug where the + $query = "SELECT health_uuid FROM health WHERE health_host_uuid = ".$anvil->Database->quote($an->data->{sys}{host_uuid})." AND health_source_name = 'less_ram_than_peer';"; + $an->Log->entry({log_level => 2, message_key => "an_variables_0001", message_variables => { + name1 => "query", value1 => $query, + }, file => $THIS_FILE, line => __LINE__}); + my $health_uuid = $an->DB->do_db_query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + $health_uuid = "" if not defined $health_uuid; + $an->Log->entry({log_level => 2, message_key => "an_variables_0001", message_variables => { + name1 => "health_uuid", value1 => $health_uuid, + }, file => $THIS_FILE, line => __LINE__}); + + if ($health_uuid) + { + # Clear it. + $clear = 1; + $an->Log->entry({log_level => 2, message_key => "an_variables_0001", message_variables => { + name1 => "clear", value1 => $clear, + }, file => $THIS_FILE, line => __LINE__}); + } + } + if ($set) + { + # Clear the warning + $an->Alert->register_alert({ + alert_level => "clear", + alert_agent_name => $THIS_FILE, + alert_sort => $an->data->{sys}{alert_sort}++, + alert_title_key => "an_alert_title_0004", + alert_message_key => "scan_hardware_note_0010", + alert_message_variables => { + difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$an->Readable->comma($difference)." #!string!suffix_0009!#)", + local_ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $hardware_ram_total})." (".$an->Readable->comma($hardware_ram_total)." #!string!suffix_0009!#)", + peer_ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $peer_ram_total})." (".$an->Readable->comma($peer_ram_total)." #!string!suffix_0009!#)", + }, + }); + + $clear = 1;; + $an->Log->entry({log_level => 2, message_key => "an_variables_0001", message_variables => { + name1 => "clear", value1 => $clear, + }, file => $THIS_FILE, line => __LINE__}); + } + + $an->Log->entry({log_level => 2, message_key => "an_variables_0001", message_variables => { + name1 => "clear", value1 => $clear, + }, file => $THIS_FILE, line => __LINE__}); + if ($clear) + { + # Get the health_uuid + my $query = "SELECT health_uuid FROM health WHERE health_host_uuid = ".$anvil->Database->quote($an->data->{sys}{host_uuid})." AND health_source_name = 'less_ram_than_peer';"; + $an->Log->entry({log_level => 2, message_key => "an_variables_0001", message_variables => { + name1 => "query", value1 => $query, + }, file => $THIS_FILE, line => __LINE__}); + my $health_uuid = $an->DB->do_db_query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + $health_uuid = "" if not defined $health_uuid; + $an->Log->entry({log_level => 2, message_key => "an_variables_0001", message_variables => { + name1 => "health_uuid", value1 => $health_uuid, + }, file => $THIS_FILE, line => __LINE__}); + + # Mark it as deleted for the history schema. + if ($health_uuid) + { + my $query = " +UPDATE + health +SET + health_source_name = 'DELETED', + health_source_weight = '0', + modified_date = ".$anvil->Database->quote($an->data->{sys}{db_timestamp})." +WHERE + health_uuid = ".$anvil->Database->quote($health_uuid)." +;"; + $an->Log->entry({log_level => 1, message_key => "an_variables_0001", message_variables => { + name1 => "query", value1 => $query, + }, file => $THIS_FILE, line => __LINE__}); + $an->DB->do_db_write({query => $query, source => $THIS_FILE, line => __LINE__}); - }}); + # Now actually delete it. + $query = " +DELETE FROM + health +WHERE + health_uuid = ".$anvil->Database->quote($health_uuid)." +;"; + $an->Log->entry({log_level => 1, message_key => "an_variables_0001", message_variables => { + name1 => "query", value1 => $query, + }, file => $THIS_FILE, line => __LINE__}); + $an->DB->do_db_write({query => $query, source => $THIS_FILE, line => __LINE__}); + } } +=cut } } + + return(0); +} + +# This reads in the last scan's data. +sub read_last_scan +{ + my ($anvil) = @_; + + + # This calls up the entry for this host. There will only be one. + my $query = " +SELECT + scan_hardware_uuid, + scan_hardware_cpu_model, + scan_hardware_cpu_cores, + scan_hardware_cpu_threads, + scan_hardware_cpu_bugs, + scan_hardware_cpu_flags, + scan_hardware_ram_total, + scan_hardware_memory_total, + scan_hardware_memory_free, + scan_hardware_swap_total, + scan_hardware_swap_free, + scan_hardware_led_id, + scan_hardware_led_css, + scan_hardware_led_error, + floor(extract(epoch from modified_date)) +FROM + scan_hardware +WHERE + scan_hardware_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." +;"; + $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 => 2, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + # We've got an entry in the 'scan_hardware' table, so now we'll look for data in the node and + # services tables. + my $scan_hardware_uuid = $row->[0]; + my $scan_hardware_cpu_model = $row->[1]; + my $scan_hardware_cpu_cores = $row->[2]; + my $scan_hardware_cpu_threads = $row->[3]; + my $scan_hardware_cpu_bugs = $row->[4]; + my $scan_hardware_cpu_flags = $row->[5]; + my $scan_hardware_ram_total = $row->[6]; + my $scan_hardware_memory_total = $row->[7]; + my $scan_hardware_memory_free = $row->[8]; + my $scan_hardware_swap_total = $row->[9]; + my $scan_hardware_swap_free = $row->[10]; + my $scan_hardware_led_id = $row->[11]; + my $scan_hardware_led_css = $row->[12]; + my $scan_hardware_led_error = $row->[13]; + my $scan_hardware_modified_date = $row->[14]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_hardware_uuid" => $scan_hardware_uuid, + "scan_hardware_cpu_model" => $scan_hardware_cpu_model, + "scan_hardware_cpu_cores" => $scan_hardware_cpu_cores, + "scan_hardware_cpu_threads" => $scan_hardware_cpu_threads, + "scan_hardware_cpu_bugs" => $scan_hardware_cpu_bugs, + "scan_hardware_cpu_flags" => $scan_hardware_cpu_flags, + "scan_hardware_ram_total" => $scan_hardware_ram_total, + "scan_hardware_memory_total" => $scan_hardware_memory_total, + "scan_hardware_memory_free" => $scan_hardware_memory_free, + "scan_hardware_swap_total" => $scan_hardware_swap_total, + "scan_hardware_swap_free" => $scan_hardware_swap_free, + "scan_hardware_led_id" => $scan_hardware_led_id, + "scan_hardware_led_css" => $scan_hardware_led_css, + "scan_hardware_led_error" => $scan_hardware_led_error, + "scan_hardware_modified_date" => $scan_hardware_modified_date, + }}); + + # Record the hardware_uuid in an easy to find place for later when looking for changes. + $anvil->data->{sql}{scan_hardware_uuid} = $scan_hardware_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sql::scan_hardware_uuid" => $anvil->data->{sql}{scan_hardware_uuid} }}); + + # Store the old data now. + $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid} = { + scan_hardware_cpu_model => $scan_hardware_cpu_model, + scan_hardware_cpu_cores => $scan_hardware_cpu_cores, + scan_hardware_cpu_threads => $scan_hardware_cpu_threads, + scan_hardware_cpu_bugs => $scan_hardware_cpu_bugs, + scan_hardware_cpu_flags => $scan_hardware_cpu_flags, + scan_hardware_ram_total => $scan_hardware_ram_total, + scan_hardware_memory_total => $scan_hardware_memory_total, + scan_hardware_memory_free => $scan_hardware_memory_free, + scan_hardware_swap_total => $scan_hardware_swap_total, + scan_hardware_swap_free => $scan_hardware_swap_free, + scan_hardware_led_id => $scan_hardware_led_id, + scan_hardware_led_css => $scan_hardware_led_css, + scan_hardware_led_error => $scan_hardware_led_error, + scan_hardware_modified_date => $scan_hardware_modified_date, + }; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_cpu_model" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_model}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_cpu_cores" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_cores}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_cpu_threads" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_threads}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_cpu_bugs" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_bugs}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_cpu_flags" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_flags}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_ram_total" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_ram_total}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_memory_total" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_memory_total}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_memory_free" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_memory_free}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_swap_total" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_swap_total}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_swap_free" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_swap_free}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_led_id" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_id}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_led_css" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_css}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_led_error" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_error}, + "sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_modified_date" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_modified_date}, + }}); + } + undef $count; + undef $results; + + # Read in the RAM module data. + $query = " +SELECT + scan_hardware_ram_module_uuid, + scan_hardware_ram_module_locator, + scan_hardware_ram_module_size, + scan_hardware_ram_module_manufacturer, + scan_hardware_ram_module_model, + scan_hardware_ram_module_serial_number +FROM + scan_hardware_ram_modules +WHERE + scan_hardware_ram_module_host_uuid = ".$anvil->Database->quote($anvil->Get->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 => 2, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + # We've got an entry in the 'scan_hardware_ram_modules' table, so now we'll look for data in the node and + # services tables. + my $scan_hardware_ram_module_uuid = $row->[0]; + my $scan_hardware_ram_module_locator = $row->[1]; + my $scan_hardware_ram_module_size = $row->[2]; + my $scan_hardware_ram_module_manufacturer = $row->[3]; + my $scan_hardware_ram_module_model = $row->[4]; + my $scan_hardware_ram_module_serial_number = $row->[5]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_hardware_ram_module_uuid" => $scan_hardware_ram_module_uuid, + "scan_hardware_ram_module_locator" => $scan_hardware_ram_module_locator, + "scan_hardware_ram_module_size" => $scan_hardware_ram_module_size, + "scan_hardware_ram_module_manufacturer" => $scan_hardware_ram_module_manufacturer, + "scan_hardware_ram_module_model" => $scan_hardware_ram_module_model, + "scan_hardware_ram_module_serial_number" => $scan_hardware_ram_module_serial_number, + }}); + + # Record the scan_hardware_ram_module_uuid in an easy to find place for later when looking for changes. + $anvil->data->{sql}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_locator} = $scan_hardware_ram_module_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_hardware_ram_module_uuid::${scan_hardware_ram_module_locator}" => $anvil->data->{sql}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_locator}, + }}); + + # Store the old data now. + $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid} = { + scan_hardware_ram_module_locator => $scan_hardware_ram_module_locator, + scan_hardware_ram_module_size => $scan_hardware_ram_module_size, + scan_hardware_ram_module_manufacturer => $scan_hardware_ram_module_manufacturer, + scan_hardware_ram_module_model => $scan_hardware_ram_module_model, + scan_hardware_ram_module_serial_number => $scan_hardware_ram_module_serial_number, + }; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_hardware_ram_module::scan_hardware_ram_module_uuid::${scan_hardware_ram_module_uuid}::scan_hardware_ram_module_locator" => $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_locator}, + "sql::scan_hardware_ram_module::scan_hardware_ram_module_uuid::${scan_hardware_ram_module_uuid}::scan_hardware_ram_module_size" => $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_size}, + "sql::scan_hardware_ram_module::scan_hardware_ram_module_uuid::${scan_hardware_ram_module_uuid}::scan_hardware_ram_module_manufacturer" => $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_manufacturer}, + "sql::scan_hardware_ram_module::scan_hardware_ram_module_uuid::${scan_hardware_ram_module_uuid}::scan_hardware_ram_module_model" => $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_model}, + "sql::scan_hardware_ram_module::scan_hardware_ram_module_uuid::${scan_hardware_ram_module_uuid}::scan_hardware_ram_module_serial_number" => $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_serial_number}, + }}); + } return(0); } diff --git a/scancore-agents/scan-hardware/scan-hardware.sql b/scancore-agents/scan-hardware/scan-hardware.sql index 4e4c3c7c..2c7b052b 100644 --- a/scancore-agents/scan-hardware/scan-hardware.sql +++ b/scancore-agents/scan-hardware/scan-hardware.sql @@ -1,155 +1,156 @@ --- This is the database schema for the 'hardware Scan Agent'. +-- This is the database schema for the 'scan-hardware Scan Agent'. -CREATE TABLE hardware ( - hardware_uuid uuid primary key, - hardware_host_uuid uuid not null, - hardware_cpu_model text not null, - hardware_cpu_cores numeric not null, -- We don't care about individual sockets / chips - hardware_cpu_threads numeric not null, - hardware_cpu_bugs text not null, - hardware_cpu_flags text not null, -- - hardware_ram_total numeric not null, -- This is the sum of the hardware memory module capacity - hardware_memory_total numeric not null, -- This is the amount seen by the OS, minus shared memory, like that allocated to video - hardware_memory_free numeric not null, -- - hardware_swap_total numeric not null, -- - hardware_swap_free numeric not null, -- - hardware_led_id text not null, -- - hardware_led_css text not null, -- - hardware_led_error text not null, -- - modified_date timestamp with time zone not null, +CREATE TABLE scan_hardware ( + scan_hardware_uuid uuid primary key, + scan_hardware_host_uuid uuid not null, + scan_hardware_cpu_model text not null, + scan_hardware_cpu_cores numeric not null, -- We don't care about individual sockets / chips + scan_hardware_cpu_threads numeric not null, + scan_hardware_cpu_bugs text not null, + scan_hardware_cpu_flags text not null, -- + scan_hardware_ram_total numeric not null, -- This is the sum of the hardware memory module capacity + scan_hardware_memory_total numeric not null, -- This is the amount seen by the OS, minus shared memory, like that allocated to video + scan_hardware_memory_free numeric not null, -- + scan_hardware_swap_total numeric not null, -- + scan_hardware_swap_free numeric not null, -- + scan_hardware_led_id text not null, -- + scan_hardware_led_css text not null, -- + scan_hardware_led_error text not null, -- + modified_date timestamp with time zone not null, - FOREIGN KEY(hardware_host_uuid) REFERENCES hosts(host_uuid) + FOREIGN KEY(scan_hardware_host_uuid) REFERENCES hosts(host_uuid) ); -ALTER TABLE hardware OWNER TO admin; +ALTER TABLE scan_hardware OWNER TO admin; -CREATE TABLE history.hardware ( - history_id bigserial, - hardware_uuid uuid, - hardware_host_uuid uuid, - hardware_cpu_model text, - hardware_cpu_cores numeric, - hardware_cpu_threads numeric, - hardware_cpu_bugs text, - hardware_cpu_flags text, - hardware_ram_total numeric, - hardware_memory_total numeric, - hardware_memory_free numeric, - hardware_swap_total numeric, - hardware_swap_free numeric, - hardware_led_id text, - hardware_led_css text, - hardware_led_error text, - modified_date timestamp with time zone not null +CREATE TABLE history.scan_hardware ( + history_id bigserial, + scan_hardware_uuid uuid, + scan_hardware_host_uuid uuid, + scan_hardware_cpu_model text, + scan_hardware_cpu_cores numeric, + scan_hardware_cpu_threads numeric, + scan_hardware_cpu_bugs text, + scan_hardware_cpu_flags text, + scan_hardware_ram_total numeric, + scan_hardware_memory_total numeric, + scan_hardware_memory_free numeric, + scan_hardware_swap_total numeric, + scan_hardware_swap_free numeric, + scan_hardware_led_id text, + scan_hardware_led_css text, + scan_hardware_led_error text, + modified_date timestamp with time zone not null ); -ALTER TABLE history.hardware OWNER TO admin; +ALTER TABLE history.scan_hardware OWNER TO admin; -CREATE FUNCTION history_hardware() RETURNS trigger +CREATE FUNCTION history_scan_hardware() RETURNS trigger AS $$ DECLARE - history_hardware RECORD; + history_scan_hardware RECORD; BEGIN - SELECT INTO history_hardware * FROM hardware WHERE hardware_uuid=new.hardware_uuid; - INSERT INTO history.hardware - (hardware_uuid, - hardware_host_uuid, - hardware_cpu_model, - hardware_cpu_cores, - hardware_cpu_threads, - hardware_cpu_bugs, - hardware_cpu_flags, - hardware_ram_total, - hardware_memory_total, - hardware_memory_free, - hardware_swap_total, - hardware_swap_free, - hardware_led_id, - hardware_led_css, - hardware_led_error, + SELECT INTO history_scan_hardware * FROM scan_hardware WHERE scan_hardware_uuid=new.scan_hardware_uuid; + INSERT INTO history.scan_hardware + (scan_hardware_uuid, + scan_hardware_host_uuid, + scan_hardware_cpu_model, + scan_hardware_cpu_cores, + scan_hardware_cpu_threads, + scan_hardware_cpu_bugs, + scan_hardware_cpu_flags, + scan_hardware_ram_total, + scan_hardware_memory_total, + scan_hardware_memory_free, + scan_hardware_swap_total, + scan_hardware_swap_free, + scan_hardware_led_id, + scan_hardware_led_css, + scan_hardware_led_error, modified_date) VALUES - (history_hardware.hardware_uuid, - history_hardware.hardware_host_uuid, - history_hardware.hardware_cpu_model, - history_hardware.hardware_cpu_cores, - history_hardware.hardware_cpu_threads, - history_hardware.hardware_cpu_bugs, - history_hardware.hardware_cpu_flags, - history_hardware.hardware_ram_total, - history_hardware.hardware_memory_total, - history_hardware.hardware_memory_free, - history_hardware.hardware_swap_total, - history_hardware.hardware_swap_free, - history_hardware.hardware_led_id, - history_hardware.hardware_led_css, - history_hardware.hardware_led_error, - history_hardware.modified_date); + (history_scan_hardware.scan_hardware_uuid, + history_scan_hardware.scan_hardware_host_uuid, + history_scan_hardware.scan_hardware_cpu_model, + history_scan_hardware.scan_hardware_cpu_cores, + history_scan_hardware.scan_hardware_cpu_threads, + history_scan_hardware.scan_hardware_cpu_bugs, + history_scan_hardware.scan_hardware_cpu_flags, + history_scan_hardware.scan_hardware_ram_total, + history_scan_hardware.scan_hardware_memory_total, + history_scan_hardware.scan_hardware_memory_free, + history_scan_hardware.scan_hardware_swap_total, + history_scan_hardware.scan_hardware_swap_free, + history_scan_hardware.scan_hardware_led_id, + history_scan_hardware.scan_hardware_led_css, + history_scan_hardware.scan_hardware_led_error, + history_scan_hardware.modified_date); RETURN NULL; END; $$ LANGUAGE plpgsql; -ALTER FUNCTION history_hardware() OWNER TO admin; +ALTER FUNCTION history_scan_hardware() OWNER TO admin; -CREATE TRIGGER trigger_hardware - AFTER INSERT OR UPDATE ON hardware - FOR EACH ROW EXECUTE PROCEDURE history_hardware(); +CREATE TRIGGER trigger_scan_hardware + AFTER INSERT OR UPDATE ON scan_hardware + FOR EACH ROW EXECUTE PROCEDURE history_scan_hardware(); -CREATE TABLE hardware_ram_modules ( - hardware_ram_module_uuid uuid primary key, - hardware_ram_module_host_uuid uuid not null, - hardware_ram_module_locator text not null, - hardware_ram_module_size numeric not null, - hardware_ram_module_manufacturer text not null, - hardware_ram_module_model text not null, - hardware_ram_module_serial_number text not null, - modified_date timestamp with time zone not null, + +CREATE TABLE scan_hardware_ram_modules ( + scan_hardware_ram_module_uuid uuid primary key, + scan_hardware_ram_module_host_uuid uuid not null, + scan_hardware_ram_module_locator text not null, + scan_hardware_ram_module_size numeric not null, + scan_hardware_ram_module_manufacturer text not null, + scan_hardware_ram_module_model text not null, + scan_hardware_ram_module_serial_number text not null, + modified_date timestamp with time zone not null, - FOREIGN KEY(hardware_ram_module_host_uuid) REFERENCES hosts(host_uuid) + FOREIGN KEY(scan_hardware_ram_module_host_uuid) REFERENCES hosts(host_uuid) ); -ALTER TABLE hardware_ram_modules OWNER TO admin; +ALTER TABLE scan_hardware_ram_modules OWNER TO admin; -CREATE TABLE history.hardware_ram_modules ( - history_id bigserial, - hardware_ram_module_uuid uuid, - hardware_ram_module_host_uuid uuid, - hardware_ram_module_locator text, - hardware_ram_module_size numeric, - hardware_ram_module_manufacturer text, - hardware_ram_module_model text, - hardware_ram_module_serial_number text, - modified_date timestamp with time zone not null +CREATE TABLE history.scan_hardware_ram_modules ( + history_id bigserial, + scan_hardware_ram_module_uuid uuid, + scan_hardware_ram_module_host_uuid uuid, + scan_hardware_ram_module_locator text, + scan_hardware_ram_module_size numeric, + scan_hardware_ram_module_manufacturer text, + scan_hardware_ram_module_model text, + scan_hardware_ram_module_serial_number text, + modified_date timestamp with time zone not null ); -ALTER TABLE history.hardware_ram_modules OWNER TO admin; +ALTER TABLE history.scan_hardware_ram_modules OWNER TO admin; -CREATE FUNCTION history_hardware_ram_modules() RETURNS trigger +CREATE FUNCTION history_scan_hardware_ram_modules() RETURNS trigger AS $$ DECLARE - history_hardware_ram_modules RECORD; + history_scan_hardware_ram_modules RECORD; BEGIN - SELECT INTO history_hardware_ram_modules * FROM hardware_ram_modules WHERE hardware_ram_module_uuid=new.hardware_ram_module_uuid; - INSERT INTO history.hardware_ram_modules - (hardware_ram_module_uuid, - hardware_ram_module_host_uuid, - hardware_ram_module_locator, - hardware_ram_module_size, - hardware_ram_module_manufacturer, - hardware_ram_module_model, - hardware_ram_module_serial_number, + SELECT INTO history_scan_hardware_ram_modules * FROM scan_hardware_ram_modules WHERE scan_hardware_ram_module_uuid=new.scan_hardware_ram_module_uuid; + INSERT INTO history.scan_hardware_ram_modules + (scan_hardware_ram_module_uuid, + scan_hardware_ram_module_host_uuid, + scan_hardware_ram_module_locator, + scan_hardware_ram_module_size, + scan_hardware_ram_module_manufacturer, + scan_hardware_ram_module_model, + scan_hardware_ram_module_serial_number, modified_date) VALUES - (history_hardware_ram_modules.hardware_ram_module_uuid, - history_hardware_ram_modules.hardware_ram_module_host_uuid, - history_hardware_ram_modules.hardware_ram_module_locator, - history_hardware_ram_modules.hardware_ram_module_size, - history_hardware_ram_modules.hardware_ram_module_manufacturer, - history_hardware_ram_modules.hardware_ram_module_model, - history_hardware_ram_modules.hardware_ram_module_serial_number, - history_hardware_ram_modules.modified_date); + (history_scan_hardware_ram_modules.scan_hardware_ram_module_uuid, + history_scan_hardware_ram_modules.scan_hardware_ram_module_host_uuid, + history_scan_hardware_ram_modules.scan_hardware_ram_module_locator, + history_scan_hardware_ram_modules.scan_hardware_ram_module_size, + history_scan_hardware_ram_modules.scan_hardware_ram_module_manufacturer, + history_scan_hardware_ram_modules.scan_hardware_ram_module_model, + history_scan_hardware_ram_modules.scan_hardware_ram_module_serial_number, + history_scan_hardware_ram_modules.modified_date); RETURN NULL; END; $$ LANGUAGE plpgsql; -ALTER FUNCTION history_hardware_ram_modules() OWNER TO admin; +ALTER FUNCTION history_scan_hardware_ram_modules() OWNER TO admin; -CREATE TRIGGER trigger_hardware_ram_modules - AFTER INSERT OR UPDATE ON hardware_ram_modules - FOR EACH ROW EXECUTE PROCEDURE history_hardware_ram_modules(); +CREATE TRIGGER trigger_scan_hardware_ram_modules + AFTER INSERT OR UPDATE ON scan_hardware_ram_modules + FOR EACH ROW EXECUTE PROCEDURE history_scan_hardware_ram_modules(); diff --git a/scancore-agents/scan-hardware/scan-hardware.xml b/scancore-agents/scan-hardware/scan-hardware.xml index 4a5544ee..f7db19a2 100644 --- a/scancore-agents/scan-hardware/scan-hardware.xml +++ b/scancore-agents/scan-hardware/scan-hardware.xml @@ -13,8 +13,149 @@ NOTE: All string keys MUST be prefixed with the agent name! ie: 'scan_hardware_l + + +For some reason, two (or more) CPU cores/threads returned different flags. This should never happen. The differences are: +The differences are: +==== +#!variable!flags!# +==== +#!variable!these_flags!# +==== + + The issue with mismatched CPU flags has been resolved. + +For some reason, two (or more) CPU cores/threads returned different bugs. This should never happen: The differences are: +The differences are: +==== +#!variable!bugs!# +==== +#!variable!these_bugs!# +==== + + The issue with mismatched CPU bugs has been resolved. + +For some reason, two (or more) CPU cores/threads returned different model names. This should never happen: The differences are: +The differences are: +==== +#!variable!model!# +==== +#!variable!this_model!# +==== + + The issue with mismatched CPU model name has been resolved. + The CPU model has changed: +- New: [#!variable!new!#] +- Old: [#!variable!old!#] + + The CPU bugs list has changed: +- New: [#!variable!new!#] +- Old: [#!variable!old!#] + + The CPU flags (register list) list has changed: +- New: [#!variable!new!#] +- Old: [#!variable!old!#] + + The number of CPU cores has changed. Was a new CPU installed? +- New: [#!variable!new!# core(s)] +- Old: [#!variable!old!# core(s)] + + The number of CPU threads has changed. Was a new CPU installed? +- New: [#!variable!new!# thread(s)] +- Old: [#!variable!old!# thread(s)] + + The amount of RAM (as reported by dmidecode) on the system has changed. If there was a hardware upgrade, then this is safe to ignore. If it was unexpected, a RAM module may have failed. +- New: [#!variable!new!#] +- Old: [#!variable!old!#] + + The amount of memory (as reported by /proc/meminfo) on the system has changed. If there was a hardware upgrade, then this is safe to ignore. If it was unexpected, a RAM module may have failed. +- New: [#!variable!new!#] +- Old: [#!variable!old!#] + + The amount of memory (as reported by /proc/meminfo) on the system has changed. If there was a hardware upgrade, then this is safe to ignore. If it was unexpected, a RAM module may have failed. +- New: [#!variable!new!#] +- Old: [#!variable!old!#] + + The ID LED (identification light) state has changed; +- New: [#!variable!new!#] +- Old: [#!variable!old!#] + + The Error (CSS) LED state has changed; +- New: [#!variable!new!#] +- Old: [#!variable!old!#] + + The Error (Hardware) LED state has changed; +- New: [#!variable!new!#] +- Old: [#!variable!old!#] + + The amount of available memory (as reported by /proc/meminfo) has changed (this is common and expected); +- New: [#!variable!new!#] +- Old: [#!variable!old!#] + + The amount of available swap space (as reported by /proc/meminfo) has changed (this is common and expected); +- New: [#!variable!new!#] +- Old: [#!variable!old!#] + + The amount of swap is high! The swap is now: [#!variable!say_used!#] of: [#!variable!say_swap!#] (#!variable!swap_percent!#% used). + The amount is back down to a low amount used. The swap is now: [#!variable!say_used!#] of: [#!variable!say_swap!#] (#!variable!swap_percent!#% used). + The CPU, RAM and LED (if possible) has been recorded; +- CPU Model: ... [#!variable!cpu_model!#] +- Total Cores: . [#!variable!total_cores!#] +- Total Threads: [#!variable!total_threads!#] +- CPU Bugs: .... [#!variable!cpu_bugs!#] +- CPU Flags: ... [#!variable!cpu_flags!#] +- ID LED state: [#!variable!id_led!#] +- CSS LED: ..... [#!variable!css_led!#] (CSS = Customer Self-Service) +- Error LED: ... [#!variable!error_led!#] +- Total RAM: ... [#!variable!ram_total_size!#] +- Usable RAM: .. [#!variable!ram_memory_total!#] (Available to the OS) +- Free Memory: . [#!variable!ram_memory_free!#] +- Total swap: .. [#!variable!ram_swap_total!#] +- Free swap: ... [#!variable!ram_swap_free!#] + + The RAM module [#!variable!locator!#] has returned! +- Size: ........ [#!variable!old_size!#] +- Manufacturer: [#!variable!old_manufacturer!#] +- Model: ....... [#!variable!old_model!#] +- Serial Number: [#!variable!old_serial_number!#] + + Something about the RAM module [#!variable!locator!#] has changed. +This shouldn't normally happen. Was the RAM module replaced? +- Size: ........ [#!variable!old_size!#] -> [#!variable!new_size!#] +- Manufacturer: [#!variable!old_manufacturer!#] -> [#!variable!new_manufacturer!#] +- Model: ....... [#!variable!old_model!#] -> [#!variable!new_model!#] +- Serial Number: [#!variable!old_serial_number!#] -> [#!variable!new_serial_number!#] + + A new RAM memory module has been found; +- Locator: ..... [#!variable!locator!#] +- Size: ........ [#!variable!size!#] +- Manufacturer: [#!variable!manufacturer!#] +- Model: ....... [#!variable!model!#] +- Serial Number: [#!variable!serial_number!#] + + The RAM module [#!variable!locator!#] has vanished! +Was the module intentionally removed? If not, it may have failed. +- Size: ........ [#!variable!old_size!#] +- Manufacturer: [#!variable!old_manufacturer!#] +- Model: ....... [#!variable!old_model!#] +- Serial Number: [#!variable!old_serial_number!#] + + This node has: [#!variable!difference!#] less RAM than the peer node. +If the RAM is being updated, this alert will clear once this node has been upgraded to have the same amount of RAM. If a memory module has failed, this warning will clear when the module has been replaced. +- Local RAM: [#!variable!local_ram!#] +- Peer's RAM: [#!variable!peer_ram!#] + + Starting: [#!variable!program!#]. + + Unknown + Lit + Off + + + bytes + diff --git a/share/anvil.sql b/share/anvil.sql index 5023135a..38e3471c 100644 --- a/share/anvil.sql +++ b/share/anvil.sql @@ -1625,6 +1625,192 @@ CREATE TRIGGER trigger_upses FOR EACH ROW EXECUTE PROCEDURE history_upses(); +-- This is used to indicate the power state of UPSes. It is used to determine when the system needs to be +-- powered off. All UPS-type scan agents must use this table. The linkage between this and the 'upses' table +-- will be sorted out automatically based on the scan agent used and the UPS host name / IP address. +CREATE TABLE power ( + power_uuid uuid primary key, + power_ups_uuid uuid not null, -- This is the 'upses' -> 'ups_uuid' of the UPS. This is used to map what UPSes are powering a given node. + power_on_battery boolean not null, -- TRUE == use "time_remaining" to determine if graceful power off is needed. FALSE == power loss NOT imminent, do not power off node. + power_seconds_left numeric, -- Should always be set, but not required *EXCEPT* when 'power_on_battery' is TRUE. + power_charge_percentage numeric, -- Percentage charge in the UPS. Used to determine when the dashboard should boot the node after AC restore + modified_date timestamp with time zone not null, + + FOREIGN KEY(power_ups_uuid) REFERENCES upses(ups_uuid) +); +ALTER TABLE power OWNER TO admin; + +CREATE TABLE history.power ( + history_id bigserial, + power_uuid uuid, + power_ups_uuid uuid, + power_on_battery boolean, + power_seconds_left numeric, + power_charge_percentage numeric, + modified_date timestamp with time zone not null +); +ALTER TABLE history.power OWNER TO admin; + +CREATE FUNCTION history_power() RETURNS trigger +AS $$ +DECLARE + history_power RECORD; +BEGIN + SELECT INTO history_power * FROM power WHERE power_uuid = new.power_uuid; + INSERT INTO history.power + (power_uuid, + power_ups_uuid, + power_on_battery, + power_seconds_left, + power_charge_percentage, + modified_date) + VALUES + (history_power.power_uuid, + history_power.power_ups_uuid, + history_power.power_on_battery, + history_power.power_seconds_left, + history_power.power_charge_percentage, + history_power.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_power() OWNER TO admin; + +CREATE TRIGGER trigger_power + AFTER INSERT OR UPDATE ON power + FOR EACH ROW EXECUTE PROCEDURE history_power(); + + +-- This stores weighted health of nodes. Agents can set one or more health values. After a scan sweep +-- completes, ScanCore will sum these weights and the node with the *highest* value is considered the +-- *least* healthy and any servers on it will be migrated to the peer. +CREATE TABLE health ( + health_uuid uuid primary key, + health_host_uuid uuid not null, -- The name of the node or dashboard that this health came from. + health_agent_name text not null, -- This is the scan agent (or program name) setting this score. + health_source_name text not null, -- This is the name of the problem, as set by the agent. + health_source_weight numeric not null, -- This is the numerical weight of this alert. The higher this value, the more severe the health issue is + modified_date timestamp with time zone not null, + + FOREIGN KEY(health_host_uuid) REFERENCES hosts(host_uuid) +); +ALTER TABLE health OWNER TO admin; + +CREATE TABLE history.health ( + history_id bigserial, + health_uuid uuid not null, + health_host_uuid uuid not null, + health_agent_name text not null, + health_source_name text not null, + health_source_weight numeric not null, + modified_date timestamp with time zone not null +); +ALTER TABLE history.health OWNER TO admin; + +CREATE FUNCTION history_health() RETURNS trigger +AS $$ +DECLARE + history_health RECORD; +BEGIN + SELECT INTO history_health * FROM health WHERE health_uuid = new.health_uuid; + INSERT INTO history.health + (health_uuid, + health_host_uuid, + health_agent_name, + health_source_name, + health_source_weight, + modified_date) + VALUES + (history_health.health_uuid, + history_health.health_host_uuid, + history_health.health_agent_name, + history_health.health_source_name, + history_health.health_source_weight, + history_health.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_health() OWNER TO admin; + +CREATE TRIGGER trigger_health + AFTER INSERT OR UPDATE ON health + FOR EACH ROW EXECUTE PROCEDURE history_health(); + + +-- This stores temperature information for a given host. ScanCore checks this data to decice if action needs +-- to be taken during a thermal event. On nodes, this is used to decide if a node should be shed or if an +-- Anvil! needs to be stopped entirely. On dashboards, this is used to check if/when it is safe to restart a +-- node that shut down because of a thermal event. +CREATE TABLE temperature ( + temperature_uuid uuid primary key, + temperature_host_uuid uuid not null, -- The name of the node or dashboard that this temperature came from. + temperature_agent_name text not null, -- This is the name of the agent that set the alert + temperature_sensor_host text not null, -- This is the host (uuid) that the sensor was read from. This is important as ScanCore on a striker will read available thermal data from a node using it's IPMI data. + temperature_sensor_name text not null, -- This is the name of the sensor reporting the temperature + temperature_value_c numeric not null, -- This is the actual temperature, in celcius of course. + temperature_weight numeric not null, -- This is the weight of the sensor value. This is the value added to the sum when testing against 'scancore::threshold::warning_temperature' and 'scancore::threshold::warning_critical'. + temperature_state text not null, -- This is a string represnting the state of the sensor. Valid values are 'ok', 'warning', and 'critical' + temperature_is text not null, -- This indicate if the temperature 'nominal', 'high' or 'low'. + modified_date timestamp with time zone not null, + + FOREIGN KEY(temperature_host_uuid) REFERENCES hosts(host_uuid) +); +ALTER TABLE temperature OWNER TO admin; + +CREATE TABLE history.temperature ( + history_id bigserial, + temperature_uuid uuid, + temperature_host_uuid uuid, + temperature_agent_name text, + temperature_sensor_host text, + temperature_sensor_name text, + temperature_value_c numeric, + temperature_weight numeric, + temperature_state text, + temperature_is text, + modified_date timestamp with time zone not null +); +ALTER TABLE history.temperature OWNER TO admin; + +CREATE FUNCTION history_temperature() RETURNS trigger +AS $$ +DECLARE + history_temperature RECORD; +BEGIN + SELECT INTO history_temperature * FROM temperature WHERE temperature_uuid = new.temperature_uuid; + INSERT INTO history.temperature + (temperature_uuid, + temperature_host_uuid, + temperature_agent_name, + temperature_sensor_host, + temperature_sensor_name, + temperature_value_c, + temperature_state, + temperature_is, + modified_date) + VALUES + (history_temperature.temperature_uuid, + history_temperature.temperature_host_uuid, + history_temperature.temperature_agent_name, + history_temperature.temperature_sensor_host, + history_temperature.temperature_sensor_name, + history_temperature.temperature_value_c, + history_temperature.temperature_state, + history_temperature.temperature_is, + history_temperature.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_temperature() OWNER TO admin; + +CREATE TRIGGER trigger_temperature + AFTER INSERT OR UPDATE ON temperature + FOR EACH ROW EXECUTE PROCEDURE history_temperature(); + + -- ------------------------------------------------------------------------------------------------------- -- -- These are special tables with no history or tracking UUIDs that simply record transient information. -- -- ------------------------------------------------------------------------------------------------------- -- diff --git a/share/words.xml b/share/words.xml index 4b1d8f13..478950cb 100644 --- a/share/words.xml +++ b/share/words.xml @@ -1031,6 +1031,8 @@ The file: [#!variable!file!#] needs to be updated. The difference is: I was asked to process alerts, but there are no configured email servers. No sense proceeding. The table: [#!variable!table!#] already exists in the database on the host: [#!variable!host!#], no need to load the schema. The table: [#!variable!table!#] does NOT exists in the database on the host: [#!variable!host!#]. Will load the schema file: [#!variable!file!#] now. + The passed in 'temperature_state' value: [#!variable!temperature_state!#] is invalid. The value must be 'ok', 'warning' or 'critical'. + The passed in 'temperature_is' value: [#!variable!temperature_is!#] is invalid. The value must be 'nominal', 'warning' or 'critical'. The host name: [#!variable!target!#] does not resolve to an IP address. @@ -1611,6 +1613,7 @@ If you are comfortable that the target has changed for a known reason, you can s Free-form description of this system. This tracks the last time a given mail server was configured for use. It allows for a round-robin switching of mail servers when one mail server stops working and two or more mail servers have been configured. No UPSes + This is a condition record, used by programs like scan agents to track how long a condition has existed for. #!variable!number!#/sec @@ -1726,7 +1729,7 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st Warning Notice Info - + Lit Up Down Mbps diff --git a/tools/scancore b/tools/scancore index 3b210218..9dd384d6 100755 --- a/tools/scancore +++ b/tools/scancore @@ -29,6 +29,13 @@ if (($running_directory =~ /^\./) && ($ENV{PWD})) my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1}); +$anvil->data->{scancore} = { + threshold => { + warning_temperature => 5, + warning_critical => 5, + }, +}; + $anvil->Storage->read_config(); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0115", variables => { program => $THIS_FILE }}); diff --git a/tools/test.pl b/tools/test.pl index 626b1b6a..fa06fc34 100755 --- a/tools/test.pl +++ b/tools/test.pl @@ -28,18 +28,3 @@ $anvil->Get->switches; print "Connecting to the database(s);\n"; $anvil->Database->connect({debug => 3}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, secure => 0, key => "log_0132"}); - -if (0) -{ - $anvil->Alert->register({ - debug => 2, - alert_level => "warning", - message => "message_0002", - set_by => $THIS_FILE, - }); -} -if (1) -{ - ### TODO: Left off here. Remove 'alert_title_X' keys and continue testing email body generation. - $anvil->Email->send_alerts({debug => 2}); -}