Local modifications to ClusterLabs/Anvil by Alteeve
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1811 lines
94 KiB

* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
package Anvil::Tools::Striker;
#
# This module contains methods used to handle common Striker (webUI) tasks.
#
use strict;
use warnings;
use Data::Dumper;
use JSON;
use Scalar::Util qw(weaken isweak);
use Text::Diff;
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
our $VERSION = "3.0.0";
my $THIS_FILE = "Striker.pm";
### Methods;
# check_httpd_conf
# generate_manifest
# get_fence_data
# get_local_repo
# get_peer_data
# get_ups_data
# load_manifest
# parse_all_status_json
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
=pod
=encoding utf8
=head1 NAME
Anvil::Tools::Striker
Provides all methods related to the Striker WebUI.
=head1 SYNOPSIS
use Anvil::Tools;
# Get a common object handle on all Anvil::Tools modules.
my $anvil = Anvil::Tools->new();
# Access to methods using '$anvil->Striker->X'.
#
# Example using 'system_call()';
=head1 METHODS
Methods in this module;
=cut
sub new
{
my $class = shift;
my $self = {};
bless $self, $class;
return ($self);
}
# Get a handle on the Anvil::Tools object. I know that technically that is a sibling module, but it makes more
# sense in this case to think of it as a parent.
sub parent
{
my $self = shift;
my $parent = shift;
$self->{HANDLE}{TOOLS} = $parent if $parent;
# Defend against memory leads. See Scalar::Util'.
if (not isweak($self->{HANDLE}{TOOLS}))
{
weaken($self->{HANDLE}{TOOLS});
}
return ($self->{HANDLE}{TOOLS});
}
#############################################################################################################
# Public methods #
#############################################################################################################
=head2 check_httpd_conf
This checks the apache configuration file to ensure it's setup for the Striker dashboard and RPM repository. This method does nothing if called on non-striker dashboard.
This method takes no parameters.
=cut
sub check_httpd_conf
{
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->check_httpd_conf()" }});
if (not -s $anvil->data->{path}{data}{httpd_conf})
{
# The apache config file doesn't exist or is empty; abort.
return(0);
}
my $is_write = 0;
my $write_shell_call = $anvil->data->{path}{exe}{augtool}." --new <<EOF\n";
my $read_shell_call;
my $shell_output;
my $shell_return_code;
my $augtool_path;
$augtool_path = "/files".$anvil->data->{path}{data}{httpd_conf}."/IfModule[arg='dir_module']/directive[.='DirectoryIndex']/arg[1]";
$read_shell_call = $anvil->data->{path}{exe}{augtool}." <<EOF\nmatch ".$augtool_path."\nquit\nEOF\n";
($shell_output, $shell_return_code) = $anvil->System->call({ shell_call => $read_shell_call });
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
shell_call => $read_shell_call,
shell_output => $shell_output,
shell_return_code => $shell_return_code
} });
if (($shell_return_code == 0) and ($shell_output =~ /^\//))
{
$is_write = 1;
# Set DirectoryIndex to cgi-bin/striker.
$write_shell_call .= "set ".$augtool_path." cgi-bin/striker\n";
}
my $header_name = "Access-Control-Allow-Origin";
$augtool_path = "/files".$anvil->data->{path}{data}{httpd_conf}."/Directory[arg='\"/var/www/cgi-bin\"']/directive[.='Header']";
$read_shell_call = $anvil->data->{path}{exe}{augtool}." <<EOF\nmatch ".$augtool_path."/arg[.='".$header_name."']\nquit\nEOF\n";
($shell_output, $shell_return_code) = $anvil->System->call({ shell_call => $read_shell_call });
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
shell_call => $read_shell_call,
shell_output => $shell_output,
shell_return_code => $shell_return_code
} });
if (($shell_return_code == 0) and (not $shell_output =~ /^\//))
{
$is_write = 1;
my $last_header_arg = $augtool_path."[last()]/arg";
# Add header Access-Control-Allow-Origin "all" to cgi-bin scripts.
$write_shell_call .= "set ".$augtool_path."[last()+1] Header\n";
$write_shell_call .= "set ".$last_header_arg."[1] set\n";
$write_shell_call .= "set ".$last_header_arg."[2] ".$header_name."\n";
$write_shell_call .= "set ".$last_header_arg."[3] \\\"*\\\"\n";
}
$augtool_path = "/files".$anvil->data->{path}{data}{httpd_conf}."/Directory[arg='\"/var/www/html\"']/IfModule";
$read_shell_call = $anvil->data->{path}{exe}{augtool}." <<EOF\nmatch ".$augtool_path."[arg='rewrite_module']\nquit\nEOF\n";
($shell_output, $shell_return_code) = $anvil->System->call({ shell_call => $read_shell_call });
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
shell_call => $read_shell_call,
shell_output => $shell_output,
shell_return_code => $shell_return_code
} });
if (($shell_return_code == 0) and (not $shell_output =~ /^\//))
{
$is_write = 1;
my $last_ifmodule_directive = $augtool_path."[last()]/directive";
my $new_directive = $last_ifmodule_directive."[last()+1]";
my $rewriteengine_directive = "RewriteEngine";
my $rewritecond_directive = "RewriteCond";
my $rewriterule_directive = "RewriteRule";
# Insert rewrite rules to redirect /page to /page.html.
# Create IfModule block to avoid setting the rewrite rules when the rewrite module isn't available.
$write_shell_call .= "set ".$augtool_path."[last()+1]/arg[1] rewrite_module\n";
# Turn on the rewrite engine to enable rewriting.
$write_shell_call .= "set ".$new_directive." ".$rewriteengine_directive."\n";
$write_shell_call .= "set ".$last_ifmodule_directive."[.='".$rewriteengine_directive."']/arg[1] on\n";
my $last_rewriterule_arg = $last_ifmodule_directive."[.='".$rewriterule_directive."'][last()]/arg";
# Set rule #1: remove all trailing slashes from the URI.
$write_shell_call .= "set ".$new_directive." ".$rewriterule_directive."\n";
$write_shell_call .= "set ".$last_rewriterule_arg."[1] ^(.*)/+\$\n";
$write_shell_call .= "set ".$last_rewriterule_arg."[2] \\\$1\n";
my $last_rewritecond_arg = $last_ifmodule_directive."[.='".$rewritecond_directive."'][last()]/arg";
# Set rule #2: redirect to [basename].html if it exists.
$write_shell_call .= "set ".$new_directive." ".$rewritecond_directive."\n";
$write_shell_call .= "set ".$last_rewritecond_arg."[1] %{REQUEST_FILENAME}.html\n";
$write_shell_call .= "set ".$last_rewritecond_arg."[2] -f\n";
$write_shell_call .= "set ".$new_directive." ".$rewriterule_directive."\n";
$write_shell_call .= "set ".$last_rewriterule_arg."[1] ![^/]+[.]html\$\n";
$write_shell_call .= "set ".$last_rewriterule_arg."[2] %{REQUEST_FILENAME}.html\n";
$write_shell_call .= "set ".$last_rewriterule_arg."[3] [L]\n";
}
# Attempt to setup forwarding from the Apache server to the Striker UI API.
$augtool_path = "/files".$anvil->data->{path}{data}{httpd_conf}."/Location[arg='\"/api\"']";
$read_shell_call = $anvil->data->{path}{exe}{augtool}." <<EOF\nmatch ".$augtool_path."\nquit\nEOF\n";
($shell_output, $shell_return_code) = $anvil->System->call({ shell_call => $read_shell_call });
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
shell_call => $read_shell_call,
shell_output => $shell_output,
shell_return_code => $shell_return_code
} });
if (($shell_return_code == 0) and (not $shell_output =~ /^\//))
{
$is_write = 1;
my $new_ifmodule_directive = $augtool_path."/IfModule[arg='proxy_module']";
my $striker_ui_api_url = "http://localhost:8080/api";
$write_shell_call .= "set ".$augtool_path =~ s/(Location)[^\s]+$/$1/r."[last()+1]/arg[1] '\"/api\"'\n";
$write_shell_call .= "set ".$augtool_path."/IfModule[last()+1]/arg[1] 'proxy_module'\n";
$write_shell_call .= "set ".$new_ifmodule_directive."/directive[last()+1] 'ProxyPass'\n";
$write_shell_call .= "set ".$new_ifmodule_directive."/directive[.='ProxyPass']/arg[1] '".$striker_ui_api_url."'\n";
}
if ($is_write)
{
$write_shell_call .= "save\nquit\nEOF\n";
($shell_output, $shell_return_code) = $anvil->System->call({ shell_call => $write_shell_call });
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
shell_call => $write_shell_call,
shell_output => $shell_output,
shell_return_code => $shell_return_code
} });
my $new_httpd_conf = $anvil->data->{path}{data}{httpd_conf}.".augnew";
if (not -s $new_httpd_conf)
{
# No new conf file created; abort.
return(0);
}
my $diff_output = diff $anvil->data->{path}{data}{httpd_conf}, $new_httpd_conf, { STYLE => 'Unified' };
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => { diff_output => $diff_output } });
# Test the new config file before overwriting.
$read_shell_call = $anvil->data->{path}{exe}{httpd}." -t -f ".$new_httpd_conf;
($shell_output, $shell_return_code) = $anvil->System->call({ shell_call => $read_shell_call });
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
shell_call => $read_shell_call,
shell_output => $shell_output,
shell_return_code => $shell_return_code
} });
if ($shell_return_code == 0)
{
# No issues found during the test; proceed to overwriting.
$anvil->Storage->backup({ debug => $debug, file => $anvil->data->{path}{data}{httpd_conf} });
$write_shell_call = $anvil->data->{path}{exe}{mv}." -f ".$new_httpd_conf." ".$anvil->data->{path}{data}{httpd_conf};
($shell_output, $shell_return_code) = $anvil->System->call({ shell_call => $write_shell_call });
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
shell_call => $write_shell_call,
shell_output => $shell_output,
shell_return_code => $shell_return_code
} });
$anvil->Storage->change_mode({ debug => $debug, path => $anvil->data->{path}{data}{httpd_conf}, mode => "0644" });
$anvil->Storage->change_owner({ debug => $debug, path => $anvil->data->{path}{data}{httpd_conf}, user => "root", group => "root" });
$anvil->System->restart_daemon({ debug => $debug, daemon => $anvil->data->{sys}{daemon}{httpd} });
}
else
{
# Found issues during the test; clean up.
$write_shell_call = $anvil->data->{path}{exe}{rm}." -f ".$new_httpd_conf;
($shell_output, $shell_return_code) = $anvil->System->call({ shell_call => $write_shell_call });
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
shell_call => $write_shell_call,
shell_output => $shell_output,
shell_return_code => $shell_return_code
} });
}
}
return(0);
}
=head2 generate_manifest
This reads the CGI data coming from the manifest form to generate the manifest XML. On success, the C<< manifest_uuid >>, and C<< manifest_uuid >> are returned. If there's a problem, C<< !!error!! >> is returned.
Parameters;
=head3 dns (optional)
This is a comma-separated list of DNS servers to use.
=head3 domain (required)
This is the domain name to use for this Anvil! node.
=head3 manifest_uuid (optional)
This allows updating an existing mannifest, or specifying the manifest UUID to use for the new manifest.
=head3 mtu (optional)
This allows specifying a custome MTU (maximum transmission unit) size. Only use this if you know all network devices support this MTU size!
=head3 ntp (optional)
This allows specifying a custom NTP (network time protocol) server to use to sync the subnode's time against.
=head3 prefix (required)
This is the node's descriptive prefix (usually 1~5 characters).
=head3 sequence (required)
This is an integer, 1 or higher, indication the node's sequence number.
=cut
sub generate_manifest
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = $parameter->{debug} // 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Striker->generate_manifest()" }});
my $network_dns = $parameter->{dns} // $anvil->data->{cgi}{dns}{value};
my $domain = $parameter->{domain} // $anvil->data->{cgi}{domain}{value};
my $manifest_uuid = $parameter->{manifest_uuid} // $anvil->data->{cgi}{manifest_uuid}{value};
my $network_mtu = $parameter->{mtu} // $anvil->data->{cgi}{mtu}{value};
my $network_ntp = $parameter->{ntp} // $anvil->data->{cgi}{ntp}{value};
my $name_prefix = $parameter->{prefix} // $anvil->data->{cgi}{prefix}{value};
my $padded_sequence = $parameter->{sequence} // $anvil->data->{cgi}{sequence}{value};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
network_dns => $network_dns,
domain => $domain,
manifest_uuid => $manifest_uuid,
network_mtu => $network_mtu,
network_ntp => $network_ntp,
name_prefix => $name_prefix,
padded_sequence => $padded_sequence,
}});
if (not $domain)
{
# No target...
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Striker->generate_manifest()", parameter => "domain" }});
return('!!error!!', '!!error!!');
}
if (not $name_prefix)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Striker->generate_manifest()", parameter => "name_prefix" }});
return('!!error!!', '!!error!!');
}
if (not $padded_sequence)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Striker->generate_manifest()", parameter => "padded_sequence" }});
return('!!error!!', '!!error!!');
}
$anvil->Database->get_upses({debug => $debug});
$anvil->Database->get_fences({debug => $debug});
if ($manifest_uuid eq "new")
{
$manifest_uuid = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { manifest_uuid => $manifest_uuid }});
}
if (length($padded_sequence) == 1)
{
$padded_sequence = sprintf("%02d", $padded_sequence);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { padded_sequence => $padded_sequence }});
}
my $anvil_name = $name_prefix."-anvil-".$padded_sequence;
my $node1_name = $name_prefix."-a".$padded_sequence."n01";
my $node2_name = $name_prefix."-a".$padded_sequence."n02";
my $machines = {};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
anvil_name => $anvil_name,
node1_name => $node1_name,
node2_name => $node2_name,
}});
my $manifest_xml = '<?xml version="1.0" encoding="UTF-8"?>
<install_manifest name="'.$anvil_name.'" domain="'.$domain.'">
<networks mtu="'.$network_mtu.'" dns="'.$network_dns.'" ntp="'.$network_ntp.'">
';
foreach my $network ("bcn", "ifn", "sn", "mn")
{
my $count_key = $network."_count";
my $count_value = $parameter->{$count_key} // $anvil->data->{cgi}{$count_key}{value};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "${count_key}" => $count_value }});
foreach my $i (1..$count_value)
{
my $network_name = $network.$i;
my $network_key = $network_name."_network";
my $network_value = $parameter->{$network_key} // $anvil->data->{cgi}{$network_key}{value};
my $subnet_key = $network_name."_subnet";
my $subnet_value = $parameter->{$subnet_key} // $anvil->data->{cgi}{$subnet_key}{value};
my $gateway_key = $network_name."_gateway";
my $gateway_value = $parameter->{$gateway_key} // $anvil->data->{cgi}{$gateway_key}{value};
$manifest_xml .= ' <network name="'.$network_name.'" network="'.$network_value.'" subnet="'.$subnet_value.'" gateway="'.$gateway_value.'" />'."\n";
# While we're here, gather the network data for the machines.
foreach my $machine ("node1", "node2")
{
# Record the network
my $ip_key = $machine."_".$network_name."_ip";
my $ip_value = ($parameter->{$ip_key} // $anvil->data->{cgi}{$ip_key}{value}) // "";
$machines->{$machine}{network}{$network_name} = $ip_value;
# On the first loop (bcn1), pull in the other information as well.
if (($network eq "bcn") && ($i eq "1"))
{
# Get the IP.
my $ipmi_ip_key = $machine."_ipmi_ip";
my $ipmi_ip_value = ($parameter->{$ipmi_ip_key} // $anvil->data->{cgi}{$ipmi_ip_key}{value}) // "";
$machines->{$machine}{ipmi_ip} = $ipmi_ip_value;
# Find the UPSes.
foreach my $ups_name (sort {$a cmp $b} keys %{$anvil->data->{upses}{ups_name}})
{
my $ups_key = $machine."_ups_".$ups_name;
my $ups_value = ($parameter->{$ups_key} // $anvil->data->{cgi}{$ups_key}{value}) // "";
$machines->{$machine}{ups}{$ups_name} = $ups_value ? "1" : "0";
}
# Find the Fence devices.
foreach my $fence_name (sort {$a cmp $b} keys %{$anvil->data->{fences}{fence_name}})
{
my $fence_key = $machine."_fence_".$fence_name;
my $fence_value = ($parameter->{$fence_key} // $anvil->data->{cgi}{$fence_key}{value}) // "";
$machines->{$machine}{fence}{$fence_name} = $fence_value;
}
}
}
}
}
$manifest_xml .= ' </networks>
<upses>
';
# We don't store information about the UPS as it may change over time. We just need the reference.
foreach my $ups_name (sort {$a cmp $b} keys %{$anvil->data->{upses}{ups_name}})
{
$manifest_xml .= ' <ups name="'.$ups_name.'" uuid="'.$anvil->data->{upses}{ups_name}{$ups_name}{ups_uuid}.'" />
';
}
$manifest_xml .= ' </upses>
<fences>
';
# We don't store information about the UPS as it may change over time. We just need the reference.
foreach my $fence_name (sort {$a cmp $b} keys %{$anvil->data->{fences}{fence_name}})
{
$manifest_xml .= ' <fence name="'.$fence_name.'" uuid="'.$anvil->data->{fences}{fence_name}{$fence_name}{fence_uuid}.'" />
';
}
$manifest_xml .= ' </fences>
<machines>
';
# Now record the info about the machines.
foreach my $machine (sort {$a cmp $b} keys %{$machines})
{
my $host_name = "";
if ($machine eq "node1") { $host_name = $node1_name; }
if ($machine eq "node2") { $host_name = $node2_name; }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_name => $host_name }});
$manifest_xml .= ' <'.$machine.' name="'.$host_name.'" ipmi_ip="'.$machines->{$machine}{ipmi_ip}.'">
<networks>
';
foreach my $network_name (sort {$a cmp $b} keys %{$machines->{$machine}{network}})
{
$manifest_xml .= ' <network name="'.$network_name.'" ip="'.$machines->{$machine}{network}{$network_name}.'" />
';
}
$manifest_xml .= ' </networks>
<upses>
';
foreach my $ups_name (sort {$a cmp $b} keys %{$machines->{$machine}{ups}})
{
$manifest_xml .= ' <ups name="'.$ups_name.'" used="'.$machines->{$machine}{ups}{$ups_name}.'" />
';
}
$manifest_xml .= ' </upses>
<fences>
';
foreach my $fence_name (sort {$a cmp $b} keys %{$machines->{$machine}{fence}})
{
$manifest_xml .= ' <fence name="'.$fence_name.'" port="'.$machines->{$machine}{fence}{$fence_name}.'" />
';
}
$manifest_xml .= ' </fences>
</'.$machine.'>
';
}
$manifest_xml .= ' </machines>
</install_manifest>
';
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { manifest_xml => $manifest_xml }});
# Now save the manifest!
($manifest_uuid) = $anvil->Database->insert_or_update_manifests({
debug => $debug,
manifest_uuid => $manifest_uuid,
manifest_name => $anvil_name,
manifest_xml => $manifest_xml,
});
return($manifest_uuid, $anvil_name);
}
* Created Database->get_bridges() that, surprise, loads data from the 'bridges' table. * Started work on Get->available_resources() that will take an 'anvil_uuid' and figure out what resources are still available for use by new servers or that can be added to existing servers. * Fixed a bug in ScanCore->agent_startup() where tables weren't being generated properly from the agent's SQL file. * Made Storage->change_mode() return silently if it's called without a mode being passed. This happens frequently and is harmless so it's not worth filling the logs with errors. * Renamed the 'start_time' key to 'at_start' when recording files' MD5 sums in Storage->record_md5sums and ->check_md5sums. * When we moved the directory scan logic out of the 'scancore' daemon and into 'Storage->scan_directory', the logic to record scan agent names in 'scancore::agent::<file>' was removed. This broke a few things and, so, it was restored when it was found that a file starts with 'scan-' and the directory matches the scancore agent directory. * Moved the 'scancore' daemon's 'load_agent_strings' to 'Words' * Updated Words->parse_banged_string() to look for variables in the format 'value=X:units=Y' and translate it properly. * Fixed a bug in scan-ipmitool where discovered sensor INSERT SQL queries were queued, but not committed. * Fixed a bug in scan-storcli where a while loop was broken, preventing execution. * Fixed a bug in the 'scancore' daemon where it wouldn't exit if sums changed. Fixed a bug where alerts weren't being sent between loops. Fixed a bug where command-line log level wasn't surviving inside the main loop. Signed-off-by: Digimer <digimer@alteeve.ca>
4 years ago
=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<< fence_data::<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({
debug => $debug,
file => $anvil->data->{path}{data}{fences_unified_metadata},
});
# Globally replace \fI (opening underline) with '[' and \fP (closing underline) with ']'.
$xml_body =~ s/\\fI/[/gs;
$xml_body =~ s/\\fP/]/gs;
$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 => $@,
}});
# Clear the error so it doesn't propogate out to a future 'die' and confuse things.
$@ = '';
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);
}
foreach my $agent_ref (@{$parsed_xml->{agent}})
{
my $fence_agent = $agent_ref->{name};
$anvil->data->{fence_data}{$fence_agent}{description} = $agent_ref->{'resource-agent'}->{longdesc};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fence_data::${fence_agent}::description" => $anvil->data->{fence_data}{$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->{fence_data}{$fence_agent}{'symlink'}{$name} = $hash_ref->{shortdesc};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fence_data::${fence_agent}::symlink::${name}" => $anvil->data->{fence_data}{$fence_agent}{'symlink'}{$name},
}});
}
}
else
{
my $name = $agent_ref->{'resource-agent'}->{'symlink'}->{name};
$anvil->data->{fence_data}{$fence_agent}{'symlink'}{$name} = $agent_ref->{'resource-agent'}->{'symlink'}->{shortdesc};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fence_data::${fence_agent}::symlink::${name}" => $anvil->data->{fence_data}{$fence_agent}{'symlink'}{$name},
}});
}
}
foreach my $hash_ref (@{$agent_ref->{'resource-agent'}->{parameters}{parameter}})
{
# We ignore some parameters that are not useful parameters in our case.
my $name = $hash_ref->{name};
next if $name =~ /debug/;
next if $name eq "delay";
next if $name eq "help";
next if $name eq "version";
next if $name eq "separator";
next if $name eq "plug";
next if $name =~ /snmp(.*?)_path/;
next if $name eq "verbose";
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->{fence_data}{$fence_agent}{parameters}{$name}{unique} = $unique;
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{required} = $required;
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{deprecated} = $deprecated;
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{obsoletes} = $obsoletes;
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{description} = $hash_ref->{shortdesc}->{content};
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{description} =~ s/\n/ /g;
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{switches} = defined $hash_ref->{getopt}->{mixed} ? $hash_ref->{getopt}->{mixed} : "";
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{content_type} = $hash_ref->{content}->{type};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fence_data::${fence_agent}::parameters::${name}::unique" => $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{unique},
"fence_data::${fence_agent}::parameters::${name}::required" => $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{required},
"fence_data::${fence_agent}::parameters::${name}::deprecated" => $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{deprecated},
"fence_data::${fence_agent}::parameters::${name}::obsoletes" => $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{obsoletes},
"fence_data::${fence_agent}::parameters::${name}::description" => $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{description},
"fence_data::${fence_agent}::parameters::${name}::switches" => $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{switches},
"fence_data::${fence_agent}::parameters::${name}::content_type" => $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{content_type},
}});
# Make it easier to tranlate a switch to a parameter name.
if ($anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{switches})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
fence_agent => $fence_agent,
name => $name,
}});
foreach my $switch (split/,/, $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{switches})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { '>> switch' => $switch }});
$switch =~ s/=.*$//;
$switch =~ s/\s//g;
$switch =~ s/^-{1,2}//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { '<< switch' => $switch }});
$anvil->data->{fence_data}{$fence_agent}{switch}{$switch}{name} = $name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fence_data::${fence_agent}::switch::${switch}::name" => $anvil->data->{fence_data}{$fence_agent}{switch}{$switch}{name},
}});
}
}
# 'action' is a string, but it has a set list of allowed values, so we manually switch it to a 'select' for the web interface
if ($name eq "action")
{
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{'default'} = exists $hash_ref->{content}->{'default'} ? $hash_ref->{content}->{'default'} : "";
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{content_type} = "select";
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{options} = [];
# Read the action
#print "Agent: [".$fence_agent."]; actions (default: [".$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{'default'}."]);\n";
foreach my $array_ref (sort {$a cmp $b} @{$agent_ref->{'resource-agent'}->{actions}->{action}})
{
# There are several options that don't make sense for us.
next if $array_ref->{name} eq "list";
next if $array_ref->{name} eq "monitor";
next if $array_ref->{name} eq "manpage";
next if $array_ref->{name} eq "status";
next if $array_ref->{name} eq "validate-all";
next if $array_ref->{name} eq "list-status";
next if $array_ref->{name} eq "metadata";
next if $array_ref->{name} eq "on";
push @{$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{options}}, $array_ref->{name};
}
foreach my $option (sort {$a cmp $b} @{$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{options}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { option => $option }});
}
}
elsif ($anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{content_type} eq "string")
{
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{'default'} = exists $hash_ref->{content}->{'default'} ? $hash_ref->{content}->{'default'} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fence_data::${fence_agent}::parameters::${name}::default" => $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{'default'},
}});
}
elsif ($anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{content_type} eq "select")
{
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{options} = [];
foreach my $option_ref (@{$hash_ref->{content}->{option}})
{
push @{$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{options}}, $option_ref->{value};
}
foreach my $option (sort {$a cmp $b} @{$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{options}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { option => $option }});
}
}
elsif ($anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{content_type} eq "boolean")
{
# Nothing to collect here.
}
elsif ($anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{content_type} eq "second")
{
# Nothing to collect here.
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{'default'} = exists $hash_ref->{content}->{'default'} ? $hash_ref->{content}->{'default'} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fence_data::${fence_agent}::parameters::${name}::default" => $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{'default'},
}});
}
elsif ($anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{content_type} eq "integer")
{
# Nothing to collect here.
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{'default'} = exists $hash_ref->{content}->{'default'} ? $hash_ref->{content}->{'default'} : "";;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fence_data::${fence_agent}::parameters::${name}::default" => $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{'default'},
}});
}
# If this obsoletes another parameter, mark it as such.
$anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{replacement} = "" if not exists $anvil->data->{fence_data}{$fence_agent}{parameters}{$name}{replacement};
if ($obsoletes)
{
$anvil->data->{fence_data}{$fence_agent}{parameters}{$obsoletes}{replacement} = $name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"fence_data::${fence_agent}::parameters::${obsoletes}::replacement" => $anvil->data->{fence_data}{$fence_agent}{parameters}{$obsoletes}{replacement},
}});
}
}
$anvil->data->{fence_data}{$fence_agent}{actions} = [];
foreach my $hash_ref (@{$agent_ref->{'resource-agent'}->{actions}{action}})
{
push @{$anvil->data->{fence_data}{$fence_agent}{actions}}, $hash_ref->{name};
}
foreach my $action (sort {$a cmp $b} @{$anvil->data->{fence_data}{$fence_agent}{actions}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { action => $action }});
}
}
* THe get_cpu endpoint was completed. * The get_mmeory endpoint was completed. * The get_replicated_storage endpoint was completed, though it requires testing and likely has issues. To prepare for the get_status endpoint work, I needed to update ScanCore and modules to track the host_status. This commit contains the work needed for this. * Updated ScanCore->post_scan_analysis_striker() to use configured fence devices (except PDUs) to check if a target host is off or on, in there is no host_ipmi interface. In all cases, if a machine can be confirmed on or off, the host_status is now updated. * To support the above fence based power checks, updated scan-cluster to store the on-disk CIB in the new scan_cluster -> scan_cluster_cib colume. * Updated ScanCore->parse_cib() to map stonith primitive IDs to fence agents. Updated ->parse_crm_mon() to not call if the executable doesn't exist to avoid unhelpful error messages in the logs when called from a Striker. * Update DRBD->gather_data() to get the size data from /sys/block/drbd<minor>/size' x '/sys/block/drbd<minor>/queue/logical_block_size so it works when a device is Secondary (and can't be promoted). * Updated Database->get_hosts_info() to record the short host name as well as the stored host name. Created ->update_host_status() as a wrapper to ->insert_or_update_hosts() that only updates the host status. * Updated anvil-join-anvil to disabled ksm and ksmtuned daemons. * Updated scancore and anvil-daemon to set the host_status to 'online' on startup. Signed-off-by: Digimer <digimer@alteeve.ca>
4 years ago
# ScanCore will load this to check nodes that are not accessible. To reduce load, as this is an
# expensive call, this time is set so a caller can decide if the data should be updated.
$anvil->data->{sys}{fence_data_updated} = time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"sys::fence_data_updated" => $anvil->data->{sys}{fence_data_updated},
}});
return(0);
}
=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.
The method takes no paramters.
=cut
sub get_local_repo
{
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_local_repo()" }});
# What is the repo directory?
my $document_root = "";
my $httpd_conf = $anvil->Storage->read_file({file => $anvil->data->{path}{data}{httpd_conf} });
foreach my $line (split/\n/, $httpd_conf)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
if ($line =~ /^DocumentRoot\s+"(\/.*?)"/)
{
$document_root = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { document_root => $document_root }});
last;
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { document_root => $document_root }});
if (not $document_root)
{
# Problem with apache.
return("");
}
$anvil->Storage->scan_directory({
debug => $debug,
directory => $document_root,
recursive => 1,
no_files => 1,
search_for => "repodata",
});
my $directory = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "scan::searched" => $anvil->data->{scan}{searched} }});
if ($anvil->data->{scan}{searched})
{
$directory = $anvil->data->{scan}{searched};
$directory =~ s/^$document_root//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { directory => $directory }});
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { directory => $directory }});
if (not $directory)
{
# No repo found.
return("");
}
# What are my IPs?
$anvil->Network->get_ips();
my $base_url = "";
my $host = $anvil->Get->short_host_name();
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$host}{interface}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { interface => $interface }});
if ($anvil->data->{network}{$host}{interface}{$interface}{ip})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "network::local::interface::${interface}::ip" => $anvil->data->{network}{$host}{interface}{$interface}{ip} }});
if (not $base_url)
{
$base_url = "baseurl=http://".$anvil->data->{network}{$host}{interface}{$interface}{ip}.$directory;
}
else
{
$base_url .= "\n http://".$anvil->data->{network}{$host}{interface}{$interface}{ip}.$directory;
}
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { base_url => $base_url }});
### NOTE: The 'module_hotfixes=1' is needed until we can figure out how to add libssh2 to
### 'modules.yaml' (from the RHEL 8.x repodata). See:
### - https://docs.fedoraproject.org/en-US/modularity/making-modules/defining-modules/
### - https://docs.fedoraproject.org/en-US/modularity/hosting-modules/
# Create the local repo file body
my $repo = "[".$anvil->Get->short_host_name."-repo]
name=Repo on ".$anvil->Get->host_name."
".$base_url."
enabled=1
gpgcheck=0
timeout=5
skip_if_unavailable=1
module_hotfixes=1";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { repo => $repo }});
return($repo);
}
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
=head2 get_peer_data
This calls the C<< call_striker-get-peer-data >> program to try to connect to the target (as C<< root >>). This method returns a string variable and a hash reference. The string variable will be C<< 1 >> if we connected successfully, C<< 0 >> if not. The hash reference will contain parsed details about the peer, assuming it connected. If the connection failed, the hash reference will exist but the values will be empty.
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
Keys in the hash;
* C<< host_uuid >> - The host's UUID.
* C<< host_name >> - The host's current (static) host name.
* C<< host_os >> - This is the host's operating system and version. The OS is returned as C<< rhel >> or C<< centos >>. The version is returned as C<< 8.x >>.
* C<< internet >> - This indicates if the target was found to have a a working Internet connection.
* C<< os_registered >> - This indicates if the OS is registered with Red Hat (if the OS is C<< rhel >>). It will be C<< yes >>, C<< no >> or C<< unknown >>.
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
my ($connected, $data) = $anvil->Striker->get_peer_data({target => 10.255.1.218, password => "Initial1"});
if ($connected)
{
print "Hostname: [".$data->{host_name}."], host UUID: [".$data->{host_uuid}."]\n";
}
Parameters;
=head3 password (required)
This is the target machine's C<< root >> password.
=head3 port (optional, default 22)
This is the TCP port to use when connecting to the target
=head3 target (required, IPv4 address)
This is the current IP address of the target machine.
=cut
sub get_peer_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_peer_data()" }});
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : 22;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
target => $target,
password => $anvil->Log->is_secure($password),
port => $port,
}});
# Store the password.
my $connected = 0;
my $data = {
host_uuid => "",
host_name => "",
host_os => "",
internet => 0,
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
os_registered => "",
};
if (not $target)
{
# No target...
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Striker->get_peer_data()", parameter => "target" }});
return($connected, $data);
}
# Record the password in the database so that we don't pass it over the command line.
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
my $state_uuid = $anvil->Database->insert_or_update_states({
debug => $debug,
file => $THIS_FILE,
line => __LINE__,
state_name => "peer::".$target."::password",
state_note => $password,
uuid => $anvil->data->{sys}{host_uuid}, # Only write to our DB, no reason to store elsewhere
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { state_uuid => $state_uuid }});
my ($output, $return_code) = $anvil->System->call({
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
debug => $debug,
shell_call => $anvil->data->{path}{exe}{'call_striker-get-peer-data'}." --target root\@".$target.":".$port." --state-uuid ".$state_uuid,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
output => $output,
return_code => $return_code,
}});
# Pull out the details
foreach my $line (split/\n/, $output)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
if ($line =~ /connected=(.*)$/)
{
# We collect this, but apparently not for any real reason...
$connected = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { connected => $connected }});
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
}
if ($line =~ /host_name=(.*)$/)
{
# We collect this, but apparently not for any real reason...
$data->{host_name} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'data->{host_name}' => $data->{host_name} }});
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
}
if ($line =~ /host_uuid=(.*)$/)
{
$data->{host_uuid} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'data->{host_uuid}' => $data->{host_uuid} }});
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
}
if ($line =~ /host_os=(.*)$/)
{
$data->{host_os} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'data->{host_os}' => $data->{host_os} }});
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
}
if ($line =~ /os_registered=(.*)$/)
{
$data->{os_registered} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'data->{os_registered}' => $data->{os_registered} }});
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
}
if ($line =~ /internet=(.*)$/)
{
$data->{internet} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'data->{internet}' => $data->{internet} }});
}
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
connected => $connected,
'data->{host_name}' => $data->{host_name},
'data->{host_uuid}' => $data->{host_uuid},
'data->{host_os}' => $data->{host_os},
'data->{internet}' => $data->{internet},
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
'data->{os_registered}' => $data->{os_registered},
}});
# Make sure the database entry is gone (striker-get-peer-data should have removed it, but lets be safe).
my $query = "DELETE FROM states WHERE state_name = ".$anvil->Database->quote("peer::".$target."::password").";";
$anvil->Database->write({uuid => $anvil->data->{sys}{host_uuid}, debug => 3, query => $query, source => $THIS_FILE, line => __LINE__});
# Verify that the host UUID is actually valid.
if (not $anvil->Validate->uuid({uuid => $data->{host_uuid}}))
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
{
$data->{host_uuid} = "";
}
return($connected, $data);
}
=head2 get_ups_data
This parses the special C<< ups_X >> string keys to create a list of supported UPSes (in ScanCore and Install Manifests).
Parsed data is stored in;
* C<< ups_data::<key>::agent >>
* C<< ups_data::<key>::brand >>
* C<< ups_data::<key>::description >>
The language used is the language returned by C<< Words->language() >>.
This method takes no parameters.
=cut
sub get_ups_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_ups_data()" }});
# In case we've loaded the data before, clear it.
if (exists $anvil->data->{ups_data})
{
delete $anvil->data->{ups_data};
}
my $language = $anvil->Words->language();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { language => $language }});
foreach my $word_file (sort {$a cmp $b} keys %{$anvil->data->{words}})
{
# Now loop through all keys looking for 'ups_*'.
foreach my $key (sort {$a cmp $b} keys %{$anvil->data->{words}{$word_file}{language}{$language}{key}})
{
next if $key !~ /^ups_(\d+)/;
# If we're here, we've got a UPS.
my $description = $anvil->data->{words}{$word_file}{language}{$language}{key}{$key}{content};
my $brand = $anvil->data->{words}{$word_file}{language}{$language}{key}{$key}{brand};
my $agent = $anvil->data->{words}{$word_file}{language}{$language}{key}{$key}{agent};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:brand' => $brand,
's2:agent' => $agent,
's3:description' => $description,
}});
$anvil->data->{ups_data}{$key}{agent} = $agent;
$anvil->data->{ups_data}{$key}{brand} = $brand;
$anvil->data->{ups_data}{$key}{description} = $description;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:ups_data::${key}::agent" => $anvil->data->{ups_data}{$key}{agent},
"s2:ups_data::${key}::brand" => $anvil->data->{ups_data}{$key}{brand},
"s3:ups_data::${key}::description" => $anvil->data->{ups_data}{$key}{description},
}});
# Make it easy to convert the agent to the brand.
$anvil->data->{ups_agent}{$agent}{brand} = $brand;
$anvil->data->{ups_agent}{$agent}{key} = $key;
$anvil->data->{ups_agent}{$agent}{description} = $description;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"ups_agent::${agent}::brand" => $anvil->data->{ups_agent}{$agent}{brand},
"ups_agent::${agent}::key" => $anvil->data->{ups_agent}{$agent}{key},
"ups_agent::${agent}::description" => $anvil->data->{ups_agent}{$agent}{description},
}});
}
}
return(0);
}
=head2 load_manifest
This takes a manifest UUID and loads and parses it. If the manifest is loaded, C<< 0 >> is returned. C<< 1 >> is returned on error.
The loaded data is stored in a hash as:
manifests::manifest_uuid::<manifest_uuid>::manifest_name = <name>
manifests::manifest_uuid::<manifest_uuid>::manifest_last_ran = <unix time>
manifests::manifest_uuid::<manifest_uuid>::manifest_xml = <raw_xml>
manifests::manifest_uuid::<manifest_uuid>::manifest_note = <user note>
manifests::manifest_uuid::<manifest_uuid>::modified_date = <unix time>
The following hash is used to facilitate manifest name to UUID look up.
manifests::name_to_uuid::<manifest_name> = <manifest_uuid>
The parsed manifest XML is stored as (<machine> == node1, node2 or dr1):
manifests::manifest_uuid::<manifest_uuid>::parsed::name = <Anvil! name>
manifests::manifest_uuid::<manifest_uuid>::parsed::domain = <Anvil! domain name>
manifests::manifest_uuid::<manifest_uuid>::parsed::prefix = <Anvil! prefix>
manifests::manifest_uuid::<manifest_uuid>::parsed::sequence = <Anvil! sequence, zero-padded to two digits>
manifests::manifest_uuid::<manifest_uuid>::parsed::upses::<ups_name>::uuid = <upses -> ups_uuid of named UPS>
manifests::manifest_uuid::<manifest_uuid>::parsed::fences::<fence_name>::uuid = <fences -> fence_uuid of named fence device>
manifests::manifest_uuid::<manifest_uuid>::parsed::networks::dns = <DNS to use, default is '8.8.8.8,8.8.4.4'>
manifests::manifest_uuid::<manifest_uuid>::parsed::networks::ntp = <NTP to use, if any>
manifests::manifest_uuid::<manifest_uuid>::parsed::networks::mtu = <MTU of network>
manifests::manifest_uuid::<manifest_uuid>::parsed::networks::count::bcn = <number of BCNs>
manifests::manifest_uuid::<manifest_uuid>::parsed::networks::count::sn = <number of SNs>
manifests::manifest_uuid::<manifest_uuid>::parsed::networks::count::ifn = <number of IFNs>
manifests::manifest_uuid::<manifest_uuid>::parsed::networks::name::<network_name>::network = <base network ip, ie: 10.255.0.0>
manifests::manifest_uuid::<manifest_uuid>::parsed::networks::name::<network_name>::subnet = <subnet mask>
manifests::manifest_uuid::<manifest_uuid>::parsed::networks::name::<network_name>::gateway = <gateway ip, if any>
manifests::manifest_uuid::<manifest_uuid>::parsed::machine::<machine>::name = <host name>
manifests::manifest_uuid::<manifest_uuid>::parsed::machine::<machine>::ipmi_ip = <ip of IPMI BMC, if any>
manifests::manifest_uuid::<manifest_uuid>::parsed::machine::<machine>::fence::<fence_name>::port = <'port' name/number (see fence agent man page)
manifests::manifest_uuid::<manifest_uuid>::parsed::machine::<machine>::ups::<ups_name>::used = <1 if powered by USB, 0 if not>
manifests::manifest_uuid::<manifest_uuid>::parsed::machine::<machine>::network::<network_name>::ip = <ip used on network>
B<Note>: The machines to use in each role is selected when the manifest is run. Unlike in M2, the manifest does not store machine-specific information (like MAC addresses, etc). The chosen machines at run time contain that information. Similarly, passwords are NOT stored in the manifest, and passed when the manifest is run.
Parameters;
=head3 manifest_uuid (required)
This is the manifest UUID to load.
=cut
sub load_manifest
{
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->load_manifest()" }});
my $manifest_uuid = defined $parameter->{manifest_uuid} ? $parameter->{manifest_uuid} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
manifest_uuid => $manifest_uuid,
}});
if (not $manifest_uuid)
{
# Didn't get a UUID
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Striker->load_manifest()", parameter => "manifest_uuid" }});
return(1);
}
elsif (not $anvil->Validate->uuid({uuid => $manifest_uuid}))
{
# UUID isn't valid
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0130", variables => { method => "Striker->load_manifest()", parameter => "manifest_uuid", uuid => $manifest_uuid}});
return(1);
}
my $query = "
SELECT
manifest_name,
manifest_last_ran,
manifest_xml,
manifest_note,
ROUND(extract(epoch FROM modified_date))
FROM
manifests
WHERE
manifest_uuid = ".$anvil->Database->quote($manifest_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,
}});
my $manifest_template = "";
if ($count)
{
my $manifest_name = $results->[0]->[0];
my $manifest_last_ran = $results->[0]->[1];
my $manifest_xml = $results->[0]->[2];
my $manifest_note = $results->[0]->[3];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
manifest_name => $manifest_name,
manifest_last_ran => $manifest_last_ran,
manifest_xml => $manifest_xml,
manifest_note => $manifest_note,
}});
# Record the data.
$anvil->data->{manifests}{$manifest_uuid}{manifest_name} = $manifest_name;
$anvil->data->{manifests}{$manifest_uuid}{manifest_last_ran} = $manifest_last_ran;
$anvil->data->{manifests}{$manifest_uuid}{manifest_xml} = $manifest_xml;
$anvil->data->{manifests}{$manifest_uuid}{manifest_note} = $manifest_note;
$anvil->data->{manifests}{name_to_uuid}{$manifest_name} = $manifest_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::${manifest_uuid}::manifest_name" => $anvil->data->{manifests}{$manifest_uuid}{manifest_name},
"manifests::${manifest_uuid}::manifest_last_ran" => $anvil->data->{manifests}{$manifest_uuid}{manifest_last_ran},
"manifests::${manifest_uuid}::manifest_xml" => $anvil->data->{manifests}{$manifest_uuid}{manifest_xml},
"manifests::${manifest_uuid}::manifest_note" => $anvil->data->{manifests}{$manifest_uuid}{manifest_note},
"manifests::name_to_uuid::${manifest_name}" => $anvil->data->{manifests}{name_to_uuid}{$manifest_name},
}});
# Whoever is calling us will want the fence data, so load it as well.
$anvil->Database->get_fences({debug => $debug});
# Parse the XML.
local $@;
my $parsed_xml = "";
my $xml = XML::Simple->new();
my $test = eval { $parsed_xml = $xml->XMLin($manifest_xml, KeyAttr => { key => 'name' }, ForceArray => []) };
if (not $test)
{
chomp $@;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0111", variables => {
xml_body => $manifest_xml,
eval_error => $@,
}});
return(1);
}
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{domain} = $parsed_xml->{domain};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{name} = $parsed_xml->{name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::domain" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{domain},
"manifests::manifest_uuid::${manifest_uuid}::parsed::name" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{name},
}});
my ($prefix, $sequence) = ($anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{name} =~ /^(.*?)-anvil-(\d+)$/);
$sequence = sprintf("%02d", $sequence);
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{prefix} = $prefix;
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{sequence} = $sequence;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::prefix" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{prefix},
"manifests::manifest_uuid::${manifest_uuid}::parsed::sequence" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{sequence},
}});
if (ref($parsed_xml->{upses}{ups}) eq "HASH")
{
# Only a single ups device
my $ups_name = $parsed_xml->{upses}{ups}{name};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{upses}{$ups_name}{uuid} = $parsed_xml->{upses}{ups}{uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::upses::${ups_name}::uuid" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{upses}{$ups_name}{uuid},
}});
}
elsif (ref($parsed_xml->{fences}{fence}) eq "ARRAY")
{
# Two or more UPSes
foreach my $hash_ref (@{$parsed_xml->{upses}{ups}})
{
my $ups_name = $hash_ref->{name};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{upses}{$ups_name}{uuid} = $hash_ref->{uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::upses::${ups_name}::uuid" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{upses}{$ups_name}{uuid},
}});
}
}
if (ref($parsed_xml->{fences}{fence}) eq "HASH")
{
# Only a single fence device
my $fence_name = $parsed_xml->{fences}{fence}{name};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{fences}{$fence_name}{uuid} = $parsed_xml->{fences}{fence}{uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::fences::${fence_name}::uuid" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{fences}{$fence_name}{uuid},
}});
}
elsif (ref($parsed_xml->{fences}{fence}) eq "ARRAY")
{
# Two or more fence devices.
foreach my $hash_ref (@{$parsed_xml->{fences}{fence}})
{
my $fence_name = $hash_ref->{name};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{fences}{$fence_name}{uuid} = $hash_ref->{uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::fences::${fence_name}::uuid" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{fences}{$fence_name}{uuid},
}});
}
}
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{dns} = $parsed_xml->{networks}{dns};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{ntp} = $parsed_xml->{networks}{ntp};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{mtu} = $parsed_xml->{networks}{mtu};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::networks::dns" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{dns},
"manifests::manifest_uuid::${manifest_uuid}::parsed::networks::ntp" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{ntp},
"manifests::manifest_uuid::${manifest_uuid}::parsed::networks::mtu" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{mtu},
}});
my $bcn_count = 0;
my $sn_count = 0;
my $ifn_count = 0;
foreach my $hash_ref (@{$parsed_xml->{networks}{network}})
{
my $network_name = $hash_ref->{name};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{name}{$network_name}{network} = $hash_ref->{network};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{name}{$network_name}{subnet} = $hash_ref->{subnet};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{name}{$network_name}{gateway} = $hash_ref->{gateway};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::networks::name::${network_name}::network" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{name}{$network_name}{network},
"manifests::manifest_uuid::${manifest_uuid}::parsed::networks::name::${network_name}::subnet" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{name}{$network_name}{subnet},
"manifests::manifest_uuid::${manifest_uuid}::parsed::networks::name::${network_name}::gateway" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{name}{$network_name}{gateway},
}});
if ($network_name =~ /^bcn/) { $bcn_count++; }
elsif ($network_name =~ /^sn/) { $sn_count++; }
elsif ($network_name =~ /^ifn/) { $ifn_count++; }
}
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{bcn} = $bcn_count;
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{sn} = $sn_count;
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{ifn} = $ifn_count;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::networks::count::bcn" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{bcn},
"manifests::manifest_uuid::${manifest_uuid}::parsed::networks::count::sn" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{sn},
"manifests::manifest_uuid::${manifest_uuid}::parsed::networks::count::ifn" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{ifn},
}});
foreach my $machine (sort {$a cmp $b} keys %{$parsed_xml->{machines}})
{
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{name} = $parsed_xml->{machines}{$machine}{name};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{ipmi_ip} = defined $parsed_xml->{machines}{$machine}{ipmi_ip} ? $parsed_xml->{machines}{$machine}{ipmi_ip} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::machine::${machine}::name" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{name},
"manifests::manifest_uuid::${manifest_uuid}::parsed::machine::${machine}::ipmi_ip" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{ipmi_ip},
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"ref(parsed_xml->{machines}{$machine}{upses}{ups})" => ref($parsed_xml->{machines}{$machine}{upses}{ups}),
}});
if (ref($parsed_xml->{machines}{$machine}{upses}{ups}) eq "HASH")
{
my $ups_name = $parsed_xml->{machines}{$machine}{upses}{ups}{name};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{ups}{$ups_name}{used} = $parsed_xml->{machines}{$machine}{upses}{ups}{used};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::machine::${machine}::ups::${ups_name}::used" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{ups}{$ups_name}{used},
}});
}
elsif (ref($parsed_xml->{machines}{$machine}{upses}{ups}) eq "ARRAY")
{
foreach my $hash_ref (@{$parsed_xml->{machines}{$machine}{upses}{ups}})
{
my $ups_name = $hash_ref->{name};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{ups}{$ups_name}{used} = $hash_ref->{used};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::machine::${machine}::ups::${ups_name}::used" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{ups}{$ups_name}{used},
}});
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"ref(parsed_xml->{machines}{$machine}{fences}{fence})" => ref($parsed_xml->{machines}{$machine}{fences}{fence}),
}});
if (ref($parsed_xml->{machines}{$machine}{fences}{fence}) eq "HASH")
{
my $fence_name = $parsed_xml->{machines}{$machine}{fences}{fence}{name};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{fence}{$fence_name}{port} = $parsed_xml->{machines}{$machine}{fences}{fence}{port};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::machine::${machine}::fence::${fence_name}::port" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{fence}{$fence_name}{port},
}});
}
elsif (ref($parsed_xml->{machines}{$machine}{fences}{fence}) eq "ARRAY")
{
foreach my $hash_ref (@{$parsed_xml->{machines}{$machine}{fences}{fence}})
{
my $fence_name = $hash_ref->{name};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{fence}{$fence_name}{port} = $hash_ref->{port};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::machine::${machine}::fence::${fence_name}::port" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{fence}{$fence_name}{port},
}});
}
}
foreach my $hash_ref (@{$parsed_xml->{machines}{$machine}{networks}{network}})
{
my $network_name = $hash_ref->{name};
$anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{network}{$network_name}{ip} = $hash_ref->{ip};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"manifests::manifest_uuid::${manifest_uuid}::parsed::machine::${machine}::network::${network_name}::ip" => $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{machine}{$machine}{network}{$network_name}{ip},
}});
}
}
}
else
{
# Not found.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "warning_0042", variables => {
table => "manifests",
column => "manifest_uuid",
value => $manifest_uuid,
}});
return(1);
}
return(0);
}
=head2 parse_all_status_json
This parses the c<< all_status.json >> file is a way that Striker can more readily use. If the read or parse failes, C<< 1 >> is returned. Otherwise C<< 0 >> is returned.
This method doesn't take any parameters.
=cut
sub parse_all_status_json
{
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->parse_all_status_json()" }});
# Read it in
my $json_file = $anvil->data->{path}{directories}{status}."/".$anvil->data->{path}{json}{all_status};
if (not -e $json_file)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0105", variables => { file => $json_file }});
return(1);
}
my $body = $anvil->Storage->read_file({debug => $debug, file => $json_file});
if ($body eq "!!error!!")
{
return(1);
}
my $json = JSON->new->allow_nonref;
my $data = $json->decode($body);
if (exists $anvil->data->{json}{all_status})
{
delete $anvil->data->{json}{all_status};
}
# We're going to look for matches as we go, so look
$anvil->Network->load_ips({
debug => $debug,
host => $anvil->Get->short_host_name(),
host_uuid => $anvil->data->{sys}{host_uuid},
});
# We'll be adding data to this JSON file over time. So this will be an ever evolving method.
foreach my $host_hash (@{$data->{hosts}})
{
my $host_name = $host_hash->{name};
my $short_name = $host_hash->{short_name};
$anvil->data->{json}{all_status}{hosts}{$host_name}{host_uuid} = $host_hash->{host_uuid};
$anvil->data->{json}{all_status}{hosts}{$host_name}{type} = $host_hash->{type};
$anvil->data->{json}{all_status}{hosts}{$host_name}{short_host_name} = $host_hash->{short_name};
$anvil->data->{json}{all_status}{hosts}{$host_name}{configured} = $host_hash->{configured};
$anvil->data->{json}{all_status}{hosts}{$host_name}{ssh_fingerprint} = $host_hash->{ssh_fingerprint};
$anvil->data->{json}{all_status}{hosts}{$host_name}{matched_interface} = $host_hash->{matched_interface};
$anvil->data->{json}{all_status}{hosts}{$host_name}{matched_ip_address} = $host_hash->{matched_ip_address};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"json::all_status::hosts::${host_name}::host_uuid" => $anvil->data->{json}{all_status}{hosts}{$host_name}{host_uuid},
"json::all_status::hosts::${host_name}::type" => $anvil->data->{json}{all_status}{hosts}{$host_name}{type},
"json::all_status::hosts::${host_name}::short_host_name" => $anvil->data->{json}{all_status}{hosts}{$host_name}{short_host_name},
"json::all_status::hosts::${host_name}::configured" => $anvil->data->{json}{all_status}{hosts}{$host_name}{configured},
"json::all_status::hosts::${host_name}::ssh_fingerprint" => $anvil->data->{json}{all_status}{hosts}{$host_name}{ssh_fingerprint},
"json::all_status::hosts::${host_name}::matched_interface" => $anvil->data->{json}{all_status}{hosts}{$host_name}{matched_interface},
"json::all_status::hosts::${host_name}::matched_ip_address" => $anvil->data->{json}{all_status}{hosts}{$host_name}{matched_ip_address},
}});
# Find what interface on this host we can use to talk to it (if we're not looking at ourselves).
my $matched_interface = "";
my $matched_ip_address = "";
if ($host_name ne $anvil->Get->host_name)
{
$anvil->Network->load_ips({
debug => $debug,
host => $short_name,
host_uuid => $anvil->data->{json}{all_status}{hosts}{$host_name}{host_uuid},
});
my ($match) = $anvil->Network->find_matches({
debug => 3,
first => $anvil->Get->short_host_name(),
second => $short_name,
source => $THIS_FILE,
line => __LINE__,
});
if ($match)
{
# Yup!
my $match_found = 0;
foreach my $interface (sort {$a cmp $b} keys %{$match->{$short_name}})
{
$matched_interface = $interface;
$matched_ip_address = $match->{$short_name}{$interface}{ip_address};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
matched_interface => $matched_interface,
matched_ip_address => $matched_ip_address,
}});
last;
}
}
}
$anvil->data->{json}{all_status}{hosts}{$host_name}{matched_interface} = $matched_interface;
$anvil->data->{json}{all_status}{hosts}{$host_name}{matched_ip_address} = $matched_ip_address;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"json::all_status::hosts::${host_name}::matched_interface" => $anvil->data->{json}{all_status}{hosts}{$host_name}{matched_interface},
"json::all_status::hosts::${host_name}::matched_ip_address" => $anvil->data->{json}{all_status}{hosts}{$host_name}{matched_ip_address},
}});
foreach my $interface_hash (@{$host_hash->{network_interfaces}})
{
my $interface_name = $interface_hash->{name};
my $interface_type = $interface_hash->{type};
my $mac_address = $interface_hash->{mac_address};
my $ip_address = $interface_hash->{ip_address};
my $subnet_mask = $interface_hash->{subnet_mask};
my $default_gateway = $interface_hash->{default_gateway};
my $gateway = $interface_hash->{gateway};
my $dns = $interface_hash->{dns};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
interface_name => $interface_name,
interface_type => $interface_type,
mac_address => $mac_address,
ip_address => $ip_address,
subnet_mask => $subnet_mask,
default_gateway => $default_gateway,
gateway => $gateway,
dns => $dns,
}});
# This lets us easily map interface names to types.
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface_name_to_type}{$interface_name} = $interface_type;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"json::all_status::hosts::${host_name}::network_interface_name_to_type::${interface_name}" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface_name_to_type}{$interface_name},
}});
# Record the rest of the data.
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{uuid} = $interface_hash->{uuid};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{mtu} = $interface_hash->{mtu};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{mac_address} = $interface_hash->{mac_address};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{ip_address} = $interface_hash->{ip_address};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{subnet_mask} = $interface_hash->{subnet_mask};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{default_gateway} = $interface_hash->{default_gateway};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{gateway} = $interface_hash->{gateway};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{dns} = $interface_hash->{dns};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::uuid" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{uuid},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::mtu" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{mtu},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::mac_address" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{mac_address},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::ip_address" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{ip_address},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::subnet_mask" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{subnet_mask},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::default_gateway" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{default_gateway},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::gateway" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{gateway},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::dns" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{dns},
}});
if ((exists $interface_hash->{connected_interfaces}) && (ref($interface_hash->{connected_interfaces}) eq "ARRAY"))
{
my $connected_interfaces_count = @{$interface_hash->{connected_interfaces}};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { connected_interfaces_count => $connected_interfaces_count }});
foreach my $connected_interface_name (sort {$a cmp $b} @{$interface_hash->{connected_interfaces}})
{
# We'll sort out the types after
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{connected_interfaces}{$connected_interface_name}{type} = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::connected_interfaces::${connected_interface_name}::type" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{connected_interfaces}{$connected_interface_name}{type},
}});
}
}
if ($interface_type eq "bridge")
{
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bridge_id} = $interface_hash->{bridge_id};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{stp_enabled} = $interface_hash->{stp_enabled};
my $say_stp_enabled = $interface_hash->{stp_enabled};
if (($say_stp_enabled eq "0") or ($say_stp_enabled eq "disabled"))
{
$say_stp_enabled = $anvil->Words->string({key => "unit_0020"});
}
elsif (($say_stp_enabled eq "1") or ($say_stp_enabled eq "enabled_kernel"))
{
$say_stp_enabled = $anvil->Words->string({key => "unit_0021"});
}
elsif (($say_stp_enabled eq "2") or ($say_stp_enabled eq "enabled_userland"))
{
$say_stp_enabled = $anvil->Words->string({key => "unit_0022"});
}
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_stp_enabled} = $interface_hash->{say_stp_enabled};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::bridge_id" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bridge_id},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::stp_enabled" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{stp_enabled},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::say_stp_enabled" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_stp_enabled},
}});
}
elsif ($interface_type eq "bond")
{
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{mode} = $interface_hash->{mode};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{active_interface} = $interface_hash->{active_interface};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{primary_interface} = $interface_hash->{primary_interface};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{primary_reselect} = $interface_hash->{primary_reselect};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{up_delay} = $interface_hash->{up_delay};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{down_delay} = $interface_hash->{down_delay};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{operational} = $interface_hash->{operational};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{mii_polling_interval} = $interface_hash->{mii_polling_interval}." ".$anvil->Words->string({key => "suffix_0012"});
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bridge_uuid} = $interface_hash->{bridge_uuid};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bridge_name} = $interface_hash->{bridge_name};
# Translate some values
my $say_mode = $interface_hash->{mode};
if (($say_mode eq "0") or ($say_mode eq "balance-rr"))
{
$say_mode = $anvil->Words->string({key => "unit_0006"});
}
elsif (($say_mode eq "1") or ($say_mode eq "active-backup"))
{
$say_mode = $anvil->Words->string({key => "unit_0007"});
}
elsif (($say_mode eq "2") or ($say_mode eq "balanced-xor"))
{
$say_mode = $anvil->Words->string({key => "unit_0008"});
}
elsif (($say_mode eq "3") or ($say_mode eq "broadcast"))
{
$say_mode = $anvil->Words->string({key => "unit_0009"});
}
elsif (($say_mode eq "4") or ($say_mode eq "802.3ad"))
{
$say_mode = $anvil->Words->string({key => "unit_0010"});
}
elsif (($say_mode eq "5") or ($say_mode eq "balanced-tlb"))
{
$say_mode = $anvil->Words->string({key => "unit_0011"});
}
elsif (($say_mode eq "6") or ($say_mode eq "balanced-alb"))
{
$say_mode = $anvil->Words->string({key => "unit_0012"});
}
my $say_operational = $interface_hash->{operational};
if ($say_operational eq "up")
{
$say_operational = $anvil->Words->string({key => "unit_0001"});
}
elsif ($say_operational eq "down")
{
$say_operational = $anvil->Words->string({key => "unit_0002"});
}
elsif ($say_operational eq "unknown")
{
$say_operational = $anvil->Words->string({key => "unit_0004"});
}
my $say_primary_reselect = $interface_hash->{primary_reselect};
if (($say_primary_reselect eq "always") or ($say_primary_reselect eq "0"))
{
$say_primary_reselect = $anvil->Words->string({key => "unit_0017"});
}
elsif (($say_primary_reselect eq "better") or ($say_primary_reselect eq "1"))
{
$say_primary_reselect = $anvil->Words->string({key => "unit_0018"});
}
elsif (($say_primary_reselect eq "failure") or ($say_primary_reselect eq "2"))
{
$say_primary_reselect = $anvil->Words->string({key => "unit_0019"});
}
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_up_delay} = $interface_hash->{up_delay}." ".$anvil->Words->string({key => "suffix_0012"});
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_down_delay} = $interface_hash->{say_down_delay}." ".$anvil->Words->string({key => "suffix_0012"});
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_mode} = $say_mode;
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_operational} = $say_operational;
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_primary_reselect} = $say_primary_reselect;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::mode" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{mode},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::active_interface" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{active_interface},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::primary_interface" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{primary_interface},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::primary_reselect" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{primary_reselect},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::up_delay" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{up_delay},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::down_delay" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{down_delay},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::operational" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{operational},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::mii_polling_interval" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{mii_polling_interval},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::bridge_uuid" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bridge_uuid},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::bridge_name" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bridge_name},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::say_up_delay" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_up_delay},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::say_down_delay" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_down_delay},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::say_mode" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_mode},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::say_operational" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_operational},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::say_primary_reselect" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_primary_reselect},
}});
}
else
{
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{speed} = $interface_hash->{speed};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{link_state} = $interface_hash->{link_state};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{operational} = $interface_hash->{operational};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{duplex} = $interface_hash->{duplex};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{medium} = $interface_hash->{medium};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bond_uuid} = $interface_hash->{bond_uuid};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bond_name} = $interface_hash->{bond_name};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bridge_uuid} = $interface_hash->{bridge_uuid};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bridge_name} = $interface_hash->{bridge_name};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{changed_order} = $interface_hash->{changed_order};
my $say_speed = $anvil->Convert->add_commas({number => $interface_hash->{speed}})." ".$anvil->Words->string({key => "suffix_0050"});
if ($interface_hash->{speed} >= 1000)
{
# Report in Gbps
$say_speed = $anvil->Convert->add_commas({number => ($interface_hash->{speed} / 1000)})." ".$anvil->Words->string({key => "suffix_0051"});
}
my $say_duplex = $interface_hash->{duplex};
if ($say_duplex eq "full")
{
$say_duplex = $anvil->Words->string({key => "unit_0015"});
}
elsif ($say_duplex eq "half")
{
$say_duplex = $anvil->Words->string({key => "unit_0016"});
}
elsif ($say_duplex eq "unknown")
{
$say_duplex = $anvil->Words->string({key => "unit_0004"});
}
my $say_link_state = $interface_hash->{link_state};
if ($say_link_state eq "1")
{
$say_link_state = $anvil->Words->string({key => "unit_0013"});
}
elsif ($say_link_state eq "0")
{
$say_link_state = $anvil->Words->string({key => "unit_0014"});
}
my $say_operational = $interface_hash->{operational};
if ($say_operational eq "up")
{
$say_operational = $anvil->Words->string({key => "unit_0013"});
}
elsif ($say_operational eq "down")
{
$say_operational = $anvil->Words->string({key => "unit_0014"});
}
elsif ($say_operational eq "unknown")
{
$say_operational = $anvil->Words->string({key => "unit_0004"});
}
# This will be flushed out later. For now, we just send out what we've got.
my $say_medium = $interface_hash->{medium};
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_speed} = $say_speed;
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_duplex} = $say_duplex;
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_link_state} = $say_link_state;
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_operationa} = $say_operational;
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_medium} = $say_medium;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::speed" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{speed},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::link_state" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{link_state},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::operational" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{operational},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::duplex" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{duplex},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::medium" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{medium},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::bond_uuid" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bond_uuid},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::bond_name" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bond_name},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::bridge_uuid" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bridge_uuid},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::bridge_name" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{bridge_name},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::changed_order" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{changed_order},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::say_speed" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_speed},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::say_duplex" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_duplex},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::say_link_state" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_link_state},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::say_operationa" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_operationa},
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::say_medium" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{say_medium},
}});
}
}
}
foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{json}{all_status}{hosts}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_name => $host_name }});
foreach my $interface_type (sort {$a cmp $b} keys %{$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { interface_type => $interface_type }});
foreach my $interface_name (sort {$a cmp $b} keys %{$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { interface_name => $interface_name }});
foreach my $connected_interface_name (sort {$a cmp $b} keys %{$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{connected_interfaces}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { connected_interface_name => $connected_interface_name }});
if (defined $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface_name_to_type}{$connected_interface_name})
{
$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{connected_interfaces}{$connected_interface_name}{type} = $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface_name_to_type}{$connected_interface_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"json::all_status::hosts::${host_name}::network_interface::${interface_type}::${interface_name}::connected_interfaces::${connected_interface_name}::type" => $anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{$interface_type}{$interface_name}{connected_interfaces}{$connected_interface_name}{type},
}});
}
}
}
}
}
return(0);
}
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair. * Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered. * Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database. * Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host. * Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper. * In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead. * Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table. * Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host. Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
1;