* Finished (initial) testing of scan-hardware. The first M3 scan agent is done!

* Updated Alert->check_alert_sent() and the 'alert_sent' table to remove 'alert_name' as it really isn't helpful given record_locator.
* Created 'Database->purge_data' that takes an array of tables and, in reverse, purges them from the database(s). This also disables archiving and resync functions now.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 4 years ago
parent 165117edfe
commit 4be943ebf3
  1. 19
      Anvil/Tools/Alert.pm
  2. 141
      Anvil/Tools/Database.pm
  3. 12
      Anvil/Tools/ScanCore.pm
  4. 1
      notes
  5. 188
      scancore-agents/scan-hardware/scan-hardware
  6. 6
      scancore-agents/scan-hardware/scan-hardware.xml
  7. 2
      share/anvil.sql
  8. 3
      share/words.xml
  9. 16
      tools/test.pl

@ -85,10 +85,6 @@ If there is a problem, C<< !!error!! >> is returned.
Parameters; Parameters;
=head3 name (required)
This is the name of the alert. So for an alert related to a critically high temperature, this might get set to C<< temperature_high_critical >>. It is meant to compliment the C<< record_locator >> parameter.
=head3 record_locator =head3 record_locator
This is a record locator, which generally allows a given alert to be tied to a given source. For example, an alert related to a temperature might use C<< an-a01n01.alteeve.com:cpu1_temperature >>. This is a record locator, which generally allows a given alert to be tied to a given source. For example, an alert related to a temperature might use C<< an-a01n01.alteeve.com:cpu1_temperature >>.
@ -112,25 +108,15 @@ sub check_alert_sent
my $anvil = $self->parent; my $anvil = $self->parent;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Alert->check_alert_sent()" }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Alert->check_alert_sent()" }});
my $name = defined $parameter->{name} ? $parameter->{name} : "";
my $record_locator = defined $parameter->{record_locator} ? $parameter->{record_locator} : ""; my $record_locator = defined $parameter->{record_locator} ? $parameter->{record_locator} : "";
my $set_by = defined $parameter->{set_by} ? $parameter->{set_by} : ""; my $set_by = defined $parameter->{set_by} ? $parameter->{set_by} : "";
my $clear = defined $parameter->{clear} ? $parameter->{clear} : 0; my $clear = defined $parameter->{clear} ? $parameter->{clear} : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
name => $name,
record_locator => $record_locator, record_locator => $record_locator,
set_by => $set_by, set_by => $set_by,
clear => $clear, clear => $clear,
}}); }});
# Do we have an alert name?
if (not $name)
{
# Nope
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->check_alert_sent()", parameter => "name" }});
return("!!error!!");
}
# Do we have an record locator? # Do we have an record locator?
if (not $record_locator) if (not $record_locator)
{ {
@ -161,8 +147,6 @@ AND
alert_set_by = ".$anvil->Database->quote($set_by)." alert_set_by = ".$anvil->Database->quote($set_by)."
AND AND
alert_record_locator = ".$anvil->Database->quote($record_locator)." alert_record_locator = ".$anvil->Database->quote($record_locator)."
AND
alert_name = ".$anvil->Database->quote($name)."
;"; ;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
@ -202,7 +186,6 @@ WHERE
clear => $clear, clear => $clear,
set_by => $set_by, set_by => $set_by,
record_locator => $record_locator, record_locator => $record_locator,
name => $name,
}}); }});
return("!!error!!"); return("!!error!!");
} }
@ -222,14 +205,12 @@ INSERT INTO
alert_sent_host_uuid, alert_sent_host_uuid,
alert_set_by, alert_set_by,
alert_record_locator, alert_record_locator,
alert_name,
modified_date modified_date
) VALUES ( ) VALUES (
".$anvil->Database->quote($anvil->Get->uuid).", ".$anvil->Database->quote($anvil->Get->uuid).",
".$anvil->Database->quote($anvil->Get->host_uuid).", ".$anvil->Database->quote($anvil->Get->host_uuid).",
".$anvil->Database->quote($set_by).", ".$anvil->Database->quote($set_by).",
".$anvil->Database->quote($record_locator).", ".$anvil->Database->quote($record_locator).",
".$anvil->Database->quote($name).",
".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
); );
"; ";

