* 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 <digimer@alteeve.ca>
main
Digimer 4 years ago
parent 5d89357c16
commit d677d19ca0
  1. 2
      Anvil/Tools.pm
  2. 117
      Anvil/Tools/Alert.pm
  3. 151
      Anvil/Tools/Cluster.pm
  4. 135
      Anvil/Tools/Database.pm
  5. 171
      Anvil/Tools/Remote.pm
  6. 4
      Anvil/Tools/ScanCore.pm
  7. 151
      Anvil/Tools/Server.pm
  8. 41
      notes
  9. 12
      ocf/alteeve/server
  10. 1
      rpm/SPECS/anvil.spec
  11. 11
      scancore-agents/scan-apc-pdu/Striker-MIB.txt
  12. 2624
      scancore-agents/scan-apc-pdu/scan-apc-pdu
  13. 256
      scancore-agents/scan-apc-pdu/scan-apc-pdu.sql
  14. 41
      scancore-agents/scan-apc-pdu/scan-apc-pdu.xml
  15. 532
      scancore-agents/scan-cluster/scan-cluster
  16. 628
      scancore-agents/scan-cluster/scan-cluster.sql
  17. 24
      scancore-agents/scan-cluster/scan-cluster.xml
  18. 5
      scancore-agents/scan-hardware/scan-hardware
  19. 6
      scancore-agents/scan-server/scan-server
  20. 2
      share/anvil.sql
  21. 1
      share/words.xml
  22. 1
      tools/anvil-daemon
  23. 389
      tools/test.pl

@ -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",

@ -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<< <scan_agent_name>::<condition_name> >>.
=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 >>.

@ -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}})

@ -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<< <scan_agent_name>::<condition_name> >>.
=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,

@ -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);
}

@ -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)
{

@ -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
{

41
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 $?

@ -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);

@ -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

@ -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

File diff suppressed because it is too large Load Diff

@ -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