diff --git a/Anvil/Tools/Alert.pm b/Anvil/Tools/Alert.pm
index b49b0944..dc1d322f 100644
--- a/Anvil/Tools/Alert.pm
+++ b/Anvil/Tools/Alert.pm
@@ -295,6 +295,7 @@ sub check_condition_age
# See if this variable has been set yet.
my ($variable_value, $variable_uuid, $epoch_modified_date, $modified_date) = $anvil->Database->read_variable({
+ debug => $debug,
variable_name => $name,
variable_source_table => $source_table,
variable_source_uuid => $host_uuid,
@@ -319,8 +320,8 @@ sub check_condition_age
});
}
- # if the value was 'clear', change it to 'set'.
- if ($variable_value eq "clear")
+ # if the 'clear' parameter isn't set, and the value is 'clear', change it to 'set'.
+ if (($variable_value eq "clear") && (not $clear))
{
# Set it.
$variable_uuid = $anvil->Database->insert_or_update_variables({
diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm
index 4fa02d39..d962a618 100644
--- a/Anvil/Tools/Database.pm
+++ b/Anvil/Tools/Database.pm
@@ -16677,10 +16677,11 @@ sub _age_out_data
# We don't use 'anvil->data' to prevent injecting SQL queries in anvil.conf
my $to_clean = {};
- # Power, temperatures and ip addresses
+ # Power, temperatures, ip addresses and variables
$to_clean->{table}{temperature}{child_table}{temperature}{uuid_column} = "temperature_uuid";
$to_clean->{table}{power}{child_table}{power}{uuid_column} = "power_uuid";
$to_clean->{table}{ip_addresses}{child_table}{ip_addresses}{uuid_column} = "ip_address_uuid";
+ $to_clean->{table}{variables}{child_table}{variables}{uuid_column} = "variable_uuid";
# scan_apc_pdu
$to_clean->{table}{scan_apc_pdus}{child_table}{scan_apc_pdu_phases}{uuid_column} = "scan_apc_pdu_phase_uuid";
@@ -16760,7 +16761,7 @@ sub _age_out_data
count => $count,
}});
- if ($count)
+ if ($count > 1)
{
# Find how many records will be left. If it's 0, we'll use an OFFSET 1.
my $query = "SELECT history_id FROM history.".$child_table." WHERE ".$uuid_column." = ".$anvil->Database->quote($column_uuid)." AND modified_date > '".$old_timestamp."';";
@@ -16781,16 +16782,17 @@ sub _age_out_data
}
else
{
- # This would delete everything, reserve at least one record.
- foreach my $row (@{$results})
- {
- my $history_id = $row->[0];
- $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { history_id => $history_id }});
-
- my $query = "DELETE FROM history.".$child_table." WHERE ".$uuid_column." = ".$anvil->Database->quote($column_uuid)." AND history_id = '".$history_id."';";
- $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
- push @{$queries}, $query;
- }
+ # This would delete everything, reserve at
+ # least one record.
+ my $query = "SELECT history_id FROM history.".$child_table." WHERE ".$uuid_column." = ".$anvil->Database->quote($column_uuid)." ORDER BY modified_date DESC LIMIT 1;";
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
+
+ my $history_id = $anvil->Database->query({uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { count => $count }});
+
+ $query = "DELETE FROM history.".$child_table." WHERE ".$uuid_column." = ".$anvil->Database->quote($column_uuid)." AND modified_date <= '".$old_timestamp."' AND history_id != '".$history_id."';";
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
+ push @{$queries}, $query;
}
}
}
diff --git a/scancore-agents/scan-network/scan-network b/scancore-agents/scan-network/scan-network
index 7267fd8e..78c5a2f2 100755
--- a/scancore-agents/scan-network/scan-network
+++ b/scancore-agents/scan-network/scan-network
@@ -3457,9 +3457,11 @@ AND
# Don't set / clear interfaces that appear down but aren't named ifn/bcn/sn as they're probably
# unconfigured/unusued interfaces.
- my $problem = 0;
- my $check = 0;
- if ($anvil->Network->is_our_interface({interface => $network_interface_name}))
+ my $problem = 0;
+ my $check = 0;
+ my $monitored = $anvil->Network->is_our_interface({interface => $network_interface_name});
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { monitored => $monitored }});
+ if ($monitored)
{
# One we monitor
$check = 1;
diff --git a/share/words.xml b/share/words.xml
index ad92ad88..bec84e10 100644
--- a/share/words.xml
+++ b/share/words.xml
@@ -2435,6 +2435,8 @@ Are you sure that you want to delete the server: [#!variable!server_name!#]? [Ty
Failed to get server VM screenshot; got non-zero return code.
Finished attempting to get server VM screenshot; no operations happened because requirements not met.>>> master
Preparing to manage DR for a server.
+ UUID Column counts for: [history.#!variable!table!#]:
+ Counting entries for each unique: [#!variable!column!#] in the table [#!variable!table!#]. Please be patient.
Saved the mail server information successfully!
@@ -3098,6 +3100,7 @@ We will sleep a bit and try again.
[ Warning ] - The storage group: [#!variable!storage_group_name!#] had the host: [#!variable!host_name!#] as a member. This host is not a member (anymore?) of the Anvil!: [#!variable!anvil_name!#]. Removing it from the storage group now.
[ Warning ] - The postgresql server is not installed yet. Sleeping for a bit, then will check again.
[ Warning ] - Failed to build or install the DRBD kernel module! It is very unlikely that this machine will be able to run any servers until this is fixed.
+ [ Warning ] - Table: [history.#!variable!table!#] not found.
diff --git a/tools/striker-db-report b/tools/striker-db-report
new file mode 100755
index 00000000..b738c468
--- /dev/null
+++ b/tools/striker-db-report
@@ -0,0 +1,381 @@
+#!/usr/bin/perl
+#
+# This tool looks at the database and counts how many records are in each database. Optionally, if given a
+# table name, it will count the number of entries exist in the history schema for each record in the public
+# schema. The goal being to help quickly identifying rapidly growing tables.
+#
+
+use strict;
+use warnings;
+use Anvil::Tools;
+use Data::Dumper;
+use Text::Diff;
+
+$| = 1;
+
+my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
+my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
+if (($running_directory =~ /^\./) && ($ENV{PWD}))
+{
+ $running_directory =~ s/^\./$ENV{PWD}/;
+}
+
+my $anvil = Anvil::Tools->new();
+
+$anvil->Database->connect({debug => 3, check_for_resync => 0});
+$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"});
+if (not $anvil->data->{sys}{database}{connections})
+{
+ # No databases, exit.
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0003"});
+ $anvil->nice_exit({exit_code => 1});
+}
+
+# When set, records are counted in the public table, the the number of history entries for each columng is
+# shown, sorted by frequency.
+$anvil->data->{switches}{table} = "";
+# When set, tables with less than the minium are ignored.
+$anvil->data->{switches}{minimum} = 0;
+$anvil->Get->switches();
+$anvil->data->{switches}{minimum} =~ s/,//g;
+
+if ($anvil->data->{switches}{table})
+{
+ count_table($anvil);
+}
+else
+{
+ count_all($anvil);
+}
+
+$anvil->nice_exit({exit_code => 0});
+
+
+
+#############################################################################################################
+# Functions #
+#############################################################################################################
+
+sub count_table
+{
+ my ($anvil) = @_;
+
+ # Make sure the table exists.
+ my $table = $anvil->Database->quote($anvil->data->{switches}{table});
+ $table =~ s/^\s+//;
+ $table =~ s/\s.*//;
+ $table =~ s/^'(.*)'$/$1/;
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { table => $table }});
+ my $query = "
+SELECT
+ COUNT(*)
+FROM
+ information_schema.tables
+WHERE
+ table_schema = 'history'
+AND
+ table_name = '".$table."'
+AND
+ table_catalog = 'anvil'
+;";
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
+ my $count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
+ if (not $count)
+ {
+ # Table doesn't exist.
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "warning_0133", variables => { table => $table }});
+ $anvil->nice_exit({exit_code => 1});
+ }
+
+ my $uuid_width = 0;
+ my $count_width = 0;
+ my $column1 = $table."_uuid";
+ my $column2 = "";
+ my $column3 = "";
+ my $column4 = "";
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { column1 => $column1 }});
+ if ($table =~ /^(.*)s$/)
+ {
+ $column2 = $1."_uuid";
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { column2 => $column2 }});
+ }
+ if ($table =~ /^(.*)es$/)
+ {
+ $column3 = $1."_uuid";
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { column3 => $column3 }});
+ }
+ if ($table =~ /^(.*)ies$/)
+ {
+ $column4 = $1."y_uuid";
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { column4 => $column4 }});
+ }
+ $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = 'anvil' AND table_schema = 'public' AND table_name = '".$table."' AND data_type = 'uuid' AND is_nullable = 'NO' AND column_name = ".$anvil->Database->quote($column1).";";
+ if ($column4)
+ {
+ $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = 'anvil' AND table_schema = 'public' AND table_name = '".$table."' AND data_type = 'uuid' AND is_nullable = 'NO' AND (column_name = ".$anvil->Database->quote($column1)." OR column_name = ".$anvil->Database->quote($column2)." OR column_name = ".$anvil->Database->quote($column3)." OR column_name = ".$anvil->Database->quote($column4).");";
+ }
+ elsif ($column3)
+ {
+ $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = 'anvil' AND table_schema = 'public' AND table_name = '".$table."' AND data_type = 'uuid' AND is_nullable = 'NO' AND (column_name = ".$anvil->Database->quote($column1)." OR column_name = ".$anvil->Database->quote($column2)." OR column_name = ".$anvil->Database->quote($column3).");";
+ }
+ elsif ($column2)
+ {
+ $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = 'anvil' AND table_schema = 'public' AND table_name = '".$table."' AND data_type = 'uuid' AND is_nullable = 'NO' AND (column_name = ".$anvil->Database->quote($column1)." OR column_name = ".$anvil->Database->quote($column2).");";
+ }
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
+ my $uuid_column = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
+ $uuid_column = "" if not defined $uuid_column;
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { uuid_column => $uuid_column }});
+ if (not $uuid_column)
+ {
+ # This is a problem
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, priority => "err", key => "error_0311", variables => { table => $table }});
+ $anvil->nice_exit({exit_code => 1});
+ }
+
+ # This can take a while, ask the user to be patient.
+ print $anvil->Words->string({key => "message_0269", variables => {
+ table => $table,
+ column => $uuid_column,
+ }})."\n";
+
+ # Count how many entries exist for each UUID.
+ $query = "
+SELECT
+ DISTINCT ".$uuid_column."
+FROM
+ history.".$table."
+;";
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
+ my $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})
+ {
+ my $column_uuid = $row->[0];
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { column_uuid => $column_uuid }});
+
+ if (length($column_uuid) > $uuid_width)
+ {
+ $uuid_width = length($column_uuid);
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { uuid_width => $uuid_width }});
+ }
+
+ my $query = "
+SELECT
+ COUNT(*)
+FROM
+ history.".$table."
+WHERE
+ ".$uuid_column." = ".$anvil->Database->quote($column_uuid)."
+;";
+ my $count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
+ my $comma_count = $anvil->Convert->add_commas({number => $count});
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ count => $count,
+ comma_count => $comma_count,
+ }});
+
+ $anvil->data->{db_counts}{count}{$count}{$column_uuid} = 1;
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ "db_counts::count::${count}::${column_uuid}" => $anvil->data->{db_counts}{count}{$count}{$column_uuid},
+ }});
+
+ if (length($comma_count) > $count_width)
+ {
+ $count_width = length($comma_count);
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count_width => $count_width }});
+ }
+ print ".";
+ }
+ print "\n";
+
+ my $queries = [];
+ my $divider = "-";
+ for (1..$uuid_width) { $divider .= "-"; }
+ $divider .= "-+-";
+ for (1..$count_width) { $divider .= "-"; }
+ $divider .= "-";
+ print $anvil->Words->string({key => "message_0268", variables => { table => $table }})."\n";
+ print $divider."\n";
+ foreach my $count (sort {$a <=> $b} keys %{$anvil->data->{db_counts}{count}})
+ {
+ my $comma_count = $anvil->Convert->add_commas({number => $count});
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ count => $count,
+ comma_count => $comma_count,
+ }});
+
+ if (($anvil->data->{switches}{minimum}) && ($anvil->data->{switches}{minimum} =~ /^\d+$/) && ($count < $anvil->data->{switches}{minimum}))
+ {
+ # Skip it.
+ next;
+ }
+
+ # Sorting by UUID doesn't really make sense, but it provides consistency run over run.
+ foreach my $column_uuid (sort {$a cmp $b} keys %{$anvil->data->{db_counts}{count}{$count}})
+ {
+ print " ".sprintf("%${uuid_width}s", $column_uuid)." | ".sprintf("%${count_width}s", $comma_count)." \n";
+
+ # This will need to be updated by the person debugging a table.
+ #push @{$queries}, "SELECT variable_name, variable_value, variable_source_table, variable_source_uuid FROM variables WHERE variable_uuid = '".$column_uuid."';";
+ }
+ }
+ print $divider."\n";
+
+ # Enable this if you're trying to figure out what data is growing, it needs to be edited on a
+ # per-table basis.
+ if (0)
+ {
+ foreach my $query (@{$queries})
+ {
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
+ my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
+ my $count = @{$results};
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
+ results => $results,
+ count => $count,
+ }});
+ foreach my $row (@{$results})
+ {
+ my $variable_name = $row->[0];
+ my $variable_value = $row->[1];
+ my $source_table = $row->[2];
+ my $source_uuid = $row->[3];
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ 's1:variable_name' => $variable_name,
+ 's2:variable_value' => $variable_value,
+ 's3:source_table' => $source_table,
+ 's4:source_uuid' => $source_uuid,
+ }});
+
+ if ($source_table eq "hosts")
+ {
+ my $host_name = $anvil->Get->host_name_from_uuid({host_uuid => $source_uuid});
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_name => $host_name }});
+ }
+ }
+ }
+ }
+
+ return(0);
+}
+
+sub count_all
+{
+ my ($anvil) = @_;
+
+ my $longest_table = 0;
+ my $longest_public = 0;
+ my $longest_history = 0;
+ my $query = "
+SELECT
+ table_schema,
+ table_name
+FROM
+ information_schema.tables
+WHERE
+ (table_schema = 'public' OR table_schema = 'history')
+AND
+ table_catalog = 'anvil'
+ORDER BY
+ table_name ASC,
+ table_schema DESC;
+;";
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { 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})
+ {
+ my $table_schema = $row->[0];
+ my $table_name = $row->[1];
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ table_schema => $table_schema,
+ table_name => $table_name,
+ }});
+
+ if (not exists $anvil->data->{db_counts}{table}{$table_name})
+ {
+ $anvil->data->{db_counts}{table}{$table_name}{public} = 0;
+ $anvil->data->{db_counts}{table}{$table_name}{history} = -1;
+ }
+
+ if (length($table_name) > $longest_table)
+ {
+ $longest_table = length($table_name);
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { longest_table => $longest_table }});
+ }
+
+ my $query = "SELECT COUNT(*) FROM ".$table_schema.".".$table_name.";";
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
+ my $count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
+ my $comma_count = $anvil->Convert->add_commas({number => $count});
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ count => $count,
+ comma_count => $comma_count,
+ }});
+
+ if ($table_schema eq "public")
+ {
+ if (length($comma_count) > $longest_public)
+ {
+ $longest_public = length($comma_count);
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { longest_public => $longest_public }});
+ }
+ }
+ else
+ {
+ if (length($comma_count) > $longest_history)
+ {
+ $longest_history = length($comma_count);
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { longest_history => $longest_history }});
+ }
+ }
+
+ $anvil->data->{db_counts}{table}{$table_name}{$table_schema} = $count;
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ "db_counts::table::${table_name}::${table_schema}" => $anvil->data->{db_counts}{table}{$table_name}{$table_schema},
+ }});
+ }
+
+ my $say_table = $anvil->Words->string({key => "header_0062"});
+ my $say_public = $anvil->Words->string({key => "header_0063"});
+ my $say_history = $anvil->Words->string({key => "header_0064"});
+ my $divider = "-";
+ for (1..$longest_table) { $divider .= "-"; }
+ $divider .= "-+-";
+ for (1..$longest_public) { $divider .= "-"; }
+ $divider .= "-+-";
+ for (1..$longest_history) { $divider .= "-"; }
+ $divider .= "-";
+
+ print " ".sprintf("%${longest_table}s", "Table")." | ".sprintf("%${longest_public}s", $say_public)." | ".sprintf("%${longest_history}s", $say_history)." \n";
+ print $divider."\n";
+ foreach my $table_name (sort {$a cmp $b} keys %{$anvil->data->{db_counts}{table}})
+ {
+ if (($anvil->data->{switches}{minimum}) && ($anvil->data->{switches}{minimum} =~ /^\d+$/))
+ {
+ if (($anvil->data->{db_counts}{table}{$table_name}{public} < $anvil->data->{switches}{minimum}) &&
+ ($anvil->data->{db_counts}{table}{$table_name}{history} < $anvil->data->{switches}{minimum}))
+ {
+ # Skip it.
+ next;
+ }
+ }
+ my $public = $anvil->Convert->add_commas({number => $anvil->data->{db_counts}{table}{$table_name}{public}});
+ my $history = $anvil->data->{db_counts}{table}{$table_name}{history} == -1 ? "--" : $anvil->Convert->add_commas({number => $anvil->data->{db_counts}{table}{$table_name}{history}});
+ print " ".sprintf("%${longest_table}s", $table_name)." | ".sprintf("%${longest_public}s", $public)." | ".sprintf("%${longest_history}s", $history)." \n";
+ }
+ print $divider."\n";
+
+ return(0);
+}
+