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