@ -497,10 +497,8 @@ sub check_agent_data
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->check_agent_data()" }}); $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 $agent = defined $parameter->{agent} ? $parameter->{agent} : "";
my $tables = defined $parameter->{tables} ? $parameter->{tables} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
agent => $agent, agent => $agent,
tables => $tables,
}}); }});
if (not $agent) if (not $agent)
@ -511,10 +509,10 @@ sub check_agent_data
my $schema_file = $anvil->data->{path}{directories}{scan_agents}."/".$agent."/".$agent.".sql"; my $schema_file = $anvil->data->{path}{directories}{scan_agents}."/".$agent."/".$agent.".sql";
my $loaded = $anvil->Database->check_for_schema({ my $loaded = $anvil->Database->check_for_schema({
debug => 2, debug => $debug,
file => $schema_file, file => $schema_file,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
loaded => $loaded, loaded => $loaded,
schema_file => $schema_file, schema_file => $schema_file,
}}); }});
@ -528,7 +526,7 @@ sub check_agent_data
record_locator => "schema_load_failure", record_locator => "schema_load_failure",
set_by => $agent, set_by => $agent,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { changed => $changed }});
if ($changed) if ($changed)
{ {
# Log and register an alert. This should never happen, so we set it as a # Log and register an alert. This should never happen, so we set it as a
@ -554,7 +552,7 @@ sub check_agent_data
set_by => $agent, set_by => $agent,
clear => 1, clear => 1,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { changed => $changed }});
if ($changed) if ($changed)
{ {
# Register an alert cleared message. # Register an alert cleared message.
@ -576,7 +574,7 @@ sub check_agent_data
foreach my $uuid (@{$loaded}) foreach my $uuid (@{$loaded})
{ {
my $host_name = $anvil->Database->get_host_from_uuid({short => 1, host_uuid => $uuid}); 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 => { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => $debug, key => "message_0183", variables => {
agent_name => $agent, agent_name => $agent,
host_name => $host_name, host_name => $host_name,
@ -649,7 +647,7 @@ sub check_for_schema
my $table = ""; my $table = "";
my $schema = "public"; my $schema = "public";
my $body = $anvil->Storage->read_file({file => $file}); my $body = $anvil->Storage->read_file({debug => $debug, file => $file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { body => $body }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { body => $body }});
foreach my $line (split/\n/, $body) foreach my $line (split/\n/, $body)
{ {
@ -677,7 +675,7 @@ sub check_for_schema
# Did we find a table? # Did we find a table?
if (not $table) if (not $table)
{ {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0050"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0097", variables => { file => $file}});
return("!!error!!"); return("!!error!!");
} }
@ -718,6 +716,7 @@ sub check_for_schema
# Write out the schema now. # Write out the schema now.
$anvil->Database->write({ $anvil->Database->write({
debug => $debug,
uuid => $uuid, uuid => $uuid,
transaction => 1, transaction => 1,
query => $body, query => $body,
@ -11232,8 +11231,8 @@ sub log_connections
}}); }});
my $query = "SELECT system_identifier FROM pg_control_system();"; my $query = "SELECT system_identifier FROM pg_control_system();";
my $identifier = $anvil->Database->query({uuid => $uuid, debug => ($debug + 1), query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; my $identifier = $anvil->Database->query({uuid => $uuid, debug => $debug, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => ($debug + 1), key => "log_0540", variables => { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0540", variables => {
uuid => $uuid, uuid => $uuid,
identifier => $identifier, identifier => $identifier,
}}); }});
@ -11746,6 +11745,114 @@ sub mark_active
} }
=head2 purge_data
This method takes an array reference of table name and will delete them in reverse order.
Specifically, it takes each table name and looks for an associated function called C<< history_<table>() >> and calls a C<< DROP ... CASCADE; >> (which takes an associated TRIGGER with it), then looks to see if there is a table in the C<< history >> and C<< public >> schemas, DROP'ing them if found.
This method is designed to allow ScanCore scan agents to be called with C<< --purge >> so that collected data can be purged from the database(s) without deleting non-ScanCore data.
This method returns C<< !!error!! >> if there is a problem, and C<< 0 >> otherwise.
Parameters;
=head3 tables (required)
This is an array reference of table tables to search more. There is no need to specify schema as both C<< public >> and C<< history >> schemas are checked automatically.
This array is walked through in reverse order to allow the same array that is used to load and resync data to be used here.
=cut
sub purge_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->purge_data()" }});
my $tables = $parameter->{tables} ? $parameter->{tables} : "";
if (not $tables)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->purge_data()", parameter => "tables" }});
return("!!error!!");
}
if (ref($tables) ne "ARRAY")
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0218", variables => { name => "tables", value => $tables }});
return("!!error!!");
}
my $count = \@{$tables};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { count => $count }});
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}})
{
my $vacuum = 0;
foreach my $table (reverse @{$tables})
{
# Check for the function.
my $safe_table = $anvil->Database->quote($table);
$safe_table =~ s/^'(.*?)'$/$1/;
my $history_function = "history_".$table;
my $function_query = "SELECT COUNT(*) FROM pg_catalog.pg_proc WHERE proname = ".$anvil->Database->quote($history_function).";";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { function_query => $function_query }});
my $function_count = $anvil->Database->query({query => $function_query, uuid => $uuid, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { function_count => $function_count }});
if ($function_count)
{
# Delete it.
$vacuum = 1;
$history_function =~ s/^'(.*?)'$/$1/;
my $query = "DROP FUNCTION ".$history_function."() CASCADE;";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }});
$anvil->Database->write({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
}
my $history_query = "SELECT COUNT(*) FROM pg_catalog.pg_tables WHERE tablename = ".$anvil->Database->quote($table)." AND schemaname='history';";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { history_query => $history_query }});
my $history_count = $anvil->Database->query({query => $history_query, uuid => $uuid, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { history_count => $history_count }});
if ($history_count)
{
# Delete it.
$vacuum = 1;
my $query = "DROP TABLE history.".$safe_table.";";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }});
$anvil->Database->write({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
}
my $public_query = "SELECT COUNT(*) FROM pg_catalog.pg_tables WHERE tablename = ".$anvil->Database->quote($table)." AND schemaname='public';";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { public_query => $public_query }});
my $public_count = $anvil->Database->query({query => $public_query, uuid => $uuid, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { public_count => $public_count }});
if ($public_count)
{
# Delete it.
$vacuum = 1;
my $query = "DROP TABLE public.".$safe_table.";";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }});
$anvil->Database->write({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { vacuum => $vacuum }});
if ($vacuum)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0458"});
my $query = "VACUUM FULL;";
$anvil->Database->write({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
}
}
return(0);
}
=head2 query =head2 query
This performs a query and returns an array reference of array references (from C<< DBO->fetchall_arrayref >>). The first array contains all the returned rows and each row is an array reference of columns in that row. This performs a query and returns an array reference of array references (from C<< DBO->fetchall_arrayref >>). The first array contains all the returned rows and each row is an array reference of columns in that row.
@ -12116,6 +12223,8 @@ sub refresh_timestamp
This will resync the database data on this and peer database(s) if needed. It takes no arguments and will immediately return unless C<< sys::database::resync_needed >> was set. This will resync the database data on this and peer database(s) if needed. It takes no arguments and will immediately return unless C<< sys::database::resync_needed >> was set.
If C<< switches::purge >> is set, this method will also return without doing anything.
=cut =cut
sub resync_databases sub resync_databases
{ {
@ -12134,6 +12243,14 @@ sub resync_databases
return(0); return(0);
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "switches::purge" => $anvil->data->{switches}{purge} }});
if ((exists $anvil->data->{switches}{purge}) && ($anvil->data->{switches}{purge}))
{
# The user is calling a purge, so skip resync for now as the data might be about to all go away anyway.
delete $anvil->data->{sys}{database}{table};
return(0);
}
#$anvil->data->{sys}{database}{log_transactions} = 1; #$anvil->data->{sys}{database}{log_transactions} = 1;
# Archive old data before resync'ing # Archive old data before resync'ing
@ -12870,7 +12987,7 @@ sub _archive_table
if (not $table) if (not $table)
{ {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Convert->_archive_table()", parameter => "table" }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->_archive_table()", parameter => "table" }});
return("!!error!!"); return("!!error!!");
} }

