* Created tools/striker-parse-fence-agents which finds all the available fence agents on a system and gathers their metadata into a common XML file.

* Created Striker->get_fence_data() that reads/parses the unified fence metadata file created by tools/striker-parse-fence-agents.
* Created the new 'fences' database table and Database->insert_or_update_fences() to handle it.
* Added hosts -> host_ipmi that will, later, store information on how to access the host's IPMI interface, when available.
* Sketched out how the new Install Manifests are going to work.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 5 years ago
parent b82051cb37
commit f636e399d7
  1. 4
      Anvil/Tools.pm
  2. 265
      Anvil/Tools/Database.pm
  3. 155
      Anvil/Tools/Striker.pm
  4. 15
      cgi-bin/striker
  5. 2
      html/skins/alteeve/anvil.html
  6. 3
      html/skins/alteeve/images/sources.txt
  7. 281
      notes
  8. 64
      share/anvil.sql
  9. 16
      share/words.xml
  10. 165
      tools/striker-parse-fence-agents

@ -907,6 +907,7 @@ sub _set_defaults
"alert_sent", "alert_sent",
"states", "states",
"manifests", "manifests",
"fences",
], ],
failed_connection_log_level => 1, failed_connection_log_level => 1,
local_lock_active => 0, local_lock_active => 0,
@ -1077,11 +1078,13 @@ sub _set_paths
network_cache => "/tmp/network_cache.anvil", network_cache => "/tmp/network_cache.anvil",
passwd => "/etc/passwd", passwd => "/etc/passwd",
'redhat-release' => "/etc/redhat-release", 'redhat-release' => "/etc/redhat-release",
fences_unified_metadata => "/tmp/fences_unified_metadata.xml",
}, },
directories => { directories => {
anvil => "/etc/anvil", anvil => "/etc/anvil",
backups => "/root/anvil-backups", backups => "/root/anvil-backups",
'cgi-bin' => "/var/www/cgi-bin", 'cgi-bin' => "/var/www/cgi-bin",
fence_agents => "/usr/sbin",
firewalld_services => "/usr/lib/firewalld/services", firewalld_services => "/usr/lib/firewalld/services",
firewalld_zones_etc => "/etc/firewalld/zones", # Changes when firewall-cmd ... --permanent is used. firewalld_zones_etc => "/etc/firewalld/zones", # Changes when firewall-cmd ... --permanent is used.
firewalld_zones => "/usr/lib/firewalld/zones", firewalld_zones => "/usr/lib/firewalld/zones",
@ -1157,6 +1160,7 @@ sub _set_paths
lvchange => "/usr/sbin/lvchange", lvchange => "/usr/sbin/lvchange",
lvs => "/usr/sbin/lvs", lvs => "/usr/sbin/lvs",
lvscan => "/usr/sbin/lvscan", lvscan => "/usr/sbin/lvscan",
man => "/usr/bin/man",
md5sum => "/usr/bin/md5sum", md5sum => "/usr/bin/md5sum",
'mkdir' => "/usr/bin/mkdir", 'mkdir' => "/usr/bin/mkdir",
modifyrepo_c => "/usr/bin/modifyrepo_c", modifyrepo_c => "/usr/bin/modifyrepo_c",

@ -33,6 +33,7 @@ my $THIS_FILE = "Database.pm";
# insert_or_update_anvils # insert_or_update_anvils
# insert_or_update_bridges # insert_or_update_bridges
# insert_or_update_bonds # insert_or_update_bonds
# insert_or_update_fences
# insert_or_update_file_locations # insert_or_update_file_locations
# insert_or_update_files # insert_or_update_files
# insert_or_update_host_keys # insert_or_update_host_keys
@ -3052,6 +3053,224 @@ WHERE
} }
=head2 insert_or_update_fences
This updates (or inserts) a record in the 'fences' table. The C<< fence_uuid >> UUID will be returned.
If there is an error, an empty string is returned.
Parameters;
=head3 uuid (optional)
If set, only the corresponding database will be written to.
=head3 file (optional)
If set, this is the file name logged as the source of any INSERTs or UPDATEs.
=head3 line (optional)
If set, this is the file line number logged as the source of any INSERTs or UPDATEs.
=head3 fence_agent (required)
This is the name of the fence agent to use when communicating with this fence device. The agent must be installed on any machine that may need to fence (or check the fence/power state of) a node.
=head3 fence_arguments (required)
This is the string that tells machines how to communicate / control the the fence device. This is used when configuring pacemaker's stonith (fencing).
The exact formatting needs to match the STDIN parameters supported by C<< fence_agent >>. Please see C<< STDIN PARAMETERS >> section of the fence agent man page for this device.
For example, this can be set to:
* C<< ip="10.201.11.1" lanplus="1" username="admin" password="super secret password"
B<< NOTES >>:
* If C<< password_script >> is used, it is required that the user has copied the script to the nodes.
* Do not use C<< action="..." >> or the fence agent name. If either is found in the string, they will be ignored.
* Do not use C<< delay >>. It will be determined automatically based on which node has the most servers running on it.
=head3 fence_name (required)
This is the name of the fence device. Genreally, this is the short host name of the device.
=head3 fence_type (required)
This is the name of the fence device type. Specifically, the corresponds C<< foo>> to the C<< <devices> <foo> ... </foo> </devices> >> section of the install manifest XML. Typically this is C<< pdu >> or C<< kvm >>.
=head3 fence_uuid (required)
The default value is the fence's UUID. When passed, the specific record is updated.
=cut
sub insert_or_update_fences
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->insert_or_update_fences()" }});
my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : "";
my $file = defined $parameter->{file} ? $parameter->{file} : "";
my $line = defined $parameter->{line} ? $parameter->{line} : "";
my $fence_agent = defined $parameter->{fence_agent} ? $parameter->{fence_agent} : "";
my $fence_arguments = defined $parameter->{fence_arguments} ? $parameter->{fence_arguments} : "";
my $fence_name = defined $parameter->{fence_name} ? $parameter->{fence_name} : "";
my $fence_type = defined $parameter->{fence_type} ? $parameter->{fence_type} : $anvil->System->get_fence_type;
my $fence_uuid = defined $parameter->{fence_uuid} ? $parameter->{fence_uuid} : $anvil->Get->fence_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
uuid => $uuid,
file => $file,
line => $line,
fence_agent => $fence_agent,
fence_arguments => $fence_arguments =~ /passwork=/ ? $anvil->Log->is_secure($fence_arguments) : $fence_arguments,
fence_name => $fence_name,
fence_type => $fence_type,
fence_uuid => $fence_uuid,
}});
### TODO: Left off here
if (not $fence_agent)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_fences()", parameter => "fence_agent" }});
return("");
}
if (not $fence_arguments)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_fences()", parameter => "fence_arguments" }});
return("");
}
if (not $fence_name)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_fences()", parameter => "fence_name" }});
return("");
}
if (not $fence_type)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_fences()", parameter => "fence_type" }});
return("");
}
# Do we have a UUID?
if (not $fence_uuid)
{
my $query = "
SELECT
fence_uuid
FROM
fences
WHERE
fence_name = ".$anvil->Database->quote($fence_name)."
AND
fence_type = ".$anvil->Database->quote($fence_type)."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
my $results = $anvil->Database->query({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results,
count => $count,
}});
if ($count)
{
$fence_uuid = $results->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { fence_uuid => $fence_uuid }});
}
}
# Do we have a UUID?
if ($fence_uuid)
{
# Yup. Has something changed?
my $query = "
SELECT
fence_agent,
fence_name,
fence_type,
fence_arguments
FROM
fences
WHERE
fence_uuid = ".$anvil->Database->quote($fence_uuid)."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
my $results = $anvil->Database->query({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
my $old_fence_agent = $row->[0];
my $old_fence_name = $row->[1];
my $old_fence_type = $row->[2];
my $old_fence_arguments = $row->[3];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
old_fence_agent => $old_fence_agent,
old_fence_name => $old_fence_name =~ /passw/ ? $anvil->Log->is_secure($old_fence_name) : $old_fence_name,
old_fence_type => $old_fence_type,
old_fence_arguments => $old_fence_arguments,
}});
if (($old_fence_agent ne $fence_agent) or
($old_fence_name ne $fence_name) or
($old_fence_type ne $fence_type) or
($old_fence_arguments ne $fence_arguments))
{
# Clear the stop data.
my $query = "
UPDATE
fences
SET
fence_name = ".$anvil->Database->quote($fence_name).",
fence_type = ".$anvil->Database->quote($fence_type).",
fence_arguments = ".$anvil->Database->quote($fence_arguments).",
fence_agent = ".$anvil->Database->quote($fence_agent).",
modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
WHERE
fence_uuid = ".$anvil->Database->quote($fence_uuid)."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query =~ /passw/ ? $anvil->Log->is_secure($query) : $query }});
$anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
}
}
}
else
{
# No, INSERT.
$fence_uuid = $anvil->Get->uuid();
my $query = "
INSERT INTO
fences
(
fence_uuid,
fence_name,
fence_type,
fence_arguments,
fence_agent,
modified_date
) VALUES (
".$anvil->Database->quote($fence_uuid).",
".$anvil->Database->quote($fence_name).",
".$anvil->Database->quote($fence_type).",
".$anvil->Database->quote($fence_arguments).",
".$anvil->Database->quote($fence_agent).",
".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
);
";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query =~ /passw/ ? $anvil->Log->is_secure($query) : $query }});
$anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
}
return($fence_uuid);
}
=head2 insert_or_update_file_locations =head2 insert_or_update_file_locations
This updates (or inserts) a record in the 'file_locations' table. The C<< file_location_uuid >> referencing the database row will be returned. This updates (or inserts) a record in the 'file_locations' table. The C<< file_location_uuid >> referencing the database row will be returned.
@ -3742,6 +3961,27 @@ If set, this is the file name logged as the source of any INSERTs or UPDATEs.
If set, this is the file line number logged as the source of any INSERTs or UPDATEs. If set, this is the file line number logged as the source of any INSERTs or UPDATEs.
=head3 host_ipmi (optional)
This is an optional string that tells machines how to check/control the power of this host. This allows C<< fence_agentlan >> to query and manipulate the power of the host from another host.
There are three times this information is used;
* When one node needs to fence the other. Specifically, the information is parsed and used to configure stonith (fencing) in pacemaker.
* When a Striker dashboard determines that, after a power or thermal event, it is safe to restart the node
* When it is time to connect a DR host to update/synchronize storage.
The exact formatting needs to match the STDIN parameters supported by C<< fence_agentlan >>. Please see C<< man fence_agentlan >> -> C<< STDIN PARAMETERS >> for more information.
For example, this can be set to:
* C<< ip="10.201.11.1" lanplus="1" username="admin" password="super secret password"
B<< NOTES >>:
* If C<< password_script >> is used, it is required that the user has copied the script to all machines on that could use this information to fence/boot a target.
* Do not use C<< fence_agentlan >> or C<< action="..." >>. If either is found in the string, it will be ignored.
* Do not use C<< delay >>. It will be determined automatically based on which node has the most servers running on it.
=head3 host_key (required) =head3 host_key (required)
The is the host's public key used by other machines to validate this machine when connecting to it using ssh. The value comes from C<< /etc/ssh/ssh_host_ecdsa_key.pub >>. An example string would be C<< ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMLEG+mcczSUgmcSuRNZc5OAFPa7IudZQv/cYWzCzmlKPMkIdcNiYDuFM1iFNiV9wVtAvkIXVSkOe2Ah/BGt6fQ= >>. The is the host's public key used by other machines to validate this machine when connecting to it using ssh. The value comes from C<< /etc/ssh/ssh_host_ecdsa_key.pub >>. An example string would be C<< ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMLEG+mcczSUgmcSuRNZc5OAFPa7IudZQv/cYWzCzmlKPMkIdcNiYDuFM1iFNiV9wVtAvkIXVSkOe2Ah/BGt6fQ= >>.
@ -3770,6 +4010,7 @@ sub insert_or_update_hosts
my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : "";
my $file = defined $parameter->{file} ? $parameter->{file} : ""; my $file = defined $parameter->{file} ? $parameter->{file} : "";
my $line = defined $parameter->{line} ? $parameter->{line} : ""; my $line = defined $parameter->{line} ? $parameter->{line} : "";
my $host_ipmi = defined $parameter->{host_ipmi} ? $parameter->{host_ipmi} : "";
my $host_key = defined $parameter->{host_key} ? $parameter->{host_key} : ""; my $host_key = defined $parameter->{host_key} ? $parameter->{host_key} : "";
my $host_name = defined $parameter->{host_name} ? $parameter->{host_name} : $anvil->_host_name; my $host_name = defined $parameter->{host_name} ? $parameter->{host_name} : $anvil->_host_name;
my $host_type = defined $parameter->{host_type} ? $parameter->{host_type} : $anvil->System->get_host_type; my $host_type = defined $parameter->{host_type} ? $parameter->{host_type} : $anvil->System->get_host_type;
@ -3778,6 +4019,7 @@ sub insert_or_update_hosts
uuid => $uuid, uuid => $uuid,
file => $file, file => $file,
line => $line, line => $line,
host_ipmi => $host_ipmi =~ /passw/ ? $anvil->Log->is_secure($host_ipmi) : $host_ipmi,
host_key => $host_key, host_key => $host_key,
host_name => $host_name, host_name => $host_name,
host_type => $host_type, host_type => $host_type,
@ -3806,11 +4048,13 @@ sub insert_or_update_hosts
} }
# Read the old values, if they exist. # Read the old values, if they exist.
my $old_host_ipmi = "";
my $old_host_name = ""; my $old_host_name = "";
my $old_host_type = ""; my $old_host_type = "";
my $old_host_key = ""; my $old_host_key = "";
my $query = " my $query = "
SELECT SELECT
host_ipmi,
host_name, host_name,
host_type, host_type,
host_key host_key
@ -3829,11 +4073,13 @@ WHERE
}}); }});
foreach my $row (@{$results}) foreach my $row (@{$results})
{ {
$old_host_name = $row->[0]; $old_host_ipmi = $row->[0];
$old_host_type = defined $row->[1] ? $row->[1] : ""; $old_host_name = $row->[1];
$old_host_key = defined $row->[2] ? $row->[2] : ""; $old_host_type = $row->[2];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $old_host_key = $row->[3];
old_host_name => $old_host_name, $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
old_host_ipmi => $old_host_ipmi,
old_host_name => $old_host_name =~ /passw/ ? $anvil->Log->is_secure($old_host_name) : $old_host_name,
old_host_type => $old_host_type, old_host_type => $old_host_type,
old_host_key => $old_host_key, old_host_key => $old_host_key,
}}); }});
@ -3849,16 +4095,18 @@ INSERT INTO
host_name, host_name,
host_type, host_type,
host_key, host_key,
host_ipmi,
modified_date modified_date
) VALUES ( ) VALUES (
".$anvil->Database->quote($host_uuid).", ".$anvil->Database->quote($host_uuid).",
".$anvil->Database->quote($host_name).", ".$anvil->Database->quote($host_name).",
".$anvil->Database->quote($host_type).", ".$anvil->Database->quote($host_type).",
".$anvil->Database->quote($host_key).", ".$anvil->Database->quote($host_key).",
".$anvil->Database->quote($host_ipmi).",
".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
); );
"; ";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query =~ /passw/ ? $anvil->Log->is_secure($query) : $query }});
$anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); $anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
} }
elsif (($old_host_name ne $host_name) or elsif (($old_host_name ne $host_name) or
@ -3873,11 +4121,12 @@ SET
host_name = ".$anvil->Database->quote($host_name).", host_name = ".$anvil->Database->quote($host_name).",
host_type = ".$anvil->Database->quote($host_type).", host_type = ".$anvil->Database->quote($host_type).",
host_key = ".$anvil->Database->quote($host_key).", host_key = ".$anvil->Database->quote($host_key).",
host_ipmi = ".$anvil->Database->quote($host_ipmi).",
modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
WHERE WHERE
host_uuid = ".$anvil->Database->quote($host_uuid)." host_uuid = ".$anvil->Database->quote($host_uuid)."
;"; ;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query =~ /passw/ ? $anvil->Log->is_secure($query) : $query }});
$anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); $anvil->Database->write({uuid => $uuid, query => $query, uuid => $uuid, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
} }
@ -4507,7 +4756,7 @@ AND
# INSERT # INSERT
$job_uuid = $anvil->Get->uuid(); $job_uuid = $anvil->Get->uuid();
my $query = " my $query = "
INSERT INTO INSERT INTO
jobs jobs
( (

@ -13,6 +13,7 @@ our $VERSION = "3.0.0";
my $THIS_FILE = "Striker.pm"; my $THIS_FILE = "Striker.pm";
### Methods; ### Methods;
# get_fence_data
# get_local_repo # get_local_repo
# get_peer_data # get_peer_data
# parse_all_status_json # parse_all_status_json
@ -77,6 +78,160 @@ sub parent
# Public methods # # Public methods #
############################################################################################################# #############################################################################################################
=head2 get_fence_data
This parses the unified metadata file from the avaialable fence_devices on this host. If the unified file (location stored in C<< path::data::fences_unified_metadata >>, default is C<< /tmp/fences_unified_metadata.xml >> is not found or fails to parse, C<< 1 >> is returned. If the file is successfully parsed. C<< 0 >> is returned.
The parsed data is stored under C<< fences::<agent_name>::... >>.
=cut
sub get_fence_data
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Striker->get_fence_data()" }});
my $parsed_xml = "";
my $xml_body = $anvil->Storage->read_file({file => $anvil->data->{path}{data}{fences_unified_metadata}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { xml_body => $xml_body }});
if ($xml_body =~ /<\?xml version="1.0" \?>/gs)
{
my $xml = XML::Simple->new();
eval { $parsed_xml = $xml->XMLin($xml_body, KeyAttr => { key => 'name' }, ForceArray => []) };
if ($@)
{
chomp $@;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0111", variables => {
xml_body => $xml_body,
eval_error => $@,
}});
return(1);
}
}
else
{
$anvil->nice_exit({exit_code => 1});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0112"});
return(1);
}
#print Dumper $parsed_xml;
foreach my $agent_ref (@{$parsed_xml->{agent}})
{
my $fence_agent = $agent_ref->{name};
$anvil->data->{fences}{$fence_agent}{description} = $agent_ref->{'resource-agent'}->{longdesc};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fences::${fence_agent}::description" => $anvil->data->{fences}{$fence_agent}{description},
}});
if (exists $agent_ref->{'resource-agent'}->{'symlink'})
{
if (ref($agent_ref->{'resource-agent'}->{'symlink'}) eq "ARRAY")
{
foreach my $hash_ref (@{$agent_ref->{'resource-agent'}->{'symlink'}})
{
my $name = $hash_ref->{name};
$anvil->data->{fences}{$fence_agent}{'symlink'}{$name} = $hash_ref->{shortdesc};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fences::${fence_agent}::symlink::${name}" => $anvil->data->{fences}{$fence_agent}{'symlink'}{$name},
}});
}
}
else
{
my $name = $agent_ref->{'resource-agent'}->{'symlink'}->{name};
$anvil->data->{fences}{$fence_agent}{'symlink'}{$name} = $agent_ref->{'resource-agent'}->{'symlink'}->{shortdesc};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fences::${fence_agent}::symlink::${name}" => $anvil->data->{fences}{$fence_agent}{'symlink'}{$name},
}});
}
}
foreach my $hash_ref (@{$agent_ref->{'resource-agent'}->{parameters}{parameter}})
{
my $name = $hash_ref->{name};
my $unique = exists $hash_ref->{unique} ? $hash_ref->{unique} : 0;
my $required = exists $hash_ref->{required} ? $hash_ref->{required} : 0;
my $deprecated = exists $hash_ref->{deprecated} ? $hash_ref->{deprecated} : 0;
my $obsoletes = exists $hash_ref->{obsoletes} ? $hash_ref->{obsoletes} : 0;
$anvil->data->{fences}{$fence_agent}{parameters}{$name}{unique} = $unique;
$anvil->data->{fences}{$fence_agent}{parameters}{$name}{required} = $required;
$anvil->data->{fences}{$fence_agent}{parameters}{$name}{deprecated} = $deprecated;
$anvil->data->{fences}{$fence_agent}{parameters}{$name}{obsoletes} = $obsoletes;
$anvil->data->{fences}{$fence_agent}{parameters}{$name}{description} = $hash_ref->{shortdesc}->{content};
$anvil->data->{fences}{$fence_agent}{parameters}{$name}{switches} = defined $hash_ref->{getopt}->{mixed} ? $hash_ref->{getopt}->{mixed} : "";
$anvil->data->{fences}{$fence_agent}{parameters}{$name}{content_type} = $hash_ref->{content}->{type};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fences::${fence_agent}::parameters::${name}::unique" => $anvil->data->{fences}{$fence_agent}{parameters}{$name}{unique},
"fences::${fence_agent}::parameters::${name}::required" => $anvil->data->{fences}{$fence_agent}{parameters}{$name}{required},
"fences::${fence_agent}::parameters::${name}::deprecated" => $anvil->data->{fences}{$fence_agent}{parameters}{$name}{deprecated},
"fences::${fence_agent}::parameters::${name}::obsoletes" => $anvil->data->{fences}{$fence_agent}{parameters}{$name}{obsoletes},
"fences::${fence_agent}::parameters::${name}::description" => $anvil->data->{fences}{$fence_agent}{parameters}{$name}{description},
"fences::${fence_agent}::parameters::${name}::switches" => $anvil->data->{fences}{$fence_agent}{parameters}{$name}{switches},
"fences::${fence_agent}::parameters::${name}::content_type" => $anvil->data->{fences}{$fence_agent}{parameters}{$name}{content_type},
}});
if ($anvil->data->{fences}{$fence_agent}{parameters}{$name}{content_type} eq "string")
{
my $string_default = exists $hash_ref->{content}->{'default'} ? $hash_ref->{content}->{'default'} : "";
$anvil->data->{fences}{$fence_agent}{parameters}{$name}{'default'} = $string_default;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fences::${fence_agent}::parameters::${name}::default" => $anvil->data->{fences}{$fence_agent}{parameters}{$name}{'default'},
}});
}
elsif ($anvil->data->{fences}{$fence_agent}{parameters}{$name}{content_type} eq "select")
{
$anvil->data->{fences}{$fence_agent}{parameters}{$name}{options} = [];
foreach my $option_ref (@{$hash_ref->{content}->{option}})
{
push @{$anvil->data->{fences}{$fence_agent}{parameters}{$name}{options}}, $option_ref->{value};
}
foreach my $option (sort {$a cmp $b} @{$anvil->data->{fences}{$fence_agent}{parameters}{$name}{options}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { option => $option }});
}
}
elsif ($anvil->data->{fences}{$fence_agent}{parameters}{$name}{content_type} eq "boolean")
{
# Nothing to collect here.
}
elsif ($anvil->data->{fences}{$fence_agent}{parameters}{$name}{content_type} eq "second")
{
# Nothing to collect here.
my $second_default = exists $hash_ref->{content}->{'default'} ? $hash_ref->{content}->{'default'} : "";
$anvil->data->{fences}{$fence_agent}{parameters}{$name}{'default'} = $second_default;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fences::${fence_agent}::parameters::${name}::default" => $anvil->data->{fences}{$fence_agent}{parameters}{$name}{'default'},
}});
}
elsif ($anvil->data->{fences}{$fence_agent}{parameters}{$name}{content_type} eq "integer")
{
# Nothing to collect here.
my $integer_default = exists $hash_ref->{content}->{'default'} ? $hash_ref->{content}->{'default'} : "";
$anvil->data->{fences}{$fence_agent}{parameters}{$name}{'default'} = $integer_default;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fences::${fence_agent}::parameters::${name}::default" => $anvil->data->{fences}{$fence_agent}{parameters}{$name}{'default'},
}});
}
}
$anvil->data->{fences}{$fence_agent}{actions} = [];
foreach my $hash_ref (@{$agent_ref->{'resource-agent'}->{actions}{action}})
{
push @{$anvil->data->{fences}{$fence_agent}{actions}}, $hash_ref->{name};
}
foreach my $action (sort {$a cmp $b} @{$anvil->data->{fences}{$fence_agent}{actions}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { action => $action }});
}
}
return(0);
}
=head2 get_local_repo =head2 get_local_repo
This builds the body of an RPM repo for the local machine. If, for some reason, this machine can't be used as a repo, an empty string will be returned. This builds the body of an RPM repo for the local machine. If, for some reason, this machine can't be used as a repo, an empty string will be returned.

