From d677d19ca0996e13fc42c95871f5aa727a40743f Mon Sep 17 00:00:00 2001 From: Digimer Date: Fri, 23 Oct 2020 01:28:21 -0400 Subject: [PATCH] * Moved Database->check_condition_age to Alert. * Created (but not finished) scan-apc-pdu * Added support to tracking maintenance-mode for nodes in Cluster->parse_cib * Created Remote->read_snmp_oid(). * Created Server->get_definition. * Updated Server->get_status() to write-out server XML files on-demand. * Finished scan-cluster. Signed-off-by: Digimer --- Anvil/Tools.pm | 2 + Anvil/Tools/Alert.pm | 117 + Anvil/Tools/Cluster.pm | 151 +- Anvil/Tools/Database.pm | 135 +- Anvil/Tools/Remote.pm | 171 +- Anvil/Tools/ScanCore.pm | 4 +- Anvil/Tools/Server.pm | 151 +- notes | 41 +- ocf/alteeve/server | 12 +- rpm/SPECS/anvil.spec | 1 + scancore-agents/scan-apc-pdu/Striker-MIB.txt | 11 + scancore-agents/scan-apc-pdu/scan-apc-pdu | 2624 +++++++++++++++++ scancore-agents/scan-apc-pdu/scan-apc-pdu.sql | 256 ++ scancore-agents/scan-apc-pdu/scan-apc-pdu.xml | 41 + scancore-agents/scan-cluster/scan-cluster | 532 +++- scancore-agents/scan-cluster/scan-cluster.sql | 628 ++-- scancore-agents/scan-cluster/scan-cluster.xml | 24 +- scancore-agents/scan-hardware/scan-hardware | 5 +- scancore-agents/scan-server/scan-server | 6 +- share/anvil.sql | 2 +- share/words.xml | 1 + tools/anvil-daemon | 1 + tools/test.pl | 389 ++- 23 files changed, 4619 insertions(+), 686 deletions(-) create mode 100644 scancore-agents/scan-apc-pdu/Striker-MIB.txt create mode 100755 scancore-agents/scan-apc-pdu/scan-apc-pdu create mode 100644 scancore-agents/scan-apc-pdu/scan-apc-pdu.sql create mode 100644 scancore-agents/scan-apc-pdu/scan-apc-pdu.xml diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm index 4d5ad1dd..7afbe95b 100644 --- a/Anvil/Tools.pm +++ b/Anvil/Tools.pm @@ -1195,6 +1195,8 @@ sub _set_paths rsync => "/usr/bin/rsync", sed => "/usr/bin/sed", 'shutdown' => "/usr/sbin/shutdown", + snmpget => "/usr/bin/snmpget", + snmpset => "/usr/bin/snmpset", 'ssh-keygen' => "/usr/bin/ssh-keygen", 'ssh-keyscan' => "/usr/bin/ssh-keyscan", 'stat' => "/usr/bin/stat", diff --git a/Anvil/Tools/Alert.pm b/Anvil/Tools/Alert.pm index 0e684ced..50bf7a1b 100644 --- a/Anvil/Tools/Alert.pm +++ b/Anvil/Tools/Alert.pm @@ -12,6 +12,7 @@ my $THIS_FILE = "Alert.pm"; ### Methods; # check_alert_sent +# check_condition_age # error # register @@ -241,6 +242,122 @@ WHERE return($changed); } + +=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 register This registers an alert to be sent later by C<< Email->send_alerts >>. diff --git a/Anvil/Tools/Cluster.pm b/Anvil/Tools/Cluster.pm index 25aeeffb..f04751dd 100644 --- a/Anvil/Tools/Cluster.pm +++ b/Anvil/Tools/Cluster.pm @@ -769,13 +769,29 @@ sub parse_cib }}); # Preload state values (in case they're not read in this CIB. - $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{in_ccm} = "false"; - $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{crmd} = "offline"; - $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{'join'} = "down"; + $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{in_ccm} = "false"; + $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{crmd} = "offline"; + $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{'join'} = "down"; + $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{'maintenance-mode'} = "off"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "cib::parsed::cib::node_state::${node_id}::in_ccm" => $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{in_ccm}, - "cib::parsed::cib::node_state::${node_id}::crmd" => $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{crmd}, - "cib::parsed::cib::node_state::${node_id}::join" => $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{'join'}, + "cib::parsed::cib::node_state::${node_id}::in_ccm" => $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{in_ccm}, + "cib::parsed::cib::node_state::${node_id}::crmd" => $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{crmd}, + "cib::parsed::cib::node_state::${node_id}::join" => $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{'join'}, + "cib::parsed::cib::node_state::${node_id}::maintenance-mode" => $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{'maintenance-mode'}, + }}); + } + } + foreach my $instance_attributes ($node->findnodes('./instance_attributes')) + { + my $instance_attributes_id = $instance_attributes->{id}; + foreach my $nvpair ($instance_attributes->findnodes('./nvpair')) + { + my $id = $nvpair->{id}; + my $name = $nvpair->{name}; + my $value = $nvpair->{value}; + $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{$name} = $value; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "cib::parsed::cib::node_state::${node_id}::${name}" => $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{$name}, }}); } } @@ -943,60 +959,6 @@ sub parse_cib } } - # Pull some data out for easier access. - $anvil->data->{cib}{parsed}{peer}{ready} = ""; - $anvil->data->{cib}{parsed}{peer}{name} = ""; - foreach my $node_name (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{data}{node}}) - { - # The "coming up" order is 'in_ccm' then 'crmd' then 'join'. - my $node_id = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{id}; - my $in_ccm = $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{in_ccm} eq "true" ? 1 : 0; # 'true' or 'false' - Corosync member - my $crmd = $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{crmd} eq "online" ? 1 : 0; # 'online' or 'offline' - In corosync process group - my $join = $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{'join'} eq "member" ? 1 : 0; # 'member' or 'down' - Completed controller join process - my $ready = (($in_ccm) && ($crmd) && ($join)) ? 1 : 0; # Our summary of if the node is "up" - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - 's1:node_name' => $node_name, - 's2:node_id' => $node_id, - 's3:in_ccm' => $in_ccm, - 's4:crmd' => $crmd, - 's5:join' => $join, - 's6:ready' => $ready, - }}); - - $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{in_ccm} = $in_ccm; - $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{crmd} = $crmd; - $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{'join'} = $join; - $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{ready} = $ready; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "cib::parsed::data::node::${node_name}::node_state::in_ccm" => $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{in_ccm}, - "cib::parsed::data::node::${node_name}::node_state::crmd" => $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{crmd}, - "cib::parsed::data::node::${node_name}::node_state::join" => $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{'join'}, - "cib::parsed::data::node::${node_name}::node_state::ready" => $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{ready}, - }}); - - # Is this me or the peer? - if (($node_name eq $anvil->Get->host_name) or ($node_name eq $anvil->Get->short_host_name)) - { - # Me. - $anvil->data->{cib}{parsed}{'local'}{ready} = $node_name; - $anvil->data->{cib}{parsed}{'local'}{name} = $node_name; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "cib::parsed::local::ready" => $anvil->data->{cib}{parsed}{'local'}{ready}, - "cib::parsed::local::name" => $anvil->data->{cib}{parsed}{'local'}{name}, - }}); - } - else - { - # It's our peer. - $anvil->data->{cib}{parsed}{peer}{ready} = $ready; - $anvil->data->{cib}{parsed}{peer}{name} = $node_name; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "cib::parsed::peer::ready" => $anvil->data->{cib}{parsed}{peer}{ready}, - "cib::parsed::peer::name" => $anvil->data->{cib}{parsed}{peer}{name}, - }}); - } - } - # Set some cluster value defaults. $anvil->data->{cib}{parsed}{data}{cluster}{'maintenance-mode'} = "false"; foreach my $nvpair_id (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{configuration}{crm_config}{cluster_property_set}{nvpair}}) @@ -1039,6 +1001,73 @@ sub parse_cib } } + # Pull some data out for easier access. + $anvil->data->{cib}{parsed}{peer}{ready} = ""; + $anvil->data->{cib}{parsed}{peer}{name} = ""; + foreach my $node_name (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{data}{node}}) + { + # The "coming up" order is 'in_ccm' then 'crmd' then 'join'. + my $node_id = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{id}; + my $maintenance_mode = $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{'maintenance-mode'} eq "on" ? 1 : 0; # 'on' or 'off' - Node is not monitoring resources + my $in_ccm = $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{in_ccm} eq "true" ? 1 : 0; # 'true' or 'false' - Corosync member + my $crmd = $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{crmd} eq "online" ? 1 : 0; # 'online' or 'offline' - In corosync process group + my $join = $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{'join'} eq "member" ? 1 : 0; # 'member' or 'down' - Completed controller join process + my $ready = (($in_ccm) && ($crmd) && ($join)) ? 1 : 0; # Our summary of if the node is "up" + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:node_name' => $node_name, + 's2:node_id' => $node_id, + 's3:maintenance_mode' => $maintenance_mode, + 's4:in_ccm' => $in_ccm, + 's5:crmd' => $crmd, + 's6:join' => $join, + 's7:ready' => $ready, + }}); + + # If the global maintenance mode is set, set maintenance mode to true. + if (($anvil->data->{cib}{parsed}{data}{cluster}{'maintenance-mode'}) && ($anvil->data->{cib}{parsed}{data}{cluster}{'maintenance-mode'} eq "true")) + { + $maintenance_mode = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { maintenance_mode => $maintenance_mode }}); + } + + $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{pacemaker_id} = $node_id; + $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{'maintenance-mode'} = $maintenance_mode; + $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{in_ccm} = $in_ccm; + $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{crmd} = $crmd; + $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{'join'} = $join; + $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{ready} = $ready; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "cib::parsed::data::node::${node_name}::node_state::pacemaker_id" => $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{pacemaker_id}, + "cib::parsed::data::node::${node_name}::node_state::maintenance_mode" => $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{'maintenance-mode'}, + "cib::parsed::data::node::${node_name}::node_state::in_ccm" => $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{in_ccm}, + "cib::parsed::data::node::${node_name}::node_state::crmd" => $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{crmd}, + "cib::parsed::data::node::${node_name}::node_state::join" => $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{'join'}, + "cib::parsed::data::node::${node_name}::node_state::ready" => $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{ready}, + }}); + + # Is this me or the peer? + if (($node_name eq $anvil->Get->host_name) or ($node_name eq $anvil->Get->short_host_name)) + { + # Me. + $anvil->data->{cib}{parsed}{'local'}{ready} = $node_name; + $anvil->data->{cib}{parsed}{'local'}{name} = $node_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "cib::parsed::local::ready" => $anvil->data->{cib}{parsed}{'local'}{ready}, + "cib::parsed::local::name" => $anvil->data->{cib}{parsed}{'local'}{name}, + }}); + } + else + { + # It's our peer. + $anvil->data->{cib}{parsed}{peer}{ready} = $ready; + $anvil->data->{cib}{parsed}{peer}{name} = $node_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "cib::parsed::peer::ready" => $anvil->data->{cib}{parsed}{peer}{ready}, + "cib::parsed::peer::name" => $anvil->data->{cib}{parsed}{peer}{name}, + }}); + } + } + # Fencing devices and levels. my $delay_set = 0; foreach my $primitive_id (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{cib}{resources}{primitive}}) @@ -1626,7 +1655,7 @@ sub which_node # Load Anvil! systems. if ((not exists $anvil->data->{anvils}{anvil_name}) && (not $anvil->data->{anvils}{anvil_name})) { - $anvil->Database->load_anvils({debug => $debug}); + $anvil->Database->get_anvils({debug => $debug}); } foreach my $anvil_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_name}}) diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index c62b5445..59e980da 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -17,7 +17,6 @@ my $THIS_FILE = "Database.pm"; ### Methods; # archive_database -# check_condition_age # check_lock_age # check_for_schema # configure_pgsql @@ -303,121 +302,6 @@ 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. @@ -2411,7 +2295,7 @@ FROM host_name => $host_name, host_type => $host_type, host_key => $host_key, - host_ipmi => $host_ipmi =~ /passw/ ? $anvil->Log->is_secure($host_ipmi) : $host_ipmi, + host_ipmi => $host_ipmi, modified_date => $modified_date, }; @@ -4212,8 +4096,15 @@ sub insert_or_update_anvils } elsif ((not $anvil_name) && (not $anvil_uuid)) { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0127", variables => { table => "anvils" }}); - return(""); + # Can we find the anvil_uuid? + $anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => $debug}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_uuid => $anvil_uuid }}); + + if (not $anvil_uuid) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0127", variables => { table => "anvils" }}); + return(""); + } } # If we don't have a UUID, see if we can find one for the given anvil name. @@ -4417,6 +4308,7 @@ SET WHERE anvil_uuid = ".$anvil->Database->quote($anvil_uuid)." "; + $query =~ s/'NULL'/NULL/g; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 1, list => { query => $query }}); $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } @@ -10005,6 +9897,7 @@ SET WHERE session_uuid = ".$anvil->Database->quote($session_uuid)." "; + $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, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); } @@ -12245,7 +12138,7 @@ sub manage_anvil_conf file => $anvil->data->{path}{configs}{'anvil.conf'}, force_read => 1, port => $port, - password => $anvil->Log->is_secure($password), + password => $password, remote_user => $remote_user, secure => 1, target => $target, @@ -12542,7 +12435,7 @@ sub manage_anvil_conf group => "admin", mode => "0644", overwrite => 1, - password => $anvil->Log->is_secure($password), + password => $password, port => $port, remote_user => $remote_user, target => $target, diff --git a/Anvil/Tools/Remote.pm b/Anvil/Tools/Remote.pm index c014ba9b..316aeb79 100644 --- a/Anvil/Tools/Remote.pm +++ b/Anvil/Tools/Remote.pm @@ -17,6 +17,7 @@ my $THIS_FILE = "Remote.pm"; ### Methods; # add_target_to_known_hosts # call +# read_snmp_oid # test_access # _call_ssh_keyscan # _check_known_hosts_for_target @@ -716,6 +717,172 @@ sub call return($output, $error, $return_code); } + +=head2 read_snmp_oid + +This connects to a remote machine using SNMP and reads (if possible) the OID specified. If unable to reach the target device, C<< !!no_connection!! >> is returned. If there is a problem with the call made to this method, C<< !!error!! >> is returned. + +Otherwise, two values are returned; first the data and second the data type. + +Parameters; + +=head3 community (optional) + +This is the SNMP community used to connect to. + +=head3 mib (optional) + +If set to a path to a file, the file is treated as a custom MIB to be fed into C<< snmpget >> + +=head3 oid (required) + +This is the OID string to query. + +=head3 target (required) + +This is the IP or (resolvable) host name to query. + +=head3 version (optional, default '2c') + +This is the SNMP protocol version to use when connecting to the target. + +=cut +sub read_snmp_oid +{ + 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 => "Remote->read_snmp_oid()" }}); + + my $community = defined $parameter->{community} ? $parameter->{community} : ""; + my $mib = defined $parameter->{mib} ? $parameter->{mib} : ""; + my $oid = defined $parameter->{oid} ? $parameter->{oid} : ""; + my $target = defined $parameter->{target} ? $parameter->{target} : ""; + my $version = defined $parameter->{version} ? $parameter->{version} : "2c"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { + community => $community, + mib => $mib, + oid => $oid, + target => $target, + version => $version, + }}); + + if (not $oid) + { + # Um, what are we supposed to read? + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Remote->read_snmp_oid()", parameter => "oid" }}); + die; + return("!!error!!"); + } + if (not $target) + { + # Who ya gonna call? No, seriously, I have no idea... + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Remote->read_snmp_oid()", parameter => "target" }}); + die; + return("!!error!!"); + } + if (($mib) && (not -r $mib)) + { + # Bad MIB path + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0163", variables => { mib => $mib }}); + die; + return("!!error!!"); + } + + my $data_type = "unknown"; + my $shell_call = $anvil->data->{path}{exe}{snmpget}." -On"; + if ($community) + { + $shell_call .= " -c ".$community; + } + if ($mib) + { + $shell_call .= " -m ".$mib; + } + $shell_call .= " -v ".$version." ".$target." ".$oid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { shell_call => $shell_call }}); + + my ($output, $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + output => $output, + return_code => $return_code, + }}); + my $value = "#!no_value!#"; + foreach my $line (split/\n/, $output) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }}); + + if ($line =~ /No Response/i) + { + $value = "#!no_connection!#"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { value => $value }}); + } + elsif (($line =~ /STRING: "(.*)"$/i) or ($line =~ /STRING: (.*)$/i)) + { + $value = $1; + $data_type = "string"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + value => $value, + data_type => $data_type, + }}); + } + elsif ($line =~ /INTEGER: (\d+)$/i) + { + $value = $1; + $data_type = "integer"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + value => $value, + data_type => $data_type, + }}); + } + elsif ($line =~ /Hex-STRING: (.*)$/i) + { + $value = $1; + $data_type = "hex-string"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + value => $value, + data_type => $data_type, + }}); + } + elsif ($line =~ /Gauge32: (.*)$/i) + { + $value = $1; + $data_type = "guage32"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + value => $value, + data_type => $data_type, + }}); + } + elsif ($line =~ /Timeticks: \((\d+)\) /i) + { + $value = $1; + $data_type = "timeticks"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + value => $value, + data_type => $data_type, + }}); + } + elsif ($line =~ /No Such Instance/i) + { + $value = "--"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { value => $value }}); + } + elsif ($line =~ /^(.*?): (.*$)/i) + { + $data_type = $1; + $value = $2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + value => $value, + data_type => $data_type, + }}); + } + } + + return($value, $data_type); +} + + =head2 test_access This attempts to log into the target to verify that the target is up and reachable. It returns C<< 1 >> on access, C<< 0 >> otherwise. @@ -759,7 +926,7 @@ sub test_access my $target = defined $parameter->{target} ? $parameter->{target} : ""; my $user = defined $parameter->{user} ? $parameter->{user} : getpwuid($<); my $access = 0; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { password => $anvil->Log->is_secure($password), port => $port, target => $target, @@ -785,7 +952,7 @@ sub test_access $access = 1; } - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { access => $access }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { access => $access }}); return($access); } diff --git a/Anvil/Tools/ScanCore.pm b/Anvil/Tools/ScanCore.pm index a587328f..f69463a7 100755 --- a/Anvil/Tools/ScanCore.pm +++ b/Anvil/Tools/ScanCore.pm @@ -163,13 +163,13 @@ sub agent_startup # 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 }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { words_file => $words_file }}); my $problem = $anvil->Words->read({ debug => $debug, 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) { diff --git a/Anvil/Tools/Server.pm b/Anvil/Tools/Server.pm index 1ee2059a..f2a65b93 100755 --- a/Anvil/Tools/Server.pm +++ b/Anvil/Tools/Server.pm @@ -14,6 +14,7 @@ my $THIS_FILE = "Server.pm"; ### Methods; # boot_virsh # find +# get_definition # get_runtime # get_status # map_network @@ -344,6 +345,70 @@ sub find } +=head2 get_definition + +This returns the server definition XML for a server. + +Parameters; + +=head3 server_uuid (optional, if 'server_name' used. required if not) + +If provided, this is the specific server's definition we'll return. If it is not provided, C<< server_name >> is required. + +=head3 server_name (optional) + +If provided, and C<< server_uuid >> is not, the server will be searched for using this name. If C<< anvil_uuid >> is included, the name will be searched on the appropriate Anvil! system only. + +=head3 anvil_uuid (optional) + +If set along with C<< server_name >>, the search for the server's XML will be restricted to the specified Anvil! system. + +=cut +sub get_definition +{ + 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 => "Server->get_runtime()" }}); + + my $definition_xml = ""; + my $anvil_uuid = defined $parameter->{anvil_uuid} ? $parameter->{anvil_uuid} : ""; + my $server_name = defined $parameter->{server_name} ? $parameter->{server_name} : ""; + my $server_uuid = defined $parameter->{server_uuid} ? $parameter->{server_uuid} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + anvil_uuid => $anvil_uuid, + server_name => $server_name, + server_uuid => $server_uuid, + }}); + + $server_uuid = $anvil->Get->server_uuid_from_name({ + debug => $debug, + server_name => $server_name, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { server_uuid => $server_uuid }}); + if ($server_uuid) + { + my $query = "SELECT server_definition_xml FROM server_definitions WHERE server_definition_server_uuid = ".$anvil->Database->quote($server_uuid).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + if ($count == 1) + { + # Found it + $definition_xml = defined $results->[0]->[0] ? $results->[0]->[0] : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { definition_xml => $definition_xml }}); + } + } + + return($definition_xml); +} + + =head2 get_runtime This returns the number of seconds that a (virtual) server has been running on this host. @@ -365,7 +430,7 @@ sub get_runtime 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 => "Server->get_status()" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Server->get_runtime()" }}); my $runtime = 0; my $server = defined $parameter->{server} ? $parameter->{server} : ""; @@ -504,7 +569,7 @@ sub get_status remote_user => $remote_user, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - error => $error, + error => $error, "server::${host}::${server}::from_virsh::xml" => $anvil->data->{server}{$host}{$server}{from_virsh}{xml}, "server::${host}::${server}::from_virsh::return_code" => $anvil->data->{server}{$host}{$server}{from_virsh}{return_code}, }}); @@ -528,6 +593,8 @@ sub get_status } # Now get the on-disk XML. + my $definition_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server.".xml"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { definition_file => $definition_file }}); ($anvil->data->{server}{$host}{$server}{from_disk}{xml}) = $anvil->Storage->read_file({ debug => $debug, password => $password, @@ -535,15 +602,89 @@ sub get_status remote_user => $remote_user, target => $target, force_read => 1, - file => $anvil->data->{path}{directories}{shared}{definitions}."/".$server.".xml", + file => $definition_file, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "server::${host}::${server}::from_disk::xml" => $anvil->data->{server}{$host}{$server}{from_disk}{xml}, }}); if (($anvil->data->{server}{$host}{$server}{from_disk}{xml} eq "!!error!!") or (not $anvil->data->{server}{$host}{$server}{from_disk}{xml})) { - # Failed to read it. - $anvil->data->{server}{$host}{$server}{from_disk}{xml} = ""; + # Failed to read it. Can we write it? + my $definition_xml = ""; + if ($anvil->data->{server}{$host}{$server}{from_virsh}{xml}) + { + $definition_xml = $anvil->data->{server}{$host}{$server}{from_virsh}{xml}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { definition_xml => $definition_xml }}); + } + else + { + # Read in from the database. + $definition_xml = $anvil->Server->get_definition({ + debug => $debug, + server_name => $server, + anvil_uuid => $anvil->Cluster->get_anvil_uuid({debug => $debug}), + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { definition_xml => $definition_xml }}); + } + + if ($definition_xml) + { + # Write it to disk + my ($failed) = $anvil->Storage->write_file({ + secure => 1, + file => $definition_file, + body => $definition_xml, + overwrite => 1, + password => $password, + port => $port, + remote_user => $remote_user, + target => $target, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { failed => $failed }}); + if ($failed) + { + # Simething went weong. + $anvil->data->{server}{$host}{$server}{from_disk}{xml} = ""; + return(1); + } + + # Now try to read it back. + ($anvil->data->{server}{$host}{$server}{from_disk}{xml}) = $anvil->Storage->read_file({ + debug => $debug, + password => $password, + port => $port, + remote_user => $remote_user, + target => $target, + force_read => 1, + file => $definition_file, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "server::${host}::${server}::from_disk::xml" => $anvil->data->{server}{$host}{$server}{from_disk}{xml}, + }}); + if (($anvil->data->{server}{$host}{$server}{from_disk}{xml} eq "!!error!!") or (not $anvil->data->{server}{$host}{$server}{from_disk}{xml})) + { + # Failed to read it. + $anvil->data->{server}{$host}{$server}{from_disk}{xml} = ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "server::${host}::${server}::from_disk::xml" => $anvil->data->{server}{$host}{$server}{from_disk}{xml}, + }}); + } + else + { + # Load + $anvil->Server->parse_definition({ + debug => $debug, + host => $this_host, + server => $server, + source => "from_disk", + definition => $anvil->data->{server}{$host}{$server}{from_disk}{xml}, + }); + } + } + else + { + $anvil->data->{server}{$host}{$server}{from_disk}{xml} = ""; + } } else { diff --git a/notes b/notes index ca87a74c..00ae5716 100644 --- a/notes +++ b/notes @@ -435,6 +435,45 @@ CREATE TABLE updated ( ); ALTER TABLE updated OWNER TO admin; + +DROP FUNCTION history_anvils() CASCADE; + +CREATE FUNCTION history_anvils() RETURNS trigger +AS $$ +DECLARE + history_anvils RECORD; +BEGIN + SELECT INTO history_anvils * FROM anvils WHERE anvil_uuid = new.anvil_uuid; + INSERT INTO history.anvils + (anvil_uuid, + anvil_name, + anvil_description, + anvil_password, + anvil_node1_host_uuid, + anvil_node2_host_uuid, + anvil_dr1_host_uuid, + anvil_fencing_enabled, + modified_date) + VALUES + (history_anvils.anvil_uuid, + history_anvils.anvil_name, + history_anvils.anvil_description, + history_anvils.anvil_password, + history_anvils.anvil_node1_host_uuid, + history_anvils.anvil_node2_host_uuid, + history_anvils.anvil_dr1_host_uuid, + history_anvils.anvil_fencing_enabled, + history_anvils.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_anvils() OWNER TO admin; + +CREATE TRIGGER trigger_anvils + AFTER INSERT OR UPDATE ON anvils + FOR EACH ROW EXECUTE PROCEDURE history_anvils(); + COMMIT; ============ @@ -719,7 +758,7 @@ pcs resource create srv07-el6 ocf:alteeve:server name="srv07-el6" meta allow-mig pcs constraint location srv07-el6 prefers mk-a02n01=200 mk-a02n02=100 pcs resource enable srv07-el6 - or - -pcs resource update srv07-el6 ocf:alteeve:server name="srv07-el6" meta allow-migrate="true" migrate_to="INFINITY" stop="INFINITY" op monitor interval="60" on-fail="block" +pcs resource update srv07-el6 ocf:alteeve:server name="srv07-el6" meta allow-migrate="true" target-role="stopped" op monitor interval="60" start timeout="INFINITY" on-fail="block" stop timeout="INFINITY" on-fail="block" migrate_to timeout="INFINITY" # Test stonith_admin --fence el8-a01n02 --verbose; crm_error $? diff --git a/ocf/alteeve/server b/ocf/alteeve/server index 5ee08113..15f57963 100755 --- a/ocf/alteeve/server +++ b/ocf/alteeve/server @@ -101,7 +101,7 @@ $anvil->Log->secure({set => 1}); # If we can connect to a database, we'll set/clear the 'migrating' flag during migrations $anvil->Database->connect(); -$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"}); +$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"}); if (not $anvil->data->{sys}{database}{connections}) { # No databases, exit. @@ -996,7 +996,7 @@ sub server_status # the server is failed, return OCF_ERR_GENERIC (1). my $state = ""; my $server = $anvil->data->{environment}{OCF_RESKEY_name}; - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0521", variables => { server => $server }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0521", variables => { server => $server }}); if (not $anvil->data->{environment}{OCF_RESKEY_CRM_meta_timeout}) { @@ -1158,7 +1158,7 @@ pmsuspended - The domain has been suspended by guest power management, e.g. ente else { # In some fashion or another, the server is running. Exit with OCF_SUCCESS (rc: 0) - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0527", variables => { 'state' => $state }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0527", variables => { 'state' => $state }}); $anvil->nice_exit({exit_code => 0}); } } @@ -1458,8 +1458,6 @@ sub validate_all my ($anvil) = @_; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0361"}); - ### TODO: When we have actual Anvil! systems, connect to the peers (nodes / DR) for this host and see - ### if the server is running elsewhere. my $server = $anvil->data->{environment}{OCF_RESKEY_name}; my $source = defined $anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_source} ? $anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_source} : ""; my $target = defined $anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_target} ? $anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_target} : ""; @@ -1469,9 +1467,9 @@ sub validate_all target => $target, }}); - # Read in an parse the server's XML. + # Read in and parse the server's XML. $anvil->System->check_storage({debug => 3}); - $anvil->Server->get_status({debug => 3, server => $server}); + $anvil->Server->get_status({debug => 2, server => $server}); # Is the name in the definition file what we expect (and did we read the XML data at all)? validate_name($anvil); diff --git a/rpm/SPECS/anvil.spec b/rpm/SPECS/anvil.spec index f79b297e..6748c84e 100644 --- a/rpm/SPECS/anvil.spec +++ b/rpm/SPECS/anvil.spec @@ -46,6 +46,7 @@ Requires: iproute Requires: lsscsi Requires: mailx Requires: mlocate +Requires: net-snmp-utils Requires: nvme-cli Requires: perl-Capture-Tiny Requires: perl-Data-Dumper diff --git a/scancore-agents/scan-apc-pdu/Striker-MIB.txt b/scancore-agents/scan-apc-pdu/Striker-MIB.txt new file mode 100644 index 00000000..77945d75 --- /dev/null +++ b/scancore-agents/scan-apc-pdu/Striker-MIB.txt @@ -0,0 +1,11 @@ +-- This is needed by scan-apc-pdu to trick/force .1.3.6.1.2.1.2.2.1.6.2 to return Hex-STRING. -- +PowerNet-MIB DEFINITIONS ::= BEGIN + +IMPORTS + enterprises, IpAddress, Gauge, TimeTicks FROM RFC1155-SMI + DisplayString FROM RFC1213-MIB + OBJECT-TYPE FROM RFC-1212 + TRAP-TYPE FROM RFC-1215; + +-- IMPORTS End + diff --git a/scancore-agents/scan-apc-pdu/scan-apc-pdu b/scancore-agents/scan-apc-pdu/scan-apc-pdu new file mode 100755 index 00000000..8ef16c82 --- /dev/null +++ b/scancore-agents/scan-apc-pdu/scan-apc-pdu @@ -0,0 +1,2624 @@ +#!/usr/bin/perl +# +# This uses data in the 'fences' database table (any agent starting with 'fence_apc_*' to get a list of APC- +# brand PDUs to scan. +# +# Examples; +# +# Exit codes; +# 0 - Success +# 1 - Something went wrong, agent aborted run. +# +# 255 - The host's UUID isn't in the hosts table yet, ScanCore itself hasn't been run. +# + +# Use my modules. +use strict; +use warnings; +use Anvil::Tools; +use Data::Dumper; +use Socket; +no warnings 'recursion'; + +=cut + +OIDs of interest; + +State data +.1.3.6.1.4.1.318.1.1.4.1.5.0 = Serial Number <- Global ID +.1.3.6.1.4.1.318.1.1.4.1.4.0 = Model Number +.1.3.6.1.4.1.318.1.1.4.1.3.0 = Date of manufacture (mm/dd/yyyy, if 'yy' year 2000 = '00') +.1.3.6.1.4.1.318.1.1.4.1.2.0 = Firmware version +.1.3.6.1.4.1.318.1.1.4.1.1.0 = Hardware version (never changes) + +Variables +.1.3.6.1.2.1.1.3.0 = Uptime (in timeticks, 900 = 9 seconds) +.1.3.6.1.4.1.318.1.1.12.1.16.0 = Wattage draw (seems to be for the full device, not per phase) + +# Phase info +.1.3.6.1.4.1.318.1.1.12.2.1.1.0 = Max Amperage out per phase +.1.3.6.1.4.1.318.1.1.12.2.1.2.0 = Number of phases on the PDU +.1.3.6.1.4.1.318.1.1.12.2.2.1.1.2.1 = Low amp threshold. (0 = Disabled; No alarm on low power. Any digit is the minimum amperage under which we throw an alert) (NOTE: last digit is phase number, so .1 == phase 1) +.1.3.6.1.4.1.318.1.1.12.2.2.1.1.3.1 = High amperage warning threshold. If the power draw exceeds this, throw a warning alert, warn on equal or greater +.1.3.6.1.4.1.318.1.1.12.2.2.1.1.4.1 = High amperage critical threshold. If the power draw exceeds this, throw a critical alert (breaker is about to pop), warn on equal or greater (NOTE: Default == max amp per phase, we should set it lower, Max - 2?) +.1.3.6.1.4.1.318.1.1.12.2.3.1.1.2.1 = Current Amperage on the phase, in 1/10 Amp (x10 to get Amp) + +Port information +.1.3.6.1.4.1.318.1.1.4.4.1.0 = Number of outlets (ie: 8) +.1.3.6.1.4.1.318.1.1.4.4.2.1.2.n = Pending action on state? (1 = command pending, 2 = no command pending, 3 = unknown. If all ports are '3', power cycle is required. If the all devices on the peer report Power is OK, do so immediately) +.1.3.6.1.4.1.318.1.1.4.4.2.1.3.n = Current state (read: 1 = on, 2 = off, 4 = unknown --- write: 1 = turn on, 2 = turn off, 3 = cycle (~5s), 5 = on after 'sPDUOutletPowerOnTime' delay, 6 = off after sPDUOutletPowerOffTime delay, 7 = off after sPDUOutletPowerOffTime delay, wait sPDUOutletRebootDuration time, then back on.) +.1.3.6.1.4.1.318.1.1.4.5.2.1.3.n = Outlet name (default is 'Outlet N'), Can be read or set, max 20 chars. <- Set this when we map the Anvil! +.1.3.6.1.4.1.318.1.1.4.5.2.1.2.n = Power on delay (in seconds), default is '0' seconds. (-1 = stay off, 0 = on with PDU, X = number of seconds to wait before powering on) +.1.3.6.1.4.1.318.1.1.4.5.2.1.4.n = Power off delay (in seconds) (-1 = stay on, 0 = off with PDU, X = number of seconds to wait before powering off) +.1.3.6.1.4.1.318.1.1.4.5.2.1.5.n = Reboot delay (sleep time), default is '5' seconds. +.1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.n = Phase that this outlet is in + +Network data (NOTE: .1 is the loopback interface, .2 is the physical interface) +.1.3.6.1.2.1.2.2.1.4.2 = MTU size +.1.3.6.1.2.1.2.2.1.5.2 = Link speed (in bps) +.1.3.6.1.2.1.2.2.1.6.2 = MAC address - This is losing the first nibble... It returns type STRING instead of Hex-STRING, even if -Ox is used. If I point to the APC MIB, it returns OK. This is what we should be using. +.1.3.6.1.6.3.10.2.1.1.0 = MAC address (with some prefix) in hex. Not sure if this is supported on other PDUs though... + +=cut + +# Disable buffering +$| = 1; + +# Prevent a discrepency between UID/GID and EUID/EGID from throwing an error. +$< = $>; +$( = $); + +my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; +my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; +if (($running_directory =~ /^\./) && ($ENV{PWD})) +{ + $running_directory =~ s/^\./$ENV{PWD}/; +} + +my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1}); +$anvil->Log->level({set => 2}); +$anvil->Log->secure({set => 1}); + +# Make sure we're running as 'root' +# $< == real UID, $> == effective UID +if (($< != 0) && ($> != 0)) +{ + # Not root + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0005"}); + $anvil->nice_exit({exit_code => 1}); +} + +$anvil->data->{'scan-apc-pdu'} = { + disable => 0, + alert_sort => 0, + # Ticks are genera lly in 10ms increments + ticks_per_second => 100, + # By default, the critical amperage is the same as the max amperage. If that is the + # case here, we will subtract this number of amps from the max and use that as our + # critical warning threshold. So if the max threshold is 12, and the critical + # threshold is also 11 or 12, we will drop it to 10. If the critical is set below 10, + # we won't adjust it at all. + critical_amps_below_max => 2, + # This drops the warning to be at least this number of amps below critical's level. + warning_amps_below_critical => 2, + # A warning or critical alert has to be this number of amps above/below the threshold + # for the alert to be cleared. + clear_alert_threshold => 2, + # This is needed to trick/force the 'scan_apc_pdu_mac_address' OID into returning a Hex-STRING. + striker_mib => "/usr/sbin/scancore-agents/".$THIS_FILE."/Striker-MIB.txt", +}; +$anvil->data->{oids} = { + scan_apc_pdu => { + scan_apc_pdu_serial_number => ".1.3.6.1.4.1.318.1.1.4.1.5.0", + scan_apc_pdu_model_number => ".1.3.6.1.4.1.318.1.1.4.1.4.0", + scan_apc_pdu_manufacture_date => ".1.3.6.1.4.1.318.1.1.4.1.3.0", + scan_apc_pdu_firmware_version => ".1.3.6.1.4.1.318.1.1.4.1.2.0", + scan_apc_pdu_hardware_version => ".1.3.6.1.4.1.318.1.1.4.1.1.0", + scan_apc_pdu_mac_address => ".1.3.6.1.2.1.2.2.1.6.2", + scan_apc_pdu_mac_address_alt => ".1.3.6.1.2.1.2.2.1.6.1", # Some PDUs use this + scan_apc_pdu_mtu_size => ".1.3.6.1.2.1.2.2.1.4.2", + scan_apc_pdu_mtu_size_alt => ".1.3.6.1.2.1.2.2.1.4.1", # Some PDUs use this + scan_apc_pdu_link_speed => ".1.3.6.1.2.1.2.2.1.5.2", + scan_apc_pdu_link_speed_alt => ".1.3.6.1.2.1.2.2.1.5.1", + scan_apc_pdu_phase_count => ".1.3.6.1.4.1.318.1.1.12.2.1.2.0", + scan_apc_pdu_outlet_count => ".1.3.6.1.4.1.318.1.1.4.4.1.0", + }, + scan_apc_pdu_phases => { + # The phase number will be appended to the OID + scan_apc_pdu_phase_current_amperage => ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.2.", + scan_apc_pdu_phase_max_amperage => ".1.3.6.1.4.1.318.1.1.12.2.1.1.0", + ### NOTE: These aren't recorded, but they are used for alert triggers. + phase_low_amp_warning => ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.2.1", + # We'll drop these to be at least 2 Amps below max. + phase_high_amp_warning => ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.3.1", + # If this is less than 'scan-apc-pdu::critical_amps_below_max' below max, it + # will be automatically dropped. + phase_high_amp_critical => ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.4.1", + }, + scan_apc_pdu_outlets => { + ### The outlet number will be appended to all of these OIDs. + scan_apc_pdu_outlet_name => ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.", + scan_apc_pdu_outlet_on_phase => ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.", + # read: 1 = on, 2 = off, 4 = unknown + # write: 1 = on, 2 = off, 3 = cycle + scan_apc_pdu_outlet_state => ".1.3.6.1.4.1.318.1.1.4.4.2.1.3.", + }, + scan_apc_pdu_variables => { + # This is in ticks, so divide the value by 'scan-apc-pdu::ticks_per_second' + uptime => ".1.3.6.1.2.1.1.3.0", + total_wattage_draw => ".1.3.6.1.4.1.318.1.1.12.1.16.0", + }, +}; +$anvil->data->{snmp} = { + community => { + version => "2c", + 'read' => "public", + 'write' => "private", + }, +}; + +$anvil->Storage->read_config(); +$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0115", variables => { program => $THIS_FILE }}); + +# Read switches +$anvil->Get->switches; + +# If we're disabled and '--force' wasn't used, exit. +if (($anvil->data->{scancore}{'scan-apc-pdu'}{disable}) && (not $anvil->data->{switches}{force})) +{ + # Exit. + $anvil->nice_exit({exit_code => 0}); +} + +# 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-apc-pdu'}{tables} = ["scan_apc_pdus", "scan_apc_pdu_phases", "scan_apc_pdu_outlets", "scan_apc_pdu_variables"]; + +# Handle start-up tasks +my $problem = $anvil->ScanCore->agent_startup({ + debug => 3, + agent => $THIS_FILE, + tables => $anvil->data->{scancore}{'scan-apc-pdu'}{tables}, +}); +if ($problem) +{ + $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-apc-pdu'}{tables}, + }); + $anvil->nice_exit({exit_code => 0}); +} + +# Find the PDUs. The number of PDUs found is returned. If 0, we exit +if (not find_pdus($anvil)) +{ + # No PDUs found. + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_pdu_message_0001", file => $THIS_FILE, line => __LINE__}); + $anvil->nice_exit({exit_code => 1}); +} + +# Collect data from PDUs. +gather_pdu_data($anvil); + +# Read the last state of any PDUs we already know about +read_last_scan($anvil); + +# Look for changes. +find_changes($anvil); + +# Update the database +$anvil->Database->insert_or_update_updated({updated_by => $THIS_FILE}); + +# Clean up and go away. +$anvil->nice_exit({exit_code => 0}); + + +############################################################################################################# +# Functions # +############################################################################################################# + +# This reads in the last scan's data. +sub read_last_scan +{ + my ($anvil) = @_; + + # Read in existing data, if any. + my $query = " +SELECT + scan_apc_pdu_uuid, + scan_apc_pdu_fence_uuid, + scan_apc_pdu_serial_number, + scan_apc_pdu_model_number, + scan_apc_pdu_manufacture_date, + scan_apc_pdu_firmware_version, + scan_apc_pdu_hardware_version, + scan_apc_pdu_ipv4_address, + scan_apc_pdu_mac_address, + scan_apc_pdu_mtu_size, + scan_apc_pdu_link_speed, + scan_apc_pdu_phase_count, + scan_apc_pdu_outlet_count +FROM + scan_apc_pdus +;"; + $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, + }}); + # One or more records were found. + foreach my $row (@{$results}) + { + my $scan_apc_pdu_uuid = $row->[0]; + my $scan_apc_pdu_fence_uuid = $row->[1]; + my $scan_apc_pdu_serial_number = $row->[2]; + my $scan_apc_pdu_model_number = $row->[3]; + my $scan_apc_pdu_manufacture_date = $row->[4]; + my $scan_apc_pdu_firmware_version = $row->[5]; + my $scan_apc_pdu_hardware_version = $row->[6]; + my $scan_apc_pdu_ipv4_address = $row->[7]; + my $scan_apc_pdu_mac_address = $row->[8]; + my $scan_apc_pdu_mtu_size = $row->[9]; + my $scan_apc_pdu_link_speed = $row->[10]; + my $scan_apc_pdu_phase_count = $row->[11]; + my $scan_apc_pdu_outlet_count = $row->[12]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_pdu_uuid => $scan_apc_pdu_uuid, + scan_apc_pdu_fence_uuid => $scan_apc_pdu_fence_uuid, + scan_apc_pdu_serial_number => $scan_apc_pdu_serial_number, + scan_apc_pdu_model_number => $scan_apc_pdu_model_number, + scan_apc_pdu_manufacture_date => $scan_apc_pdu_manufacture_date, + scan_apc_pdu_firmware_version => $scan_apc_pdu_firmware_version, + scan_apc_pdu_hardware_version => $scan_apc_pdu_hardware_version, + scan_apc_pdu_ipv4_address => $scan_apc_pdu_ipv4_address, + scan_apc_pdu_mac_address => $scan_apc_pdu_mac_address, + scan_apc_pdu_mtu_size => $scan_apc_pdu_mtu_size, + scan_apc_pdu_link_speed => $scan_apc_pdu_link_speed, + scan_apc_pdu_phase_count => $scan_apc_pdu_phase_count, + scan_apc_pdu_outlet_count => $scan_apc_pdu_outlet_count, + }}); + + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_serial_number} = $scan_apc_pdu_serial_number; + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_fence_uuid} = $scan_apc_pdu_fence_uuid; + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_model_number} = $scan_apc_pdu_model_number; + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_manufacture_date} = $scan_apc_pdu_manufacture_date; + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_firmware_version} = $scan_apc_pdu_firmware_version; + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_hardware_version} = $scan_apc_pdu_hardware_version; + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_ipv4_address} = $scan_apc_pdu_ipv4_address; + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_mac_address} = $scan_apc_pdu_mac_address; + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_mtu_size} = $scan_apc_pdu_mtu_size; + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_link_speed} = $scan_apc_pdu_link_speed; + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phase_count} = $scan_apc_pdu_phase_count; + $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlet_count} = $scan_apc_pdu_outlet_count; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_serial_number" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_serial_number}, + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_fence_uuid" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_fence_uuid}, + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_model_number" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_model_number}, + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_manufacture_date" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_manufacture_date}, + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_firmware_version" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_firmware_version}, + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_hardware_version" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_hardware_version}, + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_ipv4_address" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_ipv4_address}, + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_mac_address" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_mac_address}, + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_mtu_size" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_mtu_size}, + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_link_speed" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_link_speed}, + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_phase_count" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phase_count}, + "sql::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_outlet_count" => $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlet_count}, + }}); + } + undef $results; + + # Read in the phase data + $query = " +SELECT + scan_apc_pdu_phase_uuid, + scan_apc_pdu_phase_scan_apc_pdu_uuid, + scan_apc_pdu_phase_number, + scan_apc_pdu_phase_current_amperage, + scan_apc_pdu_phase_max_amperage +FROM + scan_apc_pdu_phases +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + + # Do the query against the source DB and loop through the results. + $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 $scan_apc_pdu_phase_uuid = $row->[0]; + my $scan_apc_pdu_phase_scan_apc_pdu_uuid = $row->[1]; + my $scan_apc_pdu_phase_number = $row->[2]; + my $scan_apc_pdu_phase_current_amperage = $row->[3]; + my $scan_apc_pdu_phase_max_amperage = $row->[4]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_pdu_phase_uuid => $scan_apc_pdu_phase_uuid, + scan_apc_pdu_phase_scan_apc_pdu_uuid => $scan_apc_pdu_phase_scan_apc_pdu_uuid, + scan_apc_pdu_phase_number => $scan_apc_pdu_phase_number, + scan_apc_pdu_phase_current_amperage => $scan_apc_pdu_phase_current_amperage, + scan_apc_pdu_phase_max_amperage => $scan_apc_pdu_phase_max_amperage, + }}); + + # Which serial number does this phase belong to? + my $scan_apc_pdu_serial_number = $anvil->data->{uuid_to_serial}{$scan_apc_pdu_phase_scan_apc_pdu_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_apc_pdu_serial_number => $scan_apc_pdu_serial_number }}); + + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_uuid} = $scan_apc_pdu_phase_uuid; + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_scan_apc_pdu_uuid} = $scan_apc_pdu_phase_scan_apc_pdu_uuid; + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_current_amperage} = $scan_apc_pdu_phase_current_amperage; + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_max_amperage} = $scan_apc_pdu_phase_max_amperage; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_phases::${scan_apc_pdu_phase_number}::scan_apc_pdu_phase_uuid" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_uuid}, + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_phases::${scan_apc_pdu_phase_number}::scan_apc_pdu_phase_scan_apc_pdu_uuid" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_scan_apc_pdu_uuid}, + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_phases::${scan_apc_pdu_phase_number}::scan_apc_pdu_phase_current_amperage" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_current_amperage}, + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_phases::${scan_apc_pdu_phase_number}::scan_apc_pdu_phase_max_amperage" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_max_amperage}, + }}); + } + undef $results; + + # Read in the outlets. + $query = " +SELECT + scan_apc_pdu_outlet_uuid, + scan_apc_pdu_outlet_scan_apc_pdu_uuid, + scan_apc_pdu_outlet_number, + scan_apc_pdu_outlet_name, + scan_apc_pdu_outlet_on_phase, + scan_apc_pdu_outlet_state +FROM + scan_apc_pdu_outlets +;"; + $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}) + { + my $scan_apc_pdu_outlet_uuid = $row->[0]; + my $scan_apc_pdu_outlet_scan_apc_pdu_uuid = $row->[1]; + my $scan_apc_pdu_outlet_number = $row->[2]; + my $scan_apc_pdu_outlet_name = $row->[3]; + my $scan_apc_pdu_outlet_on_phase = $row->[4]; + my $scan_apc_pdu_outlet_state = $row->[5]; + my $scan_apc_pdu_outlet_note = $row->[6]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_pdu_outlet_uuid => $scan_apc_pdu_outlet_uuid, + scan_apc_pdu_outlet_scan_apc_pdu_uuid => $scan_apc_pdu_outlet_scan_apc_pdu_uuid, + scan_apc_pdu_outlet_number => $scan_apc_pdu_outlet_number, + scan_apc_pdu_outlet_name => $scan_apc_pdu_outlet_name, + scan_apc_pdu_outlet_on_phase => $scan_apc_pdu_outlet_on_phase, + scan_apc_pdu_outlet_state => $scan_apc_pdu_outlet_state, + }}); + + # Which serial number does this phase belong to? + my $scan_apc_pdu_serial_number = $anvil->data->{uuid_to_serial}{$scan_apc_pdu_outlet_scan_apc_pdu_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_apc_pdu_serial_number => $scan_apc_pdu_serial_number }}); + + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_uuid} = $scan_apc_pdu_outlet_uuid; + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_scan_apc_pdu_uuid} = $scan_apc_pdu_outlet_scan_apc_pdu_uuid; + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_name} = $scan_apc_pdu_outlet_name; + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_on_phase} = $scan_apc_pdu_outlet_on_phase; + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state} = $scan_apc_pdu_outlet_state; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_outlets::${scan_apc_pdu_outlet_number}::scan_apc_pdu_outlet_uuid" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_uuid}, + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_outlets::${scan_apc_pdu_outlet_number}::scan_apc_pdu_outlet_scan_apc_pdu_uuid" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_scan_apc_pdu_uuid}, + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_outlets::${scan_apc_pdu_outlet_number}::scan_apc_pdu_outlet_name" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_name}, + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_outlets::${scan_apc_pdu_outlet_number}::scan_apc_pdu_outlet_on_phase" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_on_phase}, + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_outlets::${scan_apc_pdu_outlet_number}::scan_apc_pdu_outlet_state" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state}, + }}); + } + undef $results; + + # Read in the history schema + $query = " +SELECT + scan_apc_pdu_variable_uuid, + scan_apc_pdu_variable_scan_apc_pdu_uuid, + scan_apc_pdu_variable_is_temperature, + scan_apc_pdu_variable_name, + scan_apc_pdu_variable_value +FROM + scan_apc_pdu_variables +;"; + $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}) + { + my $scan_apc_pdu_variable_uuid = $row->[0]; + my $scan_apc_pdu_variable_scan_apc_pdu_uuid = $row->[1]; + my $scan_apc_pdu_variable_is_temperature = $row->[2]; + my $scan_apc_pdu_variable_name = $row->[3]; + my $scan_apc_pdu_variable_value = $row->[4]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_pdu_variable_uuid => $scan_apc_pdu_variable_uuid, + scan_apc_pdu_variable_scan_apc_pdu_uuid => $scan_apc_pdu_variable_scan_apc_pdu_uuid, + scan_apc_pdu_variable_is_temperature => $scan_apc_pdu_variable_is_temperature, + scan_apc_pdu_variable_name => $scan_apc_pdu_variable_name, + scan_apc_pdu_variable_value => $scan_apc_pdu_variable_value, + }}); + + # Which serial number does this phase belong to? + my $scan_apc_pdu_serial_number = $anvil->data->{uuid_to_serial}{$scan_apc_pdu_variable_scan_apc_pdu_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_apc_pdu_serial_number => $scan_apc_pdu_serial_number }}); + + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{$scan_apc_pdu_variable_name}{scan_apc_pdu_variable_uuid} = $scan_apc_pdu_variable_uuid; + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{$scan_apc_pdu_variable_name}{scan_apc_pdu_variable_scan_apc_pdu_uuid} = $scan_apc_pdu_variable_scan_apc_pdu_uuid; + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{$scan_apc_pdu_variable_name}{scan_apc_pdu_variable_is_temperature} = $scan_apc_pdu_variable_is_temperature; + $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{$scan_apc_pdu_variable_name}{scan_apc_pdu_variable_value} = $scan_apc_pdu_variable_value; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_variables::${scan_apc_pdu_variable_name}::scan_apc_pdu_variable_uuid" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{$scan_apc_pdu_variable_name}{scan_apc_pdu_variable_uuid}, + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_variables::${scan_apc_pdu_variable_name}::scan_apc_pdu_variable_scan_apc_pdu_uuid" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{$scan_apc_pdu_variable_name}{scan_apc_pdu_variable_scan_apc_pdu_uuid}, + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_variables::${scan_apc_pdu_variable_name}::scan_apc_pdu_variable_is_temperature" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{$scan_apc_pdu_variable_name}{scan_apc_pdu_variable_is_temperature}, + "sql::${scan_apc_pdu_serial_number}::scan_apc_pdu_variables::${scan_apc_pdu_variable_name}::scan_apc_pdu_variable_value" => $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{$scan_apc_pdu_variable_name}{scan_apc_pdu_variable_value}, + }}); + } + + return($count); +} + +# 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) = @_; + + # If we can't ping or can't connect to a PDU, these keys will be used to see how long that's been the + # case to decide when to send an alert. + #my $ping_condition_key = "scan-apc-pdu::".$fence_uuid."::no_ping"; + #my $connection_condition_key = "scan-apc-pdu::".$fence_uuid."::no_connection"; + + # This stores all the queries so that they're committed in one transaction. + my $queries = []; + + # Loop through each PDU we've seen this pass + foreach my $scan_apc_pdu_uuid (sort {$a cmp $b} keys %{$anvil->data->{pdu}{scan_apc_pdu_uuid}}) + { + my $new_pdu = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{new_pdu}; + my $pdu_host_name = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{pdu_host_name}; + my $scan_apc_pdu_fence_uuid = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_fence_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_pdu_uuid => $scan_apc_pdu_uuid, + new_pdu => $new_pdu, + scan_apc_pdu_fence_uuid => $scan_apc_pdu_fence_uuid, + pdu_host_name => $pdu_host_name, + }}); + + # Convert all the long hashes into shorter variables + my $new_scan_apc_pdu_serial_number = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_serial_number}; + my $new_scan_apc_pdu_model_number = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_model_number}; + my $new_scan_apc_pdu_manufacture_date = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_manufacture_date}; + my $new_scan_apc_pdu_firmware_version = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_firmware_version}; + my $new_scan_apc_pdu_hardware_version = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_hardware_version}; + my $new_scan_apc_pdu_ipv4_address = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_ipv4_address}; + my $new_scan_apc_pdu_mac_address = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address}; + my $new_scan_apc_pdu_mtu_size = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mtu_size}; + my $new_scan_apc_pdu_link_speed = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_link_speed}; + my $new_scan_apc_pdu_phase_count = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_phase_count}; + my $new_scan_apc_pdu_outlet_count = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_outlet_count}; + my $new_uptime = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{uptime}; + my $new_total_wattage_draw = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{total_wattage_draw}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + new_scan_apc_pdu_serial_number => $new_scan_apc_pdu_serial_number, + new_scan_apc_pdu_model_number => $new_scan_apc_pdu_model_number, + new_scan_apc_pdu_manufacture_date => $new_scan_apc_pdu_manufacture_date, + new_scan_apc_pdu_firmware_version => $new_scan_apc_pdu_firmware_version, + new_scan_apc_pdu_hardware_version => $new_scan_apc_pdu_hardware_version, + new_scan_apc_pdu_ipv4_address => $new_scan_apc_pdu_ipv4_address, + new_scan_apc_pdu_mac_address => $new_scan_apc_pdu_mac_address, + new_scan_apc_pdu_mtu_size => $new_scan_apc_pdu_mtu_size, + new_scan_apc_pdu_link_speed => $new_scan_apc_pdu_link_speed, + new_scan_apc_pdu_phase_count => $new_scan_apc_pdu_phase_count, + new_scan_apc_pdu_outlet_count => $new_scan_apc_pdu_outlet_count, + new_uptime => $new_uptime, + new_total_wattage_draw => $new_total_wattage_draw, + }}); + + # These are used later for checking the state of the phases. They are not stored in the + # database, except for max amps. They should never change. + my $new_scan_apc_pdu_phase_max_amperage = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{maximum}; + my $new_phase_low_amp_warning = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{low_warning}; + my $new_phase_high_amp_warning = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_warning}; + my $new_phase_high_amp_critical = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_critical}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + new_scan_apc_pdu_phase_max_amperage => $new_scan_apc_pdu_phase_max_amperage, + new_phase_low_amp_warning => $new_phase_low_amp_warning, + new_phase_high_amp_warning => $new_phase_high_amp_warning, + new_phase_high_amp_critical => $new_phase_high_amp_critical, + }}); + + # Have I seen this PDU before? + if (not $new_pdu) + { + # Yup. Search for changes. + my $old_scan_apc_pdu_serial_number = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_serial_number}; + my $old_scan_apc_pdu_model_number = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_model_number}; + my $old_scan_apc_pdu_manufacture_date = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_manufacture_date}; + my $old_scan_apc_pdu_firmware_version = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_firmware_version}; + my $old_scan_apc_pdu_hardware_version = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_hardware_version}; + my $old_scan_apc_pdu_ipv4_address = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_ipv4_address}; + my $old_scan_apc_pdu_mac_address = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_mac_address}; + my $old_scan_apc_pdu_mtu_size = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_mtu_size}; + my $old_scan_apc_pdu_link_speed = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_link_speed}; + my $old_scan_apc_pdu_phase_count = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phase_count}; + my $old_scan_apc_pdu_outlet_count = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlet_count}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + old_scan_apc_pdu_model_number => $old_scan_apc_pdu_model_number, + old_scan_apc_pdu_manufacture_date => $old_scan_apc_pdu_manufacture_date, + old_scan_apc_pdu_firmware_version => $old_scan_apc_pdu_firmware_version, + old_scan_apc_pdu_hardware_version => $old_scan_apc_pdu_hardware_version, + old_scan_apc_pdu_ipv4_address => $old_scan_apc_pdu_ipv4_address, + old_scan_apc_pdu_mac_address => $old_scan_apc_pdu_mac_address, + old_scan_apc_pdu_mtu_size => $old_scan_apc_pdu_mtu_size, + old_scan_apc_pdu_link_speed => $old_scan_apc_pdu_link_speed, + old_scan_apc_pdu_phase_count => $old_scan_apc_pdu_phase_count, + old_scan_apc_pdu_outlet_count => $old_scan_apc_pdu_outlet_count, + }}); + + # Has the phase data changed? + my $changes = 0; + if ($new_scan_apc_pdu_model_number ne $old_scan_apc_pdu_model_number) + { + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + if ($old_scan_apc_pdu_model_number eq "DELETED") + { + # It's back. + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + + my $variables = { + name => $pdu_host_name, + serial_number => $new_scan_apc_pdu_serial_number, + ip_address => $new_scan_apc_pdu_ipv4_address, + } + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0006", variables => $variables}); + $anvil->Alert->register({ + alert_level => "warning", + message => "scan_apc_pdu_message_0006", + message_variables => $variables, + set_by => $THIS_FILE, + sort_position => $anvil->data->{'scan-apc-pdu'}{alert_sort}++, + }); + } + else + { + my $variables = { + name => $pdu_host_name, + old_model_number => $old_scan_apc_pdu_model_number, + new_model_number => $new_scan_apc_pdu_model_number, + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0007", variables => $variables}); + $anvil->Alert->register({ + alert_level => "warning", + message => "scan_apc_pdu_message_0007", + message_variables => $variables, + set_by => $THIS_FILE, + sort_position => $anvil->data->{'scan-apc-pdu'}{alert_sort}++, + }); + } + } + if ($new_scan_apc_pdu_serial_number ne $old_scan_apc_pdu_serial_number) + { + } + if ($new_scan_apc_pdu_manufacture_date ne $old_scan_apc_pdu_manufacture_date) + { + # Wat? + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0011", + alert_message_variables => { + name => $pdu_host_name, + old_manufacture_date => $old_scan_apc_pdu_manufacture_date, + new_manufacture_date => $new_scan_apc_pdu_manufacture_date, + }, + }); + } + if ($new_scan_apc_pdu_firmware_version ne $old_scan_apc_pdu_firmware_version) + { + # This is about the only thing that should ever change. + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0016", + alert_message_variables => { + name => $pdu_host_name, + old_firmware_version => $old_scan_apc_pdu_firmware_version, + new_firmware_version => $new_scan_apc_pdu_firmware_version, + }, + }); + } + if ($new_scan_apc_pdu_hardware_version ne $old_scan_apc_pdu_hardware_version) + { + # Wat? + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0017", + alert_message_variables => { + name => $pdu_host_name, + old_hardware_version => $old_scan_apc_pdu_hardware_version, + new_hardware_version => $new_scan_apc_pdu_hardware_version, + }, + }); + } + if ($new_scan_apc_pdu_ipv4_address ne $old_scan_apc_pdu_ipv4_address) + { + # This could change, sure. + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0018", + alert_message_variables => { + name => $pdu_host_name, + old_ipv4_address => $old_scan_apc_pdu_ipv4_address, + new_ipv4_address => $new_scan_apc_pdu_ipv4_address, + }, + }); + } + if ($new_scan_apc_pdu_mac_address ne $old_scan_apc_pdu_mac_address) + { + # How?! + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0019", + alert_message_variables => { + name => $pdu_host_name, + old_mac_address => $old_scan_apc_pdu_mac_address, + new_mac_address => $new_scan_apc_pdu_mac_address, + }, + }); + } + if ($new_scan_apc_pdu_mtu_size ne $old_scan_apc_pdu_mtu_size) + { + # OK, maybe... + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0020", + alert_message_variables => { + name => $pdu_host_name, + old_mtu_size => $old_scan_apc_pdu_mtu_size, + new_mtu_size => $new_scan_apc_pdu_mtu_size, + }, + }); + } + if ($new_scan_apc_pdu_link_speed ne $old_scan_apc_pdu_link_speed) + { + # This could happen, too. + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + + my $say_new_link_speed_hr = $anvil->Convert->bytes_to_human_readable({ 'bytes' => ($new_scan_apc_pdu_link_speed / 8) })."/s"; + my $say_new_link_speed_mbps = ($new_scan_apc_pdu_link_speed / 100000)." Mbps"; + my $say_old_link_speed_hr = $anvil->Convert->bytes_to_human_readable({ 'bytes' => ($old_scan_apc_pdu_link_speed / 8) })."/s"; + my $say_old_link_speed_mbps = ($old_scan_apc_pdu_link_speed / 100000)." Mbps"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "say_new_link_speed_mbps" => $say_new_link_speed_mbps, + "say_new_link_speed_hr" => $say_new_link_speed_hr, + "say_old_link_speed_mbps" => $say_old_link_speed_mbps, + "say_old_link_speed_hr" => $say_old_link_speed_hr, + }}); + + # The speed has changed. Make this a proper warning + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0004", + alert_message_key => "scan_apc_pdu_message_0022", + alert_message_variables => { + name => $pdu_host_name, + old_link_speed_mbps => $say_old_link_speed_mbps, + old_link_speed_hr => $say_old_link_speed_hr, + new_link_speed_mbps => $say_new_link_speed_mbps, + new_link_speed_hr => $say_new_link_speed_hr, + }, + }); + } + if ($new_scan_apc_pdu_phase_count ne $old_scan_apc_pdu_phase_count) + { + # How?! + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0023", + alert_message_variables => { + name => $pdu_host_name, + old_phase_count => sprintf("%02d", $old_scan_apc_pdu_phase_count), + new_phase_count => sprintf("%02d", $new_scan_apc_pdu_phase_count), + }, + }); + } + if ($new_scan_apc_pdu_outlet_count ne $old_scan_apc_pdu_outlet_count) + { + # I don't even... + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0024", + alert_message_variables => { + name => $pdu_host_name, + old_outlet_count => sprintf("%02d", $old_scan_apc_pdu_outlet_count), + new_outlet_count => sprintf("%02d", $new_scan_apc_pdu_outlet_count), + }, + }); + } + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + if ($changes) + { + # Save the changes. + my $query = " +UPDATE + public.scan_apc_pdus +SET + scan_apc_pdu_serial_number = ".$anvil->Database->quote($scan_apc_pdu_serial_number).", + scan_apc_pdu_model_number = ".$anvil->Database->quote($new_scan_apc_pdu_model_number).", + scan_apc_pdu_manufacture_date = ".$anvil->Database->quote($new_scan_apc_pdu_manufacture_date).", + scan_apc_pdu_firmware_version = ".$anvil->Database->quote($new_scan_apc_pdu_firmware_version).", + scan_apc_pdu_hardware_version = ".$anvil->Database->quote($new_scan_apc_pdu_hardware_version).", + scan_apc_pdu_ipv4_address = ".$anvil->Database->quote($new_scan_apc_pdu_ipv4_address).", + scan_apc_pdu_mac_address = ".$anvil->Database->quote($new_scan_apc_pdu_mac_address).", + scan_apc_pdu_mtu_size = ".$anvil->Database->quote($new_scan_apc_pdu_mtu_size).", + scan_apc_pdu_link_speed = ".$anvil->Database->quote($new_scan_apc_pdu_link_speed).", + scan_apc_pdu_phase_count = ".$anvil->Database->quote($new_scan_apc_pdu_phase_count).", + scan_apc_pdu_outlet_count = ".$anvil->Database->quote($new_scan_apc_pdu_outlet_count).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_apc_pdu_uuid = ".$anvil->Database->quote($scan_apc_pdu_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{sql}}, $query; + } + + ### What about other variables? + # Do I have a previous uptime? + if ($anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{uptime}{scan_apc_pdu_variable_uuid}) + { + # Exists, change? + my $scan_apc_pdu_variable_uuid = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{uptime}{scan_apc_pdu_variable_uuid}; + my $old_uptime = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{uptime}{scan_apc_pdu_variable_value}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_pdu_variable_uuid" => $scan_apc_pdu_variable_uuid, + "old_uptime" => $old_uptime, + }}); + + if ($new_uptime ne $old_uptime) + { + update_variables($anvil, $scan_apc_pdu_uuid, $scan_apc_pdu_variable_uuid, "uptime", $new_uptime); + + # We expect this to change. If the new uptime is less than the old + # one, it rebooted. + if ($new_uptime > $old_uptime) + { + # Normal, info-level + $anvil->Alert->register({ + alert_level => "info", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_pdu_message_0013", + alert_message_variables => { + name => $pdu_host_name, + old_uptime => $anvil->Readable->time({ 'time' => $old_uptime }), + new_uptime => $anvil->Readable->time({ 'time' => $new_uptime }), + }, + }); + } + elsif ($new_uptime < 300) + { + # It's rebooted. This is notice level as it's generally just + # the NIC rebooting and doesn't interfere with power. + $anvil->Alert->register({ + alert_level => "notice", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_pdu_message_0014", + alert_message_variables => { + name => $pdu_host_name, + old_uptime => $anvil->Readable->time({ 'time' => $old_uptime }), + new_uptime => $anvil->Readable->time({ 'time' => $new_uptime }), + }, + }); + } + } + } + else + { + # New, INSERT it (is_temperature is always false for now). + insert_variables($anvil, $scan_apc_pdu_uuid, "uptime", $new_uptime); + } + + # Do I have a previous wattage? + if ($anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{total_wattage_draw}{scan_apc_pdu_variable_uuid}) + { + # Exists, change? + my $scan_apc_pdu_variable_uuid = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{total_wattage_draw}{scan_apc_pdu_variable_uuid}; + my $old_total_wattage_draw = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_variables}{total_wattage_draw}{scan_apc_pdu_variable_value}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_pdu_variable_uuid" => $scan_apc_pdu_variable_uuid, + "old_total_wattage_draw" => $old_total_wattage_draw, + }}); + + if ($new_total_wattage_draw ne $old_total_wattage_draw) + { + update_variables($anvil, $scan_apc_pdu_uuid, $scan_apc_pdu_variable_uuid, "total_wattage_draw", $new_total_wattage_draw); + + # We expect this to change. + $anvil->Alert->register({ + alert_level => "info", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_pdu_message_0015", + alert_message_variables => { + name => $pdu_host_name, + old_total_wattage_draw => $old_total_wattage_draw, + new_total_wattage_draw => $new_total_wattage_draw, + }, + }); + } + } + else + { + # New, INSERT it (is_temperature is always false for now). + insert_variables($anvil, $scan_apc_pdu_uuid, "total_wattage_draw", $new_total_wattage_draw); + } + + # Have any phases changed? + foreach my $scan_apc_pdu_phase_number (sort {$a cmp $b} keys %{$anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phases}}) + { + my $new_scan_apc_pdu_phase_current_amperage = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_current_amperage}; + my $new_scan_apc_pdu_phase_note = ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_pdu_phase_number" => $scan_apc_pdu_phase_number, + "new_scan_apc_pdu_phase_current_amperage" => $new_scan_apc_pdu_phase_current_amperage, + "new_scan_apc_pdu_phase_max_amperage" => $new_scan_apc_pdu_phase_max_amperage, + }}); + + if ($anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_uuid}) + { + # We've seen this phase before. Changes? + my $scan_apc_pdu_phase_uuid = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_uuid}; + my $old_scan_apc_pdu_phase_scan_apc_pdu_uuid = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_scan_apc_pdu_uuid}; + my $old_scan_apc_pdu_phase_current_amperage = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_current_amperage}; + my $old_scan_apc_pdu_phase_max_amperage = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_max_amperage}; + my $old_scan_apc_pdu_phase_note = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_note}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_pdu_phase_uuid" => $scan_apc_pdu_phase_uuid, + "old_scan_apc_pdu_phase_scan_apc_pdu_uuid" => $old_scan_apc_pdu_phase_scan_apc_pdu_uuid, + "old_scan_apc_pdu_phase_current_amperage" => $old_scan_apc_pdu_phase_current_amperage, + "old_scan_apc_pdu_phase_max_amperage" => $old_scan_apc_pdu_phase_max_amperage, + "old_scan_apc_pdu_phase_note" => $old_scan_apc_pdu_phase_note, + }}); + + # The max amps per phase is set across all phases. + if ($new_scan_apc_pdu_phase_max_amperage ne $old_scan_apc_pdu_phase_max_amperage) + { + # This should never change, but OK, whatever + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0004", + alert_message_key => "scan_apc_pdu_message_0025", + alert_message_variables => { + name => $pdu_host_name, + phase => sprintf("%02d", $scan_apc_pdu_phase_number), + old_phase_max_amperage => $old_scan_apc_pdu_phase_max_amperage, + new_phase_max_amperage => $new_scan_apc_pdu_phase_max_amperage, + }, + }); + } + if ($old_scan_apc_pdu_phase_current_amperage ne $new_scan_apc_pdu_phase_current_amperage) + { + # The phase amperage can change all the time. + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "info", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_pdu_message_0026", + alert_message_variables => { + name => $pdu_host_name, + phase => sprintf("%02d", $scan_apc_pdu_phase_number), + old_phase_current_amperage => $old_scan_apc_pdu_phase_current_amperage, + new_phase_current_amperage => $new_scan_apc_pdu_phase_current_amperage, + }, + }); + } + + # If the note was 'DELETED', the phase has returned. + if ($old_scan_apc_pdu_phase_note eq "DELETED") + { + # The phase amperage can change all the time. + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0006", + alert_message_key => "scan_apc_pdu_message_0027", + alert_message_variables => { + name => $pdu_host_name, + phase => sprintf("%02d", $scan_apc_pdu_phase_number), + }, + }); + } + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + if ($changes) + { + my $query = " +UPDATE + scan_apc_pdu_phases +SET + scan_apc_pdu_phase_scan_apc_pdu_uuid = ".$anvil->Database->quote($scan_apc_pdu_uuid).", + scan_apc_pdu_phase_number = ".$anvil->Database->quote($scan_apc_pdu_phase_number).", + scan_apc_pdu_phase_current_amperage = ".$anvil->Database->quote($new_scan_apc_pdu_phase_current_amperage).", + scan_apc_pdu_phase_max_amperage = ".$anvil->Database->quote($new_scan_apc_pdu_phase_max_amperage).", + scan_apc_pdu_phase_note = ".$anvil->Database->quote($new_scan_apc_pdu_phase_note).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_apc_pdu_phase_uuid = ".$anvil->Database->quote($scan_apc_pdu_phase_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{sql}}, $query; + } + + # Cheack for low alerts to set or high alerts to clear + if ($new_scan_apc_pdu_phase_current_amperage < $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{low_warning}) + { + # Dropped too low, clear high warn and high crit + set_phase_low_warning($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number); + + # Clear the high-warning and high-critical, if needed. + clear_phase_high_critical($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number); + clear_phase_high_warning($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number); + } + elsif ($new_scan_apc_pdu_phase_current_amperage < $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{clear_high_warning}) + { + # Dropped low enough to clear high warn and high crit + clear_phase_high_critical($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number); + clear_phase_high_warning($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number); + } + elsif ($new_scan_apc_pdu_phase_current_amperage < $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{clear_high_critical}) + { + # Dropped low enough to clear high crit. + clear_phase_high_critical($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number); + } + + # Cheack for low alerts to clear or high alerts to set + if ($new_scan_apc_pdu_phase_current_amperage > $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_critical}) + { + # Set high crit, clear low warn + set_phase_high_critical($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number); + clear_phase_low_warning($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number); + } + elsif ($new_scan_apc_pdu_phase_current_amperage > $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_warning}) + { + # Set high warn + set_phase_high_warning($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number); + clear_phase_low_warning($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number); + } + elsif ($new_scan_apc_pdu_phase_current_amperage > $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{clear_low_warning}) + { + # Clear the low warning + clear_phase_low_warning($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number); + } + } + + delete $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}; + } + + # Any phases that vanished? + foreach my $scan_apc_pdu_phase_number (sort {$a cmp $b} keys %{$anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}}) + { + # Did it just vanish? + my $scan_apc_pdu_phase_uuid = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_uuid}; + my $scan_apc_pdu_phase_note = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_note}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_pdu_phase_uuid" => $scan_apc_pdu_phase_uuid, + "scan_apc_pdu_phase_note" => $scan_apc_pdu_phase_note, + }}); + if ($scan_apc_pdu_phase_note ne "DELETED") + { + # Yup + my $query = " +UPDATE + scan_apc_pdu_phases +SET + scan_apc_pdu_phase_note = 'DELETED', + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_apc_pdu_phase_uuid = ".$anvil->Database->quote($scan_apc_pdu_phase_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{sql}}, $query; + + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0004", + alert_message_key => "scan_apc_pdu_message_0028", + alert_message_variables => { + name => $pdu_host_name, + phase => sprintf("%02d", $scan_apc_pdu_phase_number), + }, + }); + } + } + + # Have any outlets changed? + foreach my $scan_apc_pdu_outlet_number (sort {$a cmp $b} keys %{$anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}}) + { + my $new_scan_apc_pdu_outlet_name = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_name}; + my $new_scan_apc_pdu_outlet_on_phase = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_on_phase}; + my $new_scan_apc_pdu_outlet_state = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state}; + my $new_scan_apc_pdu_outlet_note = ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_pdu_outlet_name" => $new_scan_apc_pdu_outlet_name, + "new_scan_apc_pdu_outlet_on_phase" => $new_scan_apc_pdu_outlet_on_phase, + "new_scan_apc_pdu_outlet_state" => $new_scan_apc_pdu_outlet_state, + }}); + + # Do I know about this outlet? + if ($anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_uuid}) + { + # Yup! + my $scan_apc_pdu_outlet_uuid = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_uuid}; + my $old_scan_apc_pdu_outlet_note = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_note}; + my $old_scan_apc_pdu_outlet_name = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_name}; + my $old_scan_apc_pdu_outlet_on_phase = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_on_phase}; + my $old_scan_apc_pdu_outlet_state = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_pdu_outlet_uuid" => $scan_apc_pdu_outlet_uuid, + "old_scan_apc_pdu_outlet_name" => $old_scan_apc_pdu_outlet_name, + "old_scan_apc_pdu_outlet_on_phase" => $old_scan_apc_pdu_outlet_on_phase, + "old_scan_apc_pdu_outlet_state" => $old_scan_apc_pdu_outlet_state, + "old_scan_apc_pdu_outlet_note" => $old_scan_apc_pdu_outlet_note, + }}); + + # Did a vanished outlet come back? + my $changes = 0; + if ($old_scan_apc_pdu_outlet_note eq "DELETED") + { + # It's back! + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0004", + alert_message_key => "scan_apc_pdu_message_0037", + alert_message_variables => { + name => $pdu_host_name, + outlet => sprintf("%02d", $scan_apc_pdu_outlet_number), + }, + }); + } + if ($new_scan_apc_pdu_outlet_name ne $old_scan_apc_pdu_outlet_name) + { + # Name changed. + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0004", + alert_message_key => "scan_apc_pdu_message_0038", + alert_message_variables => { + name => $pdu_host_name, + outlet => sprintf("%02d", $scan_apc_pdu_outlet_number), + old_outlet_name => $old_scan_apc_pdu_outlet_name, + new_outlet_name => $new_scan_apc_pdu_outlet_name, + }, + }); + } + if ($new_scan_apc_pdu_outlet_on_phase ne $old_scan_apc_pdu_outlet_on_phase) + { + # Phase changed, but why tho? + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0004", + alert_message_key => "scan_apc_pdu_message_0039", + alert_message_variables => { + name => $pdu_host_name, + outlet => sprintf("%02d", $scan_apc_pdu_outlet_number), + old_outlet_on_phase => $old_scan_apc_pdu_outlet_on_phase, + new_outlet_on_phase => $new_scan_apc_pdu_outlet_on_phase, + }, + }); + } + if ($new_scan_apc_pdu_outlet_state ne $old_scan_apc_pdu_outlet_state) + { + # This is likely from a fence action, so we make it critical + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + $anvil->Alert->register({ + alert_level => "critical", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0005", + alert_message_key => "scan_apc_pdu_message_0040", + alert_message_variables => { + name => $pdu_host_name, + outlet => sprintf("%02d", $scan_apc_pdu_outlet_number), + old_outlet_state => $old_scan_apc_pdu_outlet_state, + new_outlet_state => $new_scan_apc_pdu_outlet_state, + }, + }); + } + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); + if ($changes) + { + # Save. + update_outlets($anvil, $scan_apc_pdu_uuid, $scan_apc_pdu_outlet_uuid, $scan_apc_pdu_outlet_number, $new_scan_apc_pdu_outlet_name, $new_scan_apc_pdu_outlet_on_phase, $new_scan_apc_pdu_outlet_state, $new_scan_apc_pdu_outlet_note); + } + + delete $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}; + } + else + { + # Nope, INSERT it. + insert_outlets($anvil, $scan_apc_pdu_uuid, $scan_apc_pdu_outlet_number, $new_scan_apc_pdu_outlet_name, $new_scan_apc_pdu_outlet_on_phase, $new_scan_apc_pdu_outlet_state, ""); + + my $say_state = "#!string!an_state_0001!#"; + if ($new_scan_apc_pdu_outlet_state eq "on") + { + + $say_state = "#!string!an_state_0005!#"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_pdu_outlet_state" => $new_scan_apc_pdu_outlet_state, + }}); + } + elsif ($new_scan_apc_pdu_outlet_state eq "off") + { + $say_state = "#!string!an_state_0006!#"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_pdu_outlet_state" => $new_scan_apc_pdu_outlet_state, + }}); + } + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0036", + alert_message_variables => { + pdu_name => $pdu_host_name, + outlet => sprintf("%02d", $scan_apc_pdu_outlet_number), + 'state' => $say_state, + on_phase => sprintf("%02d", $new_scan_apc_pdu_outlet_on_phase), + name => $new_scan_apc_pdu_outlet_name, + }, + }); + } + } + + # Any outlets vanished? + foreach my $scan_apc_pdu_outlet_number (sort {$a cmp $b} keys %{$anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}}) + { + # Lost, update the note. + my $scan_apc_pdu_outlet_uuid = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_uuid}; + my $old_scan_apc_pdu_outlet_note = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_note}; + my $old_scan_apc_pdu_outlet_name = $anvil->data->{sql}{$scan_apc_pdu_serial_number}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_pdu_outlet_uuid" => $scan_apc_pdu_outlet_uuid, + "old_scan_apc_pdu_outlet_name" => $old_scan_apc_pdu_outlet_name, + "old_scan_apc_pdu_outlet_note" => $old_scan_apc_pdu_outlet_note, + }}); + + if ($old_scan_apc_pdu_outlet_note ne "DELETED") + { + # Yup + my $query = " +UPDATE + scan_apc_pdu_outlets +SET + scan_apc_pdu_outlet_value = 'DELETED', + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_apc_pdu_outlet_uuid = ".$anvil->Database->quote($scan_apc_pdu_outlet_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{sql}}, $query; + + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0004", + alert_message_key => "scan_apc_pdu_message_0035", + alert_message_variables => { + name => $pdu_host_name, + outlet => sprintf("%02d", $scan_apc_pdu_outlet_number), + }, + }); + } + } + + # Delete this from the SQL history + delete $anvil->data->{sql}{$scan_apc_pdu_serial_number}; + } + else + { + # OK, now INSERT it. + my $query = " +INSERT INTO + scan_apc_pdus +( + scan_apc_pdu_uuid, + scan_apc_pdu_serial_number, + scan_apc_pdu_model_number, + scan_apc_pdu_manufacture_date, + scan_apc_pdu_firmware_version, + scan_apc_pdu_hardware_version, + scan_apc_pdu_ipv4_address, + scan_apc_pdu_mac_address, + scan_apc_pdu_mtu_size, + scan_apc_pdu_link_speed, + scan_apc_pdu_phase_count, + scan_apc_pdu_outlet_count, + modified_date +) VALUES ( + ".$anvil->Database->quote($scan_apc_pdu_uuid).", + ".$anvil->Database->quote($scan_apc_pdu_serial_number).", + ".$anvil->Database->quote($new_scan_apc_pdu_model_number).", + ".$anvil->Database->quote($new_scan_apc_pdu_manufacture_date).", + ".$anvil->Database->quote($new_scan_apc_pdu_firmware_version).", + ".$anvil->Database->quote($new_scan_apc_pdu_hardware_version).", + ".$anvil->Database->quote($new_scan_apc_pdu_ipv4_address).", + ".$anvil->Database->quote($new_scan_apc_pdu_mac_address).", + ".$anvil->Database->quote($new_scan_apc_pdu_mtu_size).", + ".$anvil->Database->quote($new_scan_apc_pdu_link_speed).", + ".$anvil->Database->quote($new_scan_apc_pdu_phase_count).", + ".$anvil->Database->quote($new_scan_apc_pdu_outlet_count).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + + # Send an alert telling the user about this new PDU. + my $say_link_speed_hr = "#!string!scan_apc_pdu_message_0006!#"; # "Down" + my $say_link_speed_mbps = "#!string!scan_apc_pdu_message_0006!#"; # "Down" + if (($new_scan_apc_pdu_link_speed) && ($new_scan_apc_pdu_link_speed =~ /^\d+$/)) + { + $say_link_speed_hr = $anvil->Convert->bytes_to_human_readable({ 'bytes' => ($new_scan_apc_pdu_link_speed / 8) })."/s"; + $say_link_speed_mbps = ($new_scan_apc_pdu_link_speed / 1000000)." Mbps"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "say_link_speed_hr" => $say_link_speed_hr, + "say_link_speed_mbps" => $say_link_speed_mbps, + }}); + } + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0007", + alert_message_variables => { + name => $pdu_host_name, + serial_number => $scan_apc_pdu_serial_number, + model_number => $new_scan_apc_pdu_model_number, + manufacture_date => $new_scan_apc_pdu_manufacture_date." #!string!tools_suffix_0082!#", + firmware_version => $new_scan_apc_pdu_firmware_version, + hardware_version => $new_scan_apc_pdu_hardware_version, + ipv4_address => $new_scan_apc_pdu_ipv4_address, + mac_address => $new_scan_apc_pdu_mac_address, + mtu_size => $new_scan_apc_pdu_mtu_size, + link_speed_hr => $say_link_speed_hr, + link_speed_mbps => $say_link_speed_mbps, + phase_count => $new_scan_apc_pdu_phase_count, + outlet_count => $new_scan_apc_pdu_outlet_count, + phase_max_amperage => $new_scan_apc_pdu_phase_max_amperage, + phase_low_amp_warning => $new_phase_low_amp_warning, + phase_high_amp_warning => $new_phase_high_amp_warning, + phase_high_amp_critical => $new_phase_high_amp_critical, + uptime => $anvil->Readable->time({'time' => $new_uptime}), + total_wattage_draw => $new_total_wattage_draw, + }, + }); + + # Record the variables (uptime and wattage) + foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}}) + { + my $value = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{$variable}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "variable" => $variable, + "value" => $value, + }}); + + insert_variables($anvil, $scan_apc_pdu_uuid, $variable, $value); + + # NOTE: We don't send an alert here as we incorporated the two variables we care about above. + } + + # Insert the phase data + foreach my $scan_apc_pdu_phase_number (sort {$a cmp $b} keys %{$anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phases}}) + { + my $scan_apc_pdu_phase_uuid = $anvil->Get->uuid(); + my $new_scan_apc_pdu_phase_current_amperage = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_current_amperage}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_pdu_phase_uuid" => $scan_apc_pdu_phase_uuid, + "new_scan_apc_pdu_phase_current_amperage" => $new_scan_apc_pdu_phase_current_amperage, + }}); + + my $query = " +INSERT INTO + scan_apc_pdu_phases +( + scan_apc_pdu_phase_uuid, + scan_apc_pdu_phase_scan_apc_pdu_uuid, + scan_apc_pdu_phase_number, + scan_apc_pdu_phase_current_amperage, + scan_apc_pdu_phase_max_amperage, + scan_apc_pdu_phase_note, + modified_date +) VALUES ( + ".$anvil->Database->quote($scan_apc_pdu_phase_uuid).", + ".$anvil->Database->quote($scan_apc_pdu_uuid).", + ".$anvil->Database->quote($scan_apc_pdu_phase_number).", + ".$anvil->Database->quote($new_scan_apc_pdu_phase_current_amperage).", + ".$anvil->Database->quote($new_scan_apc_pdu_phase_max_amperage).", + '', + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{sql}}, $query; + + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_header => "FALSE", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0008", + alert_message_variables => { + phase => sprintf("%02d", $scan_apc_pdu_phase_number), + amps => $new_scan_apc_pdu_phase_current_amperage, + }, + }); + } + + # Insert the outlet data + foreach my $scan_apc_pdu_outlet_number (sort {$a cmp $b} keys %{$anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}}) + { + my $scan_apc_pdu_outlet_uuid = $anvil->Get->uuid(); + my $new_scan_apc_pdu_outlet_name = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_name}; + my $new_scan_apc_pdu_outlet_on_phase = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_on_phase}; + my $new_scan_apc_pdu_outlet_state = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_pdu_outlet_uuid" => $scan_apc_pdu_outlet_uuid, + "new_scan_apc_pdu_outlet_name" => $new_scan_apc_pdu_outlet_name, + "new_scan_apc_pdu_outlet_on_phase" => $new_scan_apc_pdu_outlet_on_phase, + "new_scan_apc_pdu_outlet_state" => $new_scan_apc_pdu_outlet_state, + }}); + + insert_outlets($anvil, $scan_apc_pdu_uuid, $scan_apc_pdu_outlet_number, $new_scan_apc_pdu_outlet_name, $new_scan_apc_pdu_outlet_on_phase, $new_scan_apc_pdu_outlet_state, ""); + + my $say_state = "#!string!an_state_0001!#"; + if ($new_scan_apc_pdu_outlet_state eq "on") + { + + $say_state = "#!string!an_state_0005!#"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_pdu_outlet_state" => $new_scan_apc_pdu_outlet_state, + }}); + } + elsif ($new_scan_apc_pdu_outlet_state eq "off") + { + $say_state = "#!string!an_state_0006!#"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_pdu_outlet_state" => $new_scan_apc_pdu_outlet_state, + }}); + } + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_header => "FALSE", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0008", + alert_message_key => "scan_apc_pdu_message_0009", + alert_message_variables => { + outlet => sprintf("%02d", $scan_apc_pdu_outlet_number), + 'state' => $say_state, + on_phase => sprintf("%02d", $new_scan_apc_pdu_outlet_on_phase), + name => $new_scan_apc_pdu_outlet_name, + }, + }); + } + } + } + + # Look for PDUs that we saw before that are gone now. + foreach my $scan_apc_pdu_serial_number (sort {$a cmp $b} keys %{$anvil->data->{sql}}) + { + # Gone! Is this the first time it's disappeared? + my $scan_apc_pdu_uuid = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_uuid}; + my $scan_apc_pdu_model_number = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_model_number}; + my $scan_apc_pdu_ipv4_address = $anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_ipv4_address}; + $scan_apc_pdu_ipv4_address = "--" if not defined $scan_apc_pdu_ipv4_address; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_pdu_serial_number" => $scan_apc_pdu_serial_number, + "scan_apc_pdu_uuid" => $scan_apc_pdu_uuid, + "scan_apc_pdu_model_number" => $scan_apc_pdu_model_number, + "scan_apc_pdu_ipv4_address" => $scan_apc_pdu_ipv4_address, + "dont_delete::$scan_apc_pdu_ipv4_address" => $scan_apc_pdu_ipv4_address, + }}); + + if ($scan_apc_pdu_model_number ne "DELETED") + { + # Yup! send an alert. + my $query = " +UPDATE + scan_apc_pdus +SET + scan_apc_pdu_model_number = 'DELETED', + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_apc_pdu_uuid = ".$anvil->Database->quote($scan_apc_pdu_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{sql}}, $query; + + $anvil->Alert->register({ + alert_level => "warning", + alert_sort => $anvil->data->{sys}{alert_sort}++, + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0004", + alert_message_key => "scan_apc_pdu_message_0041", + alert_message_variables => { + model => $scan_apc_pdu_model_number, + serial_numer => $scan_apc_pdu_serial_number, + ip_address => $scan_apc_pdu_ipv4_address, + }, + }); + } + } + + # Commit the queries. + $anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__}); + + return(0); +} + +# Set the phase-low alert, if not set before +sub set_phase_low_warning +{ + my ($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number) = @_; + +# my $set = $anvil->Alert->check_alert_sent({ +# type => "warning", +# alert_sent_by => $THIS_FILE, +# alert_record_locator => $scan_apc_pdu_serial_number, +# alert_name => "phase_".$scan_apc_pdu_phase_number."_amp_low_warning", +# modified_date => $anvil->data->{sys}{database}{timestamp}, +# }); +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { +# "set" => $set, +# }}); +# if ($set) +# { +# # This is a new loss of comms. +# $anvil->Alert->register({ +# alert_level => "warning", +# alert_agent_name => $THIS_FILE, +# alert_title_key => "an_alert_title_0004", +# alert_message_key => "scan_apc_pdu_message_0029", +# alert_message_variables => { +# pdu_name => $pdu_host_name, +# phase => sprintf("%02d", $scan_apc_pdu_phase_number), +# }, +# }); +# } + + return(0); +} + +# Set the phase-low alert, if not clear before +sub clear_phase_low_warning +{ + my ($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number) = @_; + +# my $clear = $anvil->Alert->check_alert_sent({ +# type => "clear", +# alert_sent_by => $THIS_FILE, +# alert_record_locator => $scan_apc_pdu_serial_number, +# alert_name => "phase_".$scan_apc_pdu_phase_number."_amp_low_warning", +# modified_date => $anvil->data->{sys}{database}{timestamp}, +# }); +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { +# "clear" => $clear, +# }}); +# if ($clear) +# { +# # This is a new loss of comms. +# $anvil->Alert->register({ +# alert_level => "warning", +# alert_agent_name => $THIS_FILE, +# alert_title_key => "an_alert_title_0006", +# alert_message_key => "scan_apc_pdu_message_0033", +# alert_message_variables => { +# pdu_name => $pdu_host_name, +# phase => sprintf("%02d", $scan_apc_pdu_phase_number), +# }, +# }); +# } + + return(0); +} + +# This sets the phase amparage high warning alert, if set. +sub set_phase_high_warning +{ + my ($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number) = @_; + +# my $set = $anvil->Alert->check_alert_sent({ +# type => "warning", +# alert_sent_by => $THIS_FILE, +# alert_record_locator => $scan_apc_pdu_serial_number, +# alert_name => "phase_".$scan_apc_pdu_phase_number."_amp_high_warning", +# modified_date => $anvil->data->{sys}{database}{timestamp}, +# }); +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { +# "set" => $set, +# }}); +# if ($set) +# { +# # This is a new loss of comms. +# $anvil->Alert->register({ +# alert_level => "warning", +# alert_agent_name => $THIS_FILE, +# alert_title_key => "an_alert_title_0004", +# alert_message_key => "scan_apc_pdu_message_0034", +# alert_message_variables => { +# pdu_name => $pdu_host_name, +# phase => sprintf("%02d", $scan_apc_pdu_phase_number), +# }, +# }); +# } + + return(0) +} + +# This clears the phase amparage high warning alert, if set. +sub clear_phase_high_warning +{ + my ($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number) = @_; + +# my $clear = $anvil->Alert->check_alert_sent({ +# type => "clear", +# alert_sent_by => $THIS_FILE, +# alert_record_locator => $scan_apc_pdu_serial_number, +# alert_name => "phase_".$scan_apc_pdu_phase_number."_amp_high_warning", +# modified_date => $anvil->data->{sys}{database}{timestamp}, +# }); +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { +# "clear" => $clear, +# }}); +# if ($clear) +# { +# # This is a new loss of comms. +# $anvil->Alert->register({ +# alert_level => "warning", +# alert_agent_name => $THIS_FILE, +# alert_title_key => "an_alert_title_0006", +# alert_message_key => "scan_apc_pdu_message_0031", +# alert_message_variables => { +# pdu_name => $pdu_host_name, +# phase => sprintf("%02d", $scan_apc_pdu_phase_number), +# }, +# }); +# } + + return(0) +} + +# This sets the phase high-critical alert, if not already. +sub set_phase_high_critical +{ + my ($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number) = @_; + +# my $set = $anvil->Alert->check_alert_sent({ +# type => "warning", +# alert_sent_by => $THIS_FILE, +# alert_record_locator => $scan_apc_pdu_serial_number, +# alert_name => "phase_".$scan_apc_pdu_phase_number."_amp_high_critical", +# modified_date => $anvil->data->{sys}{database}{timestamp}, +# }); +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { +# "set" => $set, +# }}); +# if ($set) +# { +# $anvil->Alert->register({ +# alert_level => "critical", +# alert_agent_name => $THIS_FILE, +# alert_title_key => "an_alert_title_0005", +# alert_message_key => "scan_apc_pdu_message_0032", +# alert_message_variables => { +# pdu_name => $pdu_host_name, +# phase => sprintf("%02d", $scan_apc_pdu_phase_number), +# }, +# }); +# } + + return(0); +} + +# This clears the phase amparage high critical alert, if set. +sub clear_phase_high_critical +{ + my ($anvil, $pdu_host_name, $scan_apc_pdu_phase_number, $scan_apc_pdu_serial_number) = @_; + +# my $clear = $anvil->Alert->check_alert_sent({ +# type => "clear", +# alert_sent_by => $THIS_FILE, +# alert_record_locator => $scan_apc_pdu_serial_number, +# alert_name => "phase_".$scan_apc_pdu_phase_number."_amp_high_critical", +# modified_date => $anvil->data->{sys}{database}{timestamp}, +# }); +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { +# "clear" => $clear, +# }}); +# if ($clear) +# { +# # This is a new loss of comms. +# $anvil->Alert->register({ +# alert_level => "critical", +# alert_agent_name => $THIS_FILE, +# alert_title_key => "an_alert_title_0007", +# alert_message_key => "scan_apc_pdu_message_0030", +# alert_message_variables => { +# pdu_name => $pdu_host_name, +# phase => sprintf("%02d", $scan_apc_pdu_phase_number), +# }, +# }); +# } + + return(0) +} + +# This UPDATEs a outlet +sub update_outlets +{ + my ($anvil, $scan_apc_pdu_uuid, $scan_apc_pdu_outlet_uuid, $scan_apc_pdu_outlet_number, $scan_apc_pdu_outlet_name, $scan_apc_pdu_outlet_on_phase, $scan_apc_pdu_outlet_state, $scan_apc_pdu_outlet_note) = @_; + +# my $query = " +# UPDATE +# scan_apc_pdu_outlets +# SET +# scan_apc_pdu_outlet_scan_apc_pdu_uuid = ".$anvil->Database->quote($scan_apc_pdu_uuid).", +# scan_apc_pdu_outlet_number = ".$anvil->Database->quote($scan_apc_pdu_outlet_number).", +# scan_apc_pdu_outlet_name = ".$anvil->Database->quote($scan_apc_pdu_outlet_name).", +# scan_apc_pdu_outlet_on_phase = ".$anvil->Database->quote($scan_apc_pdu_outlet_on_phase).", +# scan_apc_pdu_outlet_state = ".$anvil->Database->quote($scan_apc_pdu_outlet_state).", +# scan_apc_pdu_outlet_note = ".$anvil->Database->quote($scan_apc_pdu_outlet_note).", +# modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +# WHERE +# scan_apc_pdu_outlet_uuid = ".$anvil->Database->quote($scan_apc_pdu_outlet_uuid)." +# ;"; +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); +# push @{$anvil->data->{sys}{sql}}, $query; + + return(0); +} + +# This INSERTs a new outlet +sub insert_outlets +{ + my ($anvil, $scan_apc_pdu_uuid, $scan_apc_pdu_outlet_number, $scan_apc_pdu_outlet_name, $scan_apc_pdu_outlet_on_phase, $scan_apc_pdu_outlet_state, $scan_apc_pdu_outlet_note) = @_; + + my $scan_apc_pdu_outlet_uuid = $anvil->Get->uuid(); +# my $query = " +# INSERT INTO +# scan_apc_pdu_outlets +# ( +# scan_apc_pdu_outlet_uuid, +# scan_apc_pdu_outlet_scan_apc_pdu_uuid, +# scan_apc_pdu_outlet_number, +# scan_apc_pdu_outlet_name, +# scan_apc_pdu_outlet_on_phase, +# scan_apc_pdu_outlet_state, +# scan_apc_pdu_outlet_note, +# modified_date +# ) VALUES ( +# ".$anvil->Database->quote($scan_apc_pdu_outlet_uuid).", +# ".$anvil->Database->quote($scan_apc_pdu_uuid).", +# ".$anvil->Database->quote($scan_apc_pdu_outlet_number).", +# ".$anvil->Database->quote($scan_apc_pdu_outlet_name).", +# ".$anvil->Database->quote($scan_apc_pdu_outlet_on_phase).", +# ".$anvil->Database->quote($scan_apc_pdu_outlet_state).", +# ".$anvil->Database->quote($scan_apc_pdu_outlet_note).", +# ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +# );"; +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); +# push @{$anvil->data->{sys}{sql}}, $query; + + return($scan_apc_pdu_outlet_uuid); +} + +# This UPDATEs a variable +sub update_variables +{ + my ($anvil, $scan_apc_pdu_uuid, $scan_apc_pdu_variable_uuid, $variable, $value) = @_; + +# my $query = " +# UPDATE +# scan_apc_pdu_variables +# SET +# scan_apc_pdu_variable_scan_apc_pdu_uuid = ".$anvil->Database->quote($scan_apc_pdu_uuid).", +# scan_apc_pdu_variable_is_temperature = FALSE, +# scan_apc_pdu_variable_name = ".$anvil->Database->quote($variable).", +# scan_apc_pdu_variable_value = ".$anvil->Database->quote($value).", +# modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +# WHERE +# scan_apc_pdu_variable_uuid = ".$anvil->Database->quote($scan_apc_pdu_variable_uuid)." +# ;"; +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); +# push @{$anvil->data->{sys}{sql}}, $query; + + return(0); +} + +# This INSERTs a new variable +sub insert_variables +{ + my ($anvil, $scan_apc_pdu_uuid, $variable, $value) = @_; + + my $scan_apc_pdu_variable_uuid = $anvil->Get->uuid(); +# my $query = " +# INSERT INTO +# scan_apc_pdu_variables +# ( +# scan_apc_pdu_variable_uuid, +# scan_apc_pdu_variable_scan_apc_pdu_uuid, +# scan_apc_pdu_variable_is_temperature, +# scan_apc_pdu_variable_name, +# scan_apc_pdu_variable_value, +# modified_date +# ) VALUES ( +# ".$anvil->Database->quote($scan_apc_pdu_variable_uuid).", +# ".$anvil->Database->quote($scan_apc_pdu_uuid).", +# FALSE, +# ".$anvil->Database->quote($variable).", +# ".$anvil->Database->quote($value).", +# ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +# );"; +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); +# push @{$anvil->data->{sys}{sql}}, $query; + + return($scan_apc_pdu_variable_uuid); +} + +# This calls each PDU, first to get the model number and update the OIDs to use if needed, then gathers the +# information from the PDU. +sub gather_pdu_data +{ + my ($anvil) = @_; + + # Loop through the PDUs we found in fences (we may miss existing entries from 'scan_apc_pdus', but + # we'll watch for that later). + foreach my $fence_uuid (sort {$a cmp $b} keys %{$anvil->data->{fences}{fence_uuid}}) + { + my $pdu_ip = $anvil->data->{fences}{fence_uuid}{$fence_uuid}{ip_address}; + my $pdu_name = $anvil->data->{fences}{fence_uuid}{$fence_uuid}{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + fence_uuid => $fence_uuid, + pdu_ip => $pdu_ip, + pdu_name => $pdu_name, + }}); + + # Have we seen this PDU before? + my $scan_apc_pdu_uuid = ""; + my $new_pdu = 0; + if ((exists $anvil->data->{pdu}{fence_uuid_to_apc_pdu_uuid}{$fence_uuid}) && (defined $anvil->data->{pdu}{fence_uuid_to_apc_pdu_uuid}{$fence_uuid})) + { + # Yup! + $scan_apc_pdu_uuid = $anvil->data->{pdu}{fence_uuid_to_apc_pdu_uuid}{$fence_uuid}; + } + else + { + # It's new, generate the UUID now. We'll set 'scan_apc_pdu_new' so we know to INSERT + # it later. + $scan_apc_pdu_uuid = $anvil->Get->uuid(); + $new_pdu = 1; + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_pdu_uuid => $scan_apc_pdu_uuid, + new_pdu => $new_pdu, + }}); + + # Can I ping it? This returns '1' if it was pingable, '0' if not. + my ($pinged) = $anvil->Network->ping({ping => $pdu_ip}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { pinged => $pinged }}); + + if (not $pinged) + { + # Can't reach it. + $anvil->data->{fences}{fence_uuid}{$fence_uuid}{ping} = 0; + $anvil->data->{fences}{fence_uuid}{$fence_uuid}{connected} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "fences::fence_uuid::${fence_uuid}::ping" => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{ping}, + "fences::fence_uuid::${fence_uuid}::connected" => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{connected}, + }}); + next; + } + + $anvil->data->{fences}{fence_uuid}{$fence_uuid}{ping} = 1; + $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version} = $anvil->data->{snmp}{community}{version}; + $anvil->data->{fences}{fence_uuid}{$fence_uuid}{scan_apc_pdu_uuid} = $scan_apc_pdu_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "fences::fence_uuid::${fence_uuid}::ping" => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{ping}, + "fences::fence_uuid::${fence_uuid}::snmp_version" => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + "fences::fence_uuid::${fence_uuid}::scan_apc_pdu_uuid" => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{scan_apc_pdu_uuid}, + }}); + + my ($scan_apc_pdu_serial_number, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_serial_number}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_pdu_serial_number => $scan_apc_pdu_serial_number, + data_type => $data_type, + }}); + + # If the serial number is '!!no_connection!!', this might be an old PDU that only supports SNMP v1. + if ($scan_apc_pdu_serial_number eq "!!no_connection!!") + { + # Try SNMP v1 + $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version} = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "fences::fence_uuid::${fence_uuid}::snmp_version" => $anvil->data->{snmp}{community}{version}, + }}); + + ($scan_apc_pdu_serial_number, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_serial_number}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_pdu_serial_number => $scan_apc_pdu_serial_number, + data_type => $data_type, + }}); + + # If it's still '!!no_connection!!', something else is wrong. Is possible it's a new + # PDU with SNMP turned off, perhaps? + if ($scan_apc_pdu_serial_number eq "!!no_connection!!") + { + # No luck + $scan_apc_pdu_serial_number = ""; + $anvil->data->{fences}{fence_uuid}{$fence_uuid}{connected} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_pdu_serial_number => $scan_apc_pdu_serial_number, + "fences::fence_uuid::${fence_uuid}::connected" => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{connected}, + }}); + } + } + + # These are set to avoid 'undefined' variable warnings later if we fail to reach this PDU. + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{pdu_host_name} = $pdu_name; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{new_pdu} = $new_pdu; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_fence_uuid} = $fence_uuid; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_serial_number} = $scan_apc_pdu_serial_number; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_model_number} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_manufacture_date} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_firmware_version} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_hardware_version} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_ipv4_address} = $pdu_ip; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mtu_size} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_link_speed} = 0; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_phase_count} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_outlet_count} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{uptime} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{total_wattage_draw} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{maximum} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{low_warning} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_warning} = ""; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_critical} = ""; + + # If I got the serial number, I found the PDU. + next if not $scan_apc_pdu_serial_number; + + ############################################################################################# + # Base PDU info # + ############################################################################################# + + ### Get the base PDU info. + # Model Number + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_model_number}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_model_number}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_model_number" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_model_number}, + data_type => $data_type, + }}); + + # Manufacture date + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_manufacture_date}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_manufacture_date}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_manufacture_date" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_manufacture_date}, + data_type => $data_type, + }}); + + # Firmware version + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_firmware_version}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_firmware_version}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_firmware_version" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_firmware_version}, + data_type => $data_type, + }}); + + # Hardware version + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_hardware_version}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_hardware_version}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_hardware_version" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_hardware_version}, + data_type => $data_type, + }}); + + # MAC address. Note, this could use a couple different OIDs, depending on the version. It also requires the custom MIB + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_mac_address}, + mib => $anvil->data->{'scan-apc-pdu'}{striker_mib}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_mac_address" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address}, + data_type => $data_type, + }}); + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address} eq "#!no_value!#") + { + # Some older PDUs use a different OID. + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_mac_address_alt}, + mib => $anvil->data->{'scan-apc-pdu'}{striker_mib}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_mac_address" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address}, + data_type => $data_type, + }}); + } + # Make the MAC address lower case and replace the spaces with colons + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address} = lc($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address}); + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address} =~ s/^\s+//g; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address} =~ s/\s+$//g; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address} =~ s/ /:/g; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_mac_address" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address}, + }}); + + # Read the MTU. This again could be at one of two OIDs. + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mtu_size}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_mtu_size}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_mtu_size" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mtu_size}, + data_type => $data_type, + }}); + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mtu_size} eq "#!no_value!#") + { + # Some older PDUs use a different OID. + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mtu_size}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_mtu_size_alt}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_mtu_size" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mtu_size}, + data_type => $data_type, + }}); + } + + # Read the link speed. Again, this could be at alternate OIDs. + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_link_speed}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_link_speed}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_link_speed" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_link_speed}, + data_type => $data_type, + }}); + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_link_speed} eq "#!no_value!#") + { + # Some older PDUs use a different OID. + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_link_speed}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_link_speed_alt}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_link_speed" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_link_speed}, + data_type => $data_type, + }}); + } + + # Read in the phase count + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_phase_count}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_phase_count}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_phase_count" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_phase_count}, + data_type => $data_type, + }}); + + # Read in the outlet count + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_outlet_count}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu}{scan_apc_pdu_outlet_count}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_outlet_count" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_outlet_count}, + data_type => $data_type, + }}); + + ### These change often and will go into the 'variables' table + # Read the uptime + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{uptime}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu_variables}{uptime}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_variables::uptime" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{uptime}, + data_type => $data_type, + }}); + + # Total watt draw + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{total_wattage_draw}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu_variables}{total_wattage_draw}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_variables::total_wattage_draw" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{total_wattage_draw}, + data_type => $data_type, + }}); + + ### These are used later for checking the state of the phases. They are not stored in the database. + # Macimum allowed amperage + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{maximum}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu_phases}{scan_apc_pdu_phase_max_amperage}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::amperage::maximum" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{maximum}, + data_type => $data_type, + }}); + + # Low amperage on a phase threshold + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{low_warning}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu_phases}{phase_low_amp_warning}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::amperage::low_warning" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{low_warning}, + data_type => $data_type, + }}); + + # High warning + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_warning}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu_phases}{phase_high_amp_warning}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::amperage::high_warning" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_warning}, + data_type => $data_type, + }}); + + # Phase high amperage threshold + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_critical}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $anvil->data->{oids}{scan_apc_pdu_phases}{phase_high_amp_critical}, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::amperage::high_critical" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_critical}, + data_type => $data_type, + }}); + + ### Convert some unknown values to values we can store in the database + # MAC address + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address} eq "#!no_value!#") + { + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address} = "xx:xx:xx:xx:xx:xx"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_mac_address" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mac_address}, + }}); + } + # MTU + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mtu_size} eq "#!no_value!#") + { + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mtu_size} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_mtu_size" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_mtu_size}, + }}); + } + # Link speed + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_link_speed} eq "#!no_value!#") + { + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_link_speed} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_link_speed" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_link_speed}, + }}); + } + # Wattage isn't available on older PDUs + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{total_wattage_draw} eq "#!no_value!#") + { + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{total_wattage_draw} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_variables::total_wattage_draw" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{total_wattage_draw}, + }}); + } + + ### TODO: We should probably sanity check the various values. Error or set? + # If the (high critical amperage + 'scan-apc-pdu::critical_amps_below_max') > maximum + # amperage, we'll set the critical warning level to be + # (maximum - 'scan-apc-pdu::critical_amps_below_max') + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan-apc-pdu::critical_amps_below_max" => $anvil->data->{'scan-apc-pdu'}{critical_amps_below_max}, + }}); + if (($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_critical} + $anvil->data->{'scan-apc-pdu'}{critical_amps_below_max}) > $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{maximum}) + { + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_critical} = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{maximum} - $anvil->data->{'scan-apc-pdu'}{critical_amps_below_max}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::amperage::high_critical" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_critical}, + }}); + } + + # Make sure that the warning is at least 'scan-apc-pdu::warning_amps_below_critical' + if (($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_warning} + $anvil->data->{'scan-apc-pdu'}{warning_amps_below_critical}) > $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_critical}) + { + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_warning} = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_critical} - $anvil->data->{'scan-apc-pdu'}{warning_amps_below_critical}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::amperage::high_warning" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_warning}, + }}); + } + + # Set the alert clear levels + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{clear_high_critical} = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_critical} - $anvil->data->{'scan-apc-pdu'}{clear_alert_threshold}; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{clear_high_warning} = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{high_warning} - $anvil->data->{'scan-apc-pdu'}{clear_alert_threshold}; + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{clear_low_warning} = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{low_warning} + $anvil->data->{'scan-apc-pdu'}{clear_alert_threshold}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::amperage::clear_high_critical" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{clear_high_critical}, + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::amperage::clear_high_warning" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{clear_high_warning}, + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::amperage::clear_low_warning" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{amperage}{clear_low_warning}, + }}); + + # Uptime is in ticks + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{uptime} /= $anvil->data->{'scan-apc-pdu'}{ticks_per_second}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu:${scan_apc_pdu_serial_number}::scan_apc_pdu_variables::uptime" => $anvil->Convert->add_commas({number => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{uptime}})." (".$anvil->Convert->time({'time' => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_variables}{uptime}, translate => 1}).")", + }}); + + # The dates are in 'mm/dd/yyyy' or 'mm/dd/yy' format, convert them to 'yyyy/mm/dd' + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_manufacture_date} =~ /(\d\d)\/(\d\d)\/(\d\d\d\d)$/) + { + my $year = $3; + my $month = $1; + my $day = $2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + year => $year, + month => $month, + day => $day, + }}); + + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_manufacture_date} = $year."/".$month."/".$day; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_manufacture_date" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_manufacture_date}, + }}); + } + elsif ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_manufacture_date} =~ /(\d\d)\/(\d\d)\/(\d\d)$/) + { + my $year = $3; + my $month = $1; + my $day = $2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "year" => $year, + "month" => $month, + "day" => $day, + }}); + + if ($year < 70) + { + $year += 2000; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "year" => $year, + }}); + } + else + { + $year += 1900; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "year" => $year, + }}); + } + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_manufacture_date} = $year."/".$month."/".$day; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu::scan_apc_pdu_manufacture_date" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_manufacture_date}, + }}); + } + + # If an alert (cleared) message/log is needed for either phase or outlet count, they'll use this hash + my $variables = { + name => $pdu_name, + ip => $pdu_ip, + serial_number => $scan_apc_pdu_serial_number, + }; + + # Verify that the phase count is valid + my $bad_phase_count_key = "scan_apc_pdu::".$scan_apc_pdu_uuid."::bad_phase_count"; + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_phase_count} !~ /^\d+$/) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "scan_apc_pdu_message_0002", variables => $variables}); + my $changed = $anvil->Alert->check_alert_sent({ + debug => 3, + record_locator => $bad_phase_count_key, + set_by => $THIS_FILE, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Register an alert. + $anvil->Alert->register({alert_level => "warning", message => "scan_apc_pdu_message_0002", message_variables => $variables, set_by => $THIS_FILE}); + } + $anvil->nice_exit({exit_code => 1}); + } + else + { + # Clear the alert? + my $changed = $anvil->Alert->check_alert_sent({ + debug => 3, + clear => 1, + record_locator => $bad_phase_count_key, + 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 => 0, priority => "err", key => "scan_apc_pdu_message_0003", variables => $variables}); + $anvil->Alert->register({alert_level => "warning", clear_alert => 1, message => "scan_apc_pdu_message_0003", message_variables => $variables, set_by => $THIS_FILE}); + } + } + # Now read the phase data + foreach my $scan_apc_pdu_phase_number (1..$anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_phase_count}) + { + # Stick the phase onto the end of the OID. + my $oid = $anvil->data->{oids}{scan_apc_pdu_phases}{scan_apc_pdu_phase_current_amperage}.$scan_apc_pdu_phase_number; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_pdu_phase_number" => $scan_apc_pdu_phase_number, + "oid" => $oid, + }}); + + # Read this phase's amperage + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_current_amperage}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $oid, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_phases::${scan_apc_pdu_phase_number}::scan_apc_pdu_phase_current_amperage" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_current_amperage}, + data_type => $data_type, + }}); + + # The amperage is in 1/10 amps, so divide by 10. + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_current_amperage} =~ /^\d+$/) + { + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_current_amperage} /= 10; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_phases::${scan_apc_pdu_phase_number}::scan_apc_pdu_phase_current_amperage" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_phases}{$scan_apc_pdu_phase_number}{scan_apc_pdu_phase_current_amperage}, + }}); + } + } + + # Read the outlet data, if the outlet count is sane. + my $bad_outlet_count_key = "scan_apc_pdu::".$scan_apc_pdu_uuid."::bad_outlet_count"; + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_outlet_count} !~ /^\d+$/) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "scan_apc_pdu_message_0004", variables => $variables}); + my $changed = $anvil->Alert->check_alert_sent({ + debug => 3, + record_locator => $bad_outlet_count_key, + set_by => $THIS_FILE, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Register an alert. + $anvil->Alert->register({alert_level => "warning", message => "scan_apc_pdu_message_0004", message_variables => $variables, set_by => $THIS_FILE}); + } + $anvil->nice_exit({exit_code => 1}); + } + else + { + # Clear the alert? + my $changed = $anvil->Alert->check_alert_sent({ + debug => 3, + clear => 1, + record_locator => $bad_outlet_count_key, + 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 => 0, priority => "err", key => "scan_apc_pdu_message_0005", variables => $variables}); + $anvil->Alert->register({alert_level => "warning", clear_alert => 1, message => "scan_apc_pdu_message_0005", message_variables => $variables, set_by => $THIS_FILE}); + } + } + # Now read the outlet data + foreach my $scan_apc_pdu_outlet_number (1..$anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_outlet_count}) + { + # Stick the outlet onto the end of the OIDs. + my $outlet_name_oid = $anvil->data->{oids}{scan_apc_pdu_outlets}{scan_apc_pdu_outlet_name}.$scan_apc_pdu_outlet_number; + my $outlet_phase_oid = $anvil->data->{oids}{scan_apc_pdu_outlets}{scan_apc_pdu_outlet_on_phase}.$scan_apc_pdu_outlet_number; + my $outlet_state_oid = $anvil->data->{oids}{scan_apc_pdu_outlets}{scan_apc_pdu_outlet_state}.$scan_apc_pdu_outlet_number; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "outlet_name_oid" => $outlet_name_oid, + "outlet_phase_oid" => $outlet_phase_oid, + "outlet_state_oid" => $outlet_state_oid, + }}); + + ### Read the OID + # outlet name + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_name}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $outlet_name_oid, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_outlets::${scan_apc_pdu_outlet_number}::scan_apc_pdu_outlet_name" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_name}, + data_type => $data_type, + }}); + + # Outlet number on phase + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_on_phase}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $outlet_phase_oid, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_outlets::${scan_apc_pdu_outlet_number}::scan_apc_pdu_outlet_on_phase" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_on_phase}, + data_type => $data_type, + }}); + + # State of the outlet + ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 3, + target => $pdu_ip, + oid => $outlet_state_oid, + version => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_outlets::${scan_apc_pdu_outlet_number}::scan_apc_pdu_outlet_state" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state}, + data_type => $data_type, + }}); + + # The state is; 1 = on, 2 = off, 4 = unknown + if ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state} eq "1") + { + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state} = "on"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_outlets::${scan_apc_pdu_outlet_number}::scan_apc_pdu_outlet_state" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state}, + }}); + } + elsif ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state} eq "2") + { + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state} = "off"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_outlets::${scan_apc_pdu_outlet_number}::scan_apc_pdu_outlet_state" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state}, + }}); + } + elsif ($anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state} eq "3") + { + $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state} = "unknown"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "pdu::scan_apc_pdu_uuid::${scan_apc_pdu_uuid}::scan_apc_pdu_outlets::${scan_apc_pdu_outlet_number}::scan_apc_pdu_outlet_state" => $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state}, + }}); + } + } + } + + return(0); +} + +# This looks for APC PDUs. +sub find_pdus +{ + my ($anvil) = @_; + + # Search in 'fences' for agents starting with 'fence_apc_'. These aren't bound to hosts (or even + # Anvil! systems), so not all may be available. + my $query = " +SELECT + fence_uuid, + fence_name, + fence_arguments +FROM + fences +WHERE + fence_agent LIKE 'fence_apc_%' +ORDER BY + fence_name ASC +;"; + $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 $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $fence_uuid = $row->[0]; + my $fence_name = $row->[1]; + my $fence_arguments = $row->[2]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { + fence_uuid => $fence_uuid, + fence_name => $fence_name, + fence_arguments => $fence_arguments, + }}); + + if ($fence_arguments =~ /ip=\"(.*?)\"/) + { + my $ip_address = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { ip_address => $ip_address }}); + + $anvil->data->{fences}{fence_uuid}{$fence_uuid}{name} = $fence_name; + $anvil->data->{fences}{fence_uuid}{$fence_uuid}{ip_address} = $ip_address; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "fences::fence_uuid::${fence_uuid}::name" => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{name}, + "fences::fence_uuid::${fence_uuid}::ip_address" => $anvil->data->{fences}{fence_uuid}{$fence_uuid}{ip_address}, + }}); + } + } + + my $pdu_count = keys %{$anvil->data->{fences}{fence_uuid}}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { pdu_count => $pdu_count }}); + return($pdu_count); +} diff --git a/scancore-agents/scan-apc-pdu/scan-apc-pdu.sql b/scancore-agents/scan-apc-pdu/scan-apc-pdu.sql new file mode 100644 index 00000000..e25bbc75 --- /dev/null +++ b/scancore-agents/scan-apc-pdu/scan-apc-pdu.sql @@ -0,0 +1,256 @@ +-- This is the database schema for the 'remote-access Scan Agent'. + +CREATE TABLE scan_apc_pdus ( + scan_apc_pdu_uuid uuid not null primary key, -- This is set by the target, not by us! + scan_apc_pdu_fence_uuid uuid not null, -- + scan_apc_pdu_serial_number text not null, -- + scan_apc_pdu_model_number text not null, -- + scan_apc_pdu_manufacture_date text not null, -- + scan_apc_pdu_firmware_version text not null, -- + scan_apc_pdu_hardware_version text not null, -- + scan_apc_pdu_ipv4_address text not null, -- + scan_apc_pdu_mac_address text not null, -- + scan_apc_pdu_mtu_size numeric not null, -- + scan_apc_pdu_link_speed numeric not null, -- in bits-per-second, set to '0' when we lose access + scan_apc_pdu_phase_count numeric not null, -- + scan_apc_pdu_outlet_count numeric not null, -- + modified_date timestamp with time zone not null, + + FOREIGN KEY(scan_apc_pdu_fence_uuid) REFERENCES fences(fence_uuid) +); +ALTER TABLE scan_apc_pdus OWNER TO admin; + +CREATE TABLE history.scan_apc_pdus ( + history_id bigserial, + scan_apc_pdu_uuid uuid, + scan_apc_pdu_fence_uuid uuid, + scan_apc_pdu_serial_number text, + scan_apc_pdu_model_number text, + scan_apc_pdu_manufacture_date text, + scan_apc_pdu_firmware_version text, + scan_apc_pdu_hardware_version text, + scan_apc_pdu_ipv4_address text, + scan_apc_pdu_mac_address text, + scan_apc_pdu_mtu_size numeric, + scan_apc_pdu_link_speed numeric, + scan_apc_pdu_phase_count numeric, + scan_apc_pdu_outlet_count numeric, + modified_date timestamp with time zone not null +); +ALTER TABLE history.scan_apc_pdus OWNER TO admin; + +CREATE FUNCTION history_scan_apc_pdus() RETURNS trigger +AS $$ +DECLARE + history_scan_apc_pdus RECORD; +BEGIN + SELECT INTO history_scan_apc_pdus * FROM scan_apc_pdus WHERE scan_apc_pdu_uuid=new.scan_apc_pdu_uuid; + INSERT INTO history.scan_apc_pdus + (scan_apc_pdu_uuid, + scan_apc_pdu_fence_uuid, + scan_apc_pdu_serial_number, + scan_apc_pdu_model_number, + scan_apc_pdu_manufacture_date, + scan_apc_pdu_firmware_version, + scan_apc_pdu_hardware_version, + scan_apc_pdu_ipv4_address, + scan_apc_pdu_mac_address, + scan_apc_pdu_mtu_size, + scan_apc_pdu_link_speed, + scan_apc_pdu_phase_count, + scan_apc_pdu_outlet_count, + modified_date) + VALUES + (history_scan_apc_pdus.scan_apc_pdu_uuid, + history_scan_apc_pdus.scan_apc_pdu_fence_uuid, + history_scan_apc_pdus.scan_apc_pdu_serial_number, + history_scan_apc_pdus.scan_apc_pdu_model_number, + history_scan_apc_pdus.scan_apc_pdu_manufacture_date, + history_scan_apc_pdus.scan_apc_pdu_firmware_version, + history_scan_apc_pdus.scan_apc_pdu_hardware_version, + history_scan_apc_pdus.scan_apc_pdu_ipv4_address, + history_scan_apc_pdus.scan_apc_pdu_mac_address, + history_scan_apc_pdus.scan_apc_pdu_mtu_size, + history_scan_apc_pdus.scan_apc_pdu_link_speed, + history_scan_apc_pdus.scan_apc_pdu_phase_count, + history_scan_apc_pdus.scan_apc_pdu_outlet_count, + history_scan_apc_pdus.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_scan_apc_pdus() OWNER TO admin; + +CREATE TRIGGER trigger_scan_apc_pdus + AFTER INSERT OR UPDATE ON scan_apc_pdus + FOR EACH ROW EXECUTE PROCEDURE history_scan_apc_pdus(); + + +-- Phases on the PDU +CREATE TABLE scan_apc_pdu_phases ( + scan_apc_pdu_phase_uuid uuid not null primary key, + scan_apc_pdu_phase_scan_apc_pdu_uuid uuid not null, -- + scan_apc_pdu_phase_number text not null, -- + scan_apc_pdu_phase_current_amperage numeric not null, -- Max, low/high warn and high critical will be read from the PDU in the given pass. + scan_apc_pdu_phase_max_amperage numeric, + modified_date timestamp with time zone not null, + + FOREIGN KEY(scan_apc_pdu_phase_scan_apc_pdu_uuid) REFERENCES scan_apc_pdus(scan_apc_pdu_uuid) +); +ALTER TABLE scan_apc_pdu_phases OWNER TO admin; + +CREATE TABLE history.scan_apc_pdu_phases ( + history_id bigserial, + scan_apc_pdu_phase_uuid uuid, + scan_apc_pdu_phase_scan_apc_pdu_uuid uuid, + scan_apc_pdu_phase_number text, + scan_apc_pdu_phase_current_amperage numeric, + scan_apc_pdu_phase_max_amperage numeric, + modified_date timestamp with time zone not null +); +ALTER TABLE history.scan_apc_pdu_phases OWNER TO admin; + +CREATE FUNCTION history_scan_apc_pdu_phases() RETURNS trigger +AS $$ +DECLARE + history_scan_apc_pdu_phases RECORD; +BEGIN + SELECT INTO history_scan_apc_pdu_phases * FROM scan_apc_pdu_phases WHERE scan_apc_pdu_phase_uuid=new.scan_apc_pdu_phase_uuid; + INSERT INTO history.scan_apc_pdu_phases + (scan_apc_pdu_phase_uuid, + scan_apc_pdu_phase_scan_apc_pdu_uuid, + scan_apc_pdu_phase_number, + scan_apc_pdu_phase_current_amperage, + scan_apc_pdu_phase_max_amperage, + modified_date) + VALUES + (history_scan_apc_pdu_phases.scan_apc_pdu_phase_uuid, + history_scan_apc_pdu_phases.scan_apc_pdu_phase_scan_apc_pdu_uuid, + history_scan_apc_pdu_phases.scan_apc_pdu_phase_number, + history_scan_apc_pdu_phases.scan_apc_pdu_phase_current_amperage, + history_scan_apc_pdu_phases.scan_apc_pdu_phase_max_amperage, + history_scan_apc_pdu_phases.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_scan_apc_pdu_phases() OWNER TO admin; + +CREATE TRIGGER trigger_scan_apc_pdu_phases + AFTER INSERT OR UPDATE ON scan_apc_pdu_phases + FOR EACH ROW EXECUTE PROCEDURE history_scan_apc_pdu_phases(); + + +-- Phases on the PDU +CREATE TABLE scan_apc_pdu_outlets ( + scan_apc_pdu_outlet_uuid uuid not null primary key, + scan_apc_pdu_outlet_scan_apc_pdu_uuid uuid not null, + scan_apc_pdu_outlet_number text not null, + scan_apc_pdu_outlet_name text not null, + scan_apc_pdu_outlet_on_phase text not null, + scan_apc_pdu_outlet_state text not null, -- on / off / unknown + modified_date timestamp with time zone not null, + + FOREIGN KEY(scan_apc_pdu_outlet_scan_apc_pdu_uuid) REFERENCES scan_apc_pdus(scan_apc_pdu_uuid) +); +ALTER TABLE scan_apc_pdu_outlets OWNER TO admin; + +CREATE TABLE history.scan_apc_pdu_outlets ( + history_id bigserial, + scan_apc_pdu_outlet_uuid uuid, + scan_apc_pdu_outlet_scan_apc_pdu_uuid uuid, + scan_apc_pdu_outlet_number text, + scan_apc_pdu_outlet_name text, + scan_apc_pdu_outlet_on_phase text, + scan_apc_pdu_outlet_state text, + modified_date timestamp with time zone not null +); +ALTER TABLE history.scan_apc_pdu_outlets OWNER TO admin; + +CREATE FUNCTION history_scan_apc_pdu_outlets() RETURNS trigger +AS $$ +DECLARE + history_scan_apc_pdu_outlets RECORD; +BEGIN + SELECT INTO history_scan_apc_pdu_outlets * FROM scan_apc_pdu_outlets WHERE scan_apc_pdu_outlet_uuid=new.scan_apc_pdu_outlet_uuid; + INSERT INTO history.scan_apc_pdu_outlets + (scan_apc_pdu_outlet_uuid, + scan_apc_pdu_outlet_scan_apc_pdu_uuid, + scan_apc_pdu_outlet_number, + scan_apc_pdu_outlet_name, + scan_apc_pdu_outlet_on_phase, + scan_apc_pdu_outlet_state, + modified_date) + VALUES + (history_scan_apc_pdu_outlets.scan_apc_pdu_outlet_uuid, + history_scan_apc_pdu_outlets.scan_apc_pdu_outlet_scan_apc_pdu_uuid, + history_scan_apc_pdu_outlets.scan_apc_pdu_outlet_number, + history_scan_apc_pdu_outlets.scan_apc_pdu_outlet_name, + history_scan_apc_pdu_outlets.scan_apc_pdu_outlet_on_phase, + history_scan_apc_pdu_outlets.scan_apc_pdu_outlet_state, + history_scan_apc_pdu_outlets.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_scan_apc_pdu_outlets() OWNER TO admin; + +CREATE TRIGGER trigger_scan_apc_pdu_outlets + AFTER INSERT OR UPDATE ON scan_apc_pdu_outlets + FOR EACH ROW EXECUTE PROCEDURE history_scan_apc_pdu_outlets(); + + +-- This stores various variables found for a given controller but not explicitely checked for (or that +-- change frequently). +CREATE TABLE scan_apc_pdu_variables ( + scan_apc_pdu_variable_uuid uuid not null primary key, -- + scan_apc_pdu_variable_scan_apc_pdu_uuid uuid not null, -- + scan_apc_pdu_variable_is_temperature boolean not null, -- + scan_apc_pdu_variable_name text not null, -- + scan_apc_pdu_variable_value text not null, -- + modified_date timestamp with time zone not null, -- + + FOREIGN KEY(scan_apc_pdu_variable_scan_apc_pdu_uuid) REFERENCES scan_apc_pdus(scan_apc_pdu_uuid) +); +ALTER TABLE scan_apc_pdu_variables OWNER TO admin; + +CREATE TABLE history.scan_apc_pdu_variables ( + history_id bigserial, + scan_apc_pdu_variable_uuid uuid, + scan_apc_pdu_variable_scan_apc_pdu_uuid uuid, + scan_apc_pdu_variable_is_temperature boolean, + scan_apc_pdu_variable_name text, + scan_apc_pdu_variable_value text, + modified_date timestamp with time zone not null +); +ALTER TABLE history.scan_apc_pdu_variables OWNER TO admin; + +CREATE FUNCTION history_scan_apc_pdu_variables() RETURNS trigger +AS $$ +DECLARE + history_scan_apc_pdu_variables RECORD; +BEGIN + SELECT INTO history_scan_apc_pdu_variables * FROM scan_apc_pdu_variables WHERE scan_apc_pdu_variable_uuid=new.scan_apc_pdu_variable_uuid; + INSERT INTO history.scan_apc_pdu_variables + (scan_apc_pdu_variable_uuid, + scan_apc_pdu_variable_scan_apc_pdu_uuid, + scan_apc_pdu_variable_is_temperature, + scan_apc_pdu_variable_name, + scan_apc_pdu_variable_value, + modified_date) + VALUES + (history_scan_apc_pdu_variables.scan_apc_pdu_variable_uuid, + history_scan_apc_pdu_variables.scan_apc_pdu_variable_scan_apc_pdu_uuid, + history_scan_apc_pdu_variables.scan_apc_pdu_variable_is_temperature, + history_scan_apc_pdu_variables.scan_apc_pdu_variable_name, + history_scan_apc_pdu_variables.scan_apc_pdu_variable_value, + history_scan_apc_pdu_variables.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_scan_apc_pdu_variables() OWNER TO admin; + +CREATE TRIGGER trigger_scan_apc_pdu_variables + AFTER INSERT OR UPDATE ON scan_apc_pdu_variables + FOR EACH ROW EXECUTE PROCEDURE history_scan_apc_pdu_variables(); diff --git a/scancore-agents/scan-apc-pdu/scan-apc-pdu.xml b/scancore-agents/scan-apc-pdu/scan-apc-pdu.xml new file mode 100644 index 00000000..0b5d0111 --- /dev/null +++ b/scancore-agents/scan-apc-pdu/scan-apc-pdu.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + Failed to read the number of phases for the PDU: [#!variable!name!#] at IP: [#!variable!ip!#] (sn: #!variable!serial_number!#). + Able to now read the number of phases for the PDU: [#!variable!name!#] at IP: [#!variable!ip!#] (sn: #!variable!serial_number!#). + Failed to read the number of outlets for the PDU: [#!variable!name!#] at IP: [#!variable!ip!#] (sn: #!variable!serial_number!#). + Able to now read the number of outlets for the PDU: [#!variable!name!#] at IP: [#!variable!ip!#] (sn: #!variable!serial_number!#). + + + Starting: [#!variable!program!#]. + + + No APC PDUs found as configured fence devices, nothing to do. + Failed to read the number of phases for the PDU: [#!variable!name!#] at IP: [#!variable!ip!#] (sn: #!variable!serial_number!#). + Able to now read the number of phases for the PDU: [#!variable!name!#] at IP: [#!variable!ip!#] (sn: #!variable!serial_number!#). + Failed to read the number of outlets for the PDU: [#!variable!name!#] at IP: [#!variable!ip!#] (sn: #!variable!serial_number!#). + Able to now read the number of outlets for the PDU: [#!variable!name!#] at IP: [#!variable!ip!#] (sn: #!variable!serial_number!#). + The PDU: [#!variable!name!#] with the serial number: [#!variable!serial_number!#] at the IP address: [#!variable!ip_address!#] has returned. + The model of the PDU: [#!variable!name!#] has changed from; [#!variable!old_model_number!#] to: [#!variable!new_model_number!#] + + + + + + diff --git a/scancore-agents/scan-cluster/scan-cluster b/scancore-agents/scan-cluster/scan-cluster index c419ad1c..0fa5c708 100755 --- a/scancore-agents/scan-cluster/scan-cluster +++ b/scancore-agents/scan-cluster/scan-cluster @@ -10,6 +10,7 @@ # Exit codes; # 0 = Normal exit. # 1 = Startup failure (not running as root, no DB, bad file read, etc) +# 2 = Not a cluster member # # TODO: # @@ -63,7 +64,7 @@ if (($anvil->data->{scancore}{'scan-cluster'}{disable}) && (not $anvil->data->{s # 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-cluster'}{tables} = ["scan_cluster"]; +$anvil->data->{scancore}{'scan-cluster'}{tables} = ["scan_cluster", "scan_cluster_nodes"]; # Handle start-up tasks my $problem = $anvil->ScanCore->agent_startup({ @@ -89,18 +90,21 @@ if ($anvil->data->{switches}{purge}) # Before we do anything, are we a node in a pacemaker cluster? my $host_type = $anvil->Get->host_type; -$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }}); +$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { host_type => $host_type }}); if ($host_type ne "node") { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_log_0002", variables => { host_type => $host_type }}); $anvil->nice_exit({exit_code => 0}); } -# Read the data. -collect_data($anvil); - # Read last scan +read_last_scan($anvil); + +# Read and process in one shot. +collect_data($anvil); +# Find changes. +find_changes($anvil); $anvil->nice_exit({exit_code => 0}); @@ -108,59 +112,511 @@ $anvil->nice_exit({exit_code => 0}); # Functions # ############################################################################################################# - -# This reads in all the data we can find on the local system -sub collect_data +# Looks for changes. +sub find_changes { my ($anvil) = @_; - # Pick out core cluster details. - my $problem = $anvil->Cluster->parse_cib({debug => 2}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); - - my $cluster_name = $anvil->data->{cib}{parsed}{data}{cluster}{name}; - my $stonith_enabled = $anvil->data->{cib}{parsed}{data}{stonith}{enabled}; - my $maintenance_mode = $anvil->data->{cib}{parsed}{data}{cluster}{'maintenance-mode'}; - my $stonith_max_attempts = $anvil->data->{cib}{parsed}{data}{cluster}{'stonith-max-attempts'}; + # We can't track a cluster through name change, so either we're INSERTing a new one, or bust. + my $scan_cluster_anvil_uuid = $anvil->Cluster->get_anvil_uuid(); + my $anvil_name = $anvil->Get->anvil_name_from_uuid({anvil_uuid => $scan_cluster_anvil_uuid}); + my $scan_cluster_uuid = ""; + my $cluster_name = $anvil->data->{cib}{parsed}{data}{cluster}{name}; + my $stonith_enabled = $anvil->data->{cib}{parsed}{data}{stonith}{enabled}; + my $stonith_max_attempts = $anvil->data->{cib}{parsed}{data}{stonith}{'max-attempts'}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cluster_name => $cluster_name, stonith_enabled => $stonith_enabled, - maintenance_mode => $maintenance_mode, stonith_max_attempts => $stonith_max_attempts, }}); - ### TODO: If we're node 2, or not in the cluster, only update our information in the - ### 'scan_cluster_nodes' table. Node 1 will update everything else if it's 'ready' (else node 2 - ### will, if it's ready). - my $i_am = $anvil->Cluster->which_node({debug => 1}); - my $my_node_name = $anvil->data->{cib}{parsed}{'local'}{name}; - my $peer_node_name = $anvil->data->{cib}{parsed}{peer}{name}; - my $peer_ready = $anvil->data->{cib}{parsed}{peer}{ready}; - my $local_ready = $anvil->data->{cib}{parsed}{data}{node}{$my_node_name}{node_state}{ready}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - i_am => $i_am, - my_node_name => $my_node_name, - peer_node_name => $peer_node_name, - peer_ready => $peer_ready, - local_ready => $local_ready, - }}); + if (exists $anvil->data->{sql}{anvil_uuid}{$scan_cluster_anvil_uuid}) + { + # Check for a name change + $scan_cluster_uuid = $anvil->data->{sql}{anvil_uuid}{$scan_cluster_anvil_uuid}; + my $old_cluster_name = $anvil->data->{sql}{scan_cluster}{scan_cluster_uuid}{$scan_cluster_uuid}{scan_cluster_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_cluster_uuid => $scan_cluster_uuid, + old_cluster_name => $old_cluster_name, + }}); + if ($cluster_name ne $old_cluster_name) + { + # The name of the cluster has changed. + my $query = " +UPDATE + scan_cluster +SET + scan_cluster_name = ".$anvil->Database->quote($cluster_name).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_cluster_uuid = ".$anvil->Database->quote($scan_cluster_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + + my $variables = { + new_cluster_name => $cluster_name, + old_cluster_name => $old_cluster_name, + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_alert_0002", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_cluster_alert_0002", message_variables => $variables, set_by => $THIS_FILE}); + } + } + else + { + # New cluster, INSERT + $scan_cluster_uuid = $anvil->Get->uuid(); + my $query = " +INSERT INTO + scan_cluster +( + scan_cluster_uuid, + scan_cluster_anvil_uuid, + scan_cluster_name, + modified_date +) VALUES ( + ".$anvil->Database->quote($scan_cluster_uuid).", + ".$anvil->Database->quote($scan_cluster_anvil_uuid).", + ".$anvil->Database->quote($cluster_name).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + + my $variables = { cluster_name => $cluster_name }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_alert_0001", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_cluster_alert_0001", message_variables => $variables, set_by => $THIS_FILE}); + } - ### TODO: Change the logic so that when both nodes are in the cluster, the node with the lowest - ### load does the scan (typically the node without VMs). - if (($i_am eq "node2") && ($peer_ready)) + $anvil->Database->get_anvils(); + foreach my $scan_cluster_node_name (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{data}{node}}) { - # We're not going to run. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_cluster_message_0001"}); - $anvil->nice_exit({exit_code => 0}); + my $scan_cluster_node_host_uuid = $anvil->Get->host_uuid_from_name({host_name => $scan_cluster_node_name}); + my $scan_cluster_node_pacemaker_id = $anvil->data->{cib}{parsed}{data}{node}{$scan_cluster_node_name}{node_state}{pacemaker_id}; + my $scan_cluster_node_in_ccm = $anvil->data->{cib}{parsed}{data}{node}{$scan_cluster_node_name}{node_state}{in_ccm}; + my $scan_cluster_node_crmd_member = $anvil->data->{cib}{parsed}{data}{node}{$scan_cluster_node_name}{node_state}{crmd}; + my $scan_cluster_node_cluster_member = $anvil->data->{cib}{parsed}{data}{node}{$scan_cluster_node_name}{node_state}{'join'}; + my $scan_cluster_node_maintenance_mode = $anvil->data->{cib}{parsed}{data}{node}{$scan_cluster_node_name}{node_state}{'maintenance-mode'}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_cluster_node_name => $scan_cluster_node_name, + scan_cluster_node_host_uuid => $scan_cluster_node_host_uuid, + scan_cluster_node_pacemaker_id => $scan_cluster_node_pacemaker_id, + scan_cluster_node_in_ccm => $scan_cluster_node_in_ccm, + scan_cluster_node_crmd_member => $scan_cluster_node_crmd_member, + scan_cluster_node_cluster_member => $scan_cluster_node_cluster_member, + scan_cluster_node_maintenance_mode => $scan_cluster_node_maintenance_mode, + }}); + + if (exists $anvil->data->{sql}{scan_cluster_node_host_uuid}{$scan_cluster_node_host_uuid}) + { + # Look for changes. + my $scan_cluster_node_uuid = $anvil->data->{sql}{scan_cluster_node_host_uuid}{$scan_cluster_node_host_uuid}; + my $old_scan_cluster_node_name = $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_name}; + my $old_scan_cluster_node_pacemaker_id = $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_pacemaker_id}; + my $old_scan_cluster_node_in_ccm = $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_in_ccm}; + my $old_scan_cluster_node_crmd_member = $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_crmd_member}; + my $old_scan_cluster_node_cluster_member = $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_cluster_member}; + my $old_scan_cluster_node_maintenance_mode = $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_maintenance_mode}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_cluster_node_uuid => $scan_cluster_node_uuid, + old_scan_cluster_node_name => $old_scan_cluster_node_name, + old_scan_cluster_node_pacemaker_id => $old_scan_cluster_node_pacemaker_id, + old_scan_cluster_node_in_ccm => $old_scan_cluster_node_in_ccm, + old_scan_cluster_node_crmd_member => $old_scan_cluster_node_crmd_member, + old_scan_cluster_node_cluster_member => $old_scan_cluster_node_cluster_member, + old_scan_cluster_node_maintenance_mode => $old_scan_cluster_node_maintenance_mode, + }}); + + my $update = 0; + if ($scan_cluster_node_name ne $old_scan_cluster_node_name) + { + $update = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); + + my $variables = { + new_node_name => $scan_cluster_node_name, + old_node_name => $old_scan_cluster_node_name, + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_alert_0008", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_cluster_alert_0009", message_variables => $variables, set_by => $THIS_FILE}); + } + if ($scan_cluster_node_pacemaker_id ne $old_scan_cluster_node_pacemaker_id) + { + $update = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); + + my $variables = { + node_name => $scan_cluster_node_name, + new_pacemaker_id => $scan_cluster_node_pacemaker_id ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + old_pacemaker_id => $old_scan_cluster_node_pacemaker_id ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_alert_0004", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_cluster_alert_0004", message_variables => $variables, set_by => $THIS_FILE}); + } + if ($scan_cluster_node_in_ccm ne $old_scan_cluster_node_in_ccm) + { + $update = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); + + my $variables = { + node_name => $scan_cluster_node_name, + new_in_ccm => $scan_cluster_node_in_ccm ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + old_in_ccm => $old_scan_cluster_node_in_ccm ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_alert_0005", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_cluster_alert_0005", message_variables => $variables, set_by => $THIS_FILE}); + } + if ($scan_cluster_node_crmd_member ne $old_scan_cluster_node_crmd_member) + { + $update = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); + + my $variables = { + node_name => $scan_cluster_node_name, + new_crmd_member => $scan_cluster_node_crmd_member ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + old_crmd_member => $old_scan_cluster_node_crmd_member ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_alert_0006", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_cluster_alert_0006", message_variables => $variables, set_by => $THIS_FILE}); + } + if ($scan_cluster_node_cluster_member ne $old_scan_cluster_node_cluster_member) + { + $update = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); + + my $variables = { + node_name => $scan_cluster_node_name, + new_cluster_member => $scan_cluster_node_cluster_member ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + old_cluster_member => $old_scan_cluster_node_cluster_member ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_alert_0007", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_cluster_alert_0007", message_variables => $variables, set_by => $THIS_FILE}); + } + if ($scan_cluster_node_maintenance_mode ne $old_scan_cluster_node_maintenance_mode) + { + $update = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); + + my $variables = { + node_name => $scan_cluster_node_name, + new_maintenance_mode => $scan_cluster_node_maintenance_mode ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + old_maintenance_mode => $old_scan_cluster_node_maintenance_mode ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_alert_0008", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_cluster_alert_0008", message_variables => $variables, set_by => $THIS_FILE}); + } + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); + if ($update) + { + my $query = " +UPDATE + scan_cluster_nodes +SET + scan_cluster_node_name = ".$anvil->Database->quote($scan_cluster_node_name).", + scan_cluster_node_pacemaker_id = ".$anvil->Database->quote($scan_cluster_node_pacemaker_id).", + scan_cluster_node_in_ccm = ".$anvil->Database->quote($scan_cluster_node_in_ccm).", + scan_cluster_node_crmd_member = ".$anvil->Database->quote($scan_cluster_node_crmd_member).", + scan_cluster_node_cluster_member = ".$anvil->Database->quote($scan_cluster_node_cluster_member).", + scan_cluster_node_maintenance_mode = ".$anvil->Database->quote($scan_cluster_node_maintenance_mode).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_cluster_node_uuid = ".$anvil->Database->quote($scan_cluster_node_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + } + } + else + { + # Add the node. + my $scan_cluster_node_uuid = $anvil->Get->uuid(); + my $query = " +INSERT INTO + scan_cluster_nodes +( + scan_cluster_node_uuid, + scan_cluster_node_scan_cluster_uuid, + scan_cluster_node_host_uuid, + scan_cluster_node_name, + scan_cluster_node_pacemaker_id, + scan_cluster_node_in_ccm, + scan_cluster_node_crmd_member, + scan_cluster_node_cluster_member, + scan_cluster_node_maintenance_mode, + modified_date +) VALUES ( + ".$anvil->Database->quote($scan_cluster_node_uuid).", + ".$anvil->Database->quote($scan_cluster_uuid).", + ".$anvil->Database->quote($scan_cluster_node_host_uuid).", + ".$anvil->Database->quote($scan_cluster_node_name).", + ".$anvil->Database->quote($scan_cluster_node_pacemaker_id).", + ".$anvil->Database->quote($scan_cluster_node_in_ccm).", + ".$anvil->Database->quote($scan_cluster_node_crmd_member).", + ".$anvil->Database->quote($scan_cluster_node_cluster_member).", + ".$anvil->Database->quote($scan_cluster_node_maintenance_mode).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + + my $host_name = $anvil->Get->host_name_from_uuid({host_uuid => $scan_cluster_node_host_uuid}); + my $variables = { + cluster_name => $cluster_name, + node_name => $scan_cluster_node_name, + host_uuid => $scan_cluster_node_host_uuid, + host_name => $host_name, + pacemaker_id => $scan_cluster_node_pacemaker_id, + in_ccm => $scan_cluster_node_in_ccm ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", # Yes or No + crmd_member => $scan_cluster_node_crmd_member ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + cluster_member => $scan_cluster_node_cluster_member ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + maintenance_mode => $scan_cluster_node_maintenance_mode ? "#!string!scan_cluster_unit_0001!#" : "#!string!scan_cluster_unit_0002!#", + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_alert_0003", variables => $variables}); + $anvil->Alert->register({debug => 2, alert_level => "notice", message => "scan_cluster_alert_0003", message_variables => $variables, set_by => $THIS_FILE}); + } } + ### TODO: Check for / repair bad cluster config issues # If we're still alive, we're either node 1, or we're node 2 and node 1 is not ready. If we're not ready, if ($stonith_max_attempts ne "INFINITY") { ### TODO: Call pcs to update } + return(0); +} + +# Read in existing data from the database. +sub read_last_scan +{ + my ($anvil) = @_; + + my $query = " +SELECT + scan_cluster_uuid, + scan_cluster_anvil_uuid, + scan_cluster_name +FROM + scan_cluster +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + # NOTE: There's no known way to track a cluster name change, so we can't really avoid having + # an entery per cluster name. + my $scan_cluster_uuid = $row->[0]; + my $scan_cluster_anvil_uuid = $row->[1]; + my $scan_cluster_name = $row->[2]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_cluster_uuid" => $scan_cluster_uuid, + "scan_cluster_anvil_uuid" => $scan_cluster_anvil_uuid, + "scan_cluster_name" => $scan_cluster_name, + }}); + + # Store the old data now. + $anvil->data->{sql}{scan_cluster}{scan_cluster_uuid}{$scan_cluster_uuid}{scan_cluster_name} = $scan_cluster_name; + $anvil->data->{sql}{scan_cluster}{scan_cluster_uuid}{$scan_cluster_uuid}{scan_cluster_anvil_uuid} = $scan_cluster_anvil_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_cluster::scan_cluster_uuid::${scan_cluster_uuid}::scan_cluster_name" => $anvil->data->{sql}{scan_cluster}{scan_cluster_uuid}{$scan_cluster_uuid}{scan_cluster_name}, + "sql::scan_cluster::scan_cluster_uuid::${scan_cluster_uuid}::scan_cluster_anvil_uuid" => $anvil->data->{sql}{scan_cluster}{scan_cluster_uuid}{$scan_cluster_uuid}{scan_cluster_anvil_uuid}, + }}); + + # Make it easy to look up the cluster_uuid from the anvil_uuid. + $anvil->data->{sql}{anvil_uuid}{$scan_cluster_anvil_uuid} = $scan_cluster_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::anvil_uuid::${scan_cluster_anvil_uuid}" => $anvil->data->{sql}{anvil_uuid}{$scan_cluster_anvil_uuid}, + }}); + } + undef $count; + undef $results; + + $query = " +SELECT + scan_cluster_node_uuid, + scan_cluster_node_scan_cluster_uuid, + scan_cluster_node_host_uuid, + scan_cluster_node_name, + scan_cluster_node_pacemaker_id, + scan_cluster_node_in_ccm, + scan_cluster_node_crmd_member, + scan_cluster_node_cluster_member, + scan_cluster_node_maintenance_mode +FROM + scan_cluster_nodes +;"; + $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_cluster_nodes' table, so now we'll look for data in the node and + # services tables. + my $scan_cluster_node_uuid = $row->[0]; + my $scan_cluster_node_scan_cluster_uuid = $row->[1]; + my $scan_cluster_node_host_uuid = $row->[2]; + my $scan_cluster_node_name = $row->[3]; + my $scan_cluster_node_pacemaker_id = $row->[4]; + my $scan_cluster_node_in_ccm = $row->[5]; + my $scan_cluster_node_crmd_member = $row->[6]; + my $scan_cluster_node_cluster_member = $row->[7]; + my $scan_cluster_node_maintenance_mode = $row->[8]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { + scan_cluster_node_uuid => $scan_cluster_node_uuid, + scan_cluster_node_scan_cluster_uuid => $scan_cluster_node_scan_cluster_uuid, + scan_cluster_node_host_uuid => $scan_cluster_node_host_uuid, + scan_cluster_node_name => $scan_cluster_node_name, + scan_cluster_node_pacemaker_id => $scan_cluster_node_pacemaker_id, + scan_cluster_node_in_ccm => $scan_cluster_node_in_ccm, + scan_cluster_node_crmd_member => $scan_cluster_node_crmd_member, + scan_cluster_node_cluster_member => $scan_cluster_node_cluster_member, + scan_cluster_node_maintenance_mode => $scan_cluster_node_maintenance_mode, + }}); + + # Store the old data now. + $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid} = { + scan_cluster_node_scan_cluster_uuid => $scan_cluster_node_scan_cluster_uuid, + scan_cluster_node_host_uuid => $scan_cluster_node_host_uuid, + scan_cluster_node_name => $scan_cluster_node_name, + scan_cluster_node_pacemaker_id => $scan_cluster_node_pacemaker_id, + scan_cluster_node_in_ccm => $scan_cluster_node_in_ccm, + scan_cluster_node_crmd_member => $scan_cluster_node_crmd_member, + scan_cluster_node_cluster_member => $scan_cluster_node_cluster_member, + scan_cluster_node_maintenance_mode => $scan_cluster_node_maintenance_mode, + }; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_cluster_node::scan_cluster_node_uuid::${scan_cluster_node_uuid}::scan_cluster_node_scan_cluster_uuid" => $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_scan_cluster_uuid}, + "sql::scan_cluster_node::scan_cluster_node_uuid::${scan_cluster_node_uuid}::scan_cluster_node_host_uuid" => $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_host_uuid}, + "sql::scan_cluster_node::scan_cluster_node_uuid::${scan_cluster_node_uuid}::scan_cluster_node_name" => $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_name}, + "sql::scan_cluster_node::scan_cluster_node_uuid::${scan_cluster_node_uuid}::scan_cluster_node_pacemaker_id" => $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_pacemaker_id}, + "sql::scan_cluster_node::scan_cluster_node_uuid::${scan_cluster_node_uuid}::scan_cluster_node_in_ccm" => $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_in_ccm}, + "sql::scan_cluster_node::scan_cluster_node_uuid::${scan_cluster_node_uuid}::scan_cluster_node_crmd_member" => $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_crmd_member}, + "sql::scan_cluster_node::scan_cluster_node_uuid::${scan_cluster_node_uuid}::scan_cluster_node_cluster_member" => $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_cluster_member}, + "sql::scan_cluster_node::scan_cluster_node_uuid::${scan_cluster_node_uuid}::scan_cluster_node_maintenance_mode" => $anvil->data->{sql}{scan_cluster_node}{scan_cluster_node_uuid}{$scan_cluster_node_uuid}{scan_cluster_node_maintenance_mode}, + }}); + + $anvil->data->{sql}{scan_cluster_node_host_uuid}{$scan_cluster_node_host_uuid} = $scan_cluster_node_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_cluster_node_host_uuid::${scan_cluster_node_host_uuid}" => $anvil->data->{sql}{scan_cluster_node_host_uuid}{$scan_cluster_node_host_uuid}, + }}); + } + + return(0); +} + +# This reads in all the data we can find on the local system +sub collect_data +{ + my ($anvil) = @_; + + # Pick out core cluster details. + my $problem = $anvil->Cluster->parse_cib({debug => 3}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); + # If there was a problem, we're not in the cluster. + if ($problem) + { + my $changed = $anvil->Alert->check_alert_sent({ + record_locator => "scan_cluster::in_cluster", + set_by => $THIS_FILE, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Register an alert. + my $variables = { host_name => $anvil->Get->host_name() }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_alert_0010", variables => $variables}); + $anvil->Alert->register({alert_level => "warning", message => "scan_cluster_alert_0010", message_variables => $variables, set_by => $THIS_FILE}); + + # See if I need to mark us as out of the cluster. Normally, our peer would do this, + # but if we went down at the same time as our peer, both of us might not update the + # membership values. + my $query = " +SELECT + scan_cluster_node_uuid, + scan_cluster_node_in_ccm, + scan_cluster_node_crmd_member, + scan_cluster_node_cluster_member +FROM + scan_cluster_nodes +WHERE + scan_cluster_node_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_cluster_nodes' table, so now we'll look for data in the node and + # services tables. + my $scan_cluster_node_uuid = $row->[0]; + my $scan_cluster_node_in_ccm = $row->[1]; + my $scan_cluster_node_crmd_member = $row->[2]; + my $scan_cluster_node_cluster_member = $row->[3]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_cluster_node_uuid => $scan_cluster_node_uuid, + scan_cluster_node_in_ccm => $scan_cluster_node_in_ccm, + scan_cluster_node_crmd_member => $scan_cluster_node_crmd_member, + scan_cluster_node_cluster_member => $scan_cluster_node_cluster_member, + }}); + + if (($scan_cluster_node_in_ccm) or ($scan_cluster_node_crmd_member) or ($scan_cluster_node_cluster_member)) + { + # Update + my $query = " +UPDATE + scan_cluster_nodes +SET + scan_cluster_node_in_ccm = '0', + scan_cluster_node_crmd_member = '0', + scan_cluster_node_cluster_member = '0', + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_cluster_node_uuid = ".$anvil->Database->quote($scan_cluster_node_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + } + } + } + + # Exit now. + $anvil->nice_exit({exit_code => 2}); + } + else + { + # See if we came back into the cluster + my $changed = $anvil->Alert->check_alert_sent({ + clear => 1, + record_locator => "scan_cluster::in_cluster", + set_by => $THIS_FILE, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); + if ($changed) + { + # Register an alert. + my $variables = { host_name => $anvil->Get->host_name() }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_cluster_alert_0011", variables => $variables}); + $anvil->Alert->register({alert_level => "warning", clear_alert => 1, message => "scan_cluster_alert_0011", message_variables => $variables, set_by => $THIS_FILE}); + } + } return(0); } diff --git a/scancore-agents/scan-cluster/scan-cluster.sql b/scancore-agents/scan-cluster/scan-cluster.sql index 9e550a40..448dca6c 100644 --- a/scancore-agents/scan-cluster/scan-cluster.sql +++ b/scancore-agents/scan-cluster/scan-cluster.sql @@ -2,25 +2,22 @@ -- -- NOTE: This agent is not host-bound. It's update by node 1 if it's in the cluster, else by node 2 if it's -- the only one online. +-- NOTE: Server data is not stored here. See scan-server for data on those resources. CREATE TABLE scan_cluster ( - scan_cluster_uuid uuid primary key, - scan_cluster_name text not null, -- The name of the cluster - scan_cluster_stonith_enabled boolean not null, -- Tracks when stonith (fencing) was enabled/disabled - scan_cluster_maintenance_mode boolean not null, -- Tracks when maintenance mode is enabled/disabled. - modified_date timestamp with time zone not null, - - FOREIGN KEY(scan_cluster_host_uuid) REFERENCES hosts(host_uuid) + scan_cluster_uuid uuid primary key, + scan_cluster_anvil_uuid uuid not null, -- The Anvil! UUID this cluster is associated with. + scan_cluster_name text not null, -- The name of the cluster + modified_date timestamp with time zone not null ); ALTER TABLE scan_cluster OWNER TO admin; CREATE TABLE history.scan_cluster ( - history_id bigserial, - scan_cluster_uuid uuid, - scan_cluster_name text, - scan_cluster_stonith_enabled boolean, - scan_cluster_maintenance_mode boolean, - modified_date timestamp with time zone not null + history_id bigserial, + scan_cluster_uuid uuid, + scan_cluster_anvil_uuid uuid, + scan_cluster_name text, + modified_date timestamp with time zone not null ); ALTER TABLE history.scan_cluster OWNER TO admin; @@ -31,17 +28,14 @@ DECLARE BEGIN SELECT INTO history_scan_cluster * FROM scan_cluster WHERE scan_cluster_uuid=new.scan_cluster_uuid; INSERT INTO history.scan_cluster - (scan_cluster_uuid, + (scan_cluster_uuid, + scan_cluster_anvil_uuid, scan_cluster_name, - scan_cluster_stonith_enabled, - scan_cluster_maintenance_mode, modified_date) VALUES - (history_scan_cluster.scan_cluster_uuid, - history_scan_cluster.scan_cluster_host_uuid, + (history_scan_cluster.scan_cluster_uuid, + history_scan_cluster.scan_cluster_anvil_uuid, history_scan_cluster.scan_cluster_name, - history_scan_cluster.scan_cluster_stonith_enabled, - history_scan_cluster.scan_cluster_maintenance_mode, history_scan_cluster.modified_date); RETURN NULL; END; @@ -53,17 +47,22 @@ CREATE TRIGGER trigger_scan_cluster AFTER INSERT OR UPDATE ON scan_cluster FOR EACH ROW EXECUTE PROCEDURE history_scan_cluster(); - + +-- Node status information CREATE TABLE scan_cluster_nodes ( scan_cluster_node_uuid uuid primary key, scan_cluster_node_scan_cluster_uuid uuid not null, -- The parent scan_cluster_uuid. scan_cluster_node_host_uuid uuid not null, -- This is the host UUID of the node. scan_cluster_node_name text not null, -- This is the host name as reported by pacemaker. It _should_ match up to a host name in 'hosts'. scan_cluster_node_pacemaker_id numeric not null, -- This is the internal pacemaker ID number of this node. + scan_cluster_node_in_ccm boolean not null, -- Indicates if the node is a corosync cluster member, first step in a node comint online. + scan_cluster_node_crmd_member boolean not null, -- Indicates if the node is in the corosync process group. Value from the CIB is 'online' or 'offline'. Second step in a node coming online + scan_cluster_node_cluster_member boolean not null, -- Indicates if the node has joined the controller and is a full member. Value from the CIB is 'member' or 'down'. Final step in the joining the cluster. + scan_cluster_node_maintenance_mode boolean not null, -- Tracks when maintenance mode is enabled/disabled. modified_date timestamp with time zone not null, FOREIGN KEY(scan_cluster_node_scan_cluster_uuid) REFERENCES scan_cluster(scan_cluster_uuid), - FOREIGN KEY(scan_cluster_host_uuid) REFERENCES hosts(host_uuid) + FOREIGN KEY(scan_cluster_node_host_uuid) REFERENCES hosts(host_uuid) ); ALTER TABLE scan_cluster_nodes OWNER TO admin; @@ -74,6 +73,10 @@ CREATE TABLE history.scan_cluster_nodes ( scan_cluster_node_host_uuid uuid, scan_cluster_node_name text, scan_cluster_node_pacemaker_id numeric, + scan_cluster_node_in_ccm boolean, + scan_cluster_node_crmd_member boolean, + scan_cluster_node_cluster_member boolean, + scan_cluster_node_maintenance_mode boolean, modified_date timestamp with time zone not null ); ALTER TABLE history.scan_cluster_nodes OWNER TO admin; @@ -90,6 +93,10 @@ BEGIN scan_cluster_node_host_uuid, scan_cluster_node_name, scan_cluster_node_pacemaker_id, + scan_cluster_node_in_ccm, + scan_cluster_node_crmd_member, + scan_cluster_node_cluster_member, + scan_cluster_node_maintenance_mode, modified_date) VALUES (history_scan_cluster_nodes.scan_cluster_node_uuid, @@ -97,6 +104,10 @@ BEGIN history_scan_cluster_nodes.scan_cluster_node_host_uuid, history_scan_cluster_nodes.scan_cluster_node_name, history_scan_cluster_nodes.scan_cluster_node_pacemaker_id, + history_scan_cluster_nodes.scan_cluster_node_in_ccm, + history_scan_cluster_nodes.scan_cluster_node_crmd_member, + history_scan_cluster_nodes.scan_cluster_node_cluster_member, + history_scan_cluster_nodes.scan_cluster_node_maintenance_mode, history_scan_cluster_nodes.modified_date); RETURN NULL; END; @@ -109,396 +120,183 @@ CREATE TRIGGER trigger_scan_cluster_nodes FOR EACH ROW EXECUTE PROCEDURE history_scan_cluster_nodes(); -CREATE TABLE scan_cluster_stoniths ( - scan_cluster_stonith_uuid uuid primary key, - scan_cluster_stonith_scan_cluster_uuid uuid not null, -- The parent scan_cluster_uuid. - scan_cluster_stonith_host_uuid uuid not null, -- This is the host UUID of the node. - scan_cluster_stonith_name text not null, -- This is the 'stonith id' - scan_cluster_stonith_arguments text not null, -- This is the fence agent + collection of primitive variable=value pairs (the nvpairs) - scan_cluster_stonith_operations text not null, -- This is the collection of operation variable=value pairs (the nvpairs) - modified_date timestamp with time zone not null, - - FOREIGN KEY(scan_cluster_stonith_scan_cluster_uuid) REFERENCES scan_cluster(scan_cluster_uuid), - FOREIGN KEY(scan_cluster_host_uuid) REFERENCES hosts(host_uuid) -); -ALTER TABLE scan_cluster_stoniths OWNER TO admin; - -CREATE TABLE history.scan_cluster_stoniths ( - history_id bigserial, - scan_cluster_stonith_uuid uuid, - scan_cluster_stonith_scan_cluster_uuid uuid, - scan_cluster_stonith_host_uuid uuid, - scan_cluster_stonith_name text, - scan_cluster_stonith_arguments text, - scan_cluster_stonith_operations text, - modified_date timestamp with time zone not null -); -ALTER TABLE history.scan_cluster_stoniths OWNER TO admin; - -CREATE FUNCTION history_scan_cluster_stoniths() RETURNS trigger -AS $$ -DECLARE - history_scan_cluster_stoniths RECORD; -BEGIN - SELECT INTO history_scan_cluster_stoniths * FROM scan_cluster_stoniths WHERE scan_cluster_stonith_uuid=new.scan_cluster_stonith_uuid; - INSERT INTO history.scan_cluster_stoniths - (scan_cluster_stonith_uuid, - scan_cluster_stonith_scan_cluster_uuid, - scan_cluster_stonith_host_uuid, - scan_cluster_stonith_name, - scan_cluster_stonith_arguments, - scan_cluster_stonith_operations, - modified_date) - VALUES - (history_scan_cluster_stoniths.scan_cluster_stonith_uuid, - history_scan_cluster_stoniths.scan_cluster_stonith_scan_cluster_uuid, - history_scan_cluster_stoniths.scan_cluster_stonith_host_uuid, - history_scan_cluster_stoniths.scan_cluster_stonith_name, - history_scan_cluster_stoniths.scan_cluster_stonith_arguments, - history_scan_cluster_stoniths.scan_cluster_stonith_operations, - history_scan_cluster_stoniths.modified_date); - RETURN NULL; -END; -$$ -LANGUAGE plpgsql; -ALTER FUNCTION history_scan_cluster_stoniths() OWNER TO admin; - -CREATE TRIGGER trigger_scan_cluster_stoniths - AFTER INSERT OR UPDATE ON scan_cluster_stoniths - FOR EACH ROW EXECUTE PROCEDURE history_scan_cluster_stoniths(); - - -CREATE TABLE scan_cluster_servers ( - scan_cluster_server_uuid uuid primary key, - scan_cluster_server_scan_cluster_uuid uuid not null, -- The parent scan_cluster_uuid. - scan_cluster_server_name text not null, -- This is the name of the server (ocf primitive id) - scan_cluster_server_state text not null, -- This is the 'running' or why it's off (off by user, etc) - scan_cluster_server_host_name uuid not null, -- This is the (cluster) name of the node hosting the server. Blank if the server is off. - scan_cluster_server_arguments text not null, -- This is the collection of primitive variable=value pairs (the nvpairs) - scan_cluster_server_operations text not null, -- This is the collection of operation variable=value pairs (the nvpairs) - scan_cluster_server_meta text not null, -- This is the collection of meta attribute variable=value pairs (the nvpairs) - modified_date timestamp with time zone not null, - - FOREIGN KEY(scan_cluster_server_scan_cluster_uuid) REFERENCES scan_cluster(scan_cluster_uuid), - FOREIGN KEY(scan_cluster_host_uuid) REFERENCES hosts(host_uuid) -); -ALTER TABLE scan_cluster_servers OWNER TO admin; - -CREATE TABLE history.scan_cluster_servers ( - history_id bigserial, - scan_cluster_server_uuid uuid, - scan_cluster_server_scan_cluster_uuid uuid, - scan_cluster_server_name text, - scan_cluster_server_arguments text, - scan_cluster_server_operations text, - scan_cluster_server_meta text, - modified_date timestamp with time zone not null -); -ALTER TABLE history.scan_cluster_servers OWNER TO admin; - -CREATE FUNCTION history_scan_cluster_servers() RETURNS trigger -AS $$ -DECLARE - history_scan_cluster_servers RECORD; -BEGIN - SELECT INTO history_scan_cluster_servers * FROM scan_cluster_servers WHERE scan_cluster_server_uuid=new.scan_cluster_server_uuid; - INSERT INTO history.scan_cluster_servers - (scan_cluster_server_uuid, - scan_cluster_server_scan_cluster_uuid, - scan_cluster_server_name, - scan_cluster_server_arguments, - scan_cluster_server_operations, - scan_cluster_server_meta, - modified_date) - VALUES - (history_scan_cluster_servers.scan_cluster_server_uuid, - history_scan_cluster_servers.scan_cluster_server_scan_cluster_uuid, - history_scan_cluster_servers.scan_cluster_server_host_uuid, - history_scan_cluster_servers.scan_cluster_server_name, - history_scan_cluster_servers.scan_cluster_server_arguments, - history_scan_cluster_servers.scan_cluster_server_operations, - history_scan_cluster_servers.modified_date); - RETURN NULL; -END; -$$ -LANGUAGE plpgsql; -ALTER FUNCTION history_scan_cluster_servers() OWNER TO admin; - -CREATE TRIGGER trigger_scan_cluster_servers - AFTER INSERT OR UPDATE ON scan_cluster_servers - FOR EACH ROW EXECUTE PROCEDURE history_scan_cluster_servers(); - - --- Example CIB -# pcs resource - * srv07-el6 (ocf::alteeve:server): Stopped (disabled) - * srv01-sql (ocf::alteeve:server): Started mk-a02n01 - * srv02-lab1 (ocf::alteeve:server): Started mk-a02n01 - * srv08-m2-psql (ocf::alteeve:server): Stopped (disabled) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --- \ No newline at end of file +-- TODO: We may want to track this data in the future. For now, we're not going to bother as we can always +-- dig through the historical cib.xml.X files on the nodes. +-- +-- -- Constraints; Useful for tracking when servers are asked to migate. +-- CREATE TABLE scan_cluster_constraints ( +-- scan_cluster_constraint_uuid uuid primary key, +-- scan_cluster_constraint_scan_cluster_uuid uuid not null, -- The parent scan_cluster_uuid. +-- scan_cluster_constraint_server_name text not null, -- This is the server name the constraint applies to. +-- scan_cluster_constraint_node1_name text not null, -- This is name of the first node +-- scan_cluster_constraint_node1_score numeric not null, -- This is the score assigned to the first node (larger number is higher priority) +-- scan_cluster_constraint_node2_name text not null, -- This is name of the second node +-- scan_cluster_constraint_node2_score numeric not null, -- This is the score assigned to the second node (larger number is higher priority) +-- modified_date timestamp with time zone not null, +-- +-- FOREIGN KEY(scan_cluster_constraint_scan_cluster_uuid) REFERENCES scan_cluster(scan_cluster_uuid) +-- ); +-- ALTER TABLE scan_cluster_constraints OWNER TO admin; +-- +-- CREATE TABLE history.scan_cluster_constraints ( +-- history_id bigserial, +-- scan_cluster_constraint_uuid uuid, +-- scan_cluster_constraint_scan_cluster_uuid uuid, +-- scan_cluster_constraint_server_name text, +-- scan_cluster_constraint_node1_name text, +-- scan_cluster_constraint_node1_score numeric, +-- scan_cluster_constraint_node2_name text, +-- scan_cluster_constraint_node2_score numeric, +-- modified_date timestamp with time zone not null +-- ); +-- ALTER TABLE history.scan_cluster_constraints OWNER TO admin; +-- +-- CREATE FUNCTION history_scan_cluster_constraints() RETURNS trigger +-- AS $$ +-- DECLARE +-- history_scan_cluster_constraints RECORD; +-- BEGIN +-- SELECT INTO history_scan_cluster_constraints * FROM scan_cluster_constraints WHERE scan_cluster_constraint_uuid=new.scan_cluster_constraint_uuid; +-- INSERT INTO history.scan_cluster_constraints +-- (scan_cluster_constraint_uuid, +-- scan_cluster_constraint_scan_cluster_uuid, +-- scan_cluster_constraint_server_name, +-- scan_cluster_constraint_node1_name, +-- scan_cluster_constraint_node1_score, +-- scan_cluster_constraint_node2_name, +-- scan_cluster_constraint_node2_score, +-- modified_date) +-- VALUES +-- (history_scan_cluster_constraints.scan_cluster_constraint_uuid, +-- history_scan_cluster_constraints.scan_cluster_constraint_scan_cluster_uuid, +-- history_scan_cluster_constraints.scan_cluster_constraint_server_name, +-- history_scan_cluster_constraints.scan_cluster_constraint_node1_name, +-- history_scan_cluster_constraints.scan_cluster_constraint_node1_score, +-- history_scan_cluster_constraints.scan_cluster_constraint_node2_name, +-- history_scan_cluster_constraints.scan_cluster_constraint_node2_score, +-- history_scan_cluster_constraints.modified_date); +-- RETURN NULL; +-- END; +-- $$ +-- LANGUAGE plpgsql; +-- ALTER FUNCTION history_scan_cluster_constraints() OWNER TO admin; +-- +-- CREATE TRIGGER trigger_scan_cluster_constraints +-- AFTER INSERT OR UPDATE ON scan_cluster_constraints +-- FOR EACH ROW EXECUTE PROCEDURE history_scan_cluster_constraints(); +-- +-- +-- -- This stores the fence (stonith) configuration data. We use 'fence' instead of 'stonith' because pacemaker +-- -- uses both (see 'fence topology', for example), and 'fence' implies fabric and power fencing, where the +-- -- name 'stonith' implies power fencing only. +-- CREATE TABLE scan_cluster_fences ( +-- scan_cluster_fence_uuid uuid primary key, +-- scan_cluster_fence_scan_cluster_uuid uuid not null, -- The parent scan_cluster_uuid. +-- scan_cluster_fence_target_node_name text not null, -- This is the node name that the fence will act on (kill) +-- scan_cluster_fence_name text not null, -- This is the 'stonith id' +-- scan_cluster_fence_arguments text not null, -- This is the fence agent + collection of primitive variable=value pairs (the nvpairs) +-- scan_cluster_fence_operations text not null, -- This is the collection of operation variable=value pairs (the nvpairs) +-- modified_date timestamp with time zone not null, +-- +-- FOREIGN KEY(scan_cluster_fence_scan_cluster_uuid) REFERENCES scan_cluster(scan_cluster_uuid) +-- ); +-- ALTER TABLE scan_cluster_fences OWNER TO admin; +-- +-- CREATE TABLE history.scan_cluster_fences ( +-- history_id bigserial, +-- scan_cluster_fence_uuid uuid, +-- scan_cluster_fence_scan_cluster_uuid uuid, +-- scan_cluster_fence_target_node_name text, +-- scan_cluster_fence_name text, +-- scan_cluster_fence_arguments text, +-- scan_cluster_fence_operations text, +-- modified_date timestamp with time zone not null +-- ); +-- ALTER TABLE history.scan_cluster_fences OWNER TO admin; +-- +-- CREATE FUNCTION history_scan_cluster_fences() RETURNS trigger +-- AS $$ +-- DECLARE +-- history_scan_cluster_fences RECORD; +-- BEGIN +-- SELECT INTO history_scan_cluster_fences * FROM scan_cluster_fences WHERE scan_cluster_fence_uuid=new.scan_cluster_fence_uuid; +-- INSERT INTO history.scan_cluster_fences +-- (scan_cluster_fence_uuid, +-- scan_cluster_fence_scan_cluster_uuid, +-- scan_cluster_fence_target_node_name, +-- scan_cluster_fence_name, +-- scan_cluster_fence_arguments, +-- scan_cluster_fence_operations, +-- modified_date) +-- VALUES +-- (history_scan_cluster_fences.scan_cluster_fence_uuid, +-- history_scan_cluster_fences.scan_cluster_fence_scan_cluster_uuid, +-- history_scan_cluster_fences.scan_cluster_fence_target_node_name, +-- history_scan_cluster_fences.scan_cluster_fence_name, +-- history_scan_cluster_fences.scan_cluster_fence_arguments, +-- history_scan_cluster_fences.scan_cluster_fence_operations, +-- history_scan_cluster_fences.modified_date); +-- RETURN NULL; +-- END; +-- $$ +-- LANGUAGE plpgsql; +-- ALTER FUNCTION history_scan_cluster_fences() OWNER TO admin; +-- +-- CREATE TRIGGER trigger_scan_cluster_fences +-- AFTER INSERT OR UPDATE ON scan_cluster_fences +-- FOR EACH ROW EXECUTE PROCEDURE history_scan_cluster_fences(); +-- +-- +-- -- This stores data about the order of fencing actions +-- CREATE TABLE scan_cluster_fence_topologies ( +-- scan_cluster_fence_topology_uuid uuid primary key, +-- scan_cluster_fence_topology_scan_cluster_uuid uuid not null, -- The parent scan_cluster_uuid. +-- scan_cluster_fence_topology_target_node_name text not null, -- This is the node that the topology applies to. +-- scan_cluster_fence_topology_index numeric not null, -- This is numerical order that the associated devices will be tried in. Lower value == higher priority. +-- scan_cluster_fence_topology_device text not null, -- This is the (comma-separated) devices used in this index +-- modified_date timestamp with time zone not null, +-- +-- FOREIGN KEY(scan_cluster_fence_topology_scan_cluster_uuid) REFERENCES scan_cluster(scan_cluster_uuid) +-- ); +-- ALTER TABLE scan_cluster_fence_topologies OWNER TO admin; +-- +-- CREATE TABLE history.scan_cluster_fence_topologies ( +-- history_id bigserial, +-- scan_cluster_fence_topology_uuid uuid, +-- scan_cluster_fence_topology_scan_cluster_uuid uuid, +-- scan_cluster_fence_topology_target_node_name text, +-- scan_cluster_fence_topology_index numeric, +-- scan_cluster_fence_topology_device text, +-- modified_date timestamp with time zone not null +-- ); +-- ALTER TABLE history.scan_cluster_fence_topologies OWNER TO admin; +-- +-- CREATE FUNCTION history_scan_cluster_fence_topologies() RETURNS trigger +-- AS $$ +-- DECLARE +-- history_scan_cluster_fence_topologies RECORD; +-- BEGIN +-- SELECT INTO history_scan_cluster_fence_topologies * FROM scan_cluster_fence_topologies WHERE scan_cluster_fence_topology_uuid=new.scan_cluster_fence_topology_uuid; +-- INSERT INTO history.scan_cluster_fence_topologies +-- (scan_cluster_fence_topology_uuid, +-- scan_cluster_fence_topology_scan_cluster_uuid, +-- scan_cluster_fence_topology_target_node_name, +-- scan_cluster_fence_topology_index, +-- scan_cluster_fence_topology_device, +-- modified_date) +-- VALUES +-- (history_scan_cluster_fence_topologies.scan_cluster_fence_topology_uuid, +-- history_scan_cluster_fence_topologies.scan_cluster_fence_topology_scan_cluster_uuid, +-- history_scan_cluster_fence_topologies.scan_cluster_fence_topology_target_node_name, +-- history_scan_cluster_fence_topologies.scan_cluster_fence_topology_index, +-- history_scan_cluster_fence_topologies.scan_cluster_fence_topology_device, +-- history_scan_cluster_fence_topologies.modified_date); +-- RETURN NULL; +-- END; +-- $$ +-- LANGUAGE plpgsql; +-- ALTER FUNCTION history_scan_cluster_fence_topologies() OWNER TO admin; +-- +-- CREATE TRIGGER trigger_scan_cluster_fence_topologies +-- AFTER INSERT OR UPDATE ON scan_cluster_fence_topologies +-- FOR EACH ROW EXECUTE PROCEDURE history_scan_cluster_fence_topologies(); diff --git a/scancore-agents/scan-cluster/scan-cluster.xml b/scancore-agents/scan-cluster/scan-cluster.xml index 49b5eeeb..1d875138 100644 --- a/scancore-agents/scan-cluster/scan-cluster.xml +++ b/scancore-agents/scan-cluster/scan-cluster.xml @@ -14,17 +14,35 @@ NOTE: All string keys MUST be prefixed with the agent name! ie: 'scan_cluster_lo - + The new cluster: [#!variable!cluster_name!#] has been found. + The cluster: [#!variable!old_cluster_name!#] has been renamed to: [#!variable!new_cluster_name!#]. + The new node: [#!variable!node_name!#] has been found in the cluster: [#!variable!cluster_name!#]; +Host Name/UUID: .......... [#!variable!host_name!#] / [#!variable!host_uuid!#] +Pacemaker ID: ............ [#!variable!pacemaker_id!#] +Corosync Cluster Member: . [#!variable!in_ccm!#] +In Corosync Process Group: [#!variable!crmd_member!#] +Joined Domain: ........... [#!variable!cluster_member!#] +In Maintenance Mode: ..... [#!variable!maintenance_mode!#] + + The node: [#!variable!node_name!#] pacemaker ID: [#!variable!old_pacemaker_id!#] has changed to: [#!variable!new_pacemaker_id!#] + The node: [#!variable!node_name!#] corosync cluster membership status has changed from: [#!variable!old_in_ccm!#] has changed to: [#!variable!new_in_ccm!#] + The node: [#!variable!node_name!#] corosync process group status changed from: [#!variable!old_crmd_member!#] has changed to: [#!variable!new_crmd_member!#] + The node: [#!variable!node_name!#] cluster domain membership status changed from: [#!variable!old_cluster_member!#] has changed to: [#!variable!new_cluster_member!#] + The node: [#!variable!node_name!#] maintenance mode status has changed from: [#!variable!old_maintenance_mode!#] has changed to: [#!variable!new_maintenance_mode!#] + The node: [#!variable!old_node_name!#] has been renamed to: [#!variable!new_node_name!#] + The node: [#!variable!host_name!#] is no longer in the cluster. + The node: [#!variable!host_name!#] is returning back into the cluster. Starting: [#!variable!program!#]. This host is a: [#!variable!host_type!#], this agent is only useful on nodes. Exiting. - We're node 2, and node 1 is running as well. Exiting as only one node needs to run this agent. + - + Yes + No diff --git a/scancore-agents/scan-hardware/scan-hardware b/scancore-agents/scan-hardware/scan-hardware index 8e61e2f3..211af10b 100755 --- a/scancore-agents/scan-hardware/scan-hardware +++ b/scancore-agents/scan-hardware/scan-hardware @@ -1314,11 +1314,12 @@ sub process_health { # 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({ + my $age = $anvil->Alert->check_condition_age({ debug => 2, name => "scan-hardware::less_ram_than_peer", host_uuid => $anvil->Get->host_uuid, }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { age => $age }}); if ($age > 300) { @@ -1350,7 +1351,7 @@ sub process_health } elsif (($peer_ram_total == $hardware_ram_total) or ($difference < $anvil->data->{scancore}{'scan-hardware'}{ram}{clear_threshold})) { - my $age = $anvil->Database->check_condition_age({ + my $age = $anvil->Alert->check_condition_age({ debug => 2, clear => 1, name => "scan-hardware::less_ram_than_peer", diff --git a/scancore-agents/scan-server/scan-server b/scancore-agents/scan-server/scan-server index 7bd61928..004d98d4 100755 --- a/scancore-agents/scan-server/scan-server +++ b/scancore-agents/scan-server/scan-server @@ -551,9 +551,9 @@ DELETED - Marks a server as no longer existing # Server migated (to the peer or to a new Anvil!) my $variables = { server => $server_name, - old_host_name => $anvil->Get->host_name_from_uuid({host_uuid => $old_server_host_uuid}), + old_host_name => $old_server_host_uuid eq "NULL" ? "NULL" : $anvil->Get->host_name_from_uuid({host_uuid => $old_server_host_uuid}), old_host_uuid => $old_server_host_uuid, - new_host_name => $anvil->Get->host_name_from_uuid({host_uuid => $server_host_uuid}), + new_host_name => $server_host_uuid eq "NULL" ? "NULL" : $anvil->Get->host_name_from_uuid({host_uuid => $server_host_uuid}), new_host_uuid => $server_host_uuid, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0012", variables => $variables}); @@ -690,7 +690,7 @@ DELETED - Marks a server as no longer existing my $host_anvil_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid}; my $host_anvil_name = $anvil->Get->anvil_name_from_uuid({anvil_uuid => $host_anvil_uuid}); my $server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid}; - my $server_host_name = $anvil->Get->host_name_from_uuid({host_uuid => $server_host_uuid}); + my $server_host_name = $server_host_uuid eq "NULL" ? "NULL" : $anvil->Get->host_name_from_uuid({host_uuid => $server_host_uuid}); my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 's1:server_name' => $server_name, diff --git a/share/anvil.sql b/share/anvil.sql index b47ad165..4cf9a407 100644 --- a/share/anvil.sql +++ b/share/anvil.sql @@ -102,7 +102,7 @@ CREATE TABLE ssh_keys ( ssh_key_host_uuid uuid not null, ssh_key_user_name text not null, -- This is the user name on the system, not a web interface user. ssh_key_public_key text not null, -- Either 'node', 'dashboard' or 'dr' - modified_date timestamp with time zone not null, + modified_date timestamp with time zone not null, FOREIGN KEY(ssh_key_host_uuid) REFERENCES hosts(host_uuid) ); diff --git a/share/words.xml b/share/words.xml index 37fe2a85..fab0c98f 100644 --- a/share/words.xml +++ b/share/words.xml @@ -235,6 +235,7 @@ The error was: The '#!variable!name!#': [#!variable!uuid!#] is not valid. Unable to mark the server with UUID: [#!variable!uuid!#] as "deleted" because it doesn't apprear to exist in the database in the first place. The 'anvil_uuid': [#!variable!anvil_uuid!#] in invalid. + The MIB file: [#!variable!mib!#] doesn't exist or can't be read. Current Network Interfaces and States diff --git a/tools/anvil-daemon b/tools/anvil-daemon index 472cf5b7..0203d600 100755 --- a/tools/anvil-daemon +++ b/tools/anvil-daemon @@ -16,6 +16,7 @@ # - Write the status of this and the scancore daemon to /etc/anvil/anvil.motd and symlink it to /etc/motd.d/ # - Write a script that runs in crontab at UTC 17:00 that sends an email if Scancore or anvil-daemon are disabled. # - Examine limites in: https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LimitCPU= +# - Write a background program to scan the BCN and uses OUI data to try and find / auto-configure PDUs and UPSes # # NOTE: # - For later; 'reboot --force --force' immediately kills the OS, like disabling ACPI on EL6 and hitting the diff --git a/tools/test.pl b/tools/test.pl index b3d696ff..c51890cc 100755 --- a/tools/test.pl +++ b/tools/test.pl @@ -33,31 +33,370 @@ 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"}); -my $host_name = "mk-a02n02"; -my $host_uuid = $anvil->Get->host_uuid_from_name({ - debug => 2, - host_name => $host_name, -}); -print "host name: [".$host_name."], host_uuid: [".$host_uuid."]\n"; +my $cib = ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +'; -exit; +$anvil->Cluster->parse_cib({debug => 2, cib => $cib}); -my $server_name = $anvil->data->{switches}{server} ? $anvil->data->{switches}{server} : "srv07-el6"; -if ($anvil->data->{switches}{boot}) -{ - print "Booting: [".$server_name."]\n"; - $anvil->Server->boot_virsh({ - debug => 2, - server => $server_name, - }); -} -elsif ($anvil->data->{switches}{'shutdown'}) -{ - print "Shutting down: [".$server_name."]\n"; - $anvil->Server->shutdown_virsh({ - debug => 2, - server => $server_name, - }); -} -exit;