@ -139,7 +139,7 @@ sub agent_startup
} }
# Connect to DBs. # Connect to DBs.
$anvil->Database->connect({debug => ($debug + 1)}); $anvil->Database->connect({debug => $debug});
$anvil->Log->entry({source => $agent, line => __LINE__, level => $debug, secure => 0, key => "log_0132"}); $anvil->Log->entry({source => $agent, line => __LINE__, level => $debug, secure => 0, key => "log_0132"});
if (not $anvil->data->{sys}{database}{connections}) if (not $anvil->data->{sys}{database}{connections})
{ {
@ -150,19 +150,19 @@ sub agent_startup
# Make sure our schema is loaded. # Make sure our schema is loaded.
$anvil->Database->check_agent_data({ $anvil->Database->check_agent_data({
debug => $debug, debug => $debug,
agent => $agent, agent => $agent,
}); });
# Read in our word strings. # Read in our word strings.
my $words_file = $anvil->data->{path}{directories}{scan_agents}."/".$agent."/".$agent.".xml"; 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 }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { words_file => $words_file }});
my $problem = $anvil->Words->read({ my $problem = $anvil->Words->read({
debug => ($debug + 1), debug => $debug,
file => $words_file, file => $words_file,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { problem => $problem }});
if ($problem) if ($problem)
{ {

@ -3,6 +3,7 @@ ALTER TABLE history.recipients RENAME recipient_new_level TO recipient_level;
ALTER TABLE history.recipients DROP COLUMN recipient_units; ALTER TABLE history.recipients DROP COLUMN recipient_units;
ALTER TABLE recipients RENAME recipient_new_level TO recipient_level; ALTER TABLE recipients RENAME recipient_new_level TO recipient_level;
ALTER TABLE recipients DROP COLUMN recipient_units; ALTER TABLE recipients DROP COLUMN recipient_units;
ALTER TABLE alert_sent DROP COLUMN alert_name;
DROP FUNCTION history_recipients() CASCADE; DROP FUNCTION history_recipients() CASCADE;
CREATE FUNCTION history_recipients() RETURNS trigger CREATE FUNCTION history_recipients() RETURNS trigger

@ -10,6 +10,7 @@
# #
# TODO: # TODO:
# - Decide if it's worth having a separate ScanCore.log file or just feed into anvil.log. # - Decide if it's worth having a separate ScanCore.log file or just feed into anvil.log.
#
use strict; use strict;
use warnings; use warnings;
@ -43,17 +44,31 @@ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "
# Read switches # Read switches
$anvil->Get->switches; $anvil->Get->switches;
# These are the tables used by this agent. The order matters as it controls to order the tables are created
# and sync'ed. For purges, this array is walked backwards.
$anvil->data->{scancore}{'scan-hardware'}{tables} = ["scan_hardware", "scan_hardware_ram_modules"];
# Handle start-up tasks # Handle start-up tasks
my $problem = $anvil->ScanCore->agent_startup({ my $problem = $anvil->ScanCore->agent_startup({
debug => 2, debug => 3,
agent => $THIS_FILE, agent => $THIS_FILE,
tables => ["scan_hardware", "scan_hardware_ram_modules"], tables => $anvil->data->{scancore}{'scan-hardware'}{tables},
}); });
if ($problem) if ($problem)
{ {
$anvil->nice_exit({exit_code => 1}); $anvil->nice_exit({exit_code => 1});
} }
if ($anvil->data->{switches}{purge})
{
# This can be called when doing bulk-database purges.
$anvil->Database->purge_data({
debug => 2,
tables => $anvil->data->{scancore}{'scan-hardware'}{tables},
});
$anvil->nice_exit({exit_code => 0});
}
# Read the data. # Read the data.
collect_data($anvil); collect_data($anvil);
@ -64,10 +79,10 @@ read_last_scan($anvil);
find_changes($anvil); find_changes($anvil);
# Finally, process health weights. # Finally, process health weights.
process_health($anvil) process_health($anvil);
# Mark that we ran. # Mark that we ran.
$anvil->Database->insert_or_update_updated({debug => 2, updated_by => $THIS_FILE}); $anvil->Database->insert_or_update_updated({updated_by => $THIS_FILE});
$anvil->nice_exit({exit_code => 0}); $anvil->nice_exit({exit_code => 0});
@ -84,18 +99,23 @@ sub collect_cpu_data
my $total_threads = 0; my $total_threads = 0;
my $cores = 0; my $cores = 0;
my $threads = 0; my $threads = 0;
my $sockets = 0;
my $in_cpu = ""; my $in_cpu = "";
my ($output, $return_code) = $anvil->System->call({level => 2, shell_call => $anvil->data->{path}{exe}{dmidecode}." --type processor", source => $THIS_FILE, line => __LINE__}); my ($output, $return_code) = $anvil->System->call({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 }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output, return_code => $return_code }});
foreach my $line (split/\n/, $output) foreach my $line (split/\n/, $output)
{ {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
if ($line =~ /Socket Designation: (.*+)$/) if ($line =~ /Socket Designation: (.*+)$/)
{ {
$in_cpu = $1; $in_cpu = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_cpu => $in_cpu }}); $sockets++;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
in_cpu => $in_cpu,
sockets => $sockets,
}});
} }
elsif (not $line) elsif (not $line)
{ {
@ -129,25 +149,24 @@ sub collect_cpu_data
}}); }});
} }
} }
close $file_handle;
# Read in /proc/cpuinfo. # Read in /proc/cpuinfo.
my $flags = ""; my $flags = "";
my $flag_mismatch = 0; my $flags_mismatch = 0;
my $bugs = ""; my $bugs = "";
my $bugs_mismatch = 0; my $bugs_mismatch = 0;
my $model = ""; my $model = "";
my $model_mismatch = 0; my $model_mismatch = 0;
my $cpu_info = $anvil->Storage->read_file({level => 2, file => $anvil->data->{path}{proc}{cpuinfo}}); my $cpu_info = $anvil->Storage->read_file({debug => 3, file => $anvil->data->{path}{proc}{cpuinfo}});
foreach my $line (split/,/, $cpu_info) foreach my $line (split/\n/, $cpu_info)
{ {
$line = $anvil->Words->clean_spaces({string => $line}); $line = $anvil->Words->clean_spaces({string => $line});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
if ($line =~ /^flags: (.*?)$/) if ($line =~ /^flags\s*: (.*?)$/)
{ {
my $these_flags = $1; my $these_flags = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { these_flags => $these_flags }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { these_flags => $these_flags }});
if (not $flags) if (not $flags)
{ {
$flags = $these_flags; $flags = $these_flags;
@ -156,8 +175,8 @@ sub collect_cpu_data
elsif ($flags ne $these_flags) elsif ($flags ne $these_flags)
{ {
# This should never happen. # This should never happen.
$flag_mismatch = 1; $flags_mismatch = 1;
my $changed = $anvil->Alert->check_alert_sent({ my $changed = $anvil->Alert->check_alert_sent({
debug => 2, debug => 2,
record_locator => "scan_hardware::cpu_flags_mismatch", record_locator => "scan_hardware::cpu_flags_mismatch",
set_by => $THIS_FILE, set_by => $THIS_FILE,
@ -178,10 +197,10 @@ sub collect_cpu_data
} }
} }
} }
if ($line =~ /^bugs: (.*?)$/) if ($line =~ /^bugs\s*: (.*?)$/)
{ {
my $these_bugs = $1; my $these_bugs = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { these_bugs => $these_bugs }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { these_bugs => $these_bugs }});
if (not $bugs) if (not $bugs)
{ {
$bugs = $these_bugs; $bugs = $these_bugs;
@ -190,9 +209,9 @@ sub collect_cpu_data
elsif ($bugs ne $these_bugs) elsif ($bugs ne $these_bugs)
{ {
# This should never happen... # This should never happen...
$bugs_mismatch = 1; $bugs_mismatch = 1;
my $changed = $anvil->Alert->check_alert_sent({ my $changed = $anvil->Alert->check_alert_sent({
debug => 2, debug => 3,
record_locator => "scan_hardware::cpu_bugs_mismatch", record_locator => "scan_hardware::cpu_bugs_mismatch",
set_by => $THIS_FILE, set_by => $THIS_FILE,
}); });
@ -212,10 +231,10 @@ sub collect_cpu_data
} }
} }
} }
if ($line =~ /^model name: (.*?)$/) if ($line =~ /^model name\s*: (.*?)$/)
{ {
my $this_model = $1; my $this_model = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { this_model => $this_model }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { this_model => $this_model }});
if (not $model) if (not $model)
{ {
$model = $this_model; $model = $this_model;
@ -226,7 +245,7 @@ sub collect_cpu_data
# This should never happen... # This should never happen...
$model_mismatch = 1; $model_mismatch = 1;
my $changed = $anvil->Alert->check_alert_sent({ my $changed = $anvil->Alert->check_alert_sent({
debug => 2, debug => 3,
record_locator => "scan_hardware::cpu_model_mismatch", record_locator => "scan_hardware::cpu_model_mismatch",
set_by => $THIS_FILE, set_by => $THIS_FILE,
}); });
@ -252,7 +271,7 @@ sub collect_cpu_data
if (not $flags_mismatch) if (not $flags_mismatch)
{ {
my $changed = $anvil->Alert->check_alert_sent({ my $changed = $anvil->Alert->check_alert_sent({
debug => 2, debug => 3,
record_locator => "scan_hardware::cpu_flags_mismatch", record_locator => "scan_hardware::cpu_flags_mismatch",
set_by => $THIS_FILE, set_by => $THIS_FILE,
clear => 1, clear => 1,
@ -272,7 +291,7 @@ sub collect_cpu_data
if (not $bugs_mismatch) if (not $bugs_mismatch)
{ {
my $changed = $anvil->Alert->check_alert_sent({ my $changed = $anvil->Alert->check_alert_sent({
debug => 2, debug => 3,
record_locator => "scan_hardware::cpu_bugs_mismatch", record_locator => "scan_hardware::cpu_bugs_mismatch",
set_by => $THIS_FILE, set_by => $THIS_FILE,
clear => 1, clear => 1,
@ -292,7 +311,7 @@ sub collect_cpu_data
if (not $model_mismatch) if (not $model_mismatch)
{ {
my $changed = $anvil->Alert->check_alert_sent({ my $changed = $anvil->Alert->check_alert_sent({
debug => 2, debug => 3,
record_locator => "scan_hardware::cpu_model_mismatch", record_locator => "scan_hardware::cpu_model_mismatch",
set_by => $THIS_FILE, set_by => $THIS_FILE,
clear => 1, clear => 1,
@ -312,12 +331,14 @@ sub collect_cpu_data
# Record what we found. # Record what we found.
$anvil->data->{summary}{cpu}{model} = $model; $anvil->data->{summary}{cpu}{model} = $model;
$anvil->data->{summary}{cpu}{sockets} = $sockets;
$anvil->data->{summary}{cpu}{cores} = $total_cores; $anvil->data->{summary}{cpu}{cores} = $total_cores;
$anvil->data->{summary}{cpu}{threads} = $total_threads; $anvil->data->{summary}{cpu}{threads} = $total_threads;
$anvil->data->{summary}{cpu}{bugs} = $bugs; $anvil->data->{summary}{cpu}{bugs} = $bugs;
$anvil->data->{summary}{cpu}{flags} = $flags; $anvil->data->{summary}{cpu}{flags} = $flags;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"summary::cpu::model" => $anvil->data->{summary}{cpu}{model}, "summary::cpu::model" => $anvil->data->{summary}{cpu}{model},
"summary::cpu::sockets" => $anvil->data->{summary}{cpu}{sockets},
"summary::cpu::cores" => $anvil->data->{summary}{cpu}{cores}, "summary::cpu::cores" => $anvil->data->{summary}{cpu}{cores},
"summary::cpu::threads" => $anvil->data->{summary}{cpu}{threads}, "summary::cpu::threads" => $anvil->data->{summary}{cpu}{threads},
"summary::cpu::bugs" => $anvil->data->{summary}{cpu}{bugs}, "summary::cpu::bugs" => $anvil->data->{summary}{cpu}{bugs},
@ -355,17 +376,22 @@ sub collect_led_states
my $css_led = "unknown"; my $css_led = "unknown";
my $error_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__}); my ($output, $return_code) = $anvil->System->call({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 }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
foreach my $line (split/\n/, $output) foreach my $line (split/\n/, $output)
{ {
$manufacturer = lc($line); $manufacturer = lc($line);
# Dell can report as 'Dell Inc.'.
if ($manufacturer =~ /dell/)
{
$manufacturer = "dell";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { manufacturer => $manufacturer }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { manufacturer => $manufacturer }});
} }
if ($manufacturer eq "fujitsu") 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__}); my ($output, $return_code) = $anvil->System->call({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 }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
foreach my $line (split/\n/, $output) foreach my $line (split/\n/, $output)
{ {
@ -393,7 +419,7 @@ sub collect_led_states
{ {
### TODO: When we get a dell, figure this out... ### 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. ### 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__}); #my ($output, $return_code) = $anvil->System->call({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 }}); #$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
#foreach my $line (split/\n/, $output) #foreach my $line (split/\n/, $output)
#{ #{
@ -427,12 +453,12 @@ sub collect_ram_data
my $part_number = ""; my $part_number = "";
my $serial_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__}); my ($output, $return_code) = $anvil->System->call({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 }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output, return_code => $return_code }});
foreach my $line (split/\n/, $output) foreach my $line (split/\n/, $output)
{ {
$line = $anvil->Words->clean_spaces({string => $line}); $line = $anvil->Words->clean_spaces({string => $line});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
if ($line =~ /^Locator: (.*?)$/) if ($line =~ /^Locator: (.*?)$/)
{ {
@ -504,7 +530,7 @@ sub collect_ram_data
$manufacturer = ""; $manufacturer = "";
$part_number = ""; $part_number = "";
$serial_number = ""; $serial_number = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"size" => $size, "size" => $size,
"locator" => $locator, "locator" => $locator,
"manufacturer" => $manufacturer, "manufacturer" => $manufacturer,
@ -514,17 +540,18 @@ sub collect_ram_data
} }
} }
my $cpu_info = $anvil->Storage->read_file({level => 2, file => $anvil->data->{path}{proc}{meminfo}}); my $cpu_info = $anvil->Storage->read_file({debug => 3, file => $anvil->data->{path}{proc}{meminfo}});
foreach my $line (split/,/, $cpu_info) $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { cpu_info => $cpu_info }});
foreach my $line (split/\n/, $cpu_info)
{ {
$line = $anvil->Words->clean_spaces({string => $line}); $line = $anvil->Words->clean_spaces({string => $line});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
if ($line =~ /^(.*?):\s+(\d+.*?)$/) if ($line =~ /^(.*?):\s+(\d+.*?)$/)
{ {
my $variable = $1; my $variable = $1;
my $size = $2; my $size = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
variable => $variable, variable => $variable,
size => $size, size => $size,
}}); }});
@ -534,22 +561,22 @@ sub collect_ram_data
if ($variable eq "MemTotal") if ($variable eq "MemTotal")
{ {
$say_variable = "memory_total"; $say_variable = "memory_total";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_variable => $say_variable }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_variable => $say_variable }});
} }
if ($variable eq "MemFree") if ($variable eq "MemFree")
{ {
$say_variable = "memory_free"; $say_variable = "memory_free";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_variable => $say_variable }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_variable => $say_variable }});
} }
if ($variable eq "SwapTotal") if ($variable eq "SwapTotal")
{ {
$say_variable = "swap_total"; $say_variable = "swap_total";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_variable => $say_variable }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_variable => $say_variable }});
} }
if ($variable eq "SwapFree") if ($variable eq "SwapFree")
{ {
$say_variable = "swap_free"; $say_variable = "swap_free";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_variable => $say_variable }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_variable => $say_variable }});
} }
next if not $say_variable; next if not $say_variable;
@ -799,16 +826,16 @@ sub find_changes
{ {
# This always changes, so it's an info-level alert # This always changes, so it's an info-level alert
$update = 1; $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_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!#)" 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->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}}); $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) if ($new_scan_hardware_swap_free ne $old_scan_hardware_swap_free)
{ {
$update = 1; $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_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!#)" 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->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}}); $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}});
@ -833,9 +860,9 @@ sub find_changes
{ {
# It's high # It's high
my $changed = $anvil->Alert->check_alert_sent({ my $changed = $anvil->Alert->check_alert_sent({
debug => 2, debug => 3,
record_locator => "scan_hardware::high_swap", record_locator => "scan_hardware::high_swap",
set_by => $agent, set_by => $THIS_FILE,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
if ($changed) if ($changed)
@ -854,7 +881,7 @@ sub find_changes
alert_level => "warning", alert_level => "warning",
message => "scan_hardware_alert_0020", message => "scan_hardware_alert_0020",
message_variables => $variables, message_variables => $variables,
set_by => $agent, set_by => $THIS_FILE,
}); });
} }
} }
@ -862,9 +889,9 @@ sub find_changes
{ {
# It's low # It's low
my $changed = $anvil->Alert->check_alert_sent({ my $changed = $anvil->Alert->check_alert_sent({
debug => 2, debug => 3,
record_locator => "scan_hardware::high_swap", record_locator => "scan_hardware::high_swap",
set_by => $agent, set_by => $THIS_FILE,
clear => 1, clear => 1,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
@ -882,7 +909,7 @@ sub find_changes
debug => 2, debug => 2,
alert_level => "warning", alert_level => "warning",
message => "scan_hardware_alert_0021,!!say_used!".$say_used."!!,!!say_swap!".$say_swap."!!,!!swap_percent!".$new_swap_percent_used."!!", message => "scan_hardware_alert_0021,!!say_used!".$say_used."!!,!!say_swap!".$say_swap."!!,!!swap_percent!".$new_swap_percent_used."!!",
set_by => $agent, set_by => $THIS_FILE,
}); });
} }
} }
@ -937,7 +964,7 @@ WHERE
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_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!#)", 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->Alert->register({debug => 2, alert_level => "notice", message => "scan_hardware_alert_0022", message_variables => $message_variables, set_by => $THIS_FILE });
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0019", variables => $message_variables}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0019", variables => $message_variables});
# INSERT # INSERT
@ -985,7 +1012,6 @@ INSERT INTO
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
} }
### TODO: Left off here.
# Now the RAM modules. # Now the RAM modules.
foreach my $hardware_ram_module_locator (sort {$a cmp $b} keys %{$anvil->data->{ram}{dmi}{locator}}) foreach my $hardware_ram_module_locator (sort {$a cmp $b} keys %{$anvil->data->{ram}{dmi}{locator}})
{ {
@ -1001,12 +1027,13 @@ INSERT INTO
new_scan_hardware_ram_module_serial_number => $new_scan_hardware_ram_module_serial_number, 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})) if ((exists $anvil->data->{sql}{ram_module_locator_to_uuid}{$hardware_ram_module_locator}) && ($anvil->data->{sql}{ram_module_locator_to_uuid}{$hardware_ram_module_locator}))
{ {
# We've seen this module before, look for changes. # 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}; my $scan_hardware_ram_module_uuid = $anvil->data->{sql}{ram_module_locator_to_uuid}{$hardware_ram_module_locator};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $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}, scan_hardware_ram_module_locator => $hardware_ram_module_locator,
scan_hardware_ram_module_uuid => $scan_hardware_ram_module_uuid,
}}); }});
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_size = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_size};
@ -1021,7 +1048,7 @@ INSERT INTO
}}); }});
# Delete the entry so we know we've processed this existing module. # 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}{ram_module_locator_to_uuid}{$hardware_ram_module_locator};
delete $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}; 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. ### We check all at once for a single alert as it's easy to see what has changed.
@ -1044,7 +1071,7 @@ INSERT INTO
old_serial_number => $old_scan_hardware_ram_module_serial_number, 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->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}); $anvil->Alert->register({debug => 2, alert_level => "warning", message => "scan_hardware_alert_0023", message_variables => $variables, set_by => $THIS_FILE});
} }
else else
{ {
@ -1061,7 +1088,7 @@ INSERT INTO
old_serial_number => $old_scan_hardware_ram_module_serial_number, 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->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}); $anvil->Alert->register({debug => 2, alert_level => "warning", message => "scan_hardware_alert_0024", message_variables => $variables, set_by => $THIS_FILE});
} }
my $query = " my $query = "
@ -1093,7 +1120,7 @@ WHERE
serial_number => $new_scan_hardware_ram_module_serial_number, 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->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}); $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_hardware_alert_0025", message_variables => $variables, set_by => $THIS_FILE});
# INSERT # INSERT
my $scan_hardware_ram_module_uuid = $anvil->Get->uuid(); my $scan_hardware_ram_module_uuid = $anvil->Get->uuid();
@ -1129,9 +1156,10 @@ INSERT INTO
foreach my $hardware_ram_module_locator (keys %{$anvil->data->{sql}{scan_hardware_ram_module_uuid}}) foreach my $hardware_ram_module_locator (keys %{$anvil->data->{sql}{scan_hardware_ram_module_uuid}})
{ {
# Module vanished! # Module vanished!
my $scan_hardware_ram_module_uuid = $anvil->data->{sql}{scan_hardware_ram_module_uuid}{$hardware_ram_module_locator}; my $scan_hardware_ram_module_uuid = $anvil->data->{sql}{ram_module_locator_to_uuid}{$hardware_ram_module_locator};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $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} scan_hardware_ram_module_locator => $hardware_ram_module_locator,
scan_hardware_ram_module_uuid => $scan_hardware_ram_module_uuid,
}}); }});
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_size = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_size};
@ -1146,7 +1174,7 @@ INSERT INTO
}}); }});
# Delete the entry so we know we've processed this existing module. # 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}{ram_module_locator_to_uuid}{$hardware_ram_module_locator};
delete $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}; delete $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid};
my $variables = { my $variables = {
@ -1157,7 +1185,7 @@ INSERT INTO
old_serial_number => $old_scan_hardware_ram_module_serial_number, 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->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}); $anvil->Alert->register({debug => 2, alert_level => "warning", message => "scan_hardware_alert_0026", message_variables => $variables, set_by => $THIS_FILE});
my $query = " my $query = "
UPDATE UPDATE
@ -1209,11 +1237,11 @@ sub process_health
if ($peer_ram_total) if ($peer_ram_total)
{ {
# We don't want to freak out unless the difference is at least 1GiB # We don't want to freak out unless the difference is at least 1GiB
my $hardware_ram_total = $an->data->{summary}{ram}{size}; my $hardware_ram_total = $anvil->data->{summary}{ram}{size};
my $difference = $peer_ram_total - $hardware_ram_total; my $difference = $peer_ram_total - $hardware_ram_total;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $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}).")", hardware_ram_total => $anvil->Convert->add_commas({number => $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}).")", difference => $anvil->Convert->add_commas({number => $difference})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $difference}).")",
}}); }});
# greater than 1 GiB (default) or less than 128 MiB (default) # greater than 1 GiB (default) or less than 128 MiB (default)
@ -1230,7 +1258,7 @@ sub process_health
if ($age > 300) if ($age > 300)
{ {
my $changed = $anvil->Alert->check_alert_sent({ my $changed = $anvil->Alert->check_alert_sent({
debug => 2, debug => 3,
record_locator => "scan_hardware::less_ram_than_peer", record_locator => "scan_hardware::less_ram_than_peer",
set_by => $THIS_FILE, set_by => $THIS_FILE,
}); });
@ -1239,9 +1267,9 @@ sub process_health
{ {
# Register an alert. # Register an alert.
my $variables = { my $variables = {
local_ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $hardware_ram_total})." (".$an->Readable->comma($hardware_ram_total)." #!string!suffix_0009!#)", local_ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $hardware_ram_total})." (".$anvil->Convert->add_commas({number => $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!#)", peer_ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $peer_ram_total})." (".$anvil->Convert->add_commas({number => $peer_ram_total})." #!string!suffix_0009!#)",
difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$an->Readable->comma($difference)." #!string!suffix_0009!#)", difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$anvil->Convert->add_commas({number => $difference})." #!string!suffix_0009!#)",
}; };
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0027", variables => $variables}); $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}); $anvil->Alert->register({alert_level => "warning", message => "scan_hardware_alert_0027", message_variables => $variables, set_by => $THIS_FILE});
@ -1264,7 +1292,7 @@ sub process_health
host_uuid => $anvil->Get->host_uuid, host_uuid => $anvil->Get->host_uuid,
}); });
my $changed = $anvil->Alert->check_alert_sent({ my $changed = $anvil->Alert->check_alert_sent({
debug => 2, debug => 3,
record_locator => "scan_hardware::less_ram_than_peer", record_locator => "scan_hardware::less_ram_than_peer",
set_by => $THIS_FILE, set_by => $THIS_FILE,
clear => 1, clear => 1,
@ -1274,7 +1302,7 @@ sub process_health
{ {
# Clear the alert. # Clear the alert.
my $variables = { my $variables = {
ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $hardware_ram_total})." (".$an->Readable->comma($hardware_ram_total)." #!string!suffix_0009!#)", ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $hardware_ram_total})." (".$anvil->Convert->add_commas({number => $hardware_ram_total})." #!string!suffix_0009!#)",
}; };
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0028", variables => $variables}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0028", variables => $variables});
$anvil->Alert->register({alert_level => "warning", message => "scan_hardware_alert_0028", message_variables => $variables, set_by => $THIS_FILE}); $anvil->Alert->register({alert_level => "warning", message => "scan_hardware_alert_0028", message_variables => $variables, set_by => $THIS_FILE});
@ -1325,7 +1353,7 @@ FROM
WHERE WHERE
scan_hardware_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." scan_hardware_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)."
;"; ;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results}; my $count = @{$results};
@ -1352,7 +1380,7 @@ WHERE
my $scan_hardware_led_css = $row->[12]; my $scan_hardware_led_css = $row->[12];
my $scan_hardware_led_error = $row->[13]; my $scan_hardware_led_error = $row->[13];
my $scan_hardware_modified_date = $row->[14]; my $scan_hardware_modified_date = $row->[14];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"scan_hardware_uuid" => $scan_hardware_uuid, "scan_hardware_uuid" => $scan_hardware_uuid,
"scan_hardware_cpu_model" => $scan_hardware_cpu_model, "scan_hardware_cpu_model" => $scan_hardware_cpu_model,
"scan_hardware_cpu_cores" => $scan_hardware_cpu_cores, "scan_hardware_cpu_cores" => $scan_hardware_cpu_cores,
@ -1425,7 +1453,7 @@ FROM
WHERE WHERE
scan_hardware_ram_module_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." 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 }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
$results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
$count = @{$results}; $count = @{$results};
@ -1443,7 +1471,7 @@ WHERE
my $scan_hardware_ram_module_manufacturer = $row->[3]; my $scan_hardware_ram_module_manufacturer = $row->[3];
my $scan_hardware_ram_module_model = $row->[4]; my $scan_hardware_ram_module_model = $row->[4];
my $scan_hardware_ram_module_serial_number = $row->[5]; my $scan_hardware_ram_module_serial_number = $row->[5];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"scan_hardware_ram_module_uuid" => $scan_hardware_ram_module_uuid, "scan_hardware_ram_module_uuid" => $scan_hardware_ram_module_uuid,
"scan_hardware_ram_module_locator" => $scan_hardware_ram_module_locator, "scan_hardware_ram_module_locator" => $scan_hardware_ram_module_locator,
"scan_hardware_ram_module_size" => $scan_hardware_ram_module_size, "scan_hardware_ram_module_size" => $scan_hardware_ram_module_size,
@ -1453,9 +1481,9 @@ WHERE
}}); }});
# Record the scan_hardware_ram_module_uuid in an easy to find place for later when looking for changes. # 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->data->{sql}{ram_module_locator_to_uuid}{$scan_hardware_ram_module_locator} = $scan_hardware_ram_module_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $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}, "sql::ram_module_locator_to_uuid::${scan_hardware_ram_module_locator}" => $anvil->data->{sql}{ram_module_locator_to_uuid}{$scan_hardware_ram_module_locator},
}}); }});
# Store the old data now. # Store the old data now.