@ -1524,11 +1524,26 @@ sub process_anvil_menu
return(0); return(0);
} }
sub handle_new_manifest
{
my ($anvil) = @_;
return(0);
}
# This handles creating an Anvil! from an existing manifest # This handles creating an Anvil! from an existing manifest
sub process_create sub process_create
{ {
my ($anvil) = @_; my ($anvil) = @_;
# Are we creating a new manifest?
if ($anvil->data->{cgi}{task}{value})
{
handle_new_manifest($anvil);
}
# Show existing manifests. # Show existing manifests.
my $query = " my $query = "
SELECT SELECT

@ -18,7 +18,7 @@
</tr> </tr>
<tr> <tr>
<td class="main_option_icon"> <td class="main_option_icon">
<a href="?anvil=true&task=create&subtask=manifest"><img src="#!data!skin::url!#/images/manifest.png" class="top_icon" ></a> <a href="?anvil=true&task=create&new=step1"><img src="#!data!skin::url!#/images/manifest.png" class="top_icon" ></a>
</td> </td>
<td class="main_option"> <td class="main_option">
<a href="?anvil=true&task=prep-host">#!string!striker_0204!#</a> <a href="?anvil=true&task=prep-host">#!string!striker_0204!#</a>

@ -73,3 +73,6 @@ Delete by Kavya from the Noun Project (https://thenounproject.com/term/delete/86
instructions by Prettycons from the Noun Project (https://thenounproject.com/term/instructions/2081362/) instructions by Prettycons from the Noun Project (https://thenounproject.com/term/instructions/2081362/)
- manifest.png - manifest.png
Fence by P Thanga Vignesh from the Noun Project (https://thenounproject.com/term/fence/1010653/)
- fence.png

281
notes

@ -1162,3 +1162,284 @@ pcs constraint remove $(pcs constraint show --full | grep ban-test_server | perl
DRBD 9 - Check; DRBD 9 - Check;
/sys/kernel/debug/drbd/resources/${resource_name}/connections/${hostname}/0/proc_drbd /sys/kernel/debug/drbd/resources/${resource_name}/connections/${hostname}/0/proc_drbd
====== New style
<?xml version="1.0" encoding="UTF-8"?>
<!--
Generated on: 2019-06-20, 15:32:27
Striker Version: 2.0.7
-->
<anvil name="xx-anvil-01">
<machines>
<node name="xx-a01n01.digimer.ca" uuid="xxx">
<network name="bcn1" ntp="" ethtool_opts="" mtu="1500" default_gateway="0" >
<!-- subnet can be in "/xx" format -->
<address ip="10.201.10.1" subnet="255.255.0.0" gateway="" default_gateway="0" dns="" />
<interface name="bcn1_link1" mac="xx:xx:xx:xx:xx:xx"/>
<interface name="bcn1_link2" mac="xx:xx:xx:xx:xx:yy"/>
</network>
<fence>
<!-- IPMI data comes from hosts -> host_ipmi. If it is found, it always is used as the first fence device -->
<!-- PDU shows how to reference devices -->
<method name="pdu" type="pdu" order="1">
<!-- The 'name' parameter has to match an entry under devices -> pdu's name -->
<device name="xx-pdu01" port="1" />
<device name="xx-pdu02" port="2" />
</method>
<!-- This would only happen on it's own, but is here for example. The 'server_name' is the name of the VM on the host -->
<method name="kvm" type="kvm" order="1">
<device name="host1" server_name="xx-a01n01" />
</method>
</fence>
<power>
<!-- The 'name' parameter has to match an entry under devices -> ups's name -->
<ups name="xx-ups01" />
<ups name="xx-ups02" />
</power>
</node>
<dr name="xx-a01dr01.digimer.ca" uuid="xxx">
<!-- IPMI is used to power on/off for scheduled, periodic resyncs. -->
</dr>
</machines>
<!-- These devices need to reference entries in the 'fences' database table.
<fences>
<!-- When a machine references these, the 'type="x"' references the child element and the contained 'name="x"' references the child's child element by name -->
<pdu>
<pdu name="xx-pdu01" agent="fence_apc_snmp" address="10.20.2.1" />
<pdu name="xx-pdu02" agent="fence_apc_snmp" address="10.20.2.2" />
</pdu>
<!-- UPSes are used so that we know which UPSes feed a given node, when deciding power event actions -->
<ups>
<ups name="xx-ups01" address="10.20.3.1" />
<ups name="xx-ups02" address="10.20.3.2" />
</ups>
<!-- In cases where VMs are being used. Later we can add support for VMWare -->
<kvm>
<kvm name="host1" address="192.168.122.1" user="root" password="xxx" />
</kvm>
</fences>
</anvil>
====== Old manifest style
<?xml version="1.0" encoding="UTF-8"?>
<!--
Generated on: 2019-06-20, 15:32:27
Striker Version: 2.0.7
-->
<config>
<node name="mk-a01n01.digimer.ca" uuid="71822143-2a4d-43b4-839b-7c66b3c2e4d7">
<network>
<bcn ip="10.20.10.1" />
<sn ip="10.10.10.1" />
<ifn ip="10.255.10.1" />
</network>
<ipmi>
<on reference="ipmi_n01" ip="10.20.11.1" netmask="255.255.0.0" user="admin" password="Initial1" gateway="" lanplus="false" privlvl="USER" />
</ipmi>
<pdu>
<on reference="pdu01" port="1" />
<on reference="pdu02" port="1" />
<on reference="pdu03" port="" />
<on reference="pdu04" port="" />
</pdu>
<kvm>
<!-- port == virsh name of VM -->
<on reference="kvm_host" port="" />
</kvm>
<interfaces>
<interface name="bcn_link1" mac="f8:0f:41:f8:6b:fe" />
<interface name="bcn_link2" mac="00:19:99:ff:ba:b4" />
<interface name="sn_link1" mac="f8:0f:41:f8:6b:ff" />
<interface name="sn_link2" mac="00:19:99:ff:8b:5a" />
<interface name="ifn_link1" mac="00:19:99:ff:ba:b5" />
<interface name="ifn_link2" mac="00:19:99:ff:8b:59" />
</interfaces>
</node>
<node name="mk-a01n02.digimer.ca" uuid="f7a7b2be-a10a-40f0-991d-2265e3ec3cce">
<network>
<bcn ip="10.20.10.2" />
<sn ip="10.10.10.2" />
<ifn ip="10.255.10.2" />
</network>
<ipmi>
<on reference="ipmi_n02" ip="10.20.11.2" netmask="255.255.0.0" user="admin" password="Initial1" gateway="" lanplus="false" privlvl="USER" />
</ipmi>
<pdu>
<on reference="pdu01" port="2" />
<on reference="pdu02" port="2" />
<on reference="pdu03" port="" />
<on reference="pdu04" port="" />
</pdu>
<kvm>
<on reference="kvm_host" port="" />
</kvm>
<interfaces>
<interface name="bcn_link1" mac="00:26:2d:0c:a8:74" />
<interface name="bcn_link2" mac="00:19:99:ff:bb:4e" />
<interface name="sn_link1" mac="00:26:2d:0c:a8:75" />
<interface name="sn_link2" mac="00:19:99:ff:bb:8b" />
<interface name="ifn_link1" mac="00:19:99:ff:bb:4f" />
<interface name="ifn_link2" mac="00:19:99:ff:bb:8a" />
</interfaces>
</node>
<common>
<networks>
<bcn netblock="10.20.0.0" netmask="255.255.0.0" gateway="" defroute="no" ethtool_opts="" />
<sn netblock="10.10.0.0" netmask="255.255.0.0" gateway="" defroute="no" ethtool_opts="" />
<ifn netblock="10.255.0.0" netmask="255.255.0.0" gateway="10.255.255.254" dns1="8.8.8.8" dns2="8.8.4.4" ntp1="" ntp2="" defroute="yes" ethtool_opts="" />
<bonding opts="mode=1 miimon=100 use_carrier=1 updelay=120000 downdelay=0">
<bcn name="bcn_bond1" primary="bcn_link1" secondary="bcn_link2" />
<sn name="sn_bond1" primary="sn_link1" secondary="sn_link2" />
<ifn name="ifn_bond1" primary="ifn_link1" secondary="ifn_link2" />
</bonding>
<bridges>
<bridge name="ifn_bridge1" on="ifn" />
</bridges>
<mtu size="1500" />
</networks>
<repository urls="" />
<media_library size="40" units="GiB" />
<storage_pool_1 size="100" units="%" />
<anvil prefix="mk" sequence="01" domain="digimer.ca" password="Initial1" striker_user="" striker_database="" />
<ssh keysize="8191" />
<cluster name="mk-anvil-01">
<!-- Set the order to 'kvm' if building on KVM-backed VMs. Also set each node's 'port=' above and '<kvm>' element attributes below. -->
<fence order="ipmi,pdu" post_join_delay="90" delay="15" delay_node="mk-a01n01.digimer.ca" />
</cluster>
<drbd>
<disk disk-barrier="no" disk-flushes="no" md-flushes="no" c-plan-ahead="1" c-max-rate="110M" c-min-rate="30M" c-fill-target="1M" />
<options cpu-mask="" />
<net max-buffers="8192" sndbuf-size="" rcvbuf-size="" />
</drbd>
<switch>
<switch name="mk-switch01.digimer.ca" ip="10.20.1.1" />
<switch name="mk-switch02.digimer.ca" ip="10.20.1.2" />
</switch>
<ups>
<ups name="mk-ups01.digimer.ca" type="apc" port="3551" ip="10.20.3.1" />
<ups name="mk-ups02.digimer.ca" type="apc" port="3552" ip="10.20.3.2" />
</ups>
<pdu>
<pdu reference="pdu01" name="mk-pdu01.digimer.ca" ip="10.20.2.1" agent="fence_apc_alteeve" />
<pdu reference="pdu02" name="mk-pdu02.digimer.ca" ip="10.20.2.2" agent="fence_apc_alteeve" />
</pdu>
<ipmi>
<ipmi reference="ipmi_n01" agent="fence_ipmilan" />
<ipmi reference="ipmi_n02" agent="fence_ipmilan" />
</ipmi>
<kvm>
<kvm reference="kvm_host" ip="192.168.122.1" user="root" password="" password_script="" agent="fence_virsh" />
</kvm>
<striker>
<striker name="mk-striker01.digimer.ca" bcn_ip="10.20.4.1" ifn_ip="10.255.4.1" database="" user="" password="" uuid="" />
<striker name="mk-striker02.digimer.ca" bcn_ip="10.20.4.2" ifn_ip="10.255.4.2" database="" user="" password="" uuid="" />
</striker>
<update os="true" />
<iptables>
<vnc ports="100" />
</iptables>
<servers>
<!-- This isn't used anymore, but this section may be useful for other things in the future, -->
<!-- <provision use_spice_graphics="0" /> -->
</servers>
<tools>
<use anvil-safe-start="true" anvil-kick-apc-ups="false" />
</tools>
</common>
</config>
fence_aliyun - Fence agent for Aliyun (Aliyun Web Services)
* action - Fencing action (Default Value: reboot)
* plug - Physical plug number on device, UUID or identification of machine This parameter is always required. Obsoletes: port
* region - Region.
* access_key - Access Key.
* secret_key - Secret Key.
* quiet - Disable logging to stderr. Does not affect --verbose or --debug-file or logging to syslog.
verbose
Verbose mode
debug_file
Write debug information to given file Obsoletes: debug
version
Display version information and exit
help Display help and exit
separator
Separator for CSV created by 'list' operation (Default Value: ,)
delay Wait X seconds before fencing is started (Default Value: 0)
login_timeout
Wait X seconds for cmd prompt after login (Default Value: 5)
power_timeout
Test X seconds for status change after ON/OFF (Default Value: 60)
power_wait
Wait X seconds after issuing ON/OFF (Default Value: 0)
shell_timeout
Wait X seconds for cmd prompt after issuing command (Default Value: 3)
retry_on
Count of attempts to retry power on (Default Value: 1)
fence_amt_ws
fence_apc
fence_apc_snmp
fence_aws
fence_azure_arm
fence_bladecenter
fence_brocade
fence_cisco_mds
fence_cisco_ucs
fence_compute
fence_drac5
fence_eaton_snmp
fence_emerson
fence_eps
fence_evacuate
fence_gce
fence_heuristics_ping
fence_hpblade
fence_ibmblade
fence_idrac
fence_ifmib
fence_ilo
fence_ilo2
fence_ilo3
fence_ilo3_ssh
fence_ilo4
fence_ilo4_ssh
fence_ilo5
fence_ilo5_ssh
fence_ilo_moonshot
fence_ilo_mp
fence_ilo_ssh
fence_imm
fence_intelmodular
fence_ipdu
fence_ipmilan
fence_kdump
fence_mpath
fence_pacemaker
fence_redfish
fence_rhevm
fence_rsa
fence_rsb
fence_sbd
fence_scsi
fence_virsh
fence_virt
fence_virtd
fence_vmware_rest
fence_vmware_soap
fence_wti
fence_xvm

@ -46,9 +46,10 @@ $$;
-- to. -- to.
CREATE TABLE hosts ( CREATE TABLE hosts (
host_uuid uuid not null primary key, -- This is the single most important record in Anvil!. Everything links back to here. host_uuid uuid not null primary key, -- This is the single most important record in Anvil!. Everything links back to here.
host_name text not null, host_name text not null, -- This is the 'hostname' of the machine
host_type text, -- Either 'node' or 'dashboard' or 'dr_host'. It is left empty until the host is configured. host_type text not null, -- Either 'node' or 'dashboard' or 'dr'. It is left empty until the host is configured.
host_key text, -- This is the host's key used to authenticate it when other machines try to ssh to it. host_key text not null, -- This is the host's key used to authenticate it when other machines try to ssh to it.
host_ipmi text not null default '', -- This is an optional string, in 'fence_ipmilan' format, that tells how to access/fence this host.
modified_date timestamp with time zone not null modified_date timestamp with time zone not null
); );
ALTER TABLE hosts OWNER TO admin; ALTER TABLE hosts OWNER TO admin;
@ -59,6 +60,7 @@ CREATE TABLE history.hosts (
host_name text, host_name text,
host_type text, host_type text,
host_key text, host_key text,
host_ipmi text,
modified_date timestamp with time zone not null modified_date timestamp with time zone not null
); );
ALTER TABLE history.hosts OWNER TO admin; ALTER TABLE history.hosts OWNER TO admin;
@ -74,12 +76,14 @@ BEGIN
host_name, host_name,
host_type, host_type,
host_key, host_key,
host_ipmi,
modified_date) modified_date)
VALUES VALUES
(history_hosts.host_uuid, (history_hosts.host_uuid,
history_hosts.host_name, history_hosts.host_name,
history_hosts.host_type, history_hosts.host_type,
history_hosts.host_key, history_hosts.host_key,
history_hosts.host_ipmi,
history_hosts.modified_date); history_hosts.modified_date);
RETURN NULL; RETURN NULL;
END; END;
@ -1509,6 +1513,60 @@ CREATE TRIGGER trigger_manifests
FOR EACH ROW EXECUTE PROCEDURE history_manifests(); FOR EACH ROW EXECUTE PROCEDURE history_manifests();
-- This stores the information about fence devices (PDUs, KVM hosts, etc, minus IPMI which is stored in 'hosts')
CREATE TABLE fences (
fence_uuid uuid not null primary key,
fence_name text not null, -- This is the name of the fence device. Usually this is the host name of the device (ie: xx-pdu01.example.com)
fence_type text not null, -- This is the fence device type. This corresponds to the '<devices> <$fence_type> ... </f$ence_type> </devices>' section of the install manifest.
fence_agent text not null, -- This is the fence agent name used to communicate with the device. ie: 'fence_apc_ups', 'fence_virsh', etc.
fence_arguments text not null, -- This is the arguemnts list used to access / authenticate with the device. What should be in this field depends on the 'STDIN PARAMETERS' section of the fence agent's man page.
modified_date timestamp with time zone not null
);
ALTER TABLE fences OWNER TO admin;
CREATE TABLE history.fences (
history_id bigserial,
fence_uuid uuid,
fence_name text,
fence_type text,
fence_agent text,
fence_arguments text,
modified_date timestamp with time zone
);
ALTER TABLE history.fences OWNER TO admin;
CREATE FUNCTION history_fences() RETURNS trigger
AS $$
DECLARE
history_fences RECORD;
BEGIN
SELECT INTO history_fences * FROM fences WHERE fence_uuid = new.fence_uuid;
INSERT INTO history.fences
(fence_uuid,
fence_name,
fence_type,
fence_agent,
fence_arguments,
modified_date)
VALUES
(history_fences.fence_uuid,
history_fences.fence_name,
history_fences.fence_type,
history_fences.fence_agent,
history_fences.fence_arguments,
history_fences.modified_date);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
ALTER FUNCTION history_fences() OWNER TO admin;
CREATE TRIGGER trigger_fences
AFTER INSERT OR UPDATE ON fences
FOR EACH ROW EXECUTE PROCEDURE history_fences();
-- ------------------------------------------------------------------------------------------------------- -- -- ------------------------------------------------------------------------------------------------------- --
-- These are special tables with no history or tracking UUIDs that simply record transient information. -- -- These are special tables with no history or tracking UUIDs that simply record transient information. --
-- ------------------------------------------------------------------------------------------------------- -- -- ------------------------------------------------------------------------------------------------------- --

@ -810,6 +810,9 @@ Failed to promote the DRBD resource: [#!variable!resource!#] primary. Expected a
<key name="log_0469">The rpm: [#!variable!rpm_path!#] appears to be a problem, removing it.</key> <key name="log_0469">The rpm: [#!variable!rpm_path!#] appears to be a problem, removing it.</key>
<key name="log_0470">The network mapping flag has aged out, clearing it.</key> <key name="log_0470">The network mapping flag has aged out, clearing it.</key>
<key name="log_0471">The network mapping flag is set. If it isn't cleared by the user, it will expire in: [#!variable!timeout!#] second(s).</key> <key name="log_0471">The network mapping flag is set. If it isn't cleared by the user, it will expire in: [#!variable!timeout!#] second(s).</key>
<key name="log_0472">The unified fences metadata file: [#!data!path::data::fences_unified_metadata!#] doesn't exist yet. It will be created now.</key>
<key name="log_0473">The unified fences metadata file: [#!data!path::data::fences_unified_metadata!#] will be refreshed on user request (--refresh passed).</key>
<key name="log_0474">The unified fences metadata file: [#!data!path::data::fences_unified_metadata!#] old and will now be refreshed.</key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. --> <!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key> <key name="t_0000">Test</key>
@ -1250,6 +1253,7 @@ Failure! The return code: [#!variable!return_code!#] was received ('0' was expec
<key name="warning_0025">There was a problem saving the mail server data. Please check the logs for more information.</key> <key name="warning_0025">There was a problem saving the mail server data. Please check the logs for more information.</key>
<key name="warning_0026">The recipient's email address appears to not be valid.</key> <key name="warning_0026">The recipient's email address appears to not be valid.</key>
<key name="warning_0027">There was a problem saving the alert recipient data. Please check the logs for more information.</key> <key name="warning_0027">There was a problem saving the alert recipient data. Please check the logs for more information.</key>
<key name="warning_0028">Failed to read the fence agent: [#!variable!agent!#] metadata. Ignoring it.</key>
<!-- Errors --> <!-- Errors -->
<key name="error_0001">There are not enough network interfaces on this machine. You have: [#!variable!interface_count!#] interface(s), and you need at least: [#!variable!required_interfaces_for_single!#] interfaces to connect to the requested networks (one for Back-Channel and one for each Internet-Facing network).</key> <key name="error_0001">There are not enough network interfaces on this machine. You have: [#!variable!interface_count!#] interface(s), and you need at least: [#!variable!required_interfaces_for_single!#] interfaces to connect to the requested networks (one for Back-Channel and one for each Internet-Facing network).</key>
@ -1377,6 +1381,18 @@ Failed to generate an RSA public key for the user: [#!variable!user!#]. The outp
<key name="error_0108">The 'recipient_new_level': [#!variable!recipient_new_level!#] is invalid. It should be '0', '1', '2', or '3'.</key> <key name="error_0108">The 'recipient_new_level': [#!variable!recipient_new_level!#] is invalid. It should be '0', '1', '2', or '3'.</key>
<key name="error_0109">The 'notification_alert_level': [#!variable!notification_alert_level!#] is invalid. It should be '0', '1', '2', or '3'.</key> <key name="error_0109">The 'notification_alert_level': [#!variable!notification_alert_level!#] is invalid. It should be '0', '1', '2', or '3'.</key>
<key name="error_0110">The 'notification_uuid': [#!variable!notification_uuid!#] was not found in the database.</key> <key name="error_0110">The 'notification_uuid': [#!variable!notification_uuid!#] was not found in the database.</key>
<key name="error_0111">
[ Error ] - The was a problem parsing the unified metadata:
===========================================================
#!variable!xml_body!#
===========================================================
The error was:
===========================================================
#!variable!eval_error!#
===========================================================
</key>
<key name="error_0112"><![CDATA[Failed to read valid unified XML data from: [#!variable!file!#]. It should start with: [<?xml version="1.0" ?>]]]></key>
<key name="error_0113">The unified metadata file: [#!data!path::data::fences_unified_metadata!#] was not found. There may have been a problem creating it.</key>
<!-- These are units, words and so on used when displaying information. --> <!-- These are units, words and so on used when displaying information. -->
<key name="unit_0001">Yes</key> <key name="unit_0001">Yes</key>

@ -0,0 +1,165 @@
#!/usr/bin/perl
#
# This walks through all installed fence agents, parses their man page, and records their description and STDIN parameters.
#
# TODO:
#
use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
my $anvil = Anvil::Tools->new();
$anvil->Log->level({set => 2});
$anvil->Log->secure({set => 0});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});
$anvil->data->{switches}{refresh} = 0;
$anvil->Get->switches;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"switches::refresh" => $anvil->data->{switches}{refresh},
}});
refresh_unified_metadata($anvil);
$anvil->Striker->get_fence_data({debug => 2});
$anvil->nice_exit({exit_code => 0});
#############################################################################################################
# Functions #
#############################################################################################################
# See if the unified fence metadata need to be (re)generated.
sub refresh_unified_metadata
{
my ($anvil) = @_;
my $refresh = 0;
if (not -e $anvil->data->{path}{data}{fences_unified_metadata})
{
$refresh = 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0472"});
}
elsif ($anvil->data->{switches}{refresh})
{
$refresh = 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0473"});
}
else
{
# How old is the file?
my $modified_time = (stat($anvil->data->{path}{data}{fences_unified_metadata}))[9];
my $age = time - $modified_time;
my $maximum_age = ((60 * 60) * 24);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
modified_time => $modified_time,
age => $age,
maximum_age => $maximum_age,
}});
if ($age > $maximum_age)
{
$refresh = 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "log_0474"});
}
}
if ($refresh)
{
# This will store the new unified XML, when regenerating the output.
$anvil->data->{fences}{unified_xml} = "<\?xml version=\"1.0\" \?>\n\n";
$anvil->data->{fences}{unified_xml} .= "<unified>\n";
# Get a list of fence agents on this system.
get_fences_metadata($anvil);
$anvil->data->{fences}{unified_xml} .= "</unified>\n";
$anvil->Storage->write_file({
overwrite => 1,
backup => 0,
file => $anvil->data->{path}{data}{fences_unified_metadata},
body => $anvil->data->{fences}{unified_xml},
user => "apache",
group => "apache",
mode => "0666",
});
}
if (not -e $anvil->data->{path}{data}{fences_unified_metadata})
{
# Failed...
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0113"});
$anvil->nice_exit({exit_code => 1});
}
return(0);
}
# The walks through the found fence_X files and reads their metadata.
sub get_fences_metadata
{
my ($anvil) = @_;
my ($ethtool, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{ls}." ".$anvil->data->{path}{directories}{fence_agents}."/fence_*"});
foreach my $line (split/\n/, $ethtool)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
# fence_pacemaker is out fence handler for DRBD 9 and it isn't a fence agent, skip it.
next if $line eq $anvil->data->{path}{directories}{fence_agents}."/fence_pacemaker";
# fence_virtd is the host daemon component of 'fence_virt', ignore it
next if $line eq $anvil->data->{path}{directories}{fence_agents}."/fence_virtd";
my $fence_agent_path = $line;
my $fence_agent_file = ($fence_agent_path =~ /^.*\/(.*)$/)[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
fence_agent_path => $fence_agent_path,
fence_agent_file => $fence_agent_file,
}});
# Add it's metadata to the unify XML.
unify_metadata($anvil, $fence_agent_path, $fence_agent_file);
}
return(0);
}
# This does the actual call to collect the agent's metadata.
sub unify_metadata
{
my ($anvil, $fence_agent_path, $fence_agent_file) = @_;
my ($metadata, $return_code) = $anvil->System->call({debug => 3, shell_call => $fence_agent_path." -o metadata"});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
metadata => $metadata,
return_code => $return_code,
}});
if ($metadata =~ /<\?xml version="1.0" \?>/gs)
{
$metadata =~ s/<\?xml version="1.0" \?>/<agent name="$fence_agent_file">/gs;
$anvil->data->{fences}{unified_xml} .= $metadata."\n";
$anvil->data->{fences}{unified_xml} .= "</agent>\n";
}
else
{
# Bad, ignore it.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, priority => "alert", key => "warning_0028", variables => { agent => $fence_agent_file }});
return(1);
}
return(0);
}
Loading…
Cancel
Save