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_firewall
# check_internet
# collect_data
# download
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
# find_access
# find_matches
# find_target_ip
# get_company_from_mac
# get_ip_from_mac
# get_ips
# get_network
# is_local
# is_our_interface
# is_ip_in_network
# load_interfaces
# load_ips
# manage_firewall
# modify_connection
# ping
# read_nmcli
# reset_connection
# wait_for_network
# wait_on_nm_online
# _check_firewalld_conf
# _get_existing_zone_interfaces
# _get_server_ports
# _get_drbd_ports
# _get_live_migration_ports
# _manage_port
# _manage_service
# _manage_dr_firewall
# _manage_node_firewall
# _manage_striker_firewall
= 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 $ host = $ target ? $ target : $ anvil - > Get - > short_host_name ( ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { host = > $ host } } ) ;
# First get the list of bridges.
my $ shell_call = $ anvil - > data - > { path } { exe } { ip } . " link show type bridge" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
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 ,
} } ) ;
}
# Find the bridge interfaces
my $ bridge = "" ;
foreach my $ line ( split /\n/ , $ output )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { line = > $ line } } ) ;
if ( $ line =~ /^\d+:\s+(.*?):/ )
{
$ bridge = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { bridge = > $ bridge } } ) ;
$ anvil - > data - > { bridge } { $ host } { $ bridge } { found } = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"bridge::${host}::${bridge}::found" = > $ anvil - > data - > { bridge } { $ host } { $ bridge } { found } ,
} } ) ;
}
next if not $ bridge ;
if ( $ line =~ /mtu (\d+) / )
{
$ anvil - > data - > { bridge } { $ host } { $ bridge } { mtu } = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"bridge::${host}::${bridge}::mtu" = > $ anvil - > data - > { bridge } { $ host } { $ bridge } { mtu } ,
} } ) ;
}
if ( $ line =~ /link\/ether (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w) / )
{
$ anvil - > data - > { bridge } { $ host } { $ bridge } { mac } = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"bridge::${host}::${bridge}::mac" = > $ anvil - > data - > { bridge } { $ host } { $ bridge } { mac } ,
} } ) ;
}
}
# Now use bridge to find the interfaces connected to the bridges.
$ shell_call = $ anvil - > data - > { path } { exe } { bridge } . " -json -pretty link show" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
$ 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 $ master = $ hash_ref - > { master } ; # This can be the bond name for bond members.
my $ interface = $ hash_ref - > { ifname } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
's1:master' = > $ master ,
's2:interface' = > $ interface ,
} } ) ;
# If the 'master' wasn't found in the call above, the 'master' is not a bridge.
next if not exists $ anvil - > data - > { bridge } { $ host } { $ master } ;
# Record this interface as being connected to this bridge.
$ anvil - > data - > { bridge } { $ host } { $ master } { interface } { $ interface } { found } = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"bridge::${host}::${master}::interface::${interface}::found" = > $ anvil - > data - > { bridge } { $ host } { $ master } { interface } { $ interface } { found } ,
} } ) ;
# 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 } { $ master } { interface } { $ interface } { $ key } = $ hash_ref - > { $ key } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"bridge::${host}::${master}::interface::${interface}::${key}" = > $ anvil - > data - > { bridge } { $ host } { $ master } { interface } { $ interface } { $ key } ,
} } ) ;
}
}
# Make it easy to find the bridge an interface is in.
delete $ anvil - > data - > { interface_to_bridge } if exists $ anvil - > data - > { interface_to_bridge } ;
foreach my $ bridge_name ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { bridge } { $ host } } )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { bridge_name = > $ bridge_name } } ) ;
foreach my $ interface_name ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { bridge } { $ host } { $ bridge_name } { interface } } )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { interface_name = > $ interface_name } } ) ;
$ anvil - > data - > { interface_to_bridge } { $ interface_name } = $ bridge_name ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"interface_to_bridge::${interface_name}" = > $ anvil - > data - > { interface_to_bridge } { $ interface_name } ,
} } ) ;
}
}
return ( 0 ) ;
}
= head2 check_firewall
This checks to see if the firewall is running . If it is not , and if C << sys::daemons:: restart_firewalld >> is not set to C << 0 >> , it will start the firewall .
It returns C << 1 >> , the firewall is running . If it returns C << 0 >> , it is not .
This method takes no parameters .
= cut
sub check_firewall
{
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_firewall()" } } ) ;
my $ running = 0 ;
return ( 0 ) ;
# Make sure firewalld is running.
my $ firewalld_running = $ anvil - > System - > check_daemon ( { daemon = > $ anvil - > data - > { sys } { daemon } { firewalld } } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { firewalld_running = > $ firewalld_running } } ) ;
if ( $ firewalld_running )
{
$ running = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { running = > $ running } } ) ;
}
else
{
if ( $ anvil - > data - > { sys } { daemons } { restart_firewalld } )
{
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 1 , priority = > "alert" , key = > "log_0127" } ) ;
my $ return_code = $ anvil - > System - > start_daemon ( { daemon = > $ anvil - > data - > { sys } { daemon } { firewalld } } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { return_code = > $ return_code } } ) ;
if ( $ return_code )
{
# non-0 means something went wrong.
return ( "!!error!!" ) ;
}
else
{
# Started
$ running = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { running = > $ running } } ) ;
}
}
else
{
# We've been asked to leave it off.
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , priority = > "alert" , key = > "log_0128" } ) ;
return ( 0 ) ;
}
}
return ( $ running ) ;
}
= 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 ) ;
}
### NOTE: This is the new way of collecting data from nmcli, it is the method to use going forward.
= head2 collect_data
This method uses Network Manager , sysfs and procfs to collect data about the current state of the network .
Stored data:
* nmcli::uuid:: <uuid> :: device = 'connection.interface-name' , or 'GENERAL.DEVICES' . See note below
* nmcli::uuid:: <uuid> :: type = interface , bond , bridge , etc
* nmcli::uuid:: <uuid> :: active = 1 , 0
* nmcli::uuid:: <uuid> :: state = activated , activating , etc
* nmcli::uuid:: <uuid> :: <variable> = all 'variable: value' pairs returned by 'nmcli connection show <uuid>'
* nmcli::uuid:: <uuid> :: mac_address = MAC address ( in lower case )
* nmcli::uuid:: <uuid> :: connected = 0 is down , unix timestamp ( seconds since epoch ) of when it connected if up .
* nmcli::uuid:: <uuid> :: mtu = This is the MTU ( maximum transimssion unit in bytes ) of the interface .
To make it easier to map a device by name or MAC address to a UUID , this lookup hash is provided . Note that 'device' is 'connection.interface-name' when available , falling back to 'GENERAL.DEVICES' otherwise .
B << NOTE >> : An inactive interface will not report the 'connection.interface-name' , and the bios device name will be returned ( which is what is stored in 'GENERAL.DEVICES' . If you 're trying to find a device, and the expected name doesn' t exist , look up the device by MAC address . If that ' s not found , then the old GENERAL . DEVICES name can help you identify a replaced interface .
* nmcli::device:: <device> :: uuid = interface name ( or device name )
* nmcli::mac_address:: <mac_address> :: uuid = MAC address ( lower case )
Given that a single interface can have multiple IP addresses and routes , the IPs on a given interface are stored using a sequence number < 1 , 2 , 3 ... n > . To make it easier to find what device has an IP , the IPs are stored with a quick access hash .
* nmcli::ipv4:: <ip_address> :: on_uuid = interface UUID
* nmcli::ipv4:: <ip_address> :: sequence = sequence number
* nmcli::uuid:: <uuid> :: ipv { 4 , 6 } :: ip:: <sequence> :: ip_address = IP address
* nmcli::uuid:: <uuid> :: ipv { 4 , 6 } :: ip:: <sequence> :: subnet_mask = subnet mask ( CIDR notation )
* nmcli::uuid:: <uuid> :: ipv { 4 , 6 } :: dns = comma - separated list of DNS IP addresses
* nmcli::uuid:: <uuid> :: ipv { 4 , 6 } :: gateway = comma - separated list of DNS IP addresses
* nmcli::uuid:: <uuid> :: ipv { 4 , 6 } :: route:: <sequence> = Route info ( ie: 'dst = 0.0.0.0/0, nh = 192.168.255.254, mt = 428' , or 'dst = 192.168.0.0/16, nh = 0.0.0.0, mt = 428' . )
Bond data is stored in these hashes ;
* nmcli::bond:: <bond_device> :: uuid = The UUID on the bond
* nmcli::bond:: <bond_device> :: carrier = 1 , 0 - indicates if the bond has a connection or not .
* nmcli::bond:: <bond_device> :: operstate = 1 , 0 - indicates if the bond is operational or not .
* nmcli::bond:: <bond_device> :: up = 1 , 0 - indicates if the bond up up or not .
* nmcli::bond:: <bond_device> :: interface:: <interface> :: up = 1 , 0 - indicates if the child interface is up or not .
Bridge data is simple , but also made easy to find . The only real data is the hash references for the interfaces connected to the bridge .
* nmcli::bridge:: <device> :: uuid = The UUID of the bridge
* nmcli::bridge:: <device> :: interface:: <interface> :: status = This is the link data for the connected interface ( ie: 'BROADCAST,MULTICAST,MASTER,UP,LOWER_UP' ) .
To make it easier to find interfaces , the following look up hash is available .
* nmcli::interface:: <device> :: uuid = The UUID of the interface
* nmcli::mac_address:: <mac_address> :: uuid = MAC address
Parameters ;
= head3 start ( optional , default '0' )
If this is set to C << 1 >> , any connetions found to be down and not referencing any devices will be assigned the unroutable IP C << 169.0 .0 . x >> , where C << x >> is a sequential number . This should bring up unconfigured devices .
= head3 up ( optional , default '0' )
If this is set to C << 1 >> , any configured interfaces ( determined by checking for C << match . interface - name >> ) that are down will be started , if possible .
= cut
sub collect_data
{
my $ self = shift ;
my $ parameter = shift ;
my $ anvil = $ self - > parent ;
my $ debug = defined $ parameter - > { debug } ? $ parameter - > { debug } : 3 ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , key = > "log_0125" , variables = > { method = > "Network->check_internet()" } } ) ;
my $ start = defined $ parameter - > { start } ? $ parameter - > { start } : "" ;
my $ up = defined $ parameter - > { up } ? $ parameter - > { up } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
start = > $ start ,
up = > $ up ,
} } ) ;
if ( exists $ anvil - > data - > { nmcli } )
{
delete $ anvil - > data - > { nmcli } ;
}
# Use nmcli to collect the data.
my $ shell_call = $ anvil - > data - > { path } { exe } { nmcli } . " --get-values uuid,type,active,state,name connection show" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
foreach my $ line ( split /\n/ , $ output )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { line = > $ line } } ) ;
if ( $ line =~ /^(.*?):(.*?):(.*?):(.*?):(.*?)$/ )
{
my $ uuid = $ 1 ;
my $ type = $ 2 ;
my $ active = $ 3 ;
my $ state = $ 4 ;
my $ nm_name = $ 4 ; # biosdevname
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
uuid = > $ uuid ,
type = > $ type ,
active = > $ active ,
'state' = > $ state ,
nm_name = > $ nm_name ,
} } ) ;
next if $ type eq "loopback" ;
if ( $ type eq "802-3-ethernet" )
{
$ type = "interface" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { type = > $ type } } ) ;
}
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { type } = $ type ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { active } = lc ( $ active ) eq "yes" ? 1 : 0 ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'state' } = lc ( $ state ) ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { nm_name } = $ nm_name ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::type" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { type } ,
"nmcli::uuid::${uuid}::active" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { active } ,
"nmcli::uuid::${uuid}::state" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'state' } ,
"nmcli::uuid::${uuid}::nm_name" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { nm_name } ,
} } ) ;
}
}
foreach my $ uuid ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { nmcli } { uuid } } )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { uuid = > $ uuid } } ) ;
# Collect all the rest of the data now.
my $ shell_call = $ anvil - > data - > { path } { exe } { nmcli } . " connection show " . $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 3 , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
foreach my $ line ( split /\n/ , $ output )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { line = > $ line } } ) ;
if ( $ line =~ /^(.*?):\s+(.*)$/ )
{
my $ variable = $ 1 ;
my $ value = $ 2 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
's1:variable' = > $ variable ,
's2:value' = > $ value ,
} } ) ;
if ( $ value eq "--" )
{
$ value = "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { value = > $ value } } ) ;
}
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ variable } = $ value ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::${variable}" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ variable } ,
} } ) ;
if ( $ variable eq "match.interface-name" )
{
# Make sure we can look up the nmcli UUID by any of the names.
foreach my $ interface ( split /,/ , $ value )
{
$ anvil - > data - > { nmcli } { interface } { $ interface } { uuid } = $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::interface::${interface}::uuid" = > $ anvil - > data - > { nmcli } { interface } { $ interface } { uuid } ,
} } ) ;
}
}
if ( $ variable =~ /IP(\d).ADDRESS\[(\d+)\]/ )
{
my $ ip_type = $ 1 ;
my $ sequence = $ 2 ;
my $ hash_key = "ipv" . $ ip_type ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
ip_type = > $ ip_type ,
sequence = > $ sequence ,
hash_key = > $ hash_key ,
} } ) ;
if ( ( $ ip_type == 4 ) && ( $ value =~ /^(.*?)\/(.*)$/ ) )
{
my $ ip_address = $ 1 ;
my $ subnet_mask = $ 2 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
ip_address = > $ ip_address ,
subnet_mask = > $ subnet_mask ,
} } ) ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { ip } { $ sequence } { ip_address } = $ 1 ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { ip } { $ sequence } { subnet_mask } = $ 2 ;
$ anvil - > data - > { nmcli } { ipv4 } { $ ip_address } { on_uuid } = $ uuid ;
$ anvil - > data - > { nmcli } { ipv4 } { $ ip_address } { sequence } = $ sequence ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::${hash_key}::ip::${sequence}::ip_address" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { ip } { $ sequence } { ip_address } ,
"nmcli::uuid::${uuid}::${hash_key}::ip::${sequence}::subnet_mask" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { ip } { $ sequence } { subnet_mask } ,
"nmcli::ipv4::${ip_address}::on_uuid" = > $ anvil - > data - > { nmcli } { ipv4 } { $ ip_address } { on_uuid } ,
"nmcli::ipv4::${ip_address}::sequence" = > $ anvil - > data - > { nmcli } { ipv4 } { $ ip_address } { sequence } ,
} } ) ;
}
else
{
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { ip } { $ sequence } { ip_address } = $ value ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { ip } { $ sequence } { subnet_mask } = "" ;
$ anvil - > data - > { nmcli } { ipv4 } { $ value } { on_uuid } = $ value ;
$ anvil - > data - > { nmcli } { ipv4 } { $ value } { sequence } = $ sequence ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::${hash_key}::ip::${sequence}::ip_address" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { ip } { $ sequence } { ip_address } ,
"nmcli::uuid::${uuid}::${hash_key}::ip::${sequence}::subnet_mask" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { ip } { $ sequence } { subnet_mask } ,
"nmcli::ipv4::${value}::on_uuid" = > $ anvil - > data - > { nmcli } { ipv4 } { $ value } { on_uuid } ,
"nmcli::ipv4::${value}::sequence" = > $ anvil - > data - > { nmcli } { ipv4 } { $ value } { sequence } ,
} } ) ;
}
# Make sure the DNS key exists.
if ( not exists $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { dns } )
{
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { dns } = "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::${hash_key}::dns" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { dns } ,
} } ) ;
}
if ( not exists $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { gateway } )
{
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { gateway } = "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::${hash_key}::gateway" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { gateway } ,
} } ) ;
}
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { gateway } = $ value ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::${hash_key}::gateway" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { gateway } ,
} } ) ;
}
if ( $ variable =~ /IP(\d).ROUTE\[(\d+)\]/ )
{
my $ ip_type = $ 1 ;
my $ sequence = $ 2 ;
my $ hash_key = "ipv" . $ ip_type ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
ip_type = > $ ip_type ,
sequence = > $ sequence ,
hash_key = > $ hash_key ,
} } ) ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { route } { $ sequence } = $ value ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::${hash_key}::route::${sequence}" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { route } { $ sequence } ,
} } ) ;
}
if ( $ variable =~ /IP(\d).DNS\[(\d+)\]/ )
{
my $ ip_type = $ 1 ;
my $ sequence = $ 2 ;
my $ hash_key = "ipv" . $ ip_type ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
ip_type = > $ ip_type ,
sequence = > $ sequence ,
hash_key = > $ hash_key ,
} } ) ;
if ( ( exists $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { dns } ) and ( $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { dns } ne "" ) )
{
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { dns } . = "," . $ value ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::${hash_key}::dns" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { dns } ,
} } ) ;
}
else
{
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { dns } = $ value ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::${hash_key}::dns" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { dns } ,
} } ) ;
}
}
if ( $ variable =~ /IP(\d).GATEWAY/ )
{
my $ ip_type = $ 1 ;
my $ hash_key = "ipv" . $ ip_type ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
ip_type = > $ ip_type ,
hash_key = > $ hash_key ,
} } ) ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { gateway } = $ value ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::${hash_key}::gateway" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { $ hash_key } { gateway } ,
} } ) ;
}
}
}
}
# Now loop through and look for the name that maps to what's shown in 'ip addr list'. This can be a
# bit tricky.
foreach my $ uuid ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { nmcli } { uuid } } )
{
### NOTE: The 'connection_id' is the 'network_interface_name' (biosdevname) and it is always
### available, 'GENERAL.IP-IFACE' is the 'network_interface_device' and is only
### available when the interface is up.
my $ connection_id = $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'connection.id' } // "" ;
my $ general_ip_iface = $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'GENERAL.IP-IFACE' } // "" ;
$ general_ip_iface = "" if $ general_ip_iface eq "--" ;
my $ device_type = $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'connection.type' } // "" ;
my $ connection_interface_name = $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'connection.interface-name' } // "" ;
my $ match_interface_name = $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'match.interface-name' } // "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
's1:uuid' = > $ uuid ,
's2:connection_id' = > $ connection_id ,
's3:general_ip_iface' = > $ general_ip_iface ,
's4:device_type' = > $ device_type ,
's5:connection_interface_name' = > $ connection_interface_name ,
's6:match_interface_name' = > $ match_interface_name ,
} } ) ;
# An unrenamed interface will have a default 'Wired connection X' name, not he biosdevname
# name. So if there's not 'match.interface_name', use the 'connection.interface-name'.
if ( ( not $ match_interface_name ) && ( $ connection_interface_name ) )
{
$ connection_id = $ connection_interface_name ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { connection_id = > $ connection_id } } ) ;
}
# If there isn't a GENERAL.DEVICES or GENERAL.IP-IFACE, the link is down. Use the match.interface-name.
if ( ( not $ general_ip_iface ) && ( $ match_interface_name ) )
{
foreach my $ interface ( split /,/ , $ match_interface_name )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { interface = > $ interface } } ) ;
next if $ connection_id eq $ interface ;
if ( $ interface )
{
$ general_ip_iface = $ interface ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { general_ip_iface = > $ general_ip_iface } } ) ;
}
last if $ general_ip_iface ;
}
}
# Make it easier to lookup this device by name.
$ anvil - > data - > { nmcli } { name } { $ connection_id } { uuid } = $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::name::${connection_id}::uuid" = > $ anvil - > data - > { nmcli } { name } { $ connection_id } { uuid } ,
} } ) ;
if ( ( not $ general_ip_iface ) && ( not $ connection_interface_name ) )
{
# This connection is down, so it's not linked to a device.
next ;
}
my $ device = $ general_ip_iface ? $ general_ip_iface : $ connection_interface_name ;
$ anvil - > data - > { nmcli } { device } { $ device } { uuid } = $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::device::${device}::uuid" = > $ anvil - > data - > { nmcli } { device } { $ device } { uuid } ,
} } ) ;
### Get some data from sysfs.
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { name } = $ connection_id ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { device } = $ device ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { mac_address } = "" ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { type } = "" ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { mtu } = 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::device" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { device } ,
} } ) ;
# The 'connection.timestamp' seems to be where the 'connected' (as in, have an IP)
# comes from.
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { connected } = $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'connection.timestamp' } ? $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'connection.timestamp' } : 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::connected" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { connected } ,
} } ) ;
if ( $ device_type eq "bond" )
{
# Bonds always have the name we chose as the connection.id as they don't have
# biosdevnames.
$ anvil - > data - > { nmcli } { bond } { $ connection_id } { uuid } = $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::bond::${connection_id}::uuid" = > $ anvil - > data - > { nmcli } { bond } { $ connection_id } { uuid } ,
} } ) ;
# Read the interface's carrier
my $ carrier_file = "/sys/class/net/" . $ connection_id . "/carrier" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { carrier_file = > $ carrier_file } } ) ;
if ( - e $ carrier_file )
{
my $ carrier = $ anvil - > Storage - > read_file ( { debug = > $ debug , file = > $ carrier_file } ) ;
chomp $ carrier ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { carrier = > $ carrier } } ) ;
$ anvil - > data - > { nmcli } { bond } { $ connection_id } { carrier } = $ carrier ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"nmcli::bond::${connection_id}::carrier" = > $ anvil - > data - > { nmcli } { bond } { $ connection_id } { carrier } ,
} } ) ;
}
my $ operstate_file = "/sys/class/net/" . $ connection_id . "/operstate" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { operstate_file = > $ operstate_file } } ) ;
if ( - e $ operstate_file )
{
my $ operstate = $ anvil - > Storage - > read_file ( { debug = > $ debug , file = > $ operstate_file } ) ;
chomp $ operstate ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { operstate = > $ operstate } } ) ;
$ anvil - > data - > { nmcli } { bond } { $ connection_id } { up } = $ operstate eq "up" ? 1 : 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"nmcli::bond::${connection_id}::operstate" = > $ anvil - > data - > { nmcli } { bond } { $ connection_id } { operstate } ,
} } ) ;
}
# Read in the /proc file.
my $ proc_file = "/proc/net/bonding/" . $ connection_id ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { proc_file = > $ proc_file } } ) ;
my $ in_link = "" ;
my $ file_body = $ anvil - > Storage - > read_file ( { debug = > $ debug , file = > $ proc_file } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { file_body = > $ file_body } } ) ;
foreach my $ line ( split /\n/ , $ file_body )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { line = > $ line } } ) ;
if ( $ line =~ /Slave Interface: (.*)$/ )
{
$ in_link = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { in_link = > $ in_link } } ) ;
next ;
}
if ( not $ line )
{
$ in_link = "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { in_link = > $ in_link } } ) ;
next ;
}
if ( $ in_link )
{
if ( $ line =~ /MII Status: (.*)$/ )
{
my $ status = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { status = > $ status } } ) ;
if ( $ status eq "up" )
{
$ anvil - > data - > { nmcli } { bond } { $ connection_id } { interface } { $ in_link } { up } = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"nmcli::bond::${connection_id}::interface::${in_link}::up" = > $ anvil - > data - > { nmcli } { bond } { $ connection_id } { interface } { $ in_link } { up } ,
} } ) ;
}
else
{
$ anvil - > data - > { nmcli } { bond } { $ connection_id } { interface } { $ in_link } { up } = 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"nmcli::bond::${connection_id}::interface::${in_link}::up" = > $ anvil - > data - > { nmcli } { bond } { $ connection_id } { interface } { $ in_link } { up } ,
} } ) ;
}
next ;
}
if ( $ line =~ / (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)$/i )
{
# This is the real MAC address of the link.
my $ mac_address = $ 1 ;
$ anvil - > data - > { nmcli } { perm_mac_address } { $ in_link } = $ mac_address ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::perm_mac_address::${in_link}" = > $ anvil - > data - > { nmcli } { perm_mac_address } { $ in_link } ,
} } ) ;
}
}
else
{
if ( $ line =~ /MII Status: (.*)$/ )
{
my $ status = $ 1 ;
$ anvil - > data - > { nmcli } { bond } { $ connection_id } { up } = $ status eq "up" ? 1 : 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
status = > $ status ,
"nmcli::bond::${connection_id}::up" = > $ anvil - > data - > { nmcli } { bond } { $ connection_id } { up } ,
} } ) ;
next ;
}
}
}
}
elsif ( $ device_type eq "bridge" )
{
# Bonds always have the name we chose as the connection.id as they don't have
# biosdevnames.
$ anvil - > data - > { nmcli } { bridge } { $ connection_id } { uuid } = $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::bridge::${connection_id}::uuid" = > $ anvil - > data - > { nmcli } { bridge } { $ connection_id } { uuid } ,
} } ) ;
# See what interfaces are connected to the bridge.
my $ shell_call = $ anvil - > data - > { path } { exe } { ip } . " link show master " . $ connection_id ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
foreach my $ line ( split /\n/ , $ output )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { line = > $ line } } ) ;
if ( $ line =~ /^\d+: (.*?): <(.*?)>/ )
{
my $ interface = $ 1 ;
my $ status = $ 2 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
interface = > $ interface ,
status = > $ status ,
} } ) ;
$ anvil - > data - > { nmcli } { bridge } { $ connection_id } { interface } { $ interface } { status } = $ status ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::bridge::${connection_id}::interface::${interface}::status" = > $ anvil - > data - > { nmcli } { bridge } { $ connection_id } { interface } { $ interface } { status } ,
} } ) ;
}
}
}
elsif ( ( $ device_type eq "802-3-ethernet" ) or ( $ device_type eq "interface" ) )
{
# If we've got the if-name, use it. Otherwise, it's likely down and not renamed by
# us, so use the connection id.
my $ device = $ connection_id ;
if ( ( $ general_ip_iface ) && ( $ general_ip_iface ne "--" ) )
{
$ device = $ general_ip_iface ;
}
$ anvil - > data - > { nmcli } { interface } { $ device } { uuid } = $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::interface::${device}::uuid" = > $ anvil - > data - > { nmcli } { interface } { $ device } { uuid } ,
} } ) ;
# MAC address
my $ mac_address_file = "/sys/class/net/" . $ device . "/address" ;
my $ type_file = "/sys/class/net/" . $ device . "/type" ;
my $ mtu_file = "/sys/class/net/" . $ device . "/mtu" ;
if ( - e $ mac_address_file )
{
### NOTE: This will always be the active link's MAC in a bond, so tis gets
### overwritten when the bond device is parsed.
my $ mac_address = $ anvil - > Storage - > read_file ( { file = > $ mac_address_file } ) ;
$ mac_address =~ s/\n$// ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { mac_address = > $ mac_address } } ) ;
if ( ( $ mac_address ) && ( $ mac_address ne "!!error!!" ) )
{
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { mac_address } = $ mac_address ;
$ anvil - > data - > { nmcli } { mac_address } { $ mac_address } { uuid } = $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::mac_address" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { mac_address } ,
"nmcli::mac_address::${mac_address}::uuid" = > $ anvil - > data - > { nmcli } { mac_address } { $ mac_address } { uuid } ,
} } ) ;
}
}
if ( - e $ type_file )
{
my $ type = $ anvil - > Storage - > read_file ( { file = > $ type_file } ) ;
$ type =~ s/\n$// ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { type = > $ type } } ) ;
if ( ( $ type ) && ( $ type ne "!!error!!" ) )
{
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { type } = $ type ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::type" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { type } ,
} } ) ;
}
}
if ( - e $ mtu_file )
{
my $ mtu = $ anvil - > Storage - > read_file ( { file = > $ mtu_file } ) ;
$ mtu =~ s/\n$// ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { mtu = > $ mtu } } ) ;
if ( ( $ mtu ) && ( $ mtu ne "!!error!!" ) )
{
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { mtu } = $ mtu ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::mtu" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { mtu } ,
} } ) ;
}
}
}
}
# Loop through interfaces and see if the MAC address needs to be updated if it's the backup interface
# in a bond.
foreach my $ device ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { nmcli } { interface } } )
{
my $ uuid = $ anvil - > data - > { nmcli } { interface } { $ device } { uuid } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"s1:device" = > $ device ,
"s2:uuid" = > $ uuid ,
} } ) ;
if ( ( exists $ anvil - > data - > { nmcli } { perm_mac_address } { $ device } ) && ( $ anvil - > data - > { nmcli } { perm_mac_address } { $ device } ) )
{
# There's a permanent MAC address, overwrite the one we read earlier.
my $ perm_mac_address = $ anvil - > data - > { nmcli } { perm_mac_address } { $ device } ;
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { mac_address } = $ perm_mac_address ;
$ anvil - > data - > { nmcli } { mac_address } { $ perm_mac_address } { uuid } = $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"s1:nmcli::uuid::${uuid}::mac_address" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { mac_address } ,
"s2:nmcli::mac_address::${perm_mac_address}::uuid" = > $ anvil - > data - > { nmcli } { mac_address } { $ perm_mac_address } { uuid } ,
} } ) ;
}
}
# Should we bring up interfaces?
if ( $ up )
{
foreach my $ uuid ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { nmcli } { uuid } } )
{
$ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'match.interface-name' } = "" if not defined $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'match.interface-name' } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::active" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { active } ,
"nmcli::uuid::${uuid}::match.interface-name" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'match.interface-name' } ,
} } ) ;
if ( ( not $ anvil - > data - > { nmcli } { uuid } { $ uuid } { active } ) && ( $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'match.interface-name' } ) )
{
my $ shell_call = $ anvil - > data - > { path } { exe } { nmcli } . " connection up " . $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
# NM seems to have a race issue, so we sleep a second after nmcli calls.
sleep 1 ;
}
}
}
# Should we start interfaces?
if ( $ start )
{
# Yup, but are there any to start? We'll set this to '1' if so and that will trigger a
# rescan.
my $ rescan = 0 ;
# Sorted for log consistency on repeat runs
foreach my $ uuid ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { nmcli } { uuid } } )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
"nmcli::uuid::${uuid}::active" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { active } ,
"nmcli::uuid::${uuid}::connection.interface-name" = > $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'connection.interface-name' } ,
} } ) ;
if ( ( not $ anvil - > data - > { nmcli } { uuid } { $ uuid } { active } ) && ( not $ anvil - > data - > { nmcli } { uuid } { $ uuid } { 'connection.interface-name' } ) )
{
# Find an IP
my $ sequence = 1 ;
my $ found = 0 ;
my $ use_ip = "" ;
until ( $ found )
{
my $ test_ip = "169.0.0." . $ sequence ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { test_ip = > $ test_ip } } ) ;
if ( not exists $ anvil - > data - > { nmcli } { ipv4 } { $ test_ip } )
{
$ found = 1 ;
$ use_ip = $ test_ip ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
found = > $ found ,
use_ip = > $ use_ip ,
} } ) ;
}
else
{
$ sequence + + ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { sequence = > $ sequence } } ) ;
}
die "Failed to find an unused IP in 169.0.0.0/24\n" if $ sequence > 255 ;
}
my $ shell_call = $ anvil - > data - > { path } { exe } { nmcli } . " connection modify " . $ uuid . " ipv4.method manual ipv4.addresses " . $ use_ip . "/8" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
# NM seems to have a race issue, so we sleep a second after nmcli calls.
sleep 1 ;
$ rescan = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { rescan = > $ rescan } } ) ;
}
}
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { rescan = > $ rescan } } ) ;
if ( $ rescan )
{
# Give things a few seconds to settle
sleep 3 ;
# Rescan
$ anvil - > Network - > collect_data ( { debug = > $ debug } ) ;
}
}
return ( 0 ) ;
}
= 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 ) ;
}
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
= head2 find_access
This takes a host ' s UUID or name , and finds networks that this host can reach it on . If the target is not found in the database , C << ! ! error ! ! >> is returned . Otherwise , the number of matches found is returned .
B << Note >> : This requires that the target has recorded it ' s network in the database .
It was written to be a saner version of C << Network - > find_matches ( ) >>
Matches will be stored as:
* network_access:: <network_name> :: local_ip_address = <local_ip_address>
* network_access:: <network_name> :: local_subnet_mask = <local_subnet_mask>
* network_access:: <network_name> :: local_interface = <local_interface_with_ip>
* network_access:: <network_name> :: local_speed = <speed_in_Mbps>
* network_access:: <network_name> :: target_ip_address = <target_ip_address>
* network_access:: <network_name> :: target_subnet_mask = <target_subnet_mask>
* network_access:: <network_name> :: target_interface = <target_interface_with_ip>
* network_access:: <network_name> :: target_speed = <speed_in_Mbps>
Where C << network_name >> will be C << bcnX >> , C << ifnX >> , etc .
Paramters ;
= head3 target ( required )
This is the host ( name or UUID ) we ' re looking for connection options with .
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
= cut
sub find_access
{
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_access()" } } ) ;
my $ target = defined $ parameter - > { target } ? $ parameter - > { target } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
target = > $ target ,
} } ) ;
if ( not $ target )
{
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 0 , priority = > "err" , key = > "log_0020" , variables = > { method = > "Network->find_access()" , parameter = > "target" } } ) ;
return ( "!!error!!" ) ;
}
# Take the target and find the host_uuid and host_name.
my $ target_host_uuid = $ anvil - > Database - > get_host_uuid_from_string ( { debug = > $ debug , string = > $ target } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { target_host_uuid = > $ target_host_uuid } } ) ;
if ( not $ target_host_uuid )
{
# Bad target.
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 0 , priority = > "err" , key = > "error_0414" , variables = > { target = > $ target } } ) ;
return ( "!!error!!" ) ;
}
if ( exists $ anvil - > data - > { network_access } )
{
delete $ anvil - > data - > { network_access } ;
}
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
my $ host_uuid = $ anvil - > Get - > host_uuid ;
my $ short_host_name = $ anvil - > Get - > short_host_name ;
my $ target_short_host_name = $ anvil - > data - > { hosts } { host_uuid } { $ target_host_uuid } { short_host_name } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
host_uuid = > $ host_uuid ,
short_host_name = > $ short_host_name ,
target_short_host_name = > $ target_short_host_name ,
} } ) ;
# Load our IPs
$ anvil - > Network - > load_ips ( {
debug = > $ debug ,
host = > $ short_host_name ,
} ) ;
# Load our target's IPs.
$ anvil - > Network - > load_ips ( {
debug = > $ debug ,
host = > $ target_short_host_name ,
host_uuid = > $ target_host_uuid ,
} ) ;
# Loop through the first, and on each interface with an IP/subnet mask, look for a match in the second.
foreach my $ local_interface ( sort { $ b cmp $ a } keys % { $ anvil - > data - > { network } { $ short_host_name } { interface } } )
{
my $ local_ip = $ anvil - > data - > { network } { $ short_host_name } { interface } { $ local_interface } { ip } ;
my $ local_subnet_mask = $ anvil - > data - > { network } { $ short_host_name } { interface } { $ local_interface } { subnet_mask } ;
my $ local_speed = $ anvil - > data - > { network } { $ short_host_name } { interface } { $ local_interface } { speed } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
local_interface = > $ local_interface ,
local_ip = > $ local_ip ,
local_subnet_mask = > $ local_subnet_mask ,
local_speed = > $ local_speed ,
} } ) ;
if ( ( $ local_ip ) && ( $ local_subnet_mask ) )
{
# Look for a match.
my $ local_network = $ anvil - > Network - > get_network ( {
debug = > $ debug ,
ip = > $ local_ip ,
subnet_mask = > $ local_subnet_mask ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { local_network = > $ local_network } } ) ;
foreach my $ target_interface ( sort { $ b cmp $ a } keys % { $ anvil - > data - > { network } { $ target_short_host_name } { interface } } )
{
my $ target_ip = $ anvil - > data - > { network } { $ target_short_host_name } { interface } { $ target_interface } { ip } ;
my $ target_subnet_mask = $ anvil - > data - > { network } { $ target_short_host_name } { interface } { $ target_interface } { subnet_mask } ;
my $ target_speed = $ anvil - > data - > { network } { $ target_short_host_name } { interface } { $ target_interface } { speed } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
target_interface = > $ target_interface ,
target_ip = > $ target_ip ,
target_subnet_mask = > $ target_subnet_mask ,
target_speed = > $ target_speed ,
} } ) ;
if ( ( $ target_ip ) && ( $ target_subnet_mask ) )
{
# Do we have a match?
my $ target_network = $ anvil - > Network - > get_network ( {
debug = > $ debug ,
ip = > $ target_ip ,
subnet_mask = > $ target_subnet_mask ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
local_network = > $ local_network ,
target_network = > $ target_network ,
} } ) ;
if ( $ local_network eq $ target_network )
{
# Match!
my $ network_name = $ target_interface ;
$ network_name =~ s/^(\w+\d+)_.*$/$1/ ;
$ anvil - > data - > { network_access } { $ network_name } { local_interface } = $ local_interface ;
$ anvil - > data - > { network_access } { $ network_name } { local_speed } = $ local_speed ;
$ anvil - > data - > { network_access } { $ network_name } { local_ip_address } = $ local_ip ;
$ anvil - > data - > { network_access } { $ network_name } { local_subnet_mask } = $ local_subnet_mask ;
$ anvil - > data - > { network_access } { $ network_name } { target_interface } = $ target_interface ;
$ anvil - > data - > { network_access } { $ network_name } { target_speed } = $ target_speed ;
$ anvil - > data - > { network_access } { $ network_name } { target_ip_address } = $ target_ip ;
$ anvil - > data - > { network_access } { $ network_name } { target_subnet_mask } = $ target_subnet_mask ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"s1:network_access::${network_name}::local_interface" = > $ anvil - > data - > { network_access } { $ network_name } { local_interface } ,
"s2:network_access::${network_name}::local_speed" = > $ anvil - > data - > { network_access } { $ network_name } { local_speed } ,
"s3:network_access::${network_name}::local_ip_address" = > $ anvil - > data - > { network_access } { $ network_name } { local_ip_address } ,
"s4:network_access::${network_name}::local_subnet_mask" = > $ anvil - > data - > { network_access } { $ network_name } { local_subnet_mask } ,
"s5:network_access::${network_name}::target_interface" = > $ anvil - > data - > { network_access } { $ network_name } { target_interface } ,
"s6:network_access::${network_name}::target_speed" = > $ anvil - > data - > { network_access } { $ network_name } { target_speed } ,
"s7:network_access::${network_name}::target_ip_address" = > $ anvil - > data - > { network_access } { $ network_name } { target_ip_address } ,
"s8:network_access::${network_name}::target_subnet_mask" = > $ anvil - > data - > { network_access } { $ network_name } { target_subnet_mask } ,
} } ) ;
}
}
}
}
}
my $ matches = keys % { $ anvil - > data - > { network_access } } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { matches = > $ matches } } ) ;
return ( $ matches ) ;
}
= 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 } : "" ;
my $ source = defined $ parameter - > { source } ? $ parameter - > { source } : $ THIS_FILE ;
my $ line = defined $ parameter - > { line } ? $ parameter - > { line } : __LINE__ ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
first = > $ first ,
second = > $ second ,
source = > $ source ,
line = > $ line ,
} } ) ;
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 ( "" ) ;
}
if ( ref ( $ anvil - > data - > { network } { $ first } ) ne "HASH" )
{
$ anvil - > Network - > load_ips ( {
debug = > $ debug ,
host = > $ first ,
} ) ;
if ( ref ( $ anvil - > data - > { network } { $ first } ) ne "HASH" )
{
# Well, we tried.
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 0 , priority = > "err" , key = > "error_0106" , variables = > {
key = > "first -> network::" . $ first ,
source = > $ source ,
line = > $ line ,
} } ) ;
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 ( "" ) ;
}
if ( ref ( $ anvil - > data - > { network } { $ second } ) ne "HASH" )
{
$ anvil - > Network - > load_ips ( {
debug = > $ debug ,
host = > $ second ,
} ) ;
if ( ref ( $ anvil - > data - > { network } { $ second } ) ne "HASH" )
{
# Well, we tried.
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 0 , priority = > "err" , key = > "error_0106" , variables = > {
key = > "second -> network::" . $ second ,
source = > $ source ,
line = > $ line ,
} } ) ;
$ anvil - > nice_exit ( { exit_code = > 1 } ) ;
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 } } )
{
next if not exists $ anvil - > data - > { network } { $ second } { interface } { $ second_interface } { ip } ;
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 = > {
's1:second' = > $ second ,
's2:second_interface' = > $ second_interface ,
's3:second_ip' = > $ second_ip ,
's4: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 .
= head3 networks ( optional , default 'bcn,mn,sn,ifn,any' )
This is the comma - separated list of networks to search for access over . The order presented is the order searched . Valid values are ;
* bcn ( Back - Channel Network )
* mn ( Migration Network )
* sn ( Storage Network )
* ifn ( Internet - Facing Network )
* any ( Any other interface )
= head3 test_access ( optional , default '0' )
If set to C << 1 >> , any matched IP will be tested . If this is set and the target can ' t be reached using that IP , it is skipped . If this is not set , the first match is returned .
= 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 $ host_uuid = defined $ parameter - > { host_uuid } ? $ parameter - > { host_uuid } : "" ;
my $ networks = defined $ parameter - > { networks } ? $ parameter - > { networks } : "" ;
my $ test_access = defined $ parameter - > { test_access } ? $ parameter - > { test_access } : 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
host_uuid = > $ host_uuid ,
networks = > $ networks ,
test_access = > $ test_access ,
} } ) ;
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 ( "" ) ;
}
if ( not $ networks )
{
$ networks = "bcn,mn,sn,ifn,any" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { networks = > $ networks } } ) ;
}
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 ,
} } ) ;
my $ target_ip = "" ;
my $ matches = $ anvil - > Network - > find_access ( {
debug = > $ debug ,
target = > $ target_host_name ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { matches = > $ matches } } ) ;
foreach my $ preferred_network ( split /,/ , $ networks )
{
last if $ target_ip ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { preferred_network = > $ preferred_network } } ) ;
foreach my $ network_name ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { network_access } } )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { network_name = > $ network_name } } ) ;
if ( ( $ network_name !~ /^$preferred_network/ ) && ( $ preferred_network ne "any" ) )
{
next ;
}
my $ this_target_ip = $ anvil - > data - > { network_access } { $ network_name } { target_ip_address } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { this_target_ip = > $ this_target_ip } } ) ;
if ( $ test_access )
{
my $ access = $ anvil - > Remote - > test_access ( { target = > $ this_target_ip } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
's1:network_name' = > $ network_name ,
's2:this_target_ip' = > $ this_target_ip ,
's3:access' = > $ access ,
} } ) ;
if ( $ access )
{
# We can use this one.
$ target_ip = $ this_target_ip ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { target_ip = > $ target_ip } } ) ;
last ;
}
}
else
{
# We're done.
$ target_ip = $ this_target_ip ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { target_ip = > $ target_ip } } ) ;
}
}
}
return ( $ target_ip ) ;
}
= 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 } } ) ;
}
if ( ( not $ company ) && ( $ mac =~ /^52:54:00/ ) )
{
$ company = "KVM/qemu" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { company = > $ company } } ) ;
}
return ( $ company ) ;
}
= head2 get_ip_from_mac
This takes a MAC address and tries to convert it to an IP address . If no IP is found , an empty string is returned .
Parameters ;
= head3 mac ( required )
This is the MAC address we ' re looking for an IP to match to . The format must be C << aa:bb:cc:dd:ee:ff >> .
= cut
sub get_ip_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_ip_from_mac()" } } ) ;
my $ ip = "" ;
my $ mac = defined $ parameter - > { mac } ? $ parameter - > { mac } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
mac = > $ mac ,
} } ) ;
my $ query = "SELECT mac_to_ip_ip_address FROM mac_to_ip WHERE mac_to_ip_mac_address = " . $ 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 )
{
$ ip = $ results - > [ 0 ] - > [ 0 ] ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { ip = > $ ip } } ) ;
}
return ( $ ip ) ;
}
= head2 get_ips
This method checks the target 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 ;
my $ status = $ 2 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
in_iface = > $ in_iface ,
status = > $ status ,
} } ) ;
$ 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 - > data - > { network } { $ host } { interface } { $ in_iface } { tx_bytes } = 0 if not defined $ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { tx_bytes } ;
$ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { rx_bytes } = 0 if not defined $ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { rx_bytes } ;
$ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { status } = $ status ;
$ 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 } ,
"network::${host}::interface::${in_iface}::status" = > $ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { status } ,
} } ) ;
if ( $ in_iface ne "lo" )
{
# Read the read and write bytes.
my $ read_bytes = 0 ;
my $ write_bytes = 0 ;
my $ shell_call = "
if [ - e '/sys/class/net/".$in_iface."/statistics/rx_bytes' ] ;
then
echo - n 'rx:' ;
cat /sys/c lass /net/ ".$in_iface." /statistics/ rx_bytes ;
echo - n 'tx:' ;
cat /sys/c lass /net/ ".$in_iface." /statistics/ tx_bytes ;
else
echo 'rx:0' ;
echo 'tx:0' ;
fi " ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my $ transmit_sizes = "" ;
if ( $ is_local )
{
# Local call.
( $ transmit_sizes , my $ return_code ) = $ anvil - > System - > call ( { debug = > $ debug , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
's1:transmit_sizes' = > $ transmit_sizes ,
's2:return_code' = > $ return_code ,
} } ) ;
}
else
{
# Remote call
( $ transmit_sizes , 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:transmit_sizes' = > $ transmit_sizes ,
's2:error' = > $ error ,
's3:return_code' = > $ return_code ,
} } ) ;
}
foreach my $ line ( split /\n/ , $ transmit_sizes )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { line = > $ line } } ) ;
if ( $ line =~ /rx:(\d+)/ )
{
$ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { rx_bytes } = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"network::${host}::interface::${in_iface}::rx_bytes" = > $ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { rx_bytes } . " (" . $ anvil - > Convert - > bytes_to_human_readable ( { 'bytes' = > $ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { rx_bytes } } ) . ")" ,
} } ) ;
}
if ( $ line =~ /tx:(\d+)/ )
{
$ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { tx_bytes } = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"network::${host}::interface::${in_iface}::tx_bytes" = > $ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { tx_bytes } . " (" . $ anvil - > Convert - > bytes_to_human_readable ( { 'bytes' = > $ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { tx_bytes } } ) . ")" ,
} } ) ;
}
}
}
}
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 } } ) ;
}
# For multiple IPs on an interface, we'll store the first we see as the "main" IP (used in
# /etc/hosts, etc).
if ( ( not $ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { ip } ) or
( not $ anvil - > Validate - > ip ( { ip = > $ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { ip } } ) ) )
{
# First IP for this interface, save it in the traditional hash.
$ 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 } ,
} } ) ;
}
# Now store the IPs in hash that handles multiple IPs per interface properly. This
# should never be duplicate, buuuuut...
if ( not exists $ anvil - > data - > { network } { $ host } { ip_address } { $ ip } )
{
$ anvil - > data - > { network } { $ host } { ip_address } { $ ip } { interface } = $ in_iface ;
$ anvil - > data - > { network } { $ host } { ip_address } { $ ip } { subnet_mask } = $ subnet_mask ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"s1:network::${host}::ip_address::${ip}::interface" = > $ anvil - > data - > { network } { $ host } { ip_address } { $ ip } { interface } ,
"s2:network::${host}::ip_address::${ip}::subnet_mask" = > $ anvil - > data - > { network } { $ host } { ip_address } { $ ip } { subnet_mask } ,
} } ) ;
}
}
if ( $ line =~ /ether (.*?) /i )
{
my $ mac_address = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { mac_address = > $ mac_address } } ) ;
# Wireless interfaces have a 'permaddr' that is stable. The MAC address shown by 'ether' changes constantly, for some odd reason.
if ( $ line =~ /permaddr (.*)$/ )
{
$ mac_address = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { mac_address = > $ mac_address } } ) ;
}
$ 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}::mac_address" = > $ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { mac_address } ,
} } ) ;
# If this is a bond or bridge, don't record the MAC address. It confuses things as
# they show the MAC of the active interface. If this is an interface, see if the file
# '/sys/class/net/<nic>/bonding_slave/perm_hwaddr' exists and, if so, read the MAC
# address from there. If not, read the MAC address from
# '/sys/class/net/<nic>/address'.
my $ shell_call = 'IFACE=' . $ in_iface . '
if [ - e "/sys/class/net/${IFACE}/bridge" ] ;
then
echo bridge ;
elif [ - e "/proc/net/bonding/${IFACE}" ] ;
then
echo bond ;
else
ethtool - P $ { IFACE }
fi ' ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
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 =~ /^.*: (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)$/ )
{
my $ real_mac = $ 1 ;
$ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { mac_address } = $ real_mac ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"network::${host}::interface::${in_iface}::mac_address" = > $ anvil - > data - > { network } { $ host } { interface } { $ in_iface } { mac_address } ,
} } ) ;
# Make it easy to look up an interface name based on a given MAC
# address.
$ anvil - > data - > { network } { $ host } { mac_address } { $ real_mac } { interface } = $ in_iface ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"network::${host}::mac_address::${real_mac}::interface" = > $ anvil - > data - > { network } { $ host } { mac_address } { $ real_mac } { 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 } } )
{
next if not defined $ 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_our_interface
This method takes an interface name and returns C << 1 >> if the interface is one of the ones we manage ( A C << BCN >> , C << IFN >> , C << SN >> or C << MN >> interface ) . If not , C << 0 >> is returned .
Parameters ;
= head3 interface ( required )
This is the name of the interface being evaluated .
= cut
sub is_our_interface
{
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_our_interface()" } } ) ;
my $ interface = $ parameter - > { interface } ? $ parameter - > { interface } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
interface = > $ interface ,
} } ) ;
if ( not $ interface )
{
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 0 , priority = > "err" , key = > "log_0020" , variables = > { method = > "Network->is_our_interface()" , parameter = > "interface" } } ) ;
return ( 0 ) ;
}
my $ ours = 0 ;
if ( ( $ interface =~ /^bcn/i ) or
( $ interface =~ /^sn/i ) or
( $ interface =~ /^ifn/i ) or
( $ interface =~ /^mn/i ) )
{
$ ours = 1 ;
}
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { ours = > $ ours } } ) ;
return ( $ ours ) ;
}
= 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 load_interfaces
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_interfaces
{
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_interfaces()" } } ) ;
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_interfaces()" , 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_uuid } ,
"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 >> .
C << Note >> : If there network has a device name , from network manager , that is the name used for C << <iface_name> >> .
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 ;
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
my $ host_uuid = defined $ parameter - > { host_uuid } ? $ parameter - > { 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 )
{
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
$ host_uuid = $ anvil - > Get - > host_uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { host_uuid = > $ host_uuid } } ) ;
}
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 } ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , key = > "log_0700" , variables = > { hash = > "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 $ device_type_with_ip = $ ip_address_on_type ;
my $ bridge_name = "" ;
my $ bond_name = "" ;
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
my $ interface_name = "" ;
my $ interface_device = "" ;
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
my $ interface_mac = "" ;
my $ network_interface_uuid = "" ;
if ( $ ip_address_on_type eq "interface" )
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
{
$ network_interface_uuid = $ ip_address_on_uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { network_interface_uuid = > $ network_interface_uuid } } ) ;
}
else
{
my $ query = "" ;
if ( $ ip_address_on_type eq "bridge" )
{
# What's the bridge UUID?
$ query = "SELECT bond_name, bond_active_interface FROM bonds WHERE bond_bridge_uuid = " . $ anvil - > Database - > quote ( $ ip_address_on_uuid ) . ";" ;
# Get the bridge name, also.
if ( 1 )
{
my $ query = "SELECT bridge_name FROM bridges WHERE bridge_uuid = " . $ anvil - > Database - > quote ( $ ip_address_on_uuid ) . ";" ;
$ bridge_name = $ anvil - > Database - > query ( { query = > $ query , source = > $ THIS_FILE , line = > __LINE__ } ) - > [ 0 ] - > [ 0 ] ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { bridge_name = > $ bridge_name } } ) ;
}
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
}
else
{
$ query = "SELECT bond_name, bond_active_interface FROM bonds WHERE bond_uuid = " . $ anvil - > Database - > quote ( $ ip_address_on_uuid ) . ";" ;
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
}
$ 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__ } ) ;
$ bond_name = $ results - > [ 0 ] - > [ 0 ] ;
my $ active_interface = $ results - > [ 0 ] - > [ 1 ] ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
bond_name = > $ bond_name ,
active_interface = > $ active_interface ,
} } ) ;
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
if ( $ active_interface )
{
my $ query = "
SELECT
network_interface_uuid
FROM
network_interfaces
WHERE
network_interface_host_uuid = ".$anvil->Database->quote($host_uuid)."
AND
(
network_interface_name = ".$anvil->Database->quote($active_interface)."
OR
network_interface_device = ".$anvil->Database->quote($active_interface)."
)
; " ;
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { query = > $ query } } ) ;
$ network_interface_uuid = $ anvil - > Database - > query ( { query = > $ query , source = > $ THIS_FILE , line = > __LINE__ } ) - > [ 0 ] - > [ 0 ] ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { network_interface_uuid = > $ network_interface_uuid } } ) ;
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
}
}
my $ device_name_with_ip = "" ;
if ( $ ip_address_on_type eq "bridge" )
{
$ device_name_with_ip = $ bridge_name ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { device_name_with_ip = > $ device_name_with_ip } } ) ;
}
elsif ( $ ip_address_on_type eq "bond" )
{
$ device_name_with_ip = $ bond_name ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { device_name_with_ip = > $ device_name_with_ip } } ) ;
}
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
if ( $ network_interface_uuid )
{
my $ query = "
SELECT
network_interface_uuid ,
network_interface_name ,
network_interface_device ,
network_interface_mac_address ,
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
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
network_interface_uuid = ".$anvil->Database->quote($network_interface_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 ] - > [ 1 ] ;
$ interface_device = $ results - > [ 0 ] - > [ 2 ] ;
$ interface_mac = $ results - > [ 0 ] - > [ 3 ] ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
interface_name = > $ interface_name ,
interface_device = > $ interface_device ,
interface_mac = > $ interface_mac ,
} } ) ;
# If we've got an interface device, use that for the hash.
if ( $ interface_device )
{
$ interface_name = $ interface_device ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { interface_name = > $ interface_name } } ) ;
}
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
$ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { network_interface_uuid } = $ results - > [ 0 ] - > [ 0 ] ;
$ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { interface_name } = $ results - > [ 0 ] - > [ 1 ] ;
$ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { interface_device } = $ results - > [ 0 ] - > [ 2 ] ;
This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially.
* To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid.
This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well.
* Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by.
* Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond.
Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved.
Signed-off-by: digimer <mkelly@alteeve.ca>
2 years ago
$ 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 } = "interface" ;
$ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { speed } = $ results - > [ 0 ] - > [ 4 ] ;
$ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { mtu } = $ results - > [ 0 ] - > [ 5 ] ;
$ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { link_state } = $ results - > [ 0 ] - > [ 6 ] ;
$ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { operational } = $ results - > [ 0 ] - > [ 7 ] ;
$ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { duplex } = $ results - > [ 0 ] - > [ 8 ] ;
$ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { medium } = $ results - > [ 0 ] - > [ 9 ] ;
$ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { bond_uuid } = defined $ results - > [ 0 ] - > [ 10 ] ? $ results - > [ 0 ] - > [ 10 ] : "" ;
$ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { bridge_uuid } = defined $ results - > [ 0 ] - > [ 11 ] ? $ results - > [ 0 ] - > [ 11 ] : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"network::${host}::interface::${interface_name}::network_interface_uuid" = > $ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { network_interface_uuid } ,
"network::${host}::interface::${interface_name}::interface_name" = > $ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { interface_name } ,
"network::${host}::interface::${interface_name}::interface_device" = > $ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { interface_device } ,
"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 } ,
"network::${host}::interface::${interface_name}::speed" = > $ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { speed } ,
"network::${host}::interface::${interface_name}::mtu" = > $ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { mtu } ,
"network::${host}::interface::${interface_name}::link_state" = > $ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { link_state } ,
"network::${host}::interface::${interface_name}::operational" = > $ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { operational } ,
"network::${host}::interface::${interface_name}::duplex" = > $ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { duplex } ,
"network::${host}::interface::${interface_name}::medium" = > $ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { medium } ,
"network::${host}::interface::${interface_name}::bond_uuid" = > $ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { bond_uuid } ,
"network::${host}::interface::${interface_name}::bridge_uuid" = > $ anvil - > data - > { network } { $ host } { interface } { $ interface_name } { bridge_uuid } ,
} } ) ;
if ( not $ device_name_with_ip )
{
$ device_name_with_ip = $ interface_name ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { device_name_with_ip = > $ device_name_with_ip } } ) ;
}
}
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 manage_firewall
B << NOTE >> : So far , only C << check >> is implemented .
This method manages a C << firewalld >> firewall .
If no parameters are passed , it works by determining what should be open , making sure those things are open , and closing anything open that shouldn ' t be .
If the firewall is off , C << 1 >> is returned . Otherwise C << 0 >> is returned , unless there was an error in which case C << ! ! error ! ! >> is returned .
When called with C << task = check >> , and if a port is specified , then C << 1 >> will be returned if the port is open and C << 0 >> if it is closed .
Parameters ;
= head3 task ( optional , default 'check' )
If set to C << open >> , it will open the corresponding C << port >> . If set to C << close >> , it will close the corresponding C << port >> . If set to c << check >> , then it depends on is a port is given . If not , the full configuration is checked , and updated to the firewall are made as needed . If a port ( and optionally zone and / or protocol ) is specified , that specific request is checked .
The default is C << all >> , which checks the entire configuration , updating the active configuration as needed .
= head3 port_number ( required )
This is the port number to work on .
If not specified , C << service >> is required .
= head3 protocol ( optional , required if 'port' set )
This can be c << tcp >> or C << upd >> and is used to specify what protocol to use with the C << port >> , when specified . Multiple protocols can be defined using comma - separated list . Example , C << tcp , udp >> .
= head3 zone ( optional )
If set to a zone name , the check / change is performed against the specific zone . Multiple zones can be specified using comma - separated list . Example , C << BCN1 >> , or C << BCN1 , IFN1 >> .
= cut
sub manage_firewall
{
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->manage_firewall()" } } ) ;
my $ task = defined $ parameter - > { task } ? $ parameter - > { task } : "check" ;
my $ port_number = defined $ parameter - > { port_number } ? $ parameter - > { port_number } : "" ;
my $ protocol = defined $ parameter - > { protocol } ? $ parameter - > { protocol } : "" ;
my $ zone = defined $ parameter - > { zone } ? $ parameter - > { zone } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
task = > $ task ,
port_number = > $ port_number ,
protocol = > $ protocol ,
zone = > $ zone
} } ) ;
### TODO: Remove when fixed.
my $ firewalld_running = $ anvil - > System - > check_daemon ( { daemon = > $ anvil - > data - > { sys } { daemon } { firewalld } } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { firewalld_running = > $ firewalld_running } } ) ;
if ( $ firewalld_running )
{
# Disable and stop.
$ anvil - > System - > disable_daemon ( {
now = > 1 ,
daemon = > $ anvil - > data - > { sys } { daemon } { firewalld } ,
} ) ;
}
return ( 0 ) ;
# Before we do anything, is the firewall even running?
# my $firewalld_running = $anvil->Network->check_firewall({debug => $debug});
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { firewalld_running => $firewalld_running }});
# if (not $firewalld_running)
# {
# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0669"});
# return(1);
# }
# What we do next depends on what we're doing.
my $ host_type = $ anvil - > Get - > host_type ;
my $ host_name = $ anvil - > Get - > short_host_name ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
host_type = > $ host_type ,
host_name = > $ host_name ,
} } ) ;
$ anvil - > Network - > get_ips ( { target = > $ host_name } ) ;
if ( ( $ task eq "check" ) && ( $ port_number eq "" ) )
{
### Check everything.
my $ reload = 0 ;
# Check the base firewalld config.
my $ changes = $ anvil - > Network - > _check_firewalld_conf ( { debug = > $ debug } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
if ( $ changes eq "1" )
{
$ reload = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { reload = > $ reload } } ) ;
}
# Get a list of zones and the interfaces already in them.
$ anvil - > Network - > _get_existing_zone_interfaces ( { debug = > $ debug } ) ;
# What zones do we need, and what zones do we have?
foreach my $ interface ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { network } { $ host_name } { interface } } )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { interface = > $ interface } } ) ;
next if not $ anvil - > data - > { network } { $ host_name } { interface } { $ interface } { ip } ;
my $ ip_address = $ anvil - > data - > { network } { $ host_name } { interface } { $ interface } { ip } ;
my $ subnet_mask = $ anvil - > data - > { network } { $ host_name } { interface } { $ interface } { subnet_mask } ;
my $ default_gateway = $ anvil - > data - > { network } { $ host_name } { interface } { $ interface } { default_gateway } ;
my $ zone = uc ( ( $ interface =~ /^(.*?)_/ ) [ 0 ] ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
ip_address = > $ ip_address ,
subnet_mask = > $ subnet_mask ,
default_gateway = > $ default_gateway ,
zone = > $ zone ,
} } ) ;
$ anvil - > data - > { firewalld } { zones } { $ zone } { needed } = 1 ;
$ anvil - > data - > { firewalld } { zones } { $ zone } { have } = 0 ;
$ anvil - > data - > { firewalld } { zones } { $ zone } { short_description } = "" ;
$ anvil - > data - > { firewalld } { zones } { $ zone } { long_description } = "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"firewalld::zones::${zone}::needed" = > $ anvil - > data - > { firewalld } { zones } { $ zone } { needed } ,
} } ) ;
}
# What zones do we have?
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"path::directories::firewalld_zones_etc" = > $ anvil - > data - > { path } { directories } { firewalld_zones_etc } ,
} } ) ;
local ( * DIRECTORY ) ;
opendir ( DIRECTORY , $ anvil - > data - > { path } { directories } { firewalld_zones_etc } ) ;
while ( my $ file = readdir ( DIRECTORY ) )
{
next if $ file !~ /\.xml$/ ;
my $ full_path = $ anvil - > data - > { path } { directories } { firewalld_zones_etc } . "/" . $ file ;
$ full_path =~ s/\/\//\//g ;
my $ zone = ( $ file =~ /(.*)\.xml$/ ) [ 0 ] ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
file = > $ file ,
full_path = > $ full_path ,
zone = > $ zone ,
} } ) ;
if ( not exists $ anvil - > data - > { firewalld } { zones } { $ zone } )
{
$ anvil - > data - > { firewalld } { zones } { $ zone } { needed } = 0 ;
}
$ anvil - > data - > { firewalld } { zones } { $ zone } { have } = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"firewalld::zones::${zone}::have" = > $ anvil - > data - > { firewalld } { zones } { $ zone } { have } ,
"firewalld::zones::${zone}::needed" = > $ anvil - > data - > { firewalld } { zones } { $ zone } { needed } ,
} } ) ;
my $ file_body = $ anvil - > Storage - > read_file ( {
debug = > $ debug ,
file = > $ full_path ,
force_read = > 1 ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { file_body = > $ file_body } } ) ;
local $@ ;
my $ dom = eval { XML::LibXML - > load_xml ( string = > $ file_body ) ; } ;
if ( $@ )
{
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 1 , key = > "warning_0146" , variables = > {
file = > $ full_path ,
body = > $ file_body ,
error = > $@ ,
} } ) ;
}
else
{
foreach my $ element ( $ dom - > findnodes ( '/zone' ) )
{
$ anvil - > data - > { firewalld } { zones } { $ zone } { short_description } = $ element - > findvalue ( './short' ) ? $ element - > findvalue ( './short' ) : "--" ;
$ anvil - > data - > { firewalld } { zones } { $ zone } { long_description } = $ element - > findvalue ( './description' ) ? $ element - > findvalue ( './description' ) : "--" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"firewalld::zones::${zone}::short_description" = > $ anvil - > data - > { firewalld } { zones } { $ zone } { short_description } ,
"firewalld::zones::${zone}::long_description" = > $ anvil - > data - > { firewalld } { zones } { $ zone } { long_description } ,
} } ) ;
}
foreach my $ service ( $ dom - > findnodes ( '/zone/service' ) )
{
my $ service_name = $ service - > { name } ;
$ anvil - > data - > { firewalld } { zones } { $ zone } { service } { $ service_name } { opened } = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"firewalld::zones::${zone}::service::${service_name}::opened" = > $ anvil - > data - > { firewalld } { zones } { $ zone } { service } { $ service_name } { opened } ,
} } ) ;
}
foreach my $ port ( $ dom - > findnodes ( '/zone/port' ) )
{
my $ port_number = $ port - > { port } ;
my $ port_protocol = $ port - > { protocol } ;
$ anvil - > data - > { firewalld } { zones } { $ zone } { port } { $ port_number } { protocol } { $ port_protocol } { opened } = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"firewalld::zones::${zone}::port::${port_number}::protocol::${port_protocol}::opened" = > $ anvil - > data - > { firewalld } { zones } { $ zone } { port } { $ port_number } { protocol } { $ port_protocol } { opened } ,
} } ) ;
}
}
}
closedir ( DIRECTORY ) ;
# Check if any zones need to be added or managed.
foreach my $ zone ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { firewalld } { zones } } )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"firewalld::zones::${zone}::have" = > $ anvil - > data - > { firewalld } { zones } { $ zone } { have } ,
"firewalld::zones::${zone}::needed" = > $ anvil - > data - > { firewalld } { zones } { $ zone } { needed } ,
} } ) ;
# If this isn't a zone I need, ignore it completely. It might be something the user
# is doing.
next if not $ anvil - > data - > { firewalld } { zones } { $ zone } { needed } ;
# Is this zone one of ours?
if ( ( $ zone !~ /^IFN/ ) && ( $ zone !~ /^BCN/ ) && ( $ zone !~ /^SN/ ) && ( $ zone !~ /^MN/ ) )
{
# Not a zone we manage
next ;
}
# If the zone doesn't exist, create it.
if ( not $ anvil - > data - > { firewalld } { zones } { $ zone } { have } )
{
# Create the zone.
$ reload = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { reload = > $ reload } } ) ;
my $ shell_call = $ anvil - > data - > { path } { exe } { 'firewall-cmd' } . " --permanent --new-zone=\"" . $ zone . "\"" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { debug = > $ debug , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , 'print' = > 1 , level = > $ debug , key = > "log_0708" , variables = > { zone = > $ zone } } ) ;
}
# Do any interfaces need to be added to this zone?
foreach my $ interface ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { network } { $ host_name } { interface } } )
{
next if not $ anvil - > data - > { network } { $ host_name } { interface } { $ interface } { ip } ;
my $ interface_zone = uc ( ( $ interface =~ /^(.*?)_/ ) [ 0 ] ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
interface = > $ interface ,
interface_zone = > $ interface_zone ,
} } ) ;
next if $ interface_zone ne $ zone ;
if ( not exists $ anvil - > data - > { firewall } { zone } { $ zone } { interface } { $ interface } )
{
# Add it.
$ reload = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { reload = > $ reload } } ) ;
my $ shell_call = $ anvil - > data - > { path } { exe } { 'firewall-cmd' } . " --permanent --zone=\"" . $ zone . "\" --add-interface=\"" . $ interface . "\"" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { debug = > $ debug , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , 'print' = > 1 , level = > $ debug , key = > "log_0709" , variables = > {
interface = > $ interface ,
zone = > $ zone ,
} } ) ;
}
}
# Does the short description need to be updated?
if ( $ anvil - > data - > { firewalld } { zones } { $ zone } { short_description } ne $ zone )
{
# Update the short description
my $ shell_call = $ anvil - > data - > { path } { exe } { 'firewall-cmd' } . " --permanent --zone=\"" . $ zone . "\" --set-short=\"" . $ zone . "\"" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { debug = > $ debug , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
}
# Does the long description need to be updated?
my $ description = "" ;
my $ network = "" ;
my $ sequence = 0 ;
if ( $ zone =~ /^(.*?)(\d+)$/ )
{
$ network = $ 1 ;
$ sequence = $ 2 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
network = > $ network ,
sequence = > $ sequence ,
} } ) ;
if ( $ network eq "BCN" )
{
$ description = $ anvil - > Words - > string ( { key = > 'message_0160' } ) . " " . $ sequence ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { description = > $ description } } ) ;
}
elsif ( $ network eq "SN" )
{
$ description = $ anvil - > Words - > string ( { key = > 'message_0161' } ) . " " . $ sequence ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { description = > $ description } } ) ;
}
elsif ( $ network eq "IFN" )
{
$ description = $ anvil - > Words - > string ( { key = > 'message_0162' } ) . " " . $ sequence ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { description = > $ description } } ) ;
}
elsif ( $ network eq "MN" )
{
$ description = $ anvil - > Words - > string ( { key = > 'message_0293' } ) . " " . $ sequence ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { description = > $ description } } ) ;
}
}
if ( ( $ description ) && ( $ anvil - > data - > { firewalld } { zones } { $ zone } { long_description } ne $ description ) )
{
my $ shell_call = $ anvil - > data - > { path } { exe } { 'firewall-cmd' } . " --permanent --zone=\"" . $ zone . "\" --set-description=\"" . $ description . "\"" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { debug = > $ debug , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
}
# Now we need to decide what should be opened for each network.
if ( $ network )
{
# Load the ports we need to open for servers and DRBD resources.
$ anvil - > Network - > _get_server_ports ( { debug = > $ debug } ) ;
$ anvil - > Network - > _get_drbd_ports ( { debug = > $ debug } ) ;
# Log found ports.
foreach my $ port ( sort { $ a <=> $ b } keys % { $ anvil - > data - > { firewall } { server } { port } } )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { "firewall::server::port::$port" = > $ anvil - > data - > { firewall } { server } { port } { $ port } } } ) ;
}
foreach my $ port ( sort { $ a <=> $ b } keys % { $ anvil - > data - > { firewall } { drbd } { port } } )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { "firewall::drbd::port::$port" = > $ anvil - > data - > { firewall } { drbd } { port } { $ port } } } ) ;
}
# If we're a striker, make sure that postgresql
if ( $ host_type eq "striker" )
{
my $ changes = $ anvil - > Network - > _manage_striker_firewall ( { debug = > $ debug , zone = > $ zone } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
if ( $ changes )
{
$ reload = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { reload = > $ reload } } ) ;
}
}
elsif ( $ host_type eq "node" )
{
my $ changes = $ anvil - > Network - > _manage_node_firewall ( { debug = > $ debug , zone = > $ zone } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
if ( $ changes )
{
$ reload = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { reload = > $ reload } } ) ;
}
}
elsif ( $ host_type eq "dr" )
{
my $ changes = $ anvil - > Network - > _manage_dr_firewall ( { debug = > $ debug , zone = > $ zone } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
if ( $ changes )
{
$ reload = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { reload = > $ reload } } ) ;
}
}
}
}
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { reload = > $ reload } } ) ;
if ( $ reload )
{
# Reload
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , 'print' = > 1 , level = > 1 , key = > "log_0716" } ) ;
my $ shell_call = $ anvil - > data - > { path } { exe } { 'firewall-cmd' } . " --reload" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { debug = > $ debug , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
}
}
return ( 0 ) ;
}
= head3 modify_connection
This takes a network manager connection UUID , and changes the requested variable to be set to the given value .
The command output and return code are returned . If there is a problem , C << ! ! error ! ! >> is returned .
Parameters
= head3 uuid ( required )
This is the network manager UUID of the connection being worked on .
= head3 variable ( required )
This is the name of the variable to set ( as shown in C << nmcli connection show <uuid> >> ) .
= head3 value ( optional , defult "" )
This is the value to set . Note that and empty string ( C << "" >> ) deletes the variable .
= cut
sub modify_connection
{
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->modify_connection()" } } ) ;
my $ uuid = defined $ parameter - > { uuid } ? $ parameter - > { uuid } : "" ;
my $ variable = defined $ parameter - > { variable } ? $ parameter - > { variable } : "" ;
my $ value = defined $ parameter - > { value } ? $ parameter - > { value } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
uuid = > $ uuid ,
variable = > $ variable ,
value = > $ value ,
} } ) ;
if ( not $ uuid )
{
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 0 , priority = > "err" , key = > "log_0020" , variables = > { method = > "Network->modify_connection()" , parameter = > "uuid" } } ) ;
return ( "!!error!!" ) ;
}
if ( not $ variable )
{
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 0 , priority = > "err" , key = > "log_0020" , variables = > { method = > "Network->modify_connection()" , parameter = > "variable" } } ) ;
return ( "!!error!!" ) ;
}
my $ shell_call = $ anvil - > data - > { path } { exe } { nmcli } . " connection modify " . $ uuid . " " . $ variable . " " . $ value ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
# NM seems to have a race issue, so we sleep a second after nmcli calls.
sleep 1 ;
return ( $ output , $ return_code ) ;
}
= 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" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my $ pinged = 0 ;
my $ average_ping_time = 0 ;
foreach my $ try ( 1 .. $ count )
{
last if $ pinged ;
$ 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.
my $ is_local = $ anvil - > Network - > is_local ( { host = > $ target } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
target = > $ target ,
is_local = > $ is_local ,
} } ) ;
if ( $ is_local )
{
### Local calls
( $ output , my $ return_code ) = $ anvil - > System - > call ( {
debug = > $ debug ,
shell_call = > $ shell_call ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { output = > $ output , return_code = > $ return_code } } ) ;
}
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 ) ;
}
### TODO: Phase this out when EL8 / ifcfg-X file support is ended.
= 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 --terse --fields name,device,state,type,uuid,filename 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 } } ) ;
my ( $ name , $ device , $ state , $ type , $ uuid , $ filename ) = ( split /:/ , $ line ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
's1:name' = > $ name ,
's2:device' = > $ device ,
's3:state' = > $ state ,
's4:type' = > $ type ,
's5:uuid' = > $ uuid ,
's6:filename' = > $ filename ,
} } ) ;
if ( $ uuid )
{
# Inactive interfaces have a name but not a device;
if ( not $ device )
{
# Read the file, see if we can find it there.
if ( - e $ filename )
{
my $ file_body = $ anvil - > Storage - > read_file ( { debug = > $ debug , file = > $ filename } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { file_body = > $ file_body } } ) ;
foreach my $ line ( split /\n/ , $ file_body )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { line = > $ line } } ) ;
$ line =~ s/#.*$// ;
if ( $ line =~ /DEVICE=(.*)$/ )
{
$ device = $ 1 ;
$ device =~ s/^\s+// ;
$ device =~ s/\s+$// ;
$ device =~ s/"(.*)"$/$1/ ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { device = > $ device } } ) ;
}
}
}
if ( not $ device )
{
# Odd. Well, pull the device off the file name.
$ device = ( $ filename =~ /\/ifcfg-(.*)$/ ) [ 0 ] ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { device = > $ device } } ) ;
}
}
# If I still don't have a device, ignore this.
next if not $ 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 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { say_state = > $ say_state } } ) ;
# Now store the data
$ anvil - > data - > { nmcli } { $ host } { uuid } { $ uuid } { name } = $ name ;
$ anvil - > data - > { nmcli } { $ host } { uuid } { $ uuid } { device } = $ device ;
$ anvil - > data - > { nmcli } { $ host } { uuid } { $ uuid } { 'state' } = $ state ;
$ anvil - > data - > { nmcli } { $ host } { uuid } { $ uuid } { type } = $ type ;
$ 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}::device" = > $ anvil - > data - > { nmcli } { $ host } { uuid } { $ uuid } { device } ,
"nmcli::${host}::uuid::${uuid}::state" = > $ anvil - > data - > { nmcli } { $ host } { uuid } { $ uuid } { 'state' } ,
"nmcli::${host}::uuid::${uuid}::type" = > $ anvil - > data - > { nmcli } { $ host } { uuid } { $ uuid } { type } ,
"nmcli::${host}::uuid::${uuid}::filename" = > $ anvil - > data - > { nmcli } { $ host } { uuid } { $ uuid } { filename } ,
} } ) ;
}
}
return ( 0 ) ;
}
= head2 reset_connection
This method takes a network manager connection UUID and resets the connection by calling it C << down >> and then C << up >> .
If there is a problem , C << ! ! error ! ! >> is returned . Otherwise , the output and return code from the C << up >> call are returned .
Parameters ;
= head3 uuid ( required )
This is the UUID of the connection to reset .
= cut
sub reset_connection
{
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->reset_connection()" } } ) ;
my $ uuid = defined $ parameter - > { uuid } ? $ parameter - > { uuid } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
uuid = > $ uuid ,
} } ) ;
if ( not $ uuid )
{
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 0 , priority = > "err" , key = > "log_0020" , variables = > { method = > "Network->reset_connection()" , parameter = > "uuid" } } ) ;
return ( "!!error!!" ) ;
}
my $ shell_call = $ anvil - > data - > { path } { exe } { nmcli } . " connection down " . $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
# NM seems to have a race issue, so we sleep a second after nmcli calls.
sleep 1 ;
$ shell_call = $ anvil - > data - > { path } { exe } { nmcli } . " connection up " . $ uuid ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > { shell_call = > $ shell_call } } ) ;
( $ output , $ return_code ) = $ anvil - > System - > call ( { shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 2 , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
# NM seems to have a race issue, so we sleep a second after nmcli calls.
sleep 1 ;
return ( $ output , $ return_code ) ;
}
= head2 wait_for_network
This method checks for Network Manager configurations . Any that are found that belong to the Anvil ! [ 1 ] will be watched until their state is C << activated >> .
B <<Note> > : 1 . Interfaces with device name starting with C << bcnX_ >> , C << ifnX_ >> , C << snX_ >> , or C << mnX_ >> , where C << X >> is an integer
B << Note >> : This method only works on Network Manager based systems .
Parameters ;
= head3 timeout ( optional , default '300' )
By default , this method will wait for five minutes . If you want to set a timeout , set this as a number of seconds . If the timeout expires and any bonds are still not up , the method will return C << 1 >> . If this is set to C << 0 >> , it will wait forever .
= cut
sub wait_for_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->wait_for_network()" } } ) ;
### TODO: all configured bonds and slaved interfaces should be 'activated' before this returns, even if their cable is out. Wait for this, with a default 300s timeout.
my $ timeout = defined $ parameter - > { timeout } ? $ parameter - > { timeout } : 300 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
timeout = > $ timeout ,
} } ) ;
# If timeout wasn't set, but network::wait_for_network::timeout is set, use it.
if ( ( exists $ anvil - > data - > { network } { wait_for_network } { timeout } ) && ( $ anvil - > data - > { network } { wait_for_network } { timeout } =~ /^\d+$/ ) )
{
$ timeout = $ anvil - > data - > { network } { wait_for_network } { timeout } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { timeout = > $ timeout } } ) ;
}
my $ short_host_name = $ anvil - > Get - > short_host_name ( ) ;
my $ directory = $ anvil - > data - > { path } { directories } { NetworkManager } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
short_host_name = > $ short_host_name ,
directory = > $ directory ,
} } ) ;
if ( not - d $ directory )
{
return ( 0 ) ;
}
$ directory . = "/system-connections" ;
$ directory =~ s/\/\//\//g ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { directory = > $ directory } } ) ;
# Find any bonds.
local ( * DIRECTORY ) ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , key = > "log_0018" , variables = > { directory = > $ directory } } ) ;
opendir ( DIRECTORY , $ directory ) ;
while ( my $ file = readdir ( DIRECTORY ) )
{
next if $ file !~ /\.nmconnection$/ ;
my $ full_path = $ directory . "/" . $ file ;
$ full_path =~ s/\/\//\//g ;
$ 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 } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { file_body = > $ file_body } } ) ;
my $ uuid = "" ;
my $ type = "" ;
my $ interface_name = "" ;
my $ id = "" ;
my $ parent_bond = "" ;
foreach my $ line ( split /\n/ , $ file_body )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { line = > $ line } } ) ;
# Collect the UUID
if ( $ line =~ /^uuid=(.*)$/ )
{
$ uuid = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { uuid = > $ uuid } } ) ;
}
# ethernet, bond, or bridge
if ( $ line =~ /^type=(.*)$/ )
{
$ type = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { type = > $ type } } ) ;
}
# Get the device name
if ( $ line =~ /^interface-name=(.*)$/ )
{
$ interface_name = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { interface_name = > $ interface_name } } ) ;
}
if ( $ line =~ /id=(.*)$/ )
{
$ id = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { id = > $ id } } ) ;
}
# Find the parent bond, if this is a child interface
if ( $ line =~ /^master=(.*)$/ )
{
$ parent_bond = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { parent_bond = > $ parent_bond } } ) ;
}
}
if ( $ uuid )
{
# If the interface_name is multiple names, pull our the name we use (if name)
if ( ( not $ interface_name ) && ( $ id ) )
{
$ interface_name = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { interface_name = > $ interface_name } } ) ;
}
if ( $ interface_name =~ /;/ )
{
$ interface_name =~ s/;$// ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { interface_name = > $ interface_name } } ) ;
foreach my $ this_interface_name ( split /;/ , $ interface_name )
{
if ( ( $ this_interface_name =~ /^bcn\d+_/ ) or
( $ this_interface_name =~ /^ifn\d+_/ ) or
( $ this_interface_name =~ /^sn\d+_/ ) or
( $ this_interface_name =~ /^mn\d+_/ ) )
{
$ interface_name = $ this_interface_name ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { interface_name = > $ interface_name } } ) ;
}
}
}
next if not $ interface_name ;
# Is this an interface we care about?
if ( ( $ interface_name =~ /^bcn\d+_/ ) or
( $ interface_name =~ /^ifn\d+_/ ) or
( $ interface_name =~ /^sn\d+_/ ) or
( $ interface_name =~ /^mn\d+_/ ) )
{
# Watch for this interface
$ anvil - > data - > { network } { watch } { $ interface_name } { uuid } = $ uuid ;
$ anvil - > data - > { network } { watch } { $ interface_name } { type } = $ type ;
$ anvil - > data - > { network } { watch } { $ interface_name } { ready } = 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"network::watch::${interface_name}::uuid" = > $ anvil - > data - > { network } { watch } { $ interface_name } { uuid } ,
"network::watch::${interface_name}::type" = > $ anvil - > data - > { network } { watch } { $ interface_name } { type } ,
} } ) ;
}
}
}
closedir ( DIRECTORY ) ;
my $ waiting = 1 ;
my $ end_time = $ timeout ? time + $ timeout : 0 ;
my $ duration = $ end_time ? $ timeout - time : 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
end_time = > $ end_time ,
duration = > $ duration ,
} } ) ;
while ( $ waiting )
{
$ anvil - > Network - > read_nmcli ( {
debug = > $ debug ,
host = > $ short_host_name ,
} ) ;
$ waiting = 0 ;
foreach my $ interface_name ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { network } { watch } } )
{
next if $ anvil - > data - > { network } { watch } { $ interface_name } { ready } ;
my $ uuid = $ anvil - > data - > { network } { watch } { $ interface_name } { uuid } ;
my $ type = $ anvil - > data - > { network } { watch } { $ interface_name } { type } ;
my $ state = defined $ anvil - > data - > { nmcli } { $ short_host_name } { uuid } { $ uuid } { 'state' } ? $ anvil - > data - > { nmcli } { $ short_host_name } { uuid } { $ uuid } { 'state' } : 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
's1:interface_name' = > $ interface_name ,
's2:type' = > $ type ,
's3:uuid' = > $ uuid ,
's4:state' = > $ state ,
} } ) ;
if ( ( $ state eq "activated" ) or ( $ state eq "1" ) )
{
$ anvil - > data - > { network } { watch } { $ interface_name } { ready } = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"network::watch::${interface_name}::ready" = > $ anvil - > data - > { network } { watch } { $ interface_name } { ready } ,
} } ) ;
}
}
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { waiting = > $ waiting } } ) ;
if ( $ waiting )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { end_time = > $ end_time } } ) ;
if ( $ end_time )
{
if ( time > $ end_time )
{
# We're done.
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 1 , key = > "log_0847" , variables = > { timeout = > $ timeout } } ) ;
return ( 1 ) ;
}
my $ time_left = $ end_time - time ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { time_left = > $ time_left } } ) ;
}
# Sleep for a few seconds.
sleep 5 ;
}
}
return ( 0 ) ;
}
= head2 wait_on_nm_online
This method calls C << nm - online - - wait - for - startup - - timeout X >> , which in turn waits for Network Manager to report C << startup complete >> in the journald logs . The default timeout used here is C << 120 >> seconds ( as opposed to the default of C << 30 >> used by C << nm - online >> itself ) .
From our testing , given the complexity of the network in Anvil ! clusters , this much time isn ' t out of the ordinaryl
Feb 24 19 : 13 : 17 an - a01n01 . ci . alteeve . com NetworkManager [ 1003 ] : <info> [ 1708801997.5155 ] NetworkManager ( version 1.44 .0 - 4 . el9_3 ) is starting ... ( boot:833ea5be - eb44 - 4214 - 9e2 d - 8 c6281dec9b6 )
...
Feb 24 19 : 14 : 53 an - a01n01 . ci . alteeve . com NetworkManager [ 1003 ] : <info> [ 1708802093.9684 ] manager: startup complete
B << Note >> : This method only works on Network Manager based systems .
The return code from C << nm - online >> is returned . See C << man nm - online >> for details , but the main return codes are C << 0 >> meaning the connection came up within the timeout , C << 1 >> if the connection failed to come up within the timeout , and C << 2 >> if there was any error .
Parameters ;
= head3 timeout ( optional , default '120' )
By default , this method will wait for two minutes . If you want to set a timeout , set this as a number of seconds . If the timeout expires and any bonds are still not up , the method will return C << 1 >> . If this is set to C << 0 >> , it will wait forever .
= cut
sub wait_on_nm_online
{
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->wait_on_nm_online()" } } ) ;
my $ timeout = defined $ parameter - > { timeout } ? $ parameter - > { timeout } : 120 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
timeout = > $ timeout ,
} } ) ;
if ( ( not $ timeout ) or ( $ timeout !~ /^\d+$/ ) )
{
# Invalid timeout.
$ timeout = 120 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { timeout = > $ timeout } } ) ;
}
my $ shell_call = $ anvil - > data - > { path } { exe } { 'nm-online' } . " --wait-for-startup --quiet --timeout " . $ timeout ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { debug = > $ debug , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
's1:output' = > $ output ,
's2:return_code' = > $ return_code ,
} } ) ;
return ( $ return_code ) ;
}
#############################################################################################################
# Private functions #
#############################################################################################################
# Return '1' if changed, '0' if not changed.
sub _check_firewalld_conf
{
my $ self = shift ;
my $ parameter = shift ;
my $ anvil = $ self - > parent ;
my $ debug = defined $ parameter - > { debug } ? $ parameter - > { debug } : 3 ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , key = > "log_0125" , variables = > { method = > "Network->_check_firewalld_conf()" } } ) ;
# Read in the firewalld.conf file.
my $ changes = 0 ;
my $ new_firewalld_conf = "" ;
my $ old_firewalld_conf = $ anvil - > Storage - > read_file ( {
debug = > $ debug ,
file = > $ anvil - > data - > { path } { configs } { 'firewalld.conf' } ,
force_read = > 1 ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { old_firewalld_conf = > $ old_firewalld_conf } } ) ;
# For now, the only thing we want to change is to disable 'AllowZoneDrifting'
# * firewalld[458395]: WARNING: AllowZoneDrifting is enabled. This is considered an insecure configuration option. It will be removed in a future release. Please consider disabling it now.
# Possible values; "yes", "no". Defaults to "yes".
my $ allowzonedrifting_seen = 0 ;
foreach my $ line ( split /\n/ , $ old_firewalld_conf )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { line = > $ line } } ) ;
if ( $ line =~ /^AllowZoneDrifting=(.*)$/ )
{
my $ old_value = $ 1 ;
$ allowzonedrifting_seen = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
old_value = > $ old_value ,
allowzonedrifting_seen = > $ allowzonedrifting_seen ,
} } ) ;
if ( $ old_value ne "no" )
{
# Change needed.
$ changes = 1 ;
$ new_firewalld_conf . = "AllowZoneDrifting=no\n" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
changes = > $ changes ,
new_firewalld_conf = > $ new_firewalld_conf ,
} } ) ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 1 , key = > "log_0707" } ) ;
next ;
}
}
$ new_firewalld_conf . = $ line . "\n" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { new_firewalld_conf = > $ new_firewalld_conf } } ) ;
}
if ( not $ allowzonedrifting_seen )
{
$ new_firewalld_conf . = $ anvil - > Words - > string ( { key = > 'message_0292' } ) . "\n" ;
$ new_firewalld_conf . = "AllowZoneDrifting=no\n" ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 1 , key = > "log_0707" } ) ;
}
if ( $ changes )
{
# Write the file out
$ anvil - > Storage - > write_file ( {
debug = > $ debug ,
backup = > 1 ,
overwrite = > 1 ,
body = > $ new_firewalld_conf ,
file = > $ anvil - > data - > { path } { configs } { 'firewalld.conf' } ,
user = > "root" ,
group = > "root" ,
mode = > "0644" ,
} ) ;
}
return ( $ changes ) ;
}
sub _get_existing_zone_interfaces
{
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_existing_zone_interfaces()" } } ) ;
my $ this_zone = "" ;
my $ shell_call = $ anvil - > data - > { path } { exe } { 'firewall-cmd' } . " --get-active-zones" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ 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 ,
} } ) ;
foreach my $ line ( split /\n/ , $ output )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { line = > $ line } } ) ;
if ( $ line =~ /interfaces: (.*)$/ )
{
my $ interfaces = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { interfaces = > $ interfaces } } ) ;
next if not $ this_zone ;
foreach my $ interface ( split /\s+/ , $ interfaces )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { interface = > $ interface } } ) ;
$ anvil - > data - > { firewall } { zone } { $ this_zone } { interface } { $ interface } = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"firewall::zone::${this_zone}::interface::${interface}" = > $ anvil - > data - > { firewall } { zone } { $ this_zone } { interface } { $ interface } ,
} } ) ;
}
}
else
{
$ this_zone = $ line ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { this_zone = > $ this_zone } } ) ;
}
}
return ( 0 ) ;
}
# This looks for all servers running here and stores their ports in a hash.
sub _get_server_ports
{
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_server_ports()" } } ) ;
my $ shell_call = $ anvil - > data - > { path } { exe } { setsid } . " --wait " . $ anvil - > data - > { path } { exe } { virsh } . " list --name" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { debug = > $ debug , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
's1:output' = > $ output ,
's2:return_code' = > $ return_code ,
} } ) ;
my $ servers = [] ;
foreach my $ server ( split /\n/ , $ output )
{
$ server = $ anvil - > Words - > clean_spaces ( { string = > $ server } ) ;
next if not $ server ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { server = > $ server } } ) ;
push @ { $ servers } , $ server ;
}
foreach my $ server ( sort { $ a cmp $ b } @ { $ servers } )
{
my $ shell_call = $ anvil - > data - > { path } { exe } { setsid } . " --wait " . $ anvil - > data - > { path } { exe } { virsh } . " dumpxml " . $ server ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { debug = > $ debug , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
's1:output' = > $ output ,
's2: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 =~ /<graphics/ ) && ( $ line =~ /port='(\d+)'/ ) )
{
my $ port = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { port = > $ port } } ) ;
$ anvil - > data - > { firewall } { server } { port } { $ port } = $ server ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"firewall::server::port::$port" = > $ anvil - > data - > { firewall } { server } { port } { $ port } ,
} } ) ;
last ;
}
}
}
return ( 0 ) ;
}
# This looks for all drbd resources configured here and stores their ports in a hash.
sub _get_drbd_ports
{
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_drbd_ports()" } } ) ;
my $ directory = $ anvil - > data - > { path } { directories } { drbd_resources } ;
if ( not - d $ directory )
{
# DRBD isn't installed.
return ( 0 ) ;
}
local ( * DIRECTORY ) ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , key = > "log_0018" , variables = > { directory = > $ directory } } ) ;
opendir ( DIRECTORY , $ directory ) ;
while ( my $ file = readdir ( DIRECTORY ) )
{
next if $ file !~ /\.res$/ ;
my $ full_path = $ directory . "/" . $ file ;
$ full_path =~ s/\/\//\//g ;
$ 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 } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { file_body = > $ file_body } } ) ;
foreach my $ line ( split /\n/ , $ file_body )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { line = > $ line } } ) ;
if ( $ line =~ /host\s+.*?address.*?\s(\d.*?):(\d+);/ )
{
my $ ip = $ 1 ;
my $ port = $ 2 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"s1:ip" = > $ ip ,
"s2:port" = > $ port ,
} } ) ;
$ anvil - > data - > { firewall } { drbd } { port } { $ port } = $ ip ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"firewall::drbd::port::$port" = > $ anvil - > data - > { firewall } { drbd } { port } { $ port } ,
} } ) ;
}
}
}
closedir ( DIRECTORY ) ;
return ( 0 ) ;
}
# This looks in qemu.conf for the minimum and maximum TCP ports
sub _get_live_migration_ports
{
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_live_migration_ports()" } } ) ;
my $ default_minimum = 49152 ;
my $ default_maximum = 49215 ;
my $ set_minimum = 0 ;
my $ set_maximum = 0 ;
my $ file_body = $ anvil - > Storage - > read_file ( { debug = > $ debug , file = > $ anvil - > data - > { path } { configs } { 'qemu.conf' } } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { file_body = > $ file_body } } ) ;
foreach my $ line ( split /\n/ , $ file_body )
{
if ( ( $ line =~ /^#/ ) && ( $ line =~ /migration_port_min.*?=.*?(\d+)$/ ) )
{
$ default_minimum = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { default_minimum = > $ default_minimum } } ) ;
}
elsif ( ( $ line =~ /^#/ ) && ( $ line =~ /migration_port_max.*?=.*?(\d+)$/ ) )
{
$ default_maximum = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { default_maximum = > $ default_maximum } } ) ;
}
elsif ( $ line =~ /migration_port_min.*?=.*?(\d+)$/ )
{
$ set_minimum = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { set_minimum = > $ set_minimum } } ) ;
}
elsif ( $ line =~ /migration_port_max.*?=.*?(\d+)$/ )
{
$ set_maximum = $ 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { set_maximum = > $ set_maximum } } ) ;
}
}
if ( not $ set_minimum )
{
$ set_minimum = $ default_minimum ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { set_minimum = > $ set_minimum } } ) ;
}
if ( not $ set_maximum )
{
$ set_maximum = $ default_maximum ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { set_maximum = > $ set_maximum } } ) ;
}
return ( $ set_minimum , $ set_maximum ) ;
}
sub _manage_port
{
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->_manage_port()" } } ) ;
my $ port = defined $ parameter - > { port } ? $ parameter - > { port } : "" ;
my $ protocol = defined $ parameter - > { protocol } ? $ parameter - > { protocol } : "" ;
my $ task = defined $ parameter - > { task } ? $ parameter - > { task } : "" ;
my $ zone = defined $ parameter - > { zone } ? $ parameter - > { zone } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
port = > $ port ,
protocol = > $ protocol ,
task = > $ task ,
zone = > $ zone ,
} } ) ;
if ( ( not $ port ) or ( not $ protocol ) or ( not $ task ) or ( not $ zone ) )
{
return ( "!!error!!" ) ;
}
if ( ( $ task ne "close" ) && ( $ task ne "open" ) )
{
return ( "!!error!!" ) ;
}
if ( ( $ protocol ne "tcp" ) && ( $ protocol ne "udp" ) )
{
return ( "!!error!!" ) ;
}
if ( $ port =~ /^(\d+)-(\d+)$/ )
{
my $ minimum_port = $ 1 ;
my $ maximum_port = $ 2 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"s1:minimum_port" = > $ minimum_port ,
"s2:maximum_port" = > $ maximum_port ,
} } ) ;
if ( ( $ minimum_port < 1 ) or ( $ minimum_port > 66635 ) or
( $ maximum_port < 1 ) or ( $ maximum_port > 66635 ) )
{
return ( "!!error!!" ) ;
}
}
elsif ( ( $ port =~ /\D/ ) or ( $ port < 1 ) or ( $ port > 65535 ) )
{
return ( "!!error!!" ) ;
}
# Do we need to actually do this?
if ( $ task eq "close" )
{
# Is it open?
if ( ( not exists $ anvil - > data - > { firewalld } { zones } { $ zone } { port } { $ port } { protocol } { $ protocol } ) or
( not $ anvil - > data - > { firewalld } { zones } { $ zone } { port } { $ port } { protocol } { $ protocol } { opened } ) )
{
# Already closed.
return ( 0 ) ;
}
}
elsif ( $ anvil - > data - > { firewalld } { zones } { $ zone } { port } { $ port } { protocol } { $ protocol } { opened } )
{
# Already opened.
return ( 0 ) ;
}
my $ shell_call = "" ;
if ( $ task eq "close" )
{
$ shell_call = $ anvil - > data - > { path } { exe } { 'firewall-cmd' } . " --permanent --zone=" . $ zone . " --remove-port=" . $ port . "/" . $ protocol ;
if ( $ port =~ /-/ )
{
# Range
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , 'print' = > 1 , level = > 1 , key = > "log_0715" , variables = > {
port = > $ port ,
protocol = > $ protocol ,
zone = > $ zone ,
} } ) ;
}
else
{
# Single port
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , 'print' = > 1 , level = > 1 , key = > "log_0714" , variables = > {
port = > $ port ,
protocol = > $ protocol ,
zone = > $ zone ,
} } ) ;
}
}
else
{
$ shell_call = $ anvil - > data - > { path } { exe } { 'firewall-cmd' } . " --permanent --zone=" . $ zone . " --add-port=" . $ port . "/" . $ protocol ;
if ( $ port =~ /-/ )
{
# Range
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , 'print' = > 1 , level = > 1 , key = > "log_0713" , variables = > {
port = > $ port ,
protocol = > $ protocol ,
zone = > $ zone ,
} } ) ;
}
else
{
# Single port
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , 'print' = > 1 , level = > 1 , key = > "log_0712" , variables = > {
port = > $ port ,
protocol = > $ protocol ,
zone = > $ zone ,
} } ) ;
}
}
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { debug = > $ debug , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
'output' = > $ output ,
'return_code' = > $ return_code ,
} } ) ;
return ( 1 ) ;
}
# Returns '0' if nothing was done, '1' otherwise.
sub _manage_service
{
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->_manage_service()" } } ) ;
my $ service = defined $ parameter - > { service } ? $ parameter - > { service } : "" ;
my $ task = defined $ parameter - > { task } ? $ parameter - > { task } : "" ;
my $ zone = defined $ parameter - > { zone } ? $ parameter - > { zone } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
service = > $ service ,
task = > $ task ,
zone = > $ zone ,
} } ) ;
if ( ( not $ service ) or ( not $ task ) or ( not $ zone ) )
{
return ( "!!error!!" ) ;
}
if ( ( $ task ne "close" ) && ( $ task ne "open" ) )
{
return ( "!!error!!" ) ;
}
# Do we actually need to do something?
if ( $ task eq "close" )
{
# Is it open?
if ( ( not exists $ anvil - > data - > { firewalld } { zones } { $ zone } { service } { $ service } ) or
( not $ anvil - > data - > { firewalld } { zones } { $ zone } { service } { $ service } { opened } ) )
{
# Already closed.
return ( 0 ) ;
}
}
elsif ( $ anvil - > data - > { firewalld } { zones } { $ zone } { service } { $ service } { opened } )
{
# Already opened.
return ( 0 ) ;
}
my $ shell_call = "" ;
if ( $ task eq "close" )
{
$ shell_call = $ anvil - > data - > { path } { exe } { 'firewall-cmd' } . " --permanent --zone=" . $ zone . " --remove-service=" . $ service ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , 'print' = > 1 , level = > 1 , key = > "log_0711" , variables = > {
service = > $ service ,
zone = > $ zone ,
} } ) ;
}
else
{
$ shell_call = $ anvil - > data - > { path } { exe } { 'firewall-cmd' } . " --permanent --zone=" . $ zone . " --add-service=" . $ service ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , 'print' = > 1 , level = > 1 , key = > "log_0710" , variables = > {
service = > $ service ,
zone = > $ zone ,
} } ) ;
}
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { debug = > $ debug , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
'output' = > $ output ,
'return_code' = > $ return_code ,
} } ) ;
return ( 1 ) ;
}
sub _manage_dr_firewall
{
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->_manage_dr_firewall()" } } ) ;
my $ zone = defined $ parameter - > { zone } ? $ parameter - > { zone } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
zone = > $ zone ,
} } ) ;
# Set the services we want opened.
my $ changes = 0 ;
my @ bcn_services = ( "audit" , "ssh" , "zabbix-agent" , "zabbix-server" ) ;
my @ ifn_services = ( "audit" , "ssh" , "zabbix-agent" , "zabbix-server" ) ;
my @ sn_services = ( "ssh" ) ; # May use as a backup corosync network later
# We need to make sure that the postgresql service is open for all networks.
if ( $ zone =~ /BCN/ )
{
foreach my $ service ( @ bcn_services )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { service = > $ service } } ) ;
my $ chenged = $ anvil - > Network - > _manage_service ( {
debug = > $ debug ,
service = > $ service ,
zone = > $ zone ,
task = > "open" ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
}
if ( $ zone =~ /IFN/ )
{
foreach my $ service ( @ ifn_services )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { service = > $ service } } ) ;
my $ chenged = $ anvil - > Network - > _manage_service ( {
debug = > $ debug ,
service = > $ service ,
zone = > $ zone ,
task = > "open" ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
}
# Open VNC/Spice/etc ports for servers/
if ( ( $ zone =~ /BCN/ ) or ( $ zone =~ /IFN/ ) )
{
foreach my $ port ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { firewall } { server } { port } } )
{
# Make sure the port is open.
my $ chenged = $ anvil - > Network - > _manage_port ( {
debug = > $ debug ,
port = > $ port ,
protocol = > "tcp" ,
task = > "open" ,
zone = > $ zone ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
# Open up live migration ports. It's possible that DR could migrate off to a prod Anvil!
# post-incident.
my ( $ migration_minimum , $ migration_maximum ) = $ anvil - > Network - > _get_live_migration_ports ( { debug = > $ debug } ) ;
my $ range = $ migration_minimum . "-" . $ migration_maximum ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"s1:migration_minimum" = > $ migration_minimum ,
"s2:migration_maximum" = > $ migration_maximum ,
"s3:range" = > $ range ,
"s4:zone" = > $ zone ,
} } ) ;
my $ chenged = $ anvil - > Network - > _manage_port ( {
debug = > $ debug ,
port = > $ range ,
protocol = > "tcp" ,
task = > "open" ,
zone = > $ zone ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
if ( $ zone =~ /SN/ )
{
foreach my $ service ( @ sn_services )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { service = > $ service } } ) ;
my $ chenged = $ anvil - > Network - > _manage_service ( {
debug = > $ debug ,
service = > $ service ,
zone = > $ zone ,
task = > "open" ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
# Open all the ports DRBD needs.
foreach my $ port ( sort { $ a <=> $ b } keys % { $ anvil - > data - > { firewall } { drbd } { port } } )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
port = > $ port ,
zone = > $ zone ,
} } ) ;
my $ chenged = $ anvil - > Network - > _manage_port ( {
debug = > $ debug ,
port = > $ port ,
protocol = > "tcp" ,
task = > "open" ,
zone = > $ zone ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
}
return ( $ changes ) ;
}
sub _manage_node_firewall
{
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->_manage_node_firewall()" } } ) ;
my $ zone = defined $ parameter - > { zone } ? $ parameter - > { zone } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
zone = > $ zone ,
} } ) ;
# We open dhcp, tftp, and dns on the BCN for the install target feature. DNS is not currently
# provided, but it should be added later.
my $ changes = 0 ;
my @ bcn_services = ( "audit" , "high-availability" , "ssh" , "zabbix-agent" , "zabbix-server" ) ;
my @ ifn_services = ( "audit" , "ssh" , "zabbix-agent" , "zabbix-server" ) ;
my @ sn_services = ( "high-availability" , "ssh" ) ; # May use as a backup corosync network later
my @ mn_services = ( "high-availability" , "ssh" ) ; # May use as a backup corosync network later
# We need to make sure that the postgresql service is open for all networks.
if ( $ zone =~ /BCN/ )
{
foreach my $ service ( @ bcn_services )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { service = > $ service } } ) ;
my $ chenged = $ anvil - > Network - > _manage_service ( {
debug = > $ debug ,
service = > $ service ,
zone = > $ zone ,
task = > "open" ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
}
if ( $ zone =~ /IFN/ )
{
foreach my $ service ( @ ifn_services )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { service = > $ service } } ) ;
my $ chenged = $ anvil - > Network - > _manage_service ( {
debug = > $ debug ,
service = > $ service ,
zone = > $ zone ,
task = > "open" ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
}
# Open VNC/Spice/etc ports for servers/
if ( ( $ zone =~ /BCN/ ) or ( $ zone =~ /IFN/ ) )
{
foreach my $ port ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { firewall } { server } { port } } )
{
# Make sure the port is open.
my $ chenged = $ anvil - > Network - > _manage_port ( {
debug = > $ debug ,
port = > $ port ,
protocol = > "tcp" ,
task = > "open" ,
zone = > $ zone ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
}
if ( $ zone =~ /SN/ )
{
foreach my $ service ( @ sn_services )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { service = > $ service } } ) ;
my $ chenged = $ anvil - > Network - > _manage_service ( {
debug = > $ debug ,
service = > $ service ,
zone = > $ zone ,
task = > "open" ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
# Open all the ports DRBD needs.
foreach my $ port ( sort { $ a <=> $ b } keys % { $ anvil - > data - > { firewall } { drbd } { port } } )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
port = > $ port ,
zone = > $ zone ,
} } ) ;
my $ chenged = $ anvil - > Network - > _manage_port ( {
debug = > $ debug ,
port = > $ port ,
protocol = > "tcp" ,
task = > "open" ,
zone = > $ zone ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
}
if ( $ zone =~ /MN/ )
{
foreach my $ service ( @ mn_services )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { service = > $ service } } ) ;
my $ chenged = $ anvil - > Network - > _manage_service ( {
debug = > $ debug ,
service = > $ service ,
zone = > $ zone ,
task = > "open" ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
}
if ( ( $ zone =~ /BCN/ ) or ( $ zone =~ /MN/ ) )
{
### TODO: Find any old instances with a different port range and remove it if needed.
# Open up live migration ports
my ( $ migration_minimum , $ migration_maximum ) = $ anvil - > Network - > _get_live_migration_ports ( { debug = > $ debug } ) ;
my $ range = $ migration_minimum . "-" . $ migration_maximum ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"s1:migration_minimum" = > $ migration_minimum ,
"s2:migration_maximum" = > $ migration_maximum ,
"s3:range" = > $ range ,
"s4:zone" = > $ zone ,
} } ) ;
my $ chenged = $ anvil - > Network - > _manage_port ( {
debug = > $ debug ,
port = > $ range ,
protocol = > "tcp" ,
task = > "open" ,
zone = > $ zone ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
return ( $ changes ) ;
}
sub _manage_striker_firewall
{
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->_manage_striker_firewall()" } } ) ;
my $ zone = defined $ parameter - > { zone } ? $ parameter - > { zone } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
zone = > $ zone ,
} } ) ;
if ( ( $ zone !~ /^BCN/ ) && ( $ zone !~ /^IFN/ ) )
{
# Not a zone used by striker.
return ( 0 ) ;
}
# We open dhcp, tftp, and dns on the BCN for the install target feature. DNS is not currently
# provided, but it should be added later.
my $ changes = 0 ;
my @ services = ( "audit" , "http" , "https" , "postgresql" , "ssh" , "vnc-server" , "zabbix-agent" , "zabbix-server" , "vnc-server" ) ;
my @ bcn_services = ( "dhcp" , "dns" , "tftp" ) ;
my @ ifn_services = ( ) ;
# We need to make sure that the postgresql service is open for all networks.
if ( $ zone =~ /BCN/ )
{
foreach my $ service ( @ bcn_services )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { service = > $ service } } ) ;
push @ services , $ service ;
}
}
foreach my $ service ( @ services )
{
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { service = > $ service } } ) ;
my $ chenged = $ anvil - > Network - > _manage_service ( {
debug = > $ debug ,
service = > $ service ,
zone = > $ zone ,
task = > "open" ,
} ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { chenged = > $ chenged } } ) ;
if ( $ chenged )
{
$ changes = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { changes = > $ changes } } ) ;
}
}
return ( $ changes ) ;
}
1 ;