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.

2588 lines
111 KiB

package Anvil::Tools::Network;
#
# This module contains methods used to deal with networking stuff.
#
use strict;
use warnings;
use Data::Dumper;
use Scalar::Util qw(weaken isweak);
use Net::Netmask;
our $VERSION = "3.0.0";
my $THIS_FILE = "Network.pm";
### Methods;
# bridge_info
# check_internet
# download
# find_matches
# find_target_ip
# get_company_from_mac
# get_ips
# get_network
# is_local
# load_interfces
# load_ips
# is_ip_in_network
# ping
# read_nmcli
=pod
=encoding utf8
=head1 NAME
Anvil::Tools::Network
Provides all methods related to networking.
=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->Storage->X'.
#
=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 bridge_info
This calls C<< bridge >> to get data on interfaces connected to bridges. A list of interfaces to connected to each bridge is stored here;
* bridge::<target>::<bridge_name>::interfaces = Array reference of interfaces connected this bridge
The rest of the variable / value pairs are stored here. See C<< man bridge -> state >> for more information of these values
* bridge::<target>::<bridge_name>::<interface_name>::<variable> = <value>
The common variables are;
* bridge::<target>::<bridge_name>::<interface_name>::ifindex = Interface index number.
* bridge::<target>::<bridge_name>::<interface_name>::flags = An array reference storing the flags set for the interface on the bridge.
* bridge::<target>::<bridge_name>::<interface_name>::mtu = The maximum transmitable unit size, in bytes.
* bridge::<target>::<bridge_name>::<interface_name>::state = The state of the bridge.
* bridge::<target>::<bridge_name>::<interface_name>::priority = The priority for this interface.
* bridge::<target>::<bridge_name>::<interface_name>::cost = The cost of this interface.
Paramters;
=head3 password (optional)
If C<< target >> is set, this is the password used to log into the remote system as the C<< remote_user >>. If it is not set, an attempt to connect without a password will be made (though this will usually fail).
=head3 port (optional, default 22)
If C<< target >> is set, this is the TCP port number used to connect to the remote machine.
=head3 remote_user (optional)
If C<< target >> is set, this is the user account that will be used when connecting to the remote system.
=head3 target (optional, default '')
If set, the bridge data will be read from the target machine. This needs to be the IP address or (resolvable) host name of the target.
=cut
sub bridge_info
{
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 => "Network->bridge_info()" }});
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : 22;
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
password => $anvil->Log->is_secure($password),
port => $port,
remote_user => $remote_user,
target => $target,
}});
my $shell_call = $anvil->data->{path}{exe}{bridge}." -json -pretty link show";
my $output = "";
if ($anvil->Network->is_local({host => $target}))
{
# Local call.
($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:return_code' => $return_code,
}});
}
else
{
# Remote call
($output, my $error, my $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:error' => $error,
's3:return_code' => $return_code,
}});
}
# Did I get usable data?
if ($output !~ /^\[/)
{
# Bad data.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0443", variables => { output => $output }});
return(1);
}
my $json = JSON->new->allow_nonref;
my $bridge_data = $json->decode($output);
foreach my $hash_ref (@{$bridge_data})
{
next if not defined $hash_ref->{master};
my $bridge = $hash_ref->{master};
my $interface = $hash_ref->{ifname};
my $host = $target ? $target : $anvil->Get->short_host_name();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:bridge' => $bridge,
's2:interface' => $interface,
's3:host' => $host,
}});
if ((not exists $anvil->data->{bridge}{$host}{$bridge}) or (ref($anvil->data->{bridge}{$host}{$bridge}{interfaces}) ne "ARRAY"))
{
$anvil->data->{bridge}{$host}{$bridge}{interfaces} = [];
}
push @{$anvil->data->{bridge}{$host}{$bridge}{interfaces}}, $interface;
# Now store the rest of the data.
foreach my $key (sort {$a cmp $b} keys %{$hash_ref})
{
next if $key eq "master";
next if $key eq "ifname";
$anvil->data->{bridge}{$host}{$bridge}{$interface}{$key} = $hash_ref->{$key};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"bridge::${host}::${bridge}::${interface}::${key}" => $anvil->data->{bridge}{$host}{$bridge}{$interface}{$key},
}});
}
}
return(0);
}
=head2 check_internet
This method tries to connect to the internet. If successful, C<< 1 >> is returned. Otherwise, C<< 0 >> is returned.
Paramters;
=head3 domains (optional, default 'defaults::network::test::domains')
If passed an array reference, the domains in the array will be checked in the order they are found in the array. As soon as any respond to a ping, the check exits and C<< 1 >> is returned.
If not passed, C<< defaults::network::test::domains >> are used.
=head3 password (optional)
If C<< target >> is set, this is the password used to log into the remote system as the C<< remote_user >>. If it is not set, an attempt to connect without a password will be made (though this will usually fail).
=head3 port (optional, default 22)
If C<< target >> is set, this is the TCP port number used to connect to the remote machine.
=head3 remote_user (optional)
If C<< target >> is set, this is the user account that will be used when connecting to the remote system.
=head3 target (optional)
If set, the file will be read from the target machine. This must be either an IP address or a resolvable host name.
=head3 tries (optional, default 3)
This is how many times we'll try to ping the target. Pings are done one ping at a time, so that if the first ping succeeds, the test can exit quickly and return success.
=cut
sub check_internet
{
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 => "Network->check_internet()" }});
my $access = 0;
my $domains = defined $parameter->{domains} ? $parameter->{domains} : $anvil->data->{defaults}{network}{test}{domains};
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : 22;
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $tries = defined $parameter->{tries} ? $parameter->{tries} : 3;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
domains => $domains,
password => $anvil->Log->is_secure($password),
port => $port,
remote_user => $remote_user,
target => $target,
tries => $tries,
}});
if (ref($domains) eq "ARRAY")
{
my $domain_count = @{$domains};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { domain_count => $domain_count }});
if (not $domain_count)
{
# Array is empty
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0440", variables => { name => "domain" }});
return($access);
}
}
else
{
# Domains isn't an array.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0218", variables => { name => "domain", value => $domains }});
return($access);
}
if (($tries =~ /\D/) or ($tries < 1))
{
# Invalid
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0441", variables => { name => "tries", value => $tries }});
return($access);
}
foreach my $domain (@{$domains})
{
# Is the domain valid?
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { domain => $domain }});
if ((not $anvil->Validate->domain_name({debug => $debug, name => $domain})) and
(not $anvil->Validate->ipv4({debug => $debug, ip => $domain})))
{
# Not valid, skip
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0442", variables => { name => $domain }});
next;
}
* Created Convert->fence_ipmilan_to_ipmitool() that takes a 'fence_ipmilan' call and converts it into a direct 'ipmitool' call. * Created Database->get_power() that loads data from the special 'power' table. * Fixed a bug in calls to Network->ping() where some weren't formatted properly for receiving two string variables. * Updated Database->get_anvils() to record the machine types when recording host information. * Updated Database->get_hosts_info() to also load the 'host_ipmi' column. * Updated Database->get_upses() to store the link to the 'power' -> 'power_uuid', when available. * Created ScanCore->call_scan_agents() that does the work of actually calling scan agents, moving the logic out from the scancore daemon. * Created ScanCore->check_power() that takes a host and the anvil it is in and returns if it's on batteries or not. If it is, the time on batteries and estimate hold-up time is returned. If not, the highest charge percentage is returned. * Created ScanCore->post_scan_analysis() that is a wrapper for calling the new ->post_scan_analysis_dr(), ->post_scan_analysis_node() and ->post_scan_analysis_striker(). Of which, _dr and _node are still empty, but _striker is complete. ** ->post_scan_analysis_striker() is complete. It now boots a node after a power loss if the UPSes powering it are OK (at least one has mains power, and the main-powered UPS(es) have reached the minimum charge percentage). If it's thermal, IPMI is called and so long as at least one thermal sensor is found and it/they are all OK, it is booted. For now, M2's thermal reboot delay logic hasn't been replicated, as it added a lot of complexity and didn't prove practically useful. * Created System->collect_ipmi_data() and moved 'scan_ipmitool's ipmitool call and parse into that method. This was done to allow ScanCore->post_scan_analysis_striker() to also call IPMI on a remote machine during thermal down events without reimplementing the logic. * Updated scan-ipmitool to only record temperature data for data collected locally. Also renamed 'machine' variables and hash keys to 'host_name' to clarify what is being stored. * Updated scancore to clear the 'system::stop_reason' variable. * Added missing packages to striker-manage-install-target. Signed-off-by: Digimer <digimer@alteeve.ca>
4 years ago
my ($pinged, $average_time) = $anvil->Network->ping({
debug => $debug,
target => $target,
port => $port,
password => $password,
remote_user => $remote_user,
ping => $domain,
count => 3,
});
* Created Convert->fence_ipmilan_to_ipmitool() that takes a 'fence_ipmilan' call and converts it into a direct 'ipmitool' call. * Created Database->get_power() that loads data from the special 'power' table. * Fixed a bug in calls to Network->ping() where some weren't formatted properly for receiving two string variables. * Updated Database->get_anvils() to record the machine types when recording host information. * Updated Database->get_hosts_info() to also load the 'host_ipmi' column. * Updated Database->get_upses() to store the link to the 'power' -> 'power_uuid', when available. * Created ScanCore->call_scan_agents() that does the work of actually calling scan agents, moving the logic out from the scancore daemon. * Created ScanCore->check_power() that takes a host and the anvil it is in and returns if it's on batteries or not. If it is, the time on batteries and estimate hold-up time is returned. If not, the highest charge percentage is returned. * Created ScanCore->post_scan_analysis() that is a wrapper for calling the new ->post_scan_analysis_dr(), ->post_scan_analysis_node() and ->post_scan_analysis_striker(). Of which, _dr and _node are still empty, but _striker is complete. ** ->post_scan_analysis_striker() is complete. It now boots a node after a power loss if the UPSes powering it are OK (at least one has mains power, and the main-powered UPS(es) have reached the minimum charge percentage). If it's thermal, IPMI is called and so long as at least one thermal sensor is found and it/they are all OK, it is booted. For now, M2's thermal reboot delay logic hasn't been replicated, as it added a lot of complexity and didn't prove practically useful. * Created System->collect_ipmi_data() and moved 'scan_ipmitool's ipmitool call and parse into that method. This was done to allow ScanCore->post_scan_analysis_striker() to also call IPMI on a remote machine during thermal down events without reimplementing the logic. * Updated scan-ipmitool to only record temperature data for data collected locally. Also renamed 'machine' variables and hash keys to 'host_name' to clarify what is being stored. * Updated scancore to clear the 'system::stop_reason' variable. * Added missing packages to striker-manage-install-target. Signed-off-by: Digimer <digimer@alteeve.ca>
4 years ago
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
pinged => $pinged,
average_time => $average_time,
}});
if ($pinged)
{
$access = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { access => $access }});
}
last if $pinged;
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { access => $access }});
return($access);
}
=head2 download
This downloads a file from a network target and saves it to a local file. This must be called on a local system so that the download progress can be reported.
On success, the saved file is returned. On failure, an empty string is returned.
Parameters;
=head3 overwrite (optional, default '0')
When set, if the output file already exists, the existing file will be removed before the download is called.
B<< NOTE >>: If the output file already exists and is 0-bytes, it is removed and the download proceeds regardless of this setting.
=head3 save_to (optional)
If set, this is where the file will be downloaded to. If this ends with C<< / >>, the file name is preserved from the C<< url >> and will be saved in the C<< save_to >>'s directory with the original file name. Otherwise, the downlaoded file is saved with the file name given. As such, be careful about the trailing C<< / >>!
When not specified, the file name in the URL will be used and the file will be saved in the active user's home directory.
=head3 status (optional, default '1')
When set to C<< 1 >>, a periodic status message is printed. When set to C<< 0 >>, no status will be printed.
=head3 url (required)
This is the URL to the file to download.
=cut
sub download
{
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 => "Network->download()" }});
my $overwrite = defined $parameter->{overwrite} ? $parameter->{overwrite} : 0;
my $save_to = defined $parameter->{save_to} ? $parameter->{save_to} : "";
my $status = defined $parameter->{status} ? $parameter->{status} : 1;
my $url = defined $parameter->{url} ? $parameter->{url} : "";
my $uuid = $anvil->Get->uuid();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
overwrite => $overwrite,
save_to => $save_to,
status => $status,
url => $url,
uuid => $uuid,
}});
if (not $url)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->download()", parameter => "url" }});
return("");
}
elsif (($url !~ /^ftp\:\/\//) && ($url !~ /^http\:\/\//) && ($url !~ /^https\:\/\//))
{
# Invalid URL.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0085", variables => { url => $url }});
return("");
}
# The name of the file to be downloaded will be used if the path isn't specified, or if it ends in '/'.
my $source_file = ($url =~ /^.*\/(.*)$/)[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { source_file => $source_file }});
if (not $save_to)
{
$save_to = $anvil->Get->users_home({debug => $debug})."/".$source_file;
$save_to =~ s/\/\//\//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 0, list => { save_to => $save_to }});
}
elsif ($save_to =~ /\/$/)
{
$save_to .= "/".$source_file;
$save_to =~ s/\/\//\//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 0, list => { save_to => $save_to }});
}
# Does the download file exist already?
if (-e $save_to)
{
# If overwrite is set, or if the file is zero-bytes, remove it.
my $size = (stat($save_to))[7];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
size => $size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $size}).")",
}});
if (($overwrite) or ($size == 0))
{
unlink $save_to;
}
else
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "error_0094", variables => {
url => $url,
save_to => $save_to,
}});
return("");
}
}
### TODO: Make this work well as a job
my $status_file = "/tmp/".$source_file.".download_status";
my $bytes_downloaded = 0;
my $running_time = 0;
my $average_rate = 0;
my $start_printed = 0;
my $percent = 0;
my $rate = 0; # Bytes/sec
my $downloaded = 0; # Bytes
my $time_left = 0; # Seconds
my $report_interval = 5; # Seconds between status file update
my $next_report = time + $report_interval;
my $error = 0;
# This should print to a status file
print "uuid=$uuid bytes_downloaded=0 percent=0 current_rate=0 average_rate=0 seconds_running=0 seconds_left=0 url=$url save_to=$save_to\n" if $status;;
# Download command
my $unix_start = 0;
my $shell_call = $anvil->data->{path}{exe}{wget}." -c --progress=dot:binary ".$url." -O ".$save_to;
my $output = "";
open (my $file_handle, $shell_call." 2>&1 |") or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }});
while(<$file_handle>)
{
chomp;
my $line = $_;
$line =~ s/^\s+//;
$line =~ s/\s+$//;
$line =~ s/\s+/ /g;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, key => "log_0017", variables => { line => $line }});
if (($line =~ /404/) && ($line =~ /Not Found/i))
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, priority => "err", key => "error_0086", variables => { url => $url }});
$error = 1;;
}
if ($line =~ /Name or service not known/i)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, priority => "err", key => "error_0087", variables => { url => $url }});
$error = 1;;
}
if ($line =~ /Connection refused/i)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, priority => "err", key => "error_0088", variables => { url => $url }});
$error = 1;;
}
if ($line =~ /route to host/i)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, priority => "err", key => "error_0089", variables => { url => $url }});
$error = 1;;
}
if ($line =~ /Network is unreachable/i)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, priority => "err", key => "error_0090", variables => { url => $url }});
$error = 1;;
}
if ($line =~ /ERROR (\d+): (.*)$/i)
{
my $error_code = $1;
my $error_message = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
error_code => $error_code,
error_message => $error_message,
}});
if ($error_code eq "403")
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, priority => "err", key => "error_0091", variables => { url => $url }});
}
elsif ($error_code eq "404")
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, priority => "err", key => "error_0092", variables => { url => $url }});
}
else
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, priority => "err", key => "error_0093", variables => {
url => $url,
error_code => $error_code,
error_message => $error_message,
}});
}
$error = 1;;
}
if ($line =~ /^(\d+)K .*? (\d+)% (.*?) (\d+.*)$/)
{
$downloaded = $1;
$percent = $2;
$rate = $3;
$time_left = $4;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
downloaded => $downloaded,
percent => $percent,
rate => $rate,
time_left => $time_left,
}});
if (not $start_printed)
{
### NOTE: This is meant to be parsed by a script, so don't translate it.
print "started:$uuid\n" if $status;
$start_printed = 1;
}
### NOTE: According to: http://savannah.gnu.org/bugs/index.php?22765, wget uses base-2.
# Convert
$bytes_downloaded = $downloaded * 1024;
my $say_downloaded = $anvil->Convert->bytes_to_human_readable({'bytes' => $bytes_downloaded});
my $say_percent = $percent."%";
my $byte_rate = $anvil->Convert->human_readable_to_bytes({size => $rate, base2 => 1});
my $say_rate = $anvil->Convert->bytes_to_human_readable({'bytes' => $byte_rate})."/s";
$running_time = time - $unix_start;
my $say_running_time = $anvil->Convert->time({'time' => $running_time, translate => 1});
# Time left is a bit more complicated
my $days = 0;
my $hours = 0;
my $minutes = 0;
my $seconds = 0;
if ($time_left =~ /(\d+)d/)
{
$days = $1;
#print "$THIS_FILE ".__LINE__."; == days: [$days]\n";
}
if ($time_left =~ /(\d+)h/)
{
$hours = $1;
#print "$THIS_FILE ".__LINE__."; == hours: [$hours]\n";
}
if ($time_left =~ /(\d+)m/)
{
$minutes = $1;
#print "$THIS_FILE ".__LINE__."; == minutes: [$minutes]\n";
}
if ($time_left =~ /(\d+)s/)
{
$seconds = $1;
#print "$THIS_FILE ".__LINE__."; == seconds: [$seconds]\n";
}
my $seconds_left = (($days * 86400) + ($hours * 3600) + ($minutes * 60) + $seconds);
my $say_time_left = $anvil->Convert->time({'time' => $seconds_left, long => 1, translate => 1});
$running_time = 1 if not $running_time;
$average_rate = int($bytes_downloaded / $running_time);
my $say_average_rate = $anvil->Convert->bytes_to_human_readable({'bytes' => $average_rate})."/s";
#print "$THIS_FILE ".__LINE__."; downloaded: [$downloaded], bytes_downloaded: [$bytes_downloaded], say_downloaded: [$say_downloaded], percent: [$percent], rate: [$rate], byte_rate: [$byte_rate], say_rate: [$say_rate], time_left: [$time_left]\n";
if (time > $next_report)
{
#print "$THIS_FILE ".__LINE__."; say_downloaded: [$say_downloaded], percent: [$percent], say_rate: [$say_rate], running_time: [$running_time], say_running_time: [$say_running_time], seconds_left: [$seconds_left], say_time_left: [$say_time_left]\n";
#print "$file; Downloaded: [$say_downloaded]/[$say_percent], Rate/Avg: [$say_rate]/[$say_average_rate], Running: [$say_running_time], Left: [$say_time_left]\n";
#print "$THIS_FILE ".__LINE__."; bytes_downloaded=$bytes_downloaded, percent=$percent, current_rate=$byte_rate, average_rate=$average_rate, seconds_running=$running_time, seconds_left=$seconds_left, save_to=$save_to\n";
$next_report += $report_interval;
# This should print to a status file
print "uuid=$uuid bytes_downloaded=$bytes_downloaded percent=$percent current_rate=$byte_rate average_rate=$average_rate seconds_running=$running_time seconds_left=$seconds_left url=$url save_to=$save_to\n" if $status;
}
}
}
close $file_handle;
chomp($output);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { error => $error }});
if ($error)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { save_to => $save_to }});
if (-e $save_to)
{
# Unlink the output file, it's empty.
my $size = (stat($save_to))[7];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
size => $size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $size}).")",
}});
if (not $size)
{
unlink $save_to;
}
}
return("");
}
return($save_to);
}
=head2 find_matches
This takes two hash keys from prior C<< Network->get_ips() >> or C<< ->load_ips() >> runs and finds which are on the same network.
A hash reference is returned using the format:
* <first>::<interface>::ip = <ip_address>
* <first>::<interface>::subnet_mask = <subnet_mask>
* <second>::<interface>::ip = <ip_address>
* <second>::<interface>::subnet_mask = <subnet_mask>
Where C<< first >> and C<< second >> are the parameters passed in below and C<< interface >> is the name of the interface on the fist/second machine that can talk to one another.
Paramters;
=head3 first (required)
This is the hash key of the first machine being compared.
=head3 second (required)
This is the hash key of the second machine being compared.
=cut
sub find_matches
{
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 => "Network->find_matches()" }});
my $first = defined $parameter->{first} ? $parameter->{first} : "";
my $second = defined $parameter->{second} ? $parameter->{second} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
first => $first,
second => $second,
}});
if (not $first)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->find_matches()", parameter => "first" }});
return("");
}
elsif (ref($anvil->data->{network}{$first}) ne "HASH")
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0106", variables => { key => "first -> network::".$first }});
return("");
}
if (not $second)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->find_matches()", parameter => "second" }});
return("");
}
elsif (ref($anvil->data->{network}{$second}) ne "HASH")
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0106", variables => { key => "second -> network::".$second }});
return("");
}
# Loop through the first, and on each interface with an IP/subnet mask, look for a match in the second.
my $match = {};
foreach my $first_interface (sort {$b cmp $a} keys %{$anvil->data->{network}{$first}{interface}})
{
my $first_ip = $anvil->data->{network}{$first}{interface}{$first_interface}{ip};
my $first_subnet_mask = $anvil->data->{network}{$first}{interface}{$first_interface}{subnet_mask};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
first => $first,
first_interface => $first_interface,
first_ip => $first_ip,
first_subnet_mask => $first_subnet_mask,
}});
if (($first_ip) && ($first_subnet_mask))
{
# Look for a match.
my $first_network = $anvil->Network->get_network({
debug => $debug,
ip => $first_ip,
subnet_mask => $first_subnet_mask,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { a_network => $first_network }});
foreach my $second_interface (sort {$b cmp $a} keys %{$anvil->data->{network}{$second}{interface}})
{
my $second_ip = $anvil->data->{network}{$second}{interface}{$second_interface}{ip};
my $second_subnet_mask = $anvil->data->{network}{$second}{interface}{$second_interface}{subnet_mask};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
second => $second,
second_interface => $second_interface,
second_ip => $second_ip,
second_subnet_mask => $second_subnet_mask,
}});
if (($second_ip) && ($second_subnet_mask))
{
# Do we have a match?
my $second_network = $anvil->Network->get_network({
debug => $debug,
ip => $second_ip,
subnet_mask => $second_subnet_mask,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
a_network => $first_network,
b_network => $second_network,
}});
if ($first_network eq $second_network)
{
# Match!
$match->{$first}{$first_interface}{ip} = $first_ip;
$match->{$first}{$first_interface}{subnet_mask} = $second_network;
$match->{$second}{$second_interface}{ip} = $second_ip;
$match->{$second}{$second_interface}{subnet_mask} = $first_network;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"${first}::${first_interface}::ip" => $match->{$first}{$first_interface}{ip},
"${first}::${first_interface}::subnet_mask" => $match->{$first}{$first_interface}{subnet_mask},
"${second}::${second_interface}::ip" => $match->{$second}{$second_interface}{ip},
"${second}::${second_interface}::subnet_mask" => $match->{$second}{$second_interface}{subnet_mask},
}});
}
}
}
}
}
return($match);
}
=head2 find_target_ip
This uses the IP information for the local machine and a target host UUID, and returns an IP address that can be used to contact it. When multiple networks are shared, the BCN IP is used. If no match is found, an empty string is returned.
my $target_ip = $anvil->Network->find_target_ip({host_uuid => "8da3d2fe-783a-4619-abb5-8ccae58f7bd6"});
Parameters;
=head3 host_uuid (required)
This is the target's C<< host_uuid >> that we're looking to contact.
=cut
sub find_target_ip
{
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 => "Network->find_target_ip()" }});
my $target_ip = "";
my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
host_uuid => $host_uuid,
}});
if (not $host_uuid)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->find_target_ip()", parameter => "host_uuid" }});
return("");
}
$anvil->Database->get_hosts();
if (not exists $anvil->data->{hosts}{host_uuid}{$host_uuid})
{
# Unknown host
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0031", variables => { host_uuid => $host_uuid }});
return("");
}
my $target_host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{short_host_name};
my $short_host_name = $anvil->Get->short_host_name({debug => $debug});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
target_host_name => $target_host_name,
short_host_name => $short_host_name,
}});
$anvil->Network->load_ips({
debug => $debug,
host_uuid => $anvil->Get->host_uuid,
host => $short_host_name,
clear => 1,
});
$anvil->Network->load_ips({
debug => $debug,
host_uuid => $host_uuid,
host => $target_host_name,
clear => 1,
});
my ($match) = $anvil->Network->find_matches({
debug => $debug,
first => $short_host_name,
second => $target_host_name,
});
if ($match)
{
# Yup!
my $match_found = 0;
foreach my $interface (sort {$a cmp $b} keys %{$match->{$target_host_name}})
{
$target_ip = $match->{$target_host_name}{$interface}{ip};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { target_ip => $target_ip }});
last;
}
}
return($target_ip);
}
=head2 load_interfces
This loads all network information for the given host UUID.
The main difference from C<< ->load_ips() >> is that this method loads information about all interfaces, regardless of if they have an IP, as well as their link state and link information.
The loaded data will be stored as:
* C<< machine::<target>::interface::<iface_name>::
Parameters;
=head3 clear (optional, default '1')
When set, any previously known information is cleared. Specifically, the C<< network::<target>> >> hash is deleted prior to the load. To prevent this, set this to C<< 0 >>.
=head3 host (optional, default is 'host_uuid' value)
This is the optional C<< target >> string to use in the hash where the data is stored.
=head3 host_uuid (optional, default 'sys::host_uuid')
This is the C<< host_uuid >> of the hosts whose IP and interface data that you want to load. The default is to load the local machine's data.
=cut
sub load_interfces
{
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 => "Network->load_interfces()" }});
my $clear = defined $parameter->{clear} ? $parameter->{clear} : 1;
my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : $anvil->data->{sys}{host_uuid};
my $host = defined $parameter->{host} ? $parameter->{host} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
clear => $clear,
host => $host,
host_uuid => $host_uuid,
}});
if (not $host_uuid)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->load_interfces()", parameter => "host_uuid" }});
return("");
}
if (not $host)
{
$host = $host_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }});
}
if (($clear) && (exists $anvil->data->{network}{$host}))
{
delete $anvil->data->{network}{$host};
}
# Now load bridge info
my $query = "
SELECT
bridge_uuid,
bridge_name,
bridge_id,
bridge_mac_address,
bridge_mtu,
bridge_stp_enabled
FROM
bridges
WHERE
bridge_id != 'DELETED'
AND
bridge_host_uuid = ".$anvil->Database->quote($host_uuid)."
;";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
my $bridge_uuid = defined $row->[0] ? $row->[0] : "";
my $bridge_name = defined $row->[1] ? $row->[1] : "";
my $bridge_id = defined $row->[2] ? $row->[2] : "";
my $bridge_mac_address = defined $row->[3] ? $row->[3] : "";
my $bridge_mtu = defined $row->[4] ? $row->[4] : "";
my $bridge_stp_enabled = defined $row->[5] ? $row->[5] : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
bridge_uuid => $bridge_uuid,
bridge_name => $bridge_name,
bridge_id => $bridge_id,
bridge_mac_address => $bridge_mac_address,
bridge_mtu => $bridge_mtu,
bridge_stp_enabled => $bridge_stp_enabled,
}});
# Record the bridge_uuid -> name
$anvil->data->{network}{$host}{bridge_uuid}{$bridge_uuid}{name} = $bridge_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::bridge_uuid::${bridge_uuid}::name" => $anvil->data->{network}{$host}{bridge_uuid}{$bridge_uuid}{name},
}});
# We'll initially load empty strings for what would be the IP information. Any interface with IPs will be populated when we call
$anvil->data->{network}{$host}{interface}{$bridge_name}{uuid} = $bridge_uuid;
$anvil->data->{network}{$host}{interface}{$bridge_name}{id} = $bridge_id;
$anvil->data->{network}{$host}{interface}{$bridge_name}{mac_address} = $bridge_mac_address;
$anvil->data->{network}{$host}{interface}{$bridge_name}{mtu} = $bridge_mtu;
$anvil->data->{network}{$host}{interface}{$bridge_name}{stp_enabled} = $bridge_stp_enabled;
$anvil->data->{network}{$host}{interface}{$bridge_name}{type} = "bridge";
$anvil->data->{network}{$host}{interface}{$bridge_name}{interfaces} = [];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${bridge_name}::uuid" => $anvil->data->{network}{$host}{interface}{$bridge_name}{uuid},
"network::${host}::interface::${bridge_name}::id" => $anvil->data->{network}{$host}{interface}{$bridge_name}{id},
"network::${host}::interface::${bridge_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$bridge_name}{mac_address},
"network::${host}::interface::${bridge_name}::mtu" => $anvil->data->{network}{$host}{interface}{$bridge_name}{mtu},
"network::${host}::interface::${bridge_name}::stp_enabled" => $anvil->data->{network}{$host}{interface}{$bridge_name}{stp_enabled},
"network::${host}::interface::${bridge_name}::type" => $anvil->data->{network}{$host}{interface}{$bridge_name}{type},
}});
}
# Now load bond info
$query = "
SELECT
bond_uuid,
bond_name,
bond_mode,
bond_mtu,
bond_primary_interface,
bond_primary_reselect,
bond_active_interface,
bond_mii_polling_interval,
bond_up_delay,
bond_down_delay,
bond_mac_address,
bond_operational,
bond_bridge_uuid
FROM
bonds WHERE bond_mode != 'DELETED'
AND
bond_host_uuid = ".$anvil->Database->quote($host_uuid)."
;";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }});
$results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
$count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
my $bond_uuid = defined $row->[0] ? $row->[0] : "";
my $bond_name = defined $row->[1] ? $row->[1] : "";
my $bond_mode = defined $row->[2] ? $row->[2] : "";
my $bond_mtu = defined $row->[3] ? $row->[3] : "";
my $bond_primary_interface = defined $row->[4] ? $row->[4] : "";
my $bond_primary_reselect = defined $row->[5] ? $row->[5] : "";
my $bond_active_interface = defined $row->[6] ? $row->[6] : "";
my $bond_mii_polling_interval = defined $row->[7] ? $row->[7] : "";
my $bond_up_delay = defined $row->[8] ? $row->[8] : "";
my $bond_down_delay = defined $row->[9] ? $row->[9] : "";
my $bond_mac_address = defined $row->[10] ? $row->[10] : "";
my $bond_operational = defined $row->[11] ? $row->[11] : "";
my $bond_bridge_uuid = defined $row->[12] ? $row->[12] : "";
my $bridge_name = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
bond_uuid => $bond_uuid,
bond_name => $bond_name,
bond_mode => $bond_mode,
bond_mtu => $bond_mtu,
bond_primary_interface => $bond_primary_interface,
bond_primary_reselect => $bond_primary_reselect,
bond_active_interface => $bond_active_interface,
bond_mii_polling_interval => $bond_mii_polling_interval,
bond_up_delay => $bond_up_delay,
bond_down_delay => $bond_down_delay,
bond_mac_address => $bond_mac_address,
bond_operational => $bond_operational,
bond_bridge_uuid => $bond_bridge_uuid,
}});
# If this bond is connected to a bridge, get the bridge name.
if (($bond_bridge_uuid) && (defined $anvil->data->{network}{$host}{bridge_uuid}{$bond_bridge_uuid}{name}))
{
$bridge_name = $anvil->data->{network}{$host}{bridge_uuid}{$bond_bridge_uuid}{name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
bond_bridge_uuid => $bond_bridge_uuid,
bridge_name => $bridge_name,
}});
push @{$anvil->data->{network}{$host}{interface}{$bridge_name}{interfaces}}, $bond_name;
}
# Record the bond_uuid -> name
$anvil->data->{network}{$host}{bond_uuid}{$bond_uuid}{name} = $bond_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::bond_uuid::${bond_uuid}::name" => $anvil->data->{network}{$host}{bond_uuid}{$bond_uuid}{name},
}});
# We'll initially load empty strings for what would be the IP information. Any interface with IPs will be populated when we call
$anvil->data->{network}{$host}{interface}{$bond_name}{uuid} = $bond_uuid;
$anvil->data->{network}{$host}{interface}{$bond_name}{mode} = $bond_mode;
$anvil->data->{network}{$host}{interface}{$bond_name}{mtu} = $bond_mtu;
$anvil->data->{network}{$host}{interface}{$bond_name}{primary_interface} = $bond_primary_interface;
$anvil->data->{network}{$host}{interface}{$bond_name}{primary_reselect} = $bond_primary_reselect;
$anvil->data->{network}{$host}{interface}{$bond_name}{active_interface} = $bond_active_interface;
$anvil->data->{network}{$host}{interface}{$bond_name}{mii_polling_interval} = $bond_mii_polling_interval;
$anvil->data->{network}{$host}{interface}{$bond_name}{up_delay} = $bond_up_delay;
$anvil->data->{network}{$host}{interface}{$bond_name}{down_delay} = $bond_down_delay;
$anvil->data->{network}{$host}{interface}{$bond_name}{mac_address} = $bond_mac_address;
$anvil->data->{network}{$host}{interface}{$bond_name}{operational} = $bond_operational;
$anvil->data->{network}{$host}{interface}{$bond_name}{bridge_uuid} = $bond_bridge_uuid;
$anvil->data->{network}{$host}{interface}{$bond_name}{type} = "bond";
$anvil->data->{network}{$host}{interface}{$bond_name}{interfaces} = [];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${bond_name}::uuid" => $anvil->data->{network}{$host}{interface}{$bond_name}{uuid},
"network::${host}::interface::${bond_name}::mode" => $anvil->data->{network}{$host}{interface}{$bond_name}{mode},
"network::${host}::interface::${bond_name}::mtu" => $anvil->data->{network}{$host}{interface}{$bond_name}{mtu},
"network::${host}::interface::${bond_name}::primary_interface" => $anvil->data->{network}{$host}{interface}{$bond_name}{primary_interface},
"network::${host}::interface::${bond_name}::primary_reselect" => $anvil->data->{network}{$host}{interface}{$bond_name}{primary_reselect},
"network::${host}::interface::${bond_name}::active_interface" => $anvil->data->{network}{$host}{interface}{$bond_name}{active_interface},
"network::${host}::interface::${bond_name}::mii_polling_interval" => $anvil->data->{network}{$host}{interface}{$bond_name}{mii_polling_interval},
"network::${host}::interface::${bond_name}::up_delay" => $anvil->data->{network}{$host}{interface}{$bond_name}{up_delay},
"network::${host}::interface::${bond_name}::down_delay" => $anvil->data->{network}{$host}{interface}{$bond_name}{down_delay},
"network::${host}::interface::${bond_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$bond_name}{mac_address},
"network::${host}::interface::${bond_name}::operational" => $anvil->data->{network}{$host}{interface}{$bond_name}{operational},
"network::${host}::interface::${bond_name}::bridge_uuid" => $anvil->data->{network}{$host}{interface}{$bond_name}{bridge},
"network::${host}::interface::${bond_name}::type" => $anvil->data->{network}{$host}{interface}{$bond_name}{type},
}});
}
# The order will allow us to show the order in which the interfaces were changed, which the user can
# use to track interfaces as they unplug and plug cables back in.
my $order = 1;
$query = "
SELECT
network_interface_uuid,
network_interface_mac_address,
network_interface_name,
network_interface_speed,
network_interface_mtu,
network_interface_link_state,
network_interface_operational,
network_interface_duplex,
network_interface_medium,
network_interface_bond_uuid,
network_interface_bridge_uuid
FROM
network_interfaces
WHERE
network_interface_operational != 'DELETED'
AND
network_interface_host_uuid = ".$anvil->Database->quote($host_uuid)."
ORDER BY
modified_date DESC
;";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }});
$results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
$count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results,
count => $count,
}});
my $changed_order = 1;
foreach my $row (@{$results})
{
my $network_interface_uuid = defined $row->[0] ? $row->[0] : "";
my $network_interface_mac_address = defined $row->[1] ? $row->[1] : "";
my $network_interface_name = defined $row->[2] ? $row->[2] : "";
my $network_interface_speed = defined $row->[3] ? $row->[3] : "";
my $network_interface_mtu = defined $row->[4] ? $row->[4] : "";
my $network_interface_link_state = defined $row->[5] ? $row->[5] : "";
my $network_interface_operational = defined $row->[6] ? $row->[6] : "";
my $network_interface_duplex = defined $row->[7] ? $row->[7] : "";
my $network_interface_medium = defined $row->[8] ? $row->[8] : "";
my $network_interface_bond_uuid = defined $row->[9] ? $row->[9] : "";
my $network_interface_bridge_uuid = defined $row->[10] ? $row->[10] : "";
my $bond_name = "";
my $bridge_name = "";
my $this_change_orger = 0;
if (($network_interface_name =~ /^virbr/) or ($network_interface_name =~ /^vnet/))
{
# This isn't a physical NIC, so it doesn't get a changed order
}
else
{
$this_change_orger = $changed_order++;
}
if (($network_interface_bond_uuid) && (defined $anvil->data->{network}{$host}{bond_uuid}{$network_interface_bond_uuid}{name}))
{
$bond_name = $anvil->data->{network}{$host}{bond_uuid}{$network_interface_bond_uuid}{name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
network_interface_name => $network_interface_name,
bond_name => $bond_name,
}});
push @{$anvil->data->{network}{$host}{interface}{$bond_name}{interfaces}}, $network_interface_name;
}
if (($network_interface_bridge_uuid) && (defined $anvil->data->{network}{$host}{bridge_uuid}{$network_interface_bridge_uuid}{name}))
{
$bridge_name = $anvil->data->{network}{$host}{bridge_uuid}{$network_interface_bridge_uuid}{name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
network_interface_name => $network_interface_name,
bridge_name => $bridge_name,
}});
push @{$anvil->data->{network}{$host}{interface}{$bridge_name}{interfaces}}, $network_interface_name;
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
network_interface_uuid => $network_interface_uuid,
network_interface_mac_address => $network_interface_mac_address,
network_interface_name => $network_interface_name,
network_interface_speed => $network_interface_speed,
network_interface_mtu => $network_interface_mtu,
network_interface_link_state => $network_interface_link_state,
network_interface_operational => $network_interface_operational,
network_interface_duplex => $network_interface_duplex,
network_interface_medium => $network_interface_medium,
network_interface_bond_uuid => $network_interface_bond_uuid,
network_interface_bridge_uuid => $network_interface_bridge_uuid,
bond_name => $bond_name,
changed_order => $this_change_orger,
}});
# We'll initially load empty strings for what would be the IP information. Any interface with IPs will be populated when we call
$anvil->data->{network}{$host}{interface}{$network_interface_name}{uuid} = $network_interface_uuid;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{mac_address} = $network_interface_mac_address;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{speed} = $network_interface_speed;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{mtu} = $network_interface_mtu;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{link_state} = $network_interface_link_state;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{operational} = $network_interface_operational;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{duplex} = $network_interface_duplex;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{medium} = $network_interface_medium;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{bond_uuid} = $network_interface_bond_uuid;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{bond_name} = $bond_name;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{bridge_uuid} = $network_interface_bridge_uuid;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{bridge_name} = $bridge_name;
$anvil->data->{network}{$host}{interface}{$network_interface_name}{type} = "interface";
$anvil->data->{network}{$host}{interface}{$network_interface_name}{changed_order} = $this_change_orger;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${network_interface_name}::uuid" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{uuid},
"network::${host}::interface::${network_interface_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{mac_address},
"network::${host}::interface::${network_interface_name}::speed" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{speed},
"network::${host}::interface::${network_interface_name}::mtu" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{mtu},
"network::${host}::interface::${network_interface_name}::link_state" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{link_state},
"network::${host}::interface::${network_interface_name}::operational" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{operational},
"network::${host}::interface::${network_interface_name}::duplex" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{duplex},
"network::${host}::interface::${network_interface_name}::medium" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{medium},
"network::${host}::interface::${network_interface_name}::bond_uuid" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{bond_uuid},
"network::${host}::interface::${network_interface_name}::bond_name" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{bond_name},
"network::${host}::interface::${network_interface_name}::bridge_uuid" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{bridge_uuid},
"network::${host}::interface::${network_interface_name}::bridge_name" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{bridge_name},
"network::${host}::interface::${network_interface_name}::type" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{type},
"network::${host}::interface::${network_interface_name}::changed_order" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{changed_order},
}});
}
# Load the IPs
$anvil->Network->load_ips({
debug => $debug,
host_uuid => $host_uuid,
host => $host,
clear => 0,
});
return(0);
}
=head2 load_ips
This method loads and stores the same data as the C<< get_ips >> method, but does so by loading data from the database, instead of collecting it directly from the host. As such, it can also be used by C<< find_matches >>.
C<< Note >>: IP addresses that have been deleted will be marked so by C<< ip >> being set to C<< DELETED >>.
The loaded data will be stored as:
* C<< network::<host>::interface::<iface_name>::ip >> - If an IP address is set
* C<< network::<host>::interface::<iface_name>::subnet_mask >> - If an IP is set
* C<< network::<host>::interface::<iface_name>::mac >> - Always set.
* C<< network::<host>::interface::<iface_name>::default_gateway >> = C<< 0 >> if not the default gateway, C<< 1 >> if so.
* C<< network::<host>::interface::<iface_name>::gateway >> = If the default gateway, this is the gateway IP address.
* C<< network::<host>::interface::<iface_name>::dns >> = If the default gateway, this is the comma-separated list of active DNS servers.
Parameters;
=head3 clear (optional, default '1')
When set, any previously known information is cleared. Specifically, the C<< network::<host>> >> hash is deleted prior to the load. To prevent this, set this to C<< 0 >>.
=head3 host (optional, default is 'host_uuid' value)
This is the optional C<< host >> string to use in the hash where the data is stored.
=head3 host_uuid (optional, default 'sys::host_uuid')
This is the C<< host_uuid >> of the hosts whose IP and interface data that you want to load. The default is to load the local machine's data.
=cut
sub load_ips
{
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 => "Network->load_ips()" }});
my $clear = defined $parameter->{clear} ? $parameter->{clear} : 1;
* 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
my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : $anvil->Get->host_uuid;
my $host = defined $parameter->{host} ? $parameter->{host} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
clear => $clear,
host => $host,
host_uuid => $host_uuid,
}});
if (not $host_uuid)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->load_ips()", parameter => "ip" }});
return("");
}
if (not $host)
{
$host = $host_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }});
}
if (($clear) && (exists $anvil->data->{network}{$host}))
{
delete $anvil->data->{network}{$host};
}
# Read in all IPs, so that we know which to remove.
my $query = "
SELECT
ip_address_address,
ip_address_subnet_mask,
ip_address_gateway,
ip_address_default_gateway,
ip_address_dns,
ip_address_on_type,
ip_address_on_uuid
FROM
ip_addresses
WHERE
ip_address_host_uuid = ".$anvil->Database->quote($host_uuid)."
AND
ip_address_note != 'DELETED'
;";
$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,
}});
foreach my $row (@{$results})
{
my $ip_address_address = $row->[0];
my $ip_address_subnet_mask = $row->[1];
my $ip_address_gateway = $row->[2];
my $ip_address_default_gateway = $row->[3];
my $ip_address_dns = $row->[4];
my $ip_address_on_type = $row->[5];
my $ip_address_on_uuid = $row->[6];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
ip_address_address => $ip_address_address,
ip_address_subnet_mask => $ip_address_subnet_mask,
ip_address_gateway => $ip_address_gateway,
ip_address_default_gateway => $ip_address_default_gateway,
ip_address_dns => $ip_address_dns,
ip_address_on_type => $ip_address_on_type,
ip_address_on_uuid => $ip_address_on_uuid,
}});
my $interface_name = "";
my $interface_mac = "";
if ($ip_address_on_type eq "interface")
{
my $query = "
SELECT
network_interface_name,
network_interface_mac_address
FROM
network_interfaces
WHERE
network_interface_uuid = ".$anvil->Database->quote($ip_address_on_uuid)."
AND
network_interface_operational != 'DELETED'
;";
$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,
}});
next if not $count;
$interface_name = $results->[0]->[0];
$interface_mac = $results->[0]->[1];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
interface_name => $interface_name,
interface_mac => $interface_mac,
}});
$anvil->data->{network}{$host}{interface}{$interface_name}{mac_address} = $interface_mac;
$anvil->data->{network}{$host}{interface}{$interface_name}{ip} = $ip_address_address;
$anvil->data->{network}{$host}{interface}{$interface_name}{subnet_mask} = $ip_address_subnet_mask;
$anvil->data->{network}{$host}{interface}{$interface_name}{default_gateway} = $ip_address_default_gateway;
$anvil->data->{network}{$host}{interface}{$interface_name}{gateway} = $ip_address_gateway;
$anvil->data->{network}{$host}{interface}{$interface_name}{dns} = $ip_address_dns;
$anvil->data->{network}{$host}{interface}{$interface_name}{type} = $ip_address_on_type;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${interface_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$interface_name}{mac_address},
"network::${host}::interface::${interface_name}::ip" => $anvil->data->{network}{$host}{interface}{$interface_name}{ip},
"network::${host}::interface::${interface_name}::subnet_mask" => $anvil->data->{network}{$host}{interface}{$interface_name}{subnet_mask},
"network::${host}::interface::${interface_name}::default_gateway" => $anvil->data->{network}{$host}{interface}{$interface_name}{default_gateway},
"network::${host}::interface::${interface_name}::gateway" => $anvil->data->{network}{$host}{interface}{$interface_name}{gateway},
"network::${host}::interface::${interface_name}::dns" => $anvil->data->{network}{$host}{interface}{$interface_name}{dns},
"network::${host}::interface::${interface_name}::type" => $anvil->data->{network}{$host}{interface}{$interface_name}{type},
}});
}
elsif ($ip_address_on_type eq "bond")
{
my $query = "
SELECT
bond_name,
bond_mac_address
FROM
bonds
WHERE
bond_uuid = ".$anvil->Database->quote($ip_address_on_uuid)."
AND
bond_operational != 'DELETED'
;";
$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,
}});
next if not $count;
$interface_name = $results->[0]->[0];
$interface_mac = $results->[0]->[1];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
interface_name => $interface_name,
interface_mac => $interface_mac,
}});
$anvil->data->{network}{$host}{interface}{$interface_name}{mac_address} = $interface_mac;
$anvil->data->{network}{$host}{interface}{$interface_name}{ip} = $ip_address_address;
$anvil->data->{network}{$host}{interface}{$interface_name}{subnet_mask} = $ip_address_subnet_mask;
$anvil->data->{network}{$host}{interface}{$interface_name}{default_gateway} = $ip_address_default_gateway;
$anvil->data->{network}{$host}{interface}{$interface_name}{gateway} = $ip_address_gateway;
$anvil->data->{network}{$host}{interface}{$interface_name}{dns} = $ip_address_dns;
$anvil->data->{network}{$host}{interface}{$interface_name}{type} = $ip_address_on_type;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${interface_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$interface_name}{mac_address},
"network::${host}::interface::${interface_name}::ip" => $anvil->data->{network}{$host}{interface}{$interface_name}{ip},
"network::${host}::interface::${interface_name}::subnet_mask" => $anvil->data->{network}{$host}{interface}{$interface_name}{subnet_mask},
"network::${host}::interface::${interface_name}::default_gateway" => $anvil->data->{network}{$host}{interface}{$interface_name}{default_gateway},
"network::${host}::interface::${interface_name}::gateway" => $anvil->data->{network}{$host}{interface}{$interface_name}{gateway},
"network::${host}::interface::${interface_name}::dns" => $anvil->data->{network}{$host}{interface}{$interface_name}{dns},
"network::${host}::interface::${interface_name}::type" => $anvil->data->{network}{$host}{interface}{$interface_name}{type},
}});
}
elsif ($ip_address_on_type eq "bridge")
{
my $query = "
SELECT
bridge_name,
bridge_mac_address
FROM
bridges
WHERE
bridge_uuid = ".$anvil->Database->quote($ip_address_on_uuid)."
AND
bridge_id != 'DELETED'
;";
$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,
}});
next if not $count;
$interface_name = $results->[0]->[0];
$interface_mac = $results->[0]->[1];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
interface_name => $interface_name,
interface_mac => $interface_mac,
}});
$anvil->data->{network}{$host}{interface}{$interface_name}{mac_address} = $interface_mac;
$anvil->data->{network}{$host}{interface}{$interface_name}{ip} = $ip_address_address;
$anvil->data->{network}{$host}{interface}{$interface_name}{subnet_mask} = $ip_address_subnet_mask;
$anvil->data->{network}{$host}{interface}{$interface_name}{default_gateway} = $ip_address_default_gateway;
$anvil->data->{network}{$host}{interface}{$interface_name}{gateway} = $ip_address_gateway;
$anvil->data->{network}{$host}{interface}{$interface_name}{dns} = $ip_address_dns;
$anvil->data->{network}{$host}{interface}{$interface_name}{type} = $ip_address_on_type;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${interface_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$interface_name}{mac_address},
"network::${host}::interface::${interface_name}::ip" => $anvil->data->{network}{$host}{interface}{$interface_name}{ip},
"network::${host}::interface::${interface_name}::subnet_mask" => $anvil->data->{network}{$host}{interface}{$interface_name}{subnet_mask},
"network::${host}::interface::${interface_name}::default_gateway" => $anvil->data->{network}{$host}{interface}{$interface_name}{default_gateway},
"network::${host}::interface::${interface_name}::gateway" => $anvil->data->{network}{$host}{interface}{$interface_name}{gateway},
"network::${host}::interface::${interface_name}::dns" => $anvil->data->{network}{$host}{interface}{$interface_name}{dns},
"network::${host}::interface::${interface_name}::type" => $anvil->data->{network}{$host}{interface}{$interface_name}{type},
}});
}
}
return(0);
}
=head2 get_company_from_mac
This takes a MAC address (or the first six bytes) and returns the company that owns the OUI. If the company name is not found, an expty string is returned.
Parameters;
=head3 mac (required)
This is the first six bytes of the mac address, C<< xx:xx:xx >> format, being searched for.
=cut
sub get_company_from_mac
{
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 => "Network->get_company_from_mac()" }});
my $mac = defined $parameter->{mac} ? lc($parameter->{mac}) : "";
my $company = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
mac => $mac,
}});
if (not $mac)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->get_company_from_mac()", parameter => "mac_prefix" }});
return("");
}
# Have I already looked this one up?
if ($anvil->data->{cache}{mac_to_oui}{$mac})
{
# Yup, no need to process.
return($anvil->data->{cache}{mac_to_oui}{$mac});
}
my $valid_mac = $anvil->Validate->mac({mac => $mac});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { mac => $mac }});
if ($valid_mac)
{
# Strip the first six bytes.
$mac = ($mac =~ /^([0-9a-f]{2}[:-][0-9a-f]{2}[:-][0-9a-f]{2})/i)[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { mac => $mac }});
}
elsif ($mac !~ /[0-9a-f]{2}[:-][0-9a-f]{2}[:-][0-9a-f]{2}/i)
{
# Bad format
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0104", variables => { mac => $mac }});
return("");
}
my $query = "SELECT oui_company_name FROM oui WHERE oui_mac_prefix = ".$anvil->Database->quote(lc($mac)).";";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results,
count => $count,
}});
if ($count)
{
$company = $results->[0]->[0];
$anvil->data->{cache}{mac_to_oui}{$mac} = $company;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { company => $company }});
}
return($company);
}
=head2 get_ips
This method checks the local system for interfaces and stores them in:
* C<< network::<target>::interface::<iface_name>::ip >> - If an IP address is set
* C<< network::<target>::interface::<iface_name>::subnet_mask >> - If an IP is set
* C<< network::<target>::interface::<iface_name>::mac_address >> - Always set.
* C<< network::<target>::interface::<iface_name>::mtu >> - Always set.
* C<< network::<target>::interface::<iface_name>::default_gateway >> = C<< 0 >> if not the default gateway, C<< 1 >> if so.
* C<< network::<target>::interface::<iface_name>::gateway >> = If the default gateway, this is the gateway IP address.
* C<< network::<target>::interface::<iface_name>::dns >> = If the default gateway, this is the comma-separated list of active DNS servers.
To make it convenient to translate a MAC address into interface names, this hash is also stored;
* C<< network::<target>::mac_address::<mac_address>::interface = Interface name.
When called without a C<< target >>, C<< local >> is used.
To aid in look-up by MAC address, C<< network::mac_address::<mac_address>::iface >> is also set. Note that this is not target-dependent.
Parameters;
=head3 password (optional)
If C<< target >> is set, this is the password used to log into the remote system as the C<< remote_user >>. If it is not set, an attempt to connect without a password will be made (though this will usually fail).
=head3 port (optional, default 22)
If C<< target >> is set, this is the TCP port number used to connect to the remote machine.
=head3 remote_user (optional)
If C<< target >> is set, this is the user account that will be used when connecting to the remote system.
=head3 target (optional)
If set, the file will be read from the target machine. This must be either an IP address or a resolvable host name.
The file will be copied to the local system using C<< $anvil->Storage->rsync() >> and stored in C<< /tmp/<file_path_and_name>.<target> >>. if C<< cache >> is set, the file will be preserved locally. Otherwise it will be deleted once it has been read into memory.
B<< Note >>: the temporary file will be prefixed with the path to the file name, with the C<< / >> converted to C<< _ >>.
=cut
sub get_ips
{
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 => "Network->get_ips()" }});
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : 22;
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
password => $anvil->Log->is_secure($password),
port => $port,
remote_user => $remote_user,
target => $target,
}});
# This is used in the hash reference when storing the data.
my $host = $target ? $target : $anvil->Get->short_host_name();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }});
if (exists $anvil->data->{network}{$host})
{
delete $anvil->data->{network}{$host};
}
# Reading locally or remote?
my $in_iface = "";
my $shell_call = $anvil->data->{path}{exe}{ip}." addr list";
my $output = "";
my $is_local = $anvil->Network->is_local({host => $target});
if ($is_local)
{
# Local call.
($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:return_code' => $return_code,
}});
}
else
{
# Remote call
($output, my $error, my $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:error' => $error,
's3:return_code' => $return_code,
}});
}
foreach my $line (split/\n/, $output)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
if ($line =~ /^\d+: (.*?): /)
{
$in_iface = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { in_iface => $in_iface }});
$anvil->data->{network}{$host}{interface}{$in_iface}{ip} = "" if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{ip};
$anvil->data->{network}{$host}{interface}{$in_iface}{subnet_mask} = "" if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{subnet_mask};
$anvil->data->{network}{$host}{interface}{$in_iface}{mac_address} = "" if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{mac_address};
$anvil->data->{network}{$host}{interface}{$in_iface}{mtu} = 0 if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{mtu};
$anvil->data->{network}{$host}{interface}{$in_iface}{default_gateway} = 0 if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{default_gateway};
$anvil->data->{network}{$host}{interface}{$in_iface}{gateway} = "" if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{gateway};
$anvil->data->{network}{$host}{interface}{$in_iface}{dns} = "" if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{dns};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${in_iface}::ip" => $anvil->data->{network}{$host}{interface}{$in_iface}{ip},
"network::${host}::interface::${in_iface}::subnet_mask" => $anvil->data->{network}{$host}{interface}{$in_iface}{subnet_mask},
"network::${host}::interface::${in_iface}::mac_address" => $anvil->data->{network}{$host}{interface}{$in_iface}{mac_address},
"network::${host}::interface::${in_iface}::mtu" => $anvil->data->{network}{$host}{interface}{$in_iface}{mtu},
"network::${host}::interface::${in_iface}::default_gateway" => $anvil->data->{network}{$host}{interface}{$in_iface}{default_gateway},
"network::${host}::interface::${in_iface}::gateway" => $anvil->data->{network}{$host}{interface}{$in_iface}{gateway},
"network::${host}::interface::${in_iface}::dns" => $anvil->data->{network}{$host}{interface}{$in_iface}{dns},
}});
}
next if not $in_iface;
if ($in_iface eq "lo")
{
# We don't care about 'lo'.
delete $anvil->data->{network}{$host}{interface}{$in_iface};
next;
}
if ($line =~ /inet (.*?)\/(.*?) /)
{
my $ip = $1;
my $cidr = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ip => $ip, cidr => $cidr }});
my $subnet_mask = $cidr;
if (($cidr =~ /^\d{1,2}$/) && ($cidr >= 0) && ($cidr <= 32))
{
# Convert to subnet mask
$subnet_mask = $anvil->Convert->cidr({cidr => $cidr});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { subnet_mask => $subnet_mask }});
}
$anvil->data->{network}{$host}{interface}{$in_iface}{ip} = $ip;
$anvil->data->{network}{$host}{interface}{$in_iface}{subnet_mask} = $subnet_mask;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:network::${host}::interface::${in_iface}::ip" => $anvil->data->{network}{$host}{interface}{$in_iface}{ip},
"s2:network::${host}::interface::${in_iface}::subnet_mask" => $anvil->data->{network}{$host}{interface}{$in_iface}{subnet_mask},
}});
}
if ($line =~ /ether (.*?) /i)
{
my $mac_address = $1;
$anvil->data->{network}{$host}{interface}{$in_iface}{mac_address} = $mac_address;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${in_iface}::mtu" => $anvil->data->{network}{$host}{interface}{$in_iface}{mtu},
}});
# Make it easy to look up an interface name based on a given MAC address.
$anvil->data->{network}{$host}{mac_address}{$mac_address}{interface} = $in_iface;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::mac_address::${mac_address}::interface" => $anvil->data->{network}{$host}{mac_address}{$mac_address}{interface},
}});
}
if ($line =~ /mtu (\d+) /i)
{
my $mtu = $1;
$anvil->data->{network}{$host}{interface}{$in_iface}{mtu} = $mtu;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${in_iface}::mtu" => $anvil->data->{network}{$host}{interface}{$in_iface}{mtu},
}});
}
}
# Read the config files for the interfaces we've found. Use 'ls' to find the interface files. Then
# we'll read them all in.
$shell_call = $anvil->data->{path}{exe}{ls}." ".$anvil->data->{path}{directories}{ifcfg};
$output = "";
if ($is_local)
{
# Local call.
($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:return_code' => $return_code,
}});
}
else
{
# Remote call
($output, my $error, my $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:error' => $error,
's3:return_code' => $return_code,
}});
}
foreach my $line (split/\n/, $output)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
next if $line !~ /^ifcfg-/;
my $full_path = $anvil->data->{path}{directories}{ifcfg}."/".$line;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { full_path => $full_path }});
my $file_body = $anvil->Storage->read_file({
debug => $debug,
file => $full_path,
target => $target,
password => $password,
port => $port,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:full_path" => $full_path,
"s2:file_body" => $file_body,
}});
# Break it apart and store any variables.
my $temp = {};
my $interface = "";
foreach my $line (split/\n/, $file_body)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
next if $line =~ /^#/;
if ($line =~ /(.*?)=(.*)/)
{
my $variable = $1;
my $value = $2;
$value =~ s/^"(.*)"$/$1/;
$temp->{$variable} = $value;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "temp->{$variable}" => $temp->{$variable} }});
if (uc($variable) eq "DEVICE")
{
# If this isn't a device we saw in 'ip addr', skip it by just not setting the interface variable
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { value => $value }});
last if not exists $anvil->data->{network}{$host}{interface}{$value};
$interface = $value;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { interface => $interface }});
}
}
if ($interface)
{
$anvil->data->{network}{$host}{interface}{$interface}{file} = $full_path;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${interface}::file" => $anvil->data->{network}{$host}{interface}{$interface}{file},
}});
foreach my $variable (sort {$a cmp $b} keys %{$temp})
{
$anvil->data->{network}{$host}{interface}{$interface}{variable}{$variable} = $temp->{$variable};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${interface}::file::variable::${variable}" => $anvil->data->{network}{$host}{interface}{$interface}{variable}{$variable},
}});
}
}
}
}
# Get the routing info.
my $lowest_metric = 99999999;
my $route_interface = "";
my $route_ip = "";
$shell_call = $anvil->data->{path}{exe}{ip}." route show";
$output = "";
if ($is_local)
{
# Local call.
($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:return_code' => $return_code,
}});
}
else
{
# Remote call
($output, my $error, my $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:error' => $error,
's3:return_code' => $return_code,
}});
}
foreach my $line (split/\n/, $output)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
if ($line =~ /default via (.*?) dev (.*?) proto .*? metric (\d+)/i)
{
my $this_ip = $1;
my $this_interface = $2;
my $metric = $3;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:this_ip' => $this_ip,
's2:this_interface' => $this_interface,
's3:metric' => $metric,
's4:lowest_metric' => $lowest_metric,
}});
if ($metric < $lowest_metric)
{
$lowest_metric = $metric;
$route_interface = $this_interface;
$route_ip = $this_ip;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
lowest_metric => $lowest_metric,
route_interface => $route_interface,
route_ip => $route_ip,
}});
}
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
route_interface => $route_interface,
route_ip => $route_ip,
}});
# If I got a route, get the DNS.
if ($route_interface)
{
# I want to build the DNS list from only the interface that is used for routing.
my $in_interface = "";
my $dns_list = "";
my $dns_hash = {};
my $shell_call = $anvil->data->{path}{exe}{nmcli}." dev show";
my $output = "";
if ($is_local)
{
# Local call.
($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:return_code' => $return_code,
}});
}
else
{
# Remote call
($output, my $error, my $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:error' => $error,
's3:return_code' => $return_code,
}});
}
foreach my $line (split/\n/, $output)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
if ($line =~ /GENERAL.DEVICE:\s+(.*)$/)
{
$in_interface = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { in_interface => $in_interface }});
}
if (not $line)
{
$in_interface = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { in_interface => $in_interface }});
}
next if $in_interface ne $route_interface;
if ($line =~ /IP4.DNS\[(\d+)\]:\s+(.*)/i)
{
my $order = $1;
my $ip = $2;
$dns_hash->{$order} = $ip;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "dns_hash->{$order}" => $dns_hash->{$order} }});
}
}
foreach my $order (sort {$a cmp $b} keys %{$dns_hash})
{
$dns_list .= $dns_hash->{$order}.", ";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:dns_hash->{$order}" => $dns_hash->{$order},
"s2:dns_list" => $dns_list,
}});
}
$dns_list =~ s/, $//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { dns_list => $dns_list }});
$anvil->data->{network}{$host}{interface}{$route_interface}{default_gateway} = 1;
$anvil->data->{network}{$host}{interface}{$route_interface}{gateway} = $route_ip;
$anvil->data->{network}{$host}{interface}{$route_interface}{dns} = $dns_list;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${route_interface}::default_gateway" => $anvil->data->{network}{$host}{interface}{$route_interface}{default_gateway},
"network::${host}::interface::${route_interface}::gateway" => $anvil->data->{network}{$host}{interface}{$route_interface}{gateway},
"network::${host}::interface::${route_interface}::dns" => $anvil->data->{network}{$host}{interface}{$route_interface}{dns},
}});
}
return(0);
}
=head2 get_network
This takes an IP address and subnet and returns the network it belongs too. For example;
my $network = $anvil->Network->get_network({ip => "10.2.4.1", subnet_mask => "255.255.0.0"});
This would set C<< $network >> to C<< 10.2.0.0 >>.
If the network can't be caluclated for any reason, and empty string will be returned.
Parameters;
=head3 ip (required)
This is the IPv4 IP address being calculated.
=head3 subnet_mask (required)
This is the subnet mask of the IP address being calculated.
=cut
sub get_network
{
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 => "Network->get_network()" }});
my $network = "";
my $ip = defined $parameter->{ip} ? $parameter->{ip} : "";
my $subnet_mask = defined $parameter->{subnet_mask} ? $parameter->{subnet_mask} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
ip => $ip,
subnet_mask => $subnet_mask,
}});
if (not $ip)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->get_network()", parameter => "ip" }});
return("");
}
if (not $subnet_mask)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->get_network()", parameter => "subnet_mask" }});
return("");
}
my $block = Net::Netmask->new($ip."/".$subnet_mask);
my $base = $block->base();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { base => $base }});
if ($anvil->Validate->ipv4({ip => $base}))
{
$network = $base;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { network => $network }});
}
return($network);
}
=head2 is_local
This method takes a host name or IP address and looks to see if it matches the local system. If it does, it returns C<< 1 >>. Otherwise it returns C<< 0 >>.
Parameters;
=head3 host (required)
This is the host name (or IP address) to check against the local system.
=cut
### NOTE: Do not log in here, it will cause a recursive loop!
sub is_local
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $host = $parameter->{host} ? $parameter->{host} : "";
return(1) if not $host;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
host => $host,
}});
# If we've checked this host before, return the cached answer
if (exists $anvil->data->{cache}{is_local}{$host})
{
return($anvil->data->{cache}{is_local}{$host});
}
$anvil->data->{cache}{is_local}{$host} = 0;
if (($host eq $anvil->Get->host_name) or
($host eq $anvil->Get->short_host_name) or
($host eq "localhost") or
($host eq "127.0.0.1"))
{
# It's local
$anvil->data->{cache}{is_local}{$host} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "cache::is_local::${host}" => $anvil->data->{cache}{is_local}{$host} }});
}
else
{
# Get the list of current IPs and see if they match.
my $local_host = $anvil->Get->short_host_name();
if (not exists $anvil->data->{network}{$local_host}{interface})
{
$anvil->Network->get_ips({debug => 9999});
}
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$local_host}{interface}})
{
#$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "network::local::interface::${interface}::ip" => $anvil->data->{network}{$local_host}{interface}{$interface}{ip} }});
if ($host eq $anvil->data->{network}{$local_host}{interface}{$interface}{ip})
{
$anvil->data->{cache}{is_local}{$host} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "cache::is_local::${host}" => $anvil->data->{cache}{is_local}{$host} }});
last;
}
}
}
#$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
return($anvil->data->{cache}{is_local}{$host});
}
=head2 is_ip_in_network
This takes an IP address, along with network and subnet mask and sees if the IP address is within the network. If it is, it returns C<< 1 >>. If the IP address doesn't match the network, C<< 0 >> is returned.
Parameters
=head3 ip (required)
This is the ip IP address being analyzed.
=head3 network (required)
This is the IP address that will be paired with the subnet mask to see if the ip matches.
=head3 subnet_mask (required)
This is the subnet mask paired against the IP address used to check the ip against.
=cut
sub is_ip_in_network
{
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 => "Network->is_ip_in_network()" }});
my $ip = defined $parameter->{ip} ? $parameter->{ip} : "";
my $network = defined $parameter->{network} ? $parameter->{network} : "";
my $subnet_mask = defined $parameter->{subnet_mask} ? $parameter->{subnet_mask} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
ip => $ip,
network => $network,
subnet_mask => $subnet_mask,
}});
if (not $network)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->is_ip_in_network()", parameter => "network" }});
return(0);
}
elsif (not $anvil->Validate->ipv4({ip => $network}))
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "warning_0019", variables => { parameter => "network", network => $network }});
return(0);
}
if (not $ip)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->is_ip_in_network()", parameter => "ip" }});
return(0);
}
elsif (not $anvil->Validate->ipv4({ip => $ip}))
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "warning_0019", variables => { parameter => "ip", network => $ip }});
return(0);
}
if (not $subnet_mask)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->is_ip_in_network()", parameter => "subnet_mask" }});
return(0);
}
elsif (not $anvil->Validate->subnet_mask({subnet_mask => $subnet_mask}))
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "warning_0020", variables => { parameter => "subnet_mask", subnet_mask => $subnet_mask }});
return(0);
}
my $match = 0;
my $block = Net::Netmask->new($network."/".$subnet_mask);
if ($block->match($ip))
{
# This is a match!
$match = 1;
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { match => $match }});
return($match);
}
=head2 ping
This method will attempt to ping a target, by host name or IP, and returns C<< 1 >> if successful, and C<< 0 >> if not.
Example;
# Test access to the internet. Allow for three attempts to account for network jitter.
* Created Convert->fence_ipmilan_to_ipmitool() that takes a 'fence_ipmilan' call and converts it into a direct 'ipmitool' call. * Created Database->get_power() that loads data from the special 'power' table. * Fixed a bug in calls to Network->ping() where some weren't formatted properly for receiving two string variables. * Updated Database->get_anvils() to record the machine types when recording host information. * Updated Database->get_hosts_info() to also load the 'host_ipmi' column. * Updated Database->get_upses() to store the link to the 'power' -> 'power_uuid', when available. * Created ScanCore->call_scan_agents() that does the work of actually calling scan agents, moving the logic out from the scancore daemon. * Created ScanCore->check_power() that takes a host and the anvil it is in and returns if it's on batteries or not. If it is, the time on batteries and estimate hold-up time is returned. If not, the highest charge percentage is returned. * Created ScanCore->post_scan_analysis() that is a wrapper for calling the new ->post_scan_analysis_dr(), ->post_scan_analysis_node() and ->post_scan_analysis_striker(). Of which, _dr and _node are still empty, but _striker is complete. ** ->post_scan_analysis_striker() is complete. It now boots a node after a power loss if the UPSes powering it are OK (at least one has mains power, and the main-powered UPS(es) have reached the minimum charge percentage). If it's thermal, IPMI is called and so long as at least one thermal sensor is found and it/they are all OK, it is booted. For now, M2's thermal reboot delay logic hasn't been replicated, as it added a lot of complexity and didn't prove practically useful. * Created System->collect_ipmi_data() and moved 'scan_ipmitool's ipmitool call and parse into that method. This was done to allow ScanCore->post_scan_analysis_striker() to also call IPMI on a remote machine during thermal down events without reimplementing the logic. * Updated scan-ipmitool to only record temperature data for data collected locally. Also renamed 'machine' variables and hash keys to 'host_name' to clarify what is being stored. * Updated scancore to clear the 'system::stop_reason' variable. * Added missing packages to striker-manage-install-target. Signed-off-by: Digimer <digimer@alteeve.ca>
4 years ago
my ($pinged, $average_time) = $anvil->Network->ping({
ping => "google.ca",
count => 3,
});
# Test 9000-byte jumbo-frame access to a target over the BCN.
* Created Convert->fence_ipmilan_to_ipmitool() that takes a 'fence_ipmilan' call and converts it into a direct 'ipmitool' call. * Created Database->get_power() that loads data from the special 'power' table. * Fixed a bug in calls to Network->ping() where some weren't formatted properly for receiving two string variables. * Updated Database->get_anvils() to record the machine types when recording host information. * Updated Database->get_hosts_info() to also load the 'host_ipmi' column. * Updated Database->get_upses() to store the link to the 'power' -> 'power_uuid', when available. * Created ScanCore->call_scan_agents() that does the work of actually calling scan agents, moving the logic out from the scancore daemon. * Created ScanCore->check_power() that takes a host and the anvil it is in and returns if it's on batteries or not. If it is, the time on batteries and estimate hold-up time is returned. If not, the highest charge percentage is returned. * Created ScanCore->post_scan_analysis() that is a wrapper for calling the new ->post_scan_analysis_dr(), ->post_scan_analysis_node() and ->post_scan_analysis_striker(). Of which, _dr and _node are still empty, but _striker is complete. ** ->post_scan_analysis_striker() is complete. It now boots a node after a power loss if the UPSes powering it are OK (at least one has mains power, and the main-powered UPS(es) have reached the minimum charge percentage). If it's thermal, IPMI is called and so long as at least one thermal sensor is found and it/they are all OK, it is booted. For now, M2's thermal reboot delay logic hasn't been replicated, as it added a lot of complexity and didn't prove practically useful. * Created System->collect_ipmi_data() and moved 'scan_ipmitool's ipmitool call and parse into that method. This was done to allow ScanCore->post_scan_analysis_striker() to also call IPMI on a remote machine during thermal down events without reimplementing the logic. * Updated scan-ipmitool to only record temperature data for data collected locally. Also renamed 'machine' variables and hash keys to 'host_name' to clarify what is being stored. * Updated scancore to clear the 'system::stop_reason' variable. * Added missing packages to striker-manage-install-target. Signed-off-by: Digimer <digimer@alteeve.ca>
4 years ago
my ($jumbo_to_peer, $average_time) = $anvil->Network->ping({
ping => "an-a01n02.bcn",
count => 1,
payload => 9000,
fragment => 0,
});
# Check to see if an Anvil! node has internet access
* Created Convert->fence_ipmilan_to_ipmitool() that takes a 'fence_ipmilan' call and converts it into a direct 'ipmitool' call. * Created Database->get_power() that loads data from the special 'power' table. * Fixed a bug in calls to Network->ping() where some weren't formatted properly for receiving two string variables. * Updated Database->get_anvils() to record the machine types when recording host information. * Updated Database->get_hosts_info() to also load the 'host_ipmi' column. * Updated Database->get_upses() to store the link to the 'power' -> 'power_uuid', when available. * Created ScanCore->call_scan_agents() that does the work of actually calling scan agents, moving the logic out from the scancore daemon. * Created ScanCore->check_power() that takes a host and the anvil it is in and returns if it's on batteries or not. If it is, the time on batteries and estimate hold-up time is returned. If not, the highest charge percentage is returned. * Created ScanCore->post_scan_analysis() that is a wrapper for calling the new ->post_scan_analysis_dr(), ->post_scan_analysis_node() and ->post_scan_analysis_striker(). Of which, _dr and _node are still empty, but _striker is complete. ** ->post_scan_analysis_striker() is complete. It now boots a node after a power loss if the UPSes powering it are OK (at least one has mains power, and the main-powered UPS(es) have reached the minimum charge percentage). If it's thermal, IPMI is called and so long as at least one thermal sensor is found and it/they are all OK, it is booted. For now, M2's thermal reboot delay logic hasn't been replicated, as it added a lot of complexity and didn't prove practically useful. * Created System->collect_ipmi_data() and moved 'scan_ipmitool's ipmitool call and parse into that method. This was done to allow ScanCore->post_scan_analysis_striker() to also call IPMI on a remote machine during thermal down events without reimplementing the logic. * Updated scan-ipmitool to only record temperature data for data collected locally. Also renamed 'machine' variables and hash keys to 'host_name' to clarify what is being stored. * Updated scancore to clear the 'system::stop_reason' variable. * Added missing packages to striker-manage-install-target. Signed-off-by: Digimer <digimer@alteeve.ca>
4 years ago
my ($pinged, $average_time) = $anvil->Network->ping({
target => "an-a01n01.alteeve.com",
port => 22,
password => "super secret",
remote_user => "admin",
ping => "google.ca",
count => 3,
});
Parameters;
=head3 count (optional, default '1')
This tells the method how many time to try to ping the target. The method will return as soon as any ping attemp succeeds (unlike pinging from the command line, which always pings the requested count times).
=head3 debug (optional, default '3')
This is an optional way to alter to level at which this method is logged. Useful when the caller is trying to debug a problem. Generally this can be ignored.
=head3 fragment (optional, default '1')
When set to C<< 0 >>, the ping will fail if the packet has to be fragmented. This is meant to be used along side C<< payload >> for testing MTU sizes.
=head3 password (optional)
This is the password used to access a remote machine. This is used when pinging from a remote machine to a given ping target.
=head3 payload (optional)
This can be used to force the ping packet size to a larger number of bytes. It is most often used along side C<< fragment => 0 >> as a way to test if jumbo frames are working as expected.
B<NOTE>: The payload will have 28 bytes removed to account for ICMP overhead. So if you want to test an MTU of '9000', specify '9000' here. You do not need to account for the ICMP overhead yourself.
=head3 port (optional, default '22')
This is the port used to access a remote machine. This is used when pinging from a remote machine to a given ping target.
B<NOTE>: See C<< Remote->call >> for additional information on specifying the SSH port as part of the target.
=head3 remote_user (optional, default root)
If C<< target >> is set, this is the user we will use to log into the remote machine to run the actual ping.
=head3 target (optional)
This is the host name or IP address of a remote machine that you want to run the ping on. This is used to test a remote machine's access to a given ping target.
=head3 timeout (optional, default '1')
This is how long we will wait for a ping to return, in seconds. Any real number is allowed (C<< 1 >> (one second), C<< 0.25 >> (1/4 second), etc). If set to C<< 0 >>, we will wait for the ping command to exit without limit.
=cut
sub ping
{
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 => "Network->ping()" }});
# my $start_time = [gettimeofday];
# print "Start time: [".$start_time->[0].".".$start_time->[1]."]\n";
#
# my $ping_time = tv_interval ($start_time, [gettimeofday]);
# print "[".$ping_time."] - Pinged: [$host]\n";
# If we were passed a target, try pinging from it instead of locally
my $count = defined $parameter->{count} ? $parameter->{count} : 1; # How many times to try to ping it? Will exit as soon as one succeeds
my $fragment = defined $parameter->{fragment} ? $parameter->{fragment} : 1; # Allow fragmented packets? Set to '0' to check MTU.
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $payload = defined $parameter->{payload} ? $parameter->{payload} : 0; # The size of the ping payload. Use when checking MTU.
my $ping = defined $parameter->{ping} ? $parameter->{ping} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $timeout = defined $parameter->{timeout} ? $parameter->{timeout} : 1; # This sets the 'timeout' delay.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
count => $count,
fragment => $fragment,
payload => $payload,
password => $anvil->Log->is_secure($password),
ping => $ping,
port => $port,
remote_user => $remote_user,
target => $target,
}});
# Was timeout specified as a simple integer?
if (($timeout !~ /^\d+$/) && ($timeout !~ /^\d+\.\d+$/))
{
# The timeout was invalid, switch it to 1
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { timeout => $timeout }});
$timeout = 1;
}
# If the payload was set, take 28 bytes off to account for ICMP overhead.
if ($payload)
{
$payload -= 28;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { payload => $payload }});
}
# Build the call. Note that we use 'timeout' because if there is no connection and the host name is
# used to ping and DNS is not available, it could take upwards of 30 seconds time timeout otherwise.
my $shell_call = "";
if ($timeout)
{
$shell_call = $anvil->data->{path}{exe}{timeout}." $timeout ";
}
* Created Convert->fence_ipmilan_to_ipmitool() that takes a 'fence_ipmilan' call and converts it into a direct 'ipmitool' call. * Created Database->get_power() that loads data from the special 'power' table. * Fixed a bug in calls to Network->ping() where some weren't formatted properly for receiving two string variables. * Updated Database->get_anvils() to record the machine types when recording host information. * Updated Database->get_hosts_info() to also load the 'host_ipmi' column. * Updated Database->get_upses() to store the link to the 'power' -> 'power_uuid', when available. * Created ScanCore->call_scan_agents() that does the work of actually calling scan agents, moving the logic out from the scancore daemon. * Created ScanCore->check_power() that takes a host and the anvil it is in and returns if it's on batteries or not. If it is, the time on batteries and estimate hold-up time is returned. If not, the highest charge percentage is returned. * Created ScanCore->post_scan_analysis() that is a wrapper for calling the new ->post_scan_analysis_dr(), ->post_scan_analysis_node() and ->post_scan_analysis_striker(). Of which, _dr and _node are still empty, but _striker is complete. ** ->post_scan_analysis_striker() is complete. It now boots a node after a power loss if the UPSes powering it are OK (at least one has mains power, and the main-powered UPS(es) have reached the minimum charge percentage). If it's thermal, IPMI is called and so long as at least one thermal sensor is found and it/they are all OK, it is booted. For now, M2's thermal reboot delay logic hasn't been replicated, as it added a lot of complexity and didn't prove practically useful. * Created System->collect_ipmi_data() and moved 'scan_ipmitool's ipmitool call and parse into that method. This was done to allow ScanCore->post_scan_analysis_striker() to also call IPMI on a remote machine during thermal down events without reimplementing the logic. * Updated scan-ipmitool to only record temperature data for data collected locally. Also renamed 'machine' variables and hash keys to 'host_name' to clarify what is being stored. * Updated scancore to clear the 'system::stop_reason' variable. * Added missing packages to striker-manage-install-target. Signed-off-by: Digimer <digimer@alteeve.ca>
4 years ago
$shell_call .= $anvil->data->{path}{exe}{'ping'}." -W 1 -n ".$ping." -c 1";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
if (not $fragment)
{
$shell_call .= " -M do";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
}
if ($payload)
{
$shell_call .= " -s $payload";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
}
$shell_call .= " || ".$anvil->data->{path}{exe}{echo}." timeout";
my $pinged = 0;
my $average_ping_time = 0;
foreach my $try (1..$count)
{
last if $pinged;
* Created Convert->fence_ipmilan_to_ipmitool() that takes a 'fence_ipmilan' call and converts it into a direct 'ipmitool' call. * Created Database->get_power() that loads data from the special 'power' table. * Fixed a bug in calls to Network->ping() where some weren't formatted properly for receiving two string variables. * Updated Database->get_anvils() to record the machine types when recording host information. * Updated Database->get_hosts_info() to also load the 'host_ipmi' column. * Updated Database->get_upses() to store the link to the 'power' -> 'power_uuid', when available. * Created ScanCore->call_scan_agents() that does the work of actually calling scan agents, moving the logic out from the scancore daemon. * Created ScanCore->check_power() that takes a host and the anvil it is in and returns if it's on batteries or not. If it is, the time on batteries and estimate hold-up time is returned. If not, the highest charge percentage is returned. * Created ScanCore->post_scan_analysis() that is a wrapper for calling the new ->post_scan_analysis_dr(), ->post_scan_analysis_node() and ->post_scan_analysis_striker(). Of which, _dr and _node are still empty, but _striker is complete. ** ->post_scan_analysis_striker() is complete. It now boots a node after a power loss if the UPSes powering it are OK (at least one has mains power, and the main-powered UPS(es) have reached the minimum charge percentage). If it's thermal, IPMI is called and so long as at least one thermal sensor is found and it/they are all OK, it is booted. For now, M2's thermal reboot delay logic hasn't been replicated, as it added a lot of complexity and didn't prove practically useful. * Created System->collect_ipmi_data() and moved 'scan_ipmitool's ipmitool call and parse into that method. This was done to allow ScanCore->post_scan_analysis_striker() to also call IPMI on a remote machine during thermal down events without reimplementing the logic. * Updated scan-ipmitool to only record temperature data for data collected locally. Also renamed 'machine' variables and hash keys to 'host_name' to clarify what is being stored. * Updated scancore to clear the 'system::stop_reason' variable. * Added missing packages to striker-manage-install-target. Signed-off-by: Digimer <digimer@alteeve.ca>
4 years ago
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { count => $count, try => $try }});
my $output = "";
my $error = "";
# If the 'target' is set, we'll call over SSH unless 'target' is our host name.
if ($anvil->Network->is_local({host => $target}))
{
### Local calls
($output, my $return_code) = $anvil->System->call({shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output, return_code => $return_code }});
}
else
{
### Remote calls
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
($output, $error, my $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
port => $port,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
error => $error,
output => $output,
return_code => $return_code,
}});
}
foreach my $line (split/\n/, $output)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
if ($line =~ /(\d+) packets transmitted, (\d+) received/)
{
# This isn't really needed, but might help folks watching the logs.
my $pings_sent = $1;
my $pings_received = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
pings_sent => $pings_sent,
pings_received => $pings_received,
}});
if ($pings_received)
{
# Contact!
$pinged = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { pinged => $pinged }});
* Created Convert->fence_ipmilan_to_ipmitool() that takes a 'fence_ipmilan' call and converts it into a direct 'ipmitool' call. * Created Database->get_power() that loads data from the special 'power' table. * Fixed a bug in calls to Network->ping() where some weren't formatted properly for receiving two string variables. * Updated Database->get_anvils() to record the machine types when recording host information. * Updated Database->get_hosts_info() to also load the 'host_ipmi' column. * Updated Database->get_upses() to store the link to the 'power' -> 'power_uuid', when available. * Created ScanCore->call_scan_agents() that does the work of actually calling scan agents, moving the logic out from the scancore daemon. * Created ScanCore->check_power() that takes a host and the anvil it is in and returns if it's on batteries or not. If it is, the time on batteries and estimate hold-up time is returned. If not, the highest charge percentage is returned. * Created ScanCore->post_scan_analysis() that is a wrapper for calling the new ->post_scan_analysis_dr(), ->post_scan_analysis_node() and ->post_scan_analysis_striker(). Of which, _dr and _node are still empty, but _striker is complete. ** ->post_scan_analysis_striker() is complete. It now boots a node after a power loss if the UPSes powering it are OK (at least one has mains power, and the main-powered UPS(es) have reached the minimum charge percentage). If it's thermal, IPMI is called and so long as at least one thermal sensor is found and it/they are all OK, it is booted. For now, M2's thermal reboot delay logic hasn't been replicated, as it added a lot of complexity and didn't prove practically useful. * Created System->collect_ipmi_data() and moved 'scan_ipmitool's ipmitool call and parse into that method. This was done to allow ScanCore->post_scan_analysis_striker() to also call IPMI on a remote machine during thermal down events without reimplementing the logic. * Updated scan-ipmitool to only record temperature data for data collected locally. Also renamed 'machine' variables and hash keys to 'host_name' to clarify what is being stored. * Updated scancore to clear the 'system::stop_reason' variable. * Added missing packages to striker-manage-install-target. Signed-off-by: Digimer <digimer@alteeve.ca>
4 years ago
last;
}
else
{
# Not yet... Sleep to give time for transient network problems to
# pass.
sleep 1;
}
}
if ($line =~ /min\/avg\/max\/mdev = .*?\/(.*?)\//)
{
$average_ping_time = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { average_ping_time => $average_ping_time }});
}
}
}
# 0 == Ping failed
# 1 == Ping success
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
pinged => $pinged,
average_ping_time => $average_ping_time,
}});
return($pinged, $average_ping_time);
}
=head2 read_nmcli
This method reads and parses the C<< nmcli >> data. The data is stored as;
nmcli::<host>::uuid::<uuid>::name
nmcli::<host>::uuid::<uuid>::type
nmcli::<host>::uuid::<uuid>::timestamp_unix
nmcli::<host>::uuid::<uuid>::timestamp
nmcli::<host>::uuid::<uuid>::autoconnect
nmcli::<host>::uuid::<uuid>::autoconnect_priority
nmcli::<host>::uuid::<uuid>::read_only
nmcli::<host>::uuid::<uuid>::dbus_path
nmcli::<host>::uuid::<uuid>::active
nmcli::<host>::uuid::<uuid>::device
nmcli::<host>::uuid::<uuid>::state
nmcli::<host>::uuid::<uuid>::active_path
nmcli::<host>::uuid::<uuid>::slave
nmcli::<host>::uuid::<uuid>::filename
Where C<< uuid >> is the UUID of the connection. For C<< host >>, please see the parameter below. For information on what each value means, please see C<< man nmcli >>.
For each of reference, the following to values are also stored;
nmcli::<host>::name_to_uuid::<name>
nmcli::<host>::device_to_uuid::<device>
Where C<< name >> is the value in the interface set by the C<< NAME= >> variable and C<< device >> is the interface name (as used in C<< ip >>) and as set in the C<< DEVICE= >> variable in the C<< ifcfg-X >> files.
Parameters;
=head3 host (optional, default 'target' or local short host name)
This is the hash key under which the parsed C<< nmcli >> data is stored. By default, this is C<< local >> when called locally, or it will be C<< target >> if C<< target >> is passed.
=head3 password (optional)
This is the password used to access a remote machine. This is used when reading C<< nmcli >> data on a remote system.
=head3 port (optional, default '22')
This is the port used to access a remote machine. This is used when reading C<< nmcli >> data on a remote system.
=head3 remote_user (optional, default root)
If C<< target >> is set, this is the remote user we use to log into the remote system.
=head3 target (optional)
This is the host name or IP address of a remote machine that you want to read C<< nmcli >> data from.
=cut
sub read_nmcli
{
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 => "Network->read_nmcli()" }});
# If we were passed a target, try pinging from it instead of locally
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $host = defined $parameter->{host} ? $parameter->{host} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
host => $host,
password => $anvil->Log->is_secure($password),
port => $port,
remote_user => $remote_user,
target => $target,
}});
if (not $host)
{
$host = $target ? $target : $anvil->Get->short_host_name();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }});
}
if (exists $anvil->data->{nmcli}{$host})
{
delete $anvil->data->{nmcli}{$host};
}
# Reading locally or remote?
my $shell_call = $anvil->data->{path}{exe}{nmcli}." --colors no --fields all --terse connection show";
my $output = "";
my $is_local = $anvil->Network->is_local({host => $target});
if ($is_local)
{
# Local call.
($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:return_code' => $return_code,
}});
}
else
{
# Remote call
($output, my $error, my $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:output' => $output,
's2:error' => $error,
's3:return_code' => $return_code,
}});
}
foreach my $line (split/\n/, $output)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'line >>' => $line }});
$line =~ s/\\:/!col!/g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'line <<' => $line }});
# NAME UUID TYPE TIMESTAMP TIMESTAMP-REAL AUTOCONNECT AUTOCONNECT-PRIORITY READONLY DBUS-PATH ACTIVE DEVICE STATE ACTIVE-PATH SLAVE FILENAME
my ($name, $uuid, $type, $timestamp_unix, $timestamp, $autoconnect, $autoconnect_priority, $read_only, $dbus_path, $active, $device, $state, $active_path, $slave, $filename) = (split/:/, $line);
$timestamp =~ s/!col!/:/g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:name' => $name,
's2:uuid' => $uuid,
's3:type' => $type,
's4:timestamp_unix' => $timestamp_unix,
's5:timestamp' => $timestamp,
's6:autoconnect' => $autoconnect,
's7:autoconnect_priority' => $autoconnect_priority,
's8:read_only' => $read_only,
's9:dbus_path' => $dbus_path,
's10:active' => $active,
's11:device' => $device,
's12:state' => $state,
's13:active_path' => $active_path,
's14:slave' => $slave,
's15:filename' => $filename,
}});
if ($uuid)
{
# Inactive interfaces have a name but not a device;
if (not $device)
{
$device = $name;
$device =~ s/ /_/g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { device => $device }});
}
# Make it easy to look up a device's UUID by device or name.
$anvil->data->{nmcli}{$host}{name_to_uuid}{$name} = $uuid;
$anvil->data->{nmcli}{$host}{device_to_uuid}{$device} = $uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"nmcli::${host}::name_to_uuid::${name}" => $anvil->data->{nmcli}{$host}{name_to_uuid}{$name},
"nmcli::${host}::device_to_uuid::${device}" => $anvil->data->{nmcli}{$host}{device_to_uuid}{$device},
}});
# Translate some values;
my $say_state = not $state ? 0 : 1;
my $say_active = $active eq "no" ? 0 : 1;
my $say_read_only = $read_only eq "no" ? 0 : 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
say_state => $say_state,
say_active => $say_active,
say_read_only => $say_read_only,
}});
# Now store the data
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{name} = $name;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{type} = $type;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{timestamp_unix} = $timestamp_unix;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{timestamp} = $timestamp;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{autoconnect} = $autoconnect;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{autoconnect_priority} = $autoconnect_priority;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{read_only} = $say_read_only;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{dbus_path} = $dbus_path;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{active} = $say_active;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{device} = $device;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{'state'} = $say_state;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{active_path} = $active_path;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{slave} = $slave;
$anvil->data->{nmcli}{$host}{uuid}{$uuid}{filename} = $filename;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"nmcli::${host}::uuid::${uuid}::name" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{name},
"nmcli::${host}::uuid::${uuid}::type" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{type},
"nmcli::${host}::uuid::${uuid}::timestamp_unix" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{timestamp_unix},
"nmcli::${host}::uuid::${uuid}::timestamp" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{timestamp},
"nmcli::${host}::uuid::${uuid}::autoconnect" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{autoconnect},
"nmcli::${host}::uuid::${uuid}::autoconnect_priority" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{autoconnect_priority},
"nmcli::${host}::uuid::${uuid}::read_only" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{read_only},
"nmcli::${host}::uuid::${uuid}::dbus_path" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{dbus_path},
"nmcli::${host}::uuid::${uuid}::active" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{active},
"nmcli::${host}::uuid::${uuid}::device" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{device},
"nmcli::${host}::uuid::${uuid}::state" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{'state'},
"nmcli::${host}::uuid::${uuid}::active_path" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{active_path},
"nmcli::${host}::uuid::${uuid}::slave" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{slave},
"nmcli::${host}::uuid::${uuid}::filename" => $anvil->data->{nmcli}{$host}{uuid}{$uuid}{filename},
}});
}
}
return(0);
}
#############################################################################################################
# Private functions #
#############################################################################################################
1;