@ -23,7 +23,7 @@ The differences are:
#!variable!these_flags!# #!variable!these_flags!#
==== ====
</key> </key>
<key name="scan_hardware_alert_0002">The issue with mismatched CPU flags has been resolved.</keys> <key name="scan_hardware_alert_0002">The issue with mismatched CPU flags has been resolved.</key>
<key name="scan_hardware_alert_0003"> <key name="scan_hardware_alert_0003">
For some reason, two (or more) CPU cores/threads returned different bugs. This should never happen: The differences are: For some reason, two (or more) CPU cores/threads returned different bugs. This should never happen: The differences are:
The differences are: The differences are:
@ -33,7 +33,7 @@ The differences are:
#!variable!these_bugs!# #!variable!these_bugs!#
==== ====
</key> </key>
<key name="scan_hardware_alert_0004">The issue with mismatched CPU bugs has been resolved.</keys> <key name="scan_hardware_alert_0004">The issue with mismatched CPU bugs has been resolved.</key>
<key name="scan_hardware_alert_0005"> <key name="scan_hardware_alert_0005">
For some reason, two (or more) CPU cores/threads returned different model names. This should never happen: The differences are: For some reason, two (or more) CPU cores/threads returned different model names. This should never happen: The differences are:
The differences are: The differences are:
@ -43,7 +43,7 @@ The differences are:
#!variable!this_model!# #!variable!this_model!#
==== ====
</key> </key>
<key name="scan_hardware_alert_0006">The issue with mismatched CPU model name has been resolved.</keys> <key name="scan_hardware_alert_0006">The issue with mismatched CPU model name has been resolved.</key>
<key name="scan_hardware_alert_0007">The CPU model has changed: <key name="scan_hardware_alert_0007">The CPU model has changed:
- New: [#!variable!new!#] - New: [#!variable!new!#]
- Old: [#!variable!old!#] - Old: [#!variable!old!#]

@ -1819,6 +1819,7 @@ CREATE TRIGGER trigger_temperature
-- This table records the last time a scan ran. It's sole purpose is to make sure at least one table's -- This table records the last time a scan ran. It's sole purpose is to make sure at least one table's
-- 'modified_date' changes per run, so that database resyncs can be triggered reliably. -- 'modified_date' changes per run, so that database resyncs can be triggered reliably.
CREATE TABLE updated ( CREATE TABLE updated (
updated_uuid uuid not null primary key,
updated_host_uuid uuid not null, updated_host_uuid uuid not null,
updated_by text not null, -- The name of the agent (or "ScanCore' itself) that updated. updated_by text not null, -- The name of the agent (or "ScanCore' itself) that updated.
modified_date timestamp with time zone not null, modified_date timestamp with time zone not null,
@ -1838,7 +1839,6 @@ CREATE TABLE alert_sent (
alert_sent_host_uuid uuid not null, -- The node associated with this alert alert_sent_host_uuid uuid not null, -- The node associated with this alert
alert_set_by text not null, -- name of the program that set this alert alert_set_by text not null, -- name of the program that set this alert
alert_record_locator text not null, -- String used by the agent to identify the source of the alert (ie: UPS serial number) alert_record_locator text not null, -- String used by the agent to identify the source of the alert (ie: UPS serial number)
alert_name text not null, -- A free-form name used by the caller to identify this alert.
modified_date timestamp with time zone not null, modified_date timestamp with time zone not null,
FOREIGN KEY(alert_sent_host_uuid) REFERENCES hosts(host_uuid) FOREIGN KEY(alert_sent_host_uuid) REFERENCES hosts(host_uuid)

@ -518,14 +518,13 @@ The database connection error was:
<key name="log_0094">[ Error ] - Failed to start the Postgres server. Please check the system logs for details.</key> <key name="log_0094">[ Error ] - Failed to start the Postgres server. Please check the system logs for details.</key>
<key name="log_0095">The database user: [#!variable!user!#] was created with UUID: [#!variable!id!#].</key> <key name="log_0095">The database user: [#!variable!user!#] was created with UUID: [#!variable!id!#].</key>
<key name="log_0096">[ Error ] - Failed to add the database user: [#!variable!user!#]! Unable to proceed.</key> <key name="log_0096">[ Error ] - Failed to add the database user: [#!variable!user!#]! Unable to proceed.</key>
<key name="log_0097"></key> <!-- free --> <key name="log_0097">[ Error ] - Failed to find any tables in: [#!variable!file!#]. Unable to check/load the agent's schema.</key>
<key name="log_0098"> <key name="log_0098">
[ Warning ] - Failed to set an alert because this host is not yet in the database. This can happen if the alert was set before this host was added to the database. [ Warning ] - Failed to set an alert because this host is not yet in the database. This can happen if the alert was set before this host was added to the database.
* Details of the alert: * Details of the alert:
- Type: ......... [#!variable!type!#] - Type: ......... [#!variable!type!#]
- Clear? ........ [#!variable!clear!#] - Clear? ........ [#!variable!clear!#]
- Record Locator: [#!variable!record_locator!#] - Record Locator: [#!variable!record_locator!#]
- Name: ......... [#!variable!name!#]
- Timestamp: .... [#!variable!modified_date!#] - Timestamp: .... [#!variable!modified_date!#]
</key> </key>
<key name="log_0099">[ Error ] - There is no #!string!brand_0002!# database user set for the local machine. Please check: [#!data!path::config::anvil.conf!#]'s DB entry: [#!variable!uuid!#].</key> <key name="log_0099">[ Error ] - There is no #!string!brand_0002!# database user set for the local machine. Please check: [#!data!path::config::anvil.conf!#]'s DB entry: [#!variable!uuid!#].</key>

@ -25,6 +25,22 @@ $anvil->Log->level({set => 2});
$anvil->Log->secure({set => 1}); $anvil->Log->secure({set => 1});
$anvil->Get->switches; $anvil->Get->switches;
my $array = ["1", "2", "a", "b"];
print "Normal:\n";
foreach my $thing (@{$array})
{
print "- ".$thing."\n";
}
print "Reverse:\n";
foreach my $thing (reverse @{$array})
{
print "- ".$thing."\n";
}
exit;
print "Connecting to the database(s);\n"; print "Connecting to the database(s);\n";
$anvil->Database->connect({debug => 3}); $anvil->Database->connect({debug => 3});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, secure => 0, key => "log_0132"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, secure => 0, key => "log_0132"});

Loading…
Cancel
Save