diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm index 5b235a5f..78ab0a10 100644 --- a/Anvil/Tools.pm +++ b/Anvil/Tools.pm @@ -1169,7 +1169,7 @@ sub _set_paths ip => "/usr/sbin/ip", 'ipmi-oem' => "/usr/sbin/ipmi-oem", ipmitool => "/usr/bin/ipmitool", - ### NOTE: When System->manage_firewall() is done, search for and replace all + ### NOTE: When Network->manage_firewall() is done, search for and replace all ### instances where iptables is called and replace with firewall-cmd ### calls iptables => "/usr/sbin/iptables", diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index 9076a0e7..6b5e2404 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -1154,12 +1154,7 @@ sub configure_pgsql } # Make sure the psql TCP port is open. - $anvil->data->{database}{$uuid}{port} = 5432 if not $anvil->data->{database}{$uuid}{port}; - my $port_status = $anvil->System->manage_firewall({ - task => "open", - port_number => $anvil->data->{database}{$uuid}{port}, - }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { port_status => $port_status }}); + $anvil->Network->manage_firewall({debug => $debug}); return(0); } @@ -14322,8 +14317,9 @@ sub load_database my $start_time = time; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { start_time => $start_time }}); - ### TODO: Replace this with System->manage_firewall(). # Throw up the firewall. Have the open call ready in case we hit an error. + $anvil->Network->manage_firewall({debug => $debug}); + ### TODO: Delete this when done with manage_firewall(). my $block_call = $anvil->data->{path}{exe}{iptables}." -I INPUT -p tcp --dport 5432 -j REJECT"; my $open_call = $anvil->data->{path}{exe}{iptables}." -D INPUT -p tcp --dport 5432 -j REJECT"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { block_call => $block_call }}); diff --git a/Anvil/Tools/Network.pm b/Anvil/Tools/Network.pm index 9b3e437e..b06e9636 100644 --- a/Anvil/Tools/Network.pm +++ b/Anvil/Tools/Network.pm @@ -14,22 +14,25 @@ my $THIS_FILE = "Network.pm"; ### Methods; # bridge_info -# check_internet +# check_firewall # check_network +# check_internet # download # find_matches # find_target_ip # get_company_from_mac -# get_ips # get_ip_from_mac +# get_ips # get_network # is_local # is_our_interface +# is_ip_in_network # load_interfces # load_ips -# is_ip_in_network +# manage_firewall # ping # read_nmcli +# _check_firewalld_conf =pod @@ -224,8 +227,69 @@ sub bridge_info } +=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; + + # 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 => 0, priority => "err", 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 => 0, priority => "err", key => "log_0128"}); + return(0); + } + } + + + return($running); +} + + =head2 check_network +B<< NOTE >>: This method is not yet implemented. + This method checks to see if bridges and the links in bonds are up. It can simply report the bridge, bond and link states, or it can try to bring up interfaces that are down. This method returns C<< 0 >> if nothing was done. It returns C<< 1 >> if any repairs were done. @@ -544,7 +608,7 @@ sub check_network foreach my $bond (sort {$a cmp $b} keys %{$anvil->data->{bond_health}}) { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "s1:bond_health::${bond}::name" => $anvil->data->{bond_health}{$bond}{name}, "s2:bond_health::${bond}::up" => $anvil->data->{bond_health}{$bond}{up}, "s3:bond_health::${bond}::configured_links" => $anvil->data->{bond_health}{$bond}{configured_links}, @@ -553,7 +617,7 @@ sub check_network }}); foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{bond_health}{$bond}{interface}}) { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "s1:bond_health::${bond}::interface::${interface}::name" => $anvil->data->{bond_health}{$bond}{interface}{$interface}{name}, "s2:bond_health::${bond}::interface::${interface}::in_bond" => $anvil->data->{bond_health}{$bond}{interface}{$interface}{in_bond}, "s3:bond_health::${bond}::interface::${interface}::up" => $anvil->data->{bond_health}{$bond}{interface}{$interface}{up}, @@ -1271,81 +1335,60 @@ sub find_target_ip } -=head2 load_interfces - -This loads all network information for the given host UUID. - -The main difference from C<< ->load_ips() >> is that this method loads information about all interfaces, regardless of if they have an IP, as well as their link state and link information. - -The loaded data will be stored as: +=head2 get_company_from_mac -* C<< machine::::interface:::: +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 clear (optional, default '1') - -When set, any previously known information is cleared. Specifically, the C<< network::> >> 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') +=head3 mac (required) -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. +This is the first six bytes of the mac address, C<< xx:xx:xx >> format, being searched for. =cut -sub load_interfces +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->load_interfces()" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Network->get_company_from_mac()" }}); - 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} : ""; + my $mac = defined $parameter->{mac} ? lc($parameter->{mac}) : ""; + my $company = ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - clear => $clear, - host => $host, - host_uuid => $host_uuid, + mac => $mac, }}); - if (not $host_uuid) + if (not $mac) { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->load_interfces()", parameter => "host_uuid" }}); + $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(""); } - if (not $host) + # Have I already looked this one up? + if ($anvil->data->{cache}{mac_to_oui}{$mac}) { - $host = $host_uuid; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }}); + # Yup, no need to process. + return($anvil->data->{cache}{mac_to_oui}{$mac}); } - if (($clear) && (exists $anvil->data->{network}{$host})) + my $valid_mac = $anvil->Validate->mac({mac => $mac}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { mac => $mac }}); + if ($valid_mac) { - delete $anvil->data->{network}{$host}; + # 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(""); } - # 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)." -;"; + 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}; @@ -1353,762 +1396,471 @@ AND results => $results, count => $count, }}); - foreach my $row (@{$results}) + if ($count) { - 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}, - }}); + $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 }}); } - # 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)." -;"; + 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 }}); - $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); - $count = @{$results}; + 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}) + if ($count) { - my $bond_uuid = defined $row->[0] ? $row->[0] : ""; - my $bond_name = defined $row->[1] ? $row->[1] : ""; - my $bond_mode = defined $row->[2] ? $row->[2] : ""; - my $bond_mtu = defined $row->[3] ? $row->[3] : ""; - my $bond_primary_interface = defined $row->[4] ? $row->[4] : ""; - my $bond_primary_reselect = defined $row->[5] ? $row->[5] : ""; - my $bond_active_interface = defined $row->[6] ? $row->[6] : ""; - my $bond_mii_polling_interval = defined $row->[7] ? $row->[7] : ""; - my $bond_up_delay = defined $row->[8] ? $row->[8] : ""; - my $bond_down_delay = defined $row->[9] ? $row->[9] : ""; - my $bond_mac_address = defined $row->[10] ? $row->[10] : ""; - my $bond_operational = defined $row->[11] ? $row->[11] : ""; - my $bond_bridge_uuid = defined $row->[12] ? $row->[12] : ""; - my $bridge_name = ""; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - bond_uuid => $bond_uuid, - bond_name => $bond_name, - bond_mode => $bond_mode, - bond_mtu => $bond_mtu, - bond_primary_interface => $bond_primary_interface, - bond_primary_reselect => $bond_primary_reselect, - bond_active_interface => $bond_active_interface, - bond_mii_polling_interval => $bond_mii_polling_interval, - bond_up_delay => $bond_up_delay, - bond_down_delay => $bond_down_delay, - bond_mac_address => $bond_mac_address, - bond_operational => $bond_operational, - bond_bridge_uuid => $bond_bridge_uuid, - }}); - - # If this bond is connected to a bridge, get the bridge name. - if (($bond_bridge_uuid) && (defined $anvil->data->{network}{$host}{bridge_uuid}{$bond_bridge_uuid}{name})) - { - $bridge_name = $anvil->data->{network}{$host}{bridge_uuid}{$bond_bridge_uuid}{name}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - bond_bridge_uuid => $bond_bridge_uuid, - bridge_name => $bridge_name, - }}); - push @{$anvil->data->{network}{$host}{interface}{$bridge_name}{interfaces}}, $bond_name; - } - - # Record the bond_uuid -> name - $anvil->data->{network}{$host}{bond_uuid}{$bond_uuid}{name} = $bond_name; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "network::${host}::bond_uuid::${bond_uuid}::name" => $anvil->data->{network}{$host}{bond_uuid}{$bond_uuid}{name}, - }}); - - # We'll initially load empty strings for what would be the IP information. Any interface with IPs will be populated when we call - $anvil->data->{network}{$host}{interface}{$bond_name}{uuid} = $bond_uuid; - $anvil->data->{network}{$host}{interface}{$bond_name}{mode} = $bond_mode; - $anvil->data->{network}{$host}{interface}{$bond_name}{mtu} = $bond_mtu; - $anvil->data->{network}{$host}{interface}{$bond_name}{primary_interface} = $bond_primary_interface; - $anvil->data->{network}{$host}{interface}{$bond_name}{primary_reselect} = $bond_primary_reselect; - $anvil->data->{network}{$host}{interface}{$bond_name}{active_interface} = $bond_active_interface; - $anvil->data->{network}{$host}{interface}{$bond_name}{mii_polling_interval} = $bond_mii_polling_interval; - $anvil->data->{network}{$host}{interface}{$bond_name}{up_delay} = $bond_up_delay; - $anvil->data->{network}{$host}{interface}{$bond_name}{down_delay} = $bond_down_delay; - $anvil->data->{network}{$host}{interface}{$bond_name}{mac_address} = $bond_mac_address; - $anvil->data->{network}{$host}{interface}{$bond_name}{operational} = $bond_operational; - $anvil->data->{network}{$host}{interface}{$bond_name}{bridge_uuid} = $bond_bridge_uuid; - $anvil->data->{network}{$host}{interface}{$bond_name}{type} = "bond"; - $anvil->data->{network}{$host}{interface}{$bond_name}{interfaces} = []; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "network::${host}::interface::${bond_name}::uuid" => $anvil->data->{network}{$host}{interface}{$bond_name}{uuid}, - "network::${host}::interface::${bond_name}::mode" => $anvil->data->{network}{$host}{interface}{$bond_name}{mode}, - "network::${host}::interface::${bond_name}::mtu" => $anvil->data->{network}{$host}{interface}{$bond_name}{mtu}, - "network::${host}::interface::${bond_name}::primary_interface" => $anvil->data->{network}{$host}{interface}{$bond_name}{primary_interface}, - "network::${host}::interface::${bond_name}::primary_reselect" => $anvil->data->{network}{$host}{interface}{$bond_name}{primary_reselect}, - "network::${host}::interface::${bond_name}::active_interface" => $anvil->data->{network}{$host}{interface}{$bond_name}{active_interface}, - "network::${host}::interface::${bond_name}::mii_polling_interval" => $anvil->data->{network}{$host}{interface}{$bond_name}{mii_polling_interval}, - "network::${host}::interface::${bond_name}::up_delay" => $anvil->data->{network}{$host}{interface}{$bond_name}{up_delay}, - "network::${host}::interface::${bond_name}::down_delay" => $anvil->data->{network}{$host}{interface}{$bond_name}{down_delay}, - "network::${host}::interface::${bond_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$bond_name}{mac_address}, - "network::${host}::interface::${bond_name}::operational" => $anvil->data->{network}{$host}{interface}{$bond_name}{operational}, - "network::${host}::interface::${bond_name}::bridge_uuid" => $anvil->data->{network}{$host}{interface}{$bond_name}{bridge}, - "network::${host}::interface::${bond_name}::type" => $anvil->data->{network}{$host}{interface}{$bond_name}{type}, - }}); - } - - # The order will allow us to show the order in which the interfaces were changed, which the user can - # use to track interfaces as they unplug and plug cables back in. - my $order = 1; - $query = " -SELECT - network_interface_uuid, - network_interface_mac_address, - network_interface_name, - network_interface_speed, - network_interface_mtu, - network_interface_link_state, - network_interface_operational, - network_interface_duplex, - network_interface_medium, - network_interface_bond_uuid, - network_interface_bridge_uuid -FROM - network_interfaces -WHERE - network_interface_operational != 'DELETED' -AND - network_interface_host_uuid = ".$anvil->Database->quote($host_uuid)." -ORDER BY - modified_date DESC -;"; - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }}); - $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); - $count = @{$results}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - results => $results, - count => $count, - }}); - my $changed_order = 1; - foreach my $row (@{$results}) - { - my $network_interface_uuid = defined $row->[0] ? $row->[0] : ""; - my $network_interface_mac_address = defined $row->[1] ? $row->[1] : ""; - my $network_interface_name = defined $row->[2] ? $row->[2] : ""; - my $network_interface_speed = defined $row->[3] ? $row->[3] : ""; - my $network_interface_mtu = defined $row->[4] ? $row->[4] : ""; - my $network_interface_link_state = defined $row->[5] ? $row->[5] : ""; - my $network_interface_operational = defined $row->[6] ? $row->[6] : ""; - my $network_interface_duplex = defined $row->[7] ? $row->[7] : ""; - my $network_interface_medium = defined $row->[8] ? $row->[8] : ""; - my $network_interface_bond_uuid = defined $row->[9] ? $row->[9] : ""; - my $network_interface_bridge_uuid = defined $row->[10] ? $row->[10] : ""; - my $bond_name = ""; - my $bridge_name = ""; - my $this_change_orger = 0; - if (($network_interface_name =~ /^virbr/) or ($network_interface_name =~ /^vnet/)) - { - # This isn't a physical NIC, so it doesn't get a changed order - } - else - { - $this_change_orger = $changed_order++; - } - if (($network_interface_bond_uuid) && (defined $anvil->data->{network}{$host}{bond_uuid}{$network_interface_bond_uuid}{name})) - { - $bond_name = $anvil->data->{network}{$host}{bond_uuid}{$network_interface_bond_uuid}{name}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - network_interface_name => $network_interface_name, - bond_name => $bond_name, - }}); - push @{$anvil->data->{network}{$host}{interface}{$bond_name}{interfaces}}, $network_interface_name; - } - if (($network_interface_bridge_uuid) && (defined $anvil->data->{network}{$host}{bridge_uuid}{$network_interface_bridge_uuid}{name})) - { - $bridge_name = $anvil->data->{network}{$host}{bridge_uuid}{$network_interface_bridge_uuid}{name}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - network_interface_name => $network_interface_name, - bridge_name => $bridge_name, - }}); - push @{$anvil->data->{network}{$host}{interface}{$bridge_name}{interfaces}}, $network_interface_name; - } - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - network_interface_uuid => $network_interface_uuid, - network_interface_mac_address => $network_interface_mac_address, - network_interface_name => $network_interface_name, - network_interface_speed => $network_interface_speed, - network_interface_mtu => $network_interface_mtu, - network_interface_link_state => $network_interface_link_state, - network_interface_operational => $network_interface_operational, - network_interface_duplex => $network_interface_duplex, - network_interface_medium => $network_interface_medium, - network_interface_bond_uuid => $network_interface_bond_uuid, - network_interface_bridge_uuid => $network_interface_bridge_uuid, - bond_name => $bond_name, - changed_order => $this_change_orger, - }}); - - # We'll initially load empty strings for what would be the IP information. Any interface with IPs will be populated when we call - $anvil->data->{network}{$host}{interface}{$network_interface_name}{uuid} = $network_interface_uuid; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{mac_address} = $network_interface_mac_address; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{speed} = $network_interface_speed; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{mtu} = $network_interface_mtu; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{link_state} = $network_interface_link_state; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{operational} = $network_interface_operational; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{duplex} = $network_interface_duplex; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{medium} = $network_interface_medium; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{bond_uuid} = $network_interface_bond_uuid; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{bond_name} = $bond_name; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{bridge_uuid} = $network_interface_bridge_uuid; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{bridge_name} = $bridge_name; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{type} = "interface"; - $anvil->data->{network}{$host}{interface}{$network_interface_name}{changed_order} = $this_change_orger; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "network::${host}::interface::${network_interface_name}::uuid" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{uuid}, - "network::${host}::interface::${network_interface_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{mac_address}, - "network::${host}::interface::${network_interface_name}::speed" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{speed}, - "network::${host}::interface::${network_interface_name}::mtu" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{mtu}, - "network::${host}::interface::${network_interface_name}::link_state" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{link_state}, - "network::${host}::interface::${network_interface_name}::operational" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{operational}, - "network::${host}::interface::${network_interface_name}::duplex" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{duplex}, - "network::${host}::interface::${network_interface_name}::medium" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{medium}, - "network::${host}::interface::${network_interface_name}::bond_uuid" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{bond_uuid}, - "network::${host}::interface::${network_interface_name}::bond_name" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{bond_name}, - "network::${host}::interface::${network_interface_name}::bridge_uuid" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{bridge_uuid}, - "network::${host}::interface::${network_interface_name}::bridge_name" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{bridge_name}, - "network::${host}::interface::${network_interface_name}::type" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{type}, - "network::${host}::interface::${network_interface_name}::changed_order" => $anvil->data->{network}{$host}{interface}{$network_interface_name}{changed_order}, - }}); + $ip = $results->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ip => $ip }}); } - # Load the IPs - $anvil->Network->load_ips({ - debug => $debug, - host_uuid => $host_uuid, - host => $host, - clear => 0, - }); - - return(0); + return($ip); } -=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 >>. +=head2 get_ips -C<< Note >>: IP addresses that have been deleted will be marked so by C<< ip >> being set to C<< DELETED >>. +This method checks the local system for interfaces and stores them in: -The loaded data will be stored as: +* C<< network::::interface::::ip >> - If an IP address is set +* C<< network::::interface::::subnet_mask >> - If an IP is set +* C<< network::::interface::::mac_address >> - Always set. +* C<< network::::interface::::mtu >> - Always set. +* C<< network::::interface::::default_gateway >> = C<< 0 >> if not the default gateway, C<< 1 >> if so. +* C<< network::::interface::::gateway >> = If the default gateway, this is the gateway IP address. +* C<< network::::interface::::dns >> = If the default gateway, this is the comma-separated list of active DNS servers. -* C<< network::::interface::::ip >> - If an IP address is set -* C<< network::::interface::::subnet_mask >> - If an IP is set -* C<< network::::interface::::mac >> - Always set. -* C<< network::::interface::::default_gateway >> = C<< 0 >> if not the default gateway, C<< 1 >> if so. -* C<< network::::interface::::gateway >> = If the default gateway, this is the gateway IP address. -* C<< network::::interface::::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::::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::::iface >> is also set. Note that this is not target-dependent. Parameters; -=head3 clear (optional, default '1') +=head3 password (optional) -When set, any previously known information is cleared. Specifically, the C<< network::> >> hash is deleted prior to the load. To prevent this, set this to C<< 0 >>. +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 host (optional, default is 'host_uuid' value) +=head3 port (optional, default 22) -This is the optional C<< host >> string to use in the hash where the data is stored. +If C<< target >> is set, this is the TCP port number used to connect to the remote machine. -=head3 host_uuid (optional, default 'sys::host_uuid') +=head3 remote_user (optional) -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. +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/. >>. 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 load_ips +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->load_ips()" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Network->get_ips()" }}); - my $clear = defined $parameter->{clear} ? $parameter->{clear} : 1; - my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : $anvil->Get->host_uuid; - my $host = defined $parameter->{host} ? $parameter->{host} : ""; + 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 => { - clear => $clear, - host => $host, - host_uuid => $host_uuid, + password => $anvil->Log->is_secure($password), + port => $port, + remote_user => $remote_user, + target => $target, }}); - if (not $host_uuid) - { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->load_ips()", parameter => "ip" }}); - return(""); - } - - if (not $host) - { - $host = $host_uuid; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }}); - } + # 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 (($clear) && (exists $anvil->data->{network}{$host})) + if (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}) + # 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) { - 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]; + # 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 => { - 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, + 's1:output' => $output, + 's2:return_code' => $return_code, }}); - - my $interface_name = ""; - my $interface_mac = ""; - if ($ip_address_on_type eq "interface") + } + 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+: (.*?): /) { - my $query = " -SELECT - network_interface_uuid, - network_interface_name, - 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 - network_interface_uuid = ".$anvil->Database->quote($ip_address_on_uuid)." -AND - network_interface_operational != 'DELETED' -;"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); - my $count = @{$results}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - results => $results, - count => $count, - }}); - next if not $count; + $in_iface = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { in_iface => $in_iface }}); - $interface_name = $results->[0]->[1]; - $interface_mac = $results->[0]->[2]; + $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->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - interface_name => $interface_name, - interface_mac => $interface_mac, + "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}, }}); - $anvil->data->{network}{$host}{interface}{$interface_name}{network_interface_uuid} = $results->[0]->[0]; - $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->data->{network}{$host}{interface}{$interface_name}{speed} = $results->[0]->[3]; - $anvil->data->{network}{$host}{interface}{$interface_name}{mtu} = $results->[0]->[4]; - $anvil->data->{network}{$host}{interface}{$interface_name}{link_state} = $results->[0]->[5]; - $anvil->data->{network}{$host}{interface}{$interface_name}{operational} = $results->[0]->[6]; - $anvil->data->{network}{$host}{interface}{$interface_name}{duplex} = $results->[0]->[7]; - $anvil->data->{network}{$host}{interface}{$interface_name}{medium} = $results->[0]->[8]; - $anvil->data->{network}{$host}{interface}{$interface_name}{bond_uuid} = $results->[0]->[9]; - $anvil->data->{network}{$host}{interface}{$interface_name}{bridge_uuid} = $results->[0]->[10]; + 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/class/net/".$in_iface."/statistics/rx_bytes; + echo -n 'tx:'; + cat /sys/class/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 }}); + } + + $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 => { - "network::${host}::interface::${interface_name}::network_interface_uuid" => $anvil->data->{network}{$host}{interface}{$interface_name}{network_interface_uuid}, - "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}, + "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}, }}); } - elsif ($ip_address_on_type eq "bond") + if ($line =~ /ether (.*?) /i) { - 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; + my $mac_address = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { mac_address => $mac_address }}); - $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, - }}); + # 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}{$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->data->{network}{$host}{interface}{$in_iface}{mac_address} = $mac_address; $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}, + "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//bonding_slave/perm_hwaddr' exists and, if so, read the MAC + # address from there. If not, read the MAC address from + # '/sys/class/net//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}, + }}); + } + } } - elsif ($ip_address_on_type eq "bridge") + if ($line =~ /mtu (\d+) /i) { - 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; + 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::${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::${in_iface}::mtu" => $anvil->data->{network}{$host}{interface}{$in_iface}{mtu}, }}); } } - return(0); -} - -=head2 get_company_from_mac - -This takes a MAC address (or the first six bytes) and returns the company that owns the OUI. If the company name is not found, an expty string is returned. - -Parameters; - -=head3 mac (required) - -This is the first six bytes of the mac address, C<< xx:xx:xx >> format, being searched for. - -=cut -sub get_company_from_mac -{ - my $self = shift; - my $parameter = shift; - my $anvil = $self->parent; - my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Network->get_company_from_mac()" }}); - - my $mac = defined $parameter->{mac} ? lc($parameter->{mac}) : ""; - my $company = ""; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - mac => $mac, - }}); - - if (not $mac) - { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->get_company_from_mac()", parameter => "mac_prefix" }}); - return(""); - } - - # Have I already looked this one up? - if ($anvil->data->{cache}{mac_to_oui}{$mac}) - { - # Yup, no need to process. - return($anvil->data->{cache}{mac_to_oui}{$mac}); - } - - my $valid_mac = $anvil->Validate->mac({mac => $mac}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { mac => $mac }}); - if ($valid_mac) + # 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) { - # 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 }}); + # 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, + }}); } - elsif ($mac !~ /[0-9a-f]{2}[:-][0-9a-f]{2}[:-][0-9a-f]{2}/i) + else { - # Bad format - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0104", variables => { mac => $mac }}); - return(""); + # 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, + }}); } - - 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) + foreach my $line (split/\n/, $output) { - $company = $results->[0]->[0]; - $anvil->data->{cache}{mac_to_oui}{$mac} = $company; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { company => $company }}); - } - - return($company); -} - - -=head2 get_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 local system for interfaces and stores them in: - -* C<< network::::interface::::ip >> - If an IP address is set -* C<< network::::interface::::subnet_mask >> - If an IP is set -* C<< network::::interface::::mac_address >> - Always set. -* C<< network::::interface::::mtu >> - Always set. -* C<< network::::interface::::default_gateway >> = C<< 0 >> if not the default gateway, C<< 1 >> if so. -* C<< network::::interface::::gateway >> = If the default gateway, this is the gateway IP address. -* C<< network::::interface::::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::::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::::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/. >>. 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}; + $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}, + }}); + } + } + } } - # 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}); + # 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. @@ -2138,705 +1890,1534 @@ sub get_ips foreach my $line (split/\n/, $output) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }}); - if ($line =~ /^\d+: (.*?): /) + if ($line =~ /default via (.*?) dev (.*?) proto .*? metric (\d+)/i) { - $in_iface = $1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { in_iface => $in_iface }}); - - $anvil->data->{network}{$host}{interface}{$in_iface}{ip} = "" if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{ip}; - $anvil->data->{network}{$host}{interface}{$in_iface}{subnet_mask} = "" if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{subnet_mask}; - $anvil->data->{network}{$host}{interface}{$in_iface}{mac_address} = "" if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{mac_address}; - $anvil->data->{network}{$host}{interface}{$in_iface}{mtu} = 0 if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{mtu}; - $anvil->data->{network}{$host}{interface}{$in_iface}{default_gateway} = 0 if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{default_gateway}; - $anvil->data->{network}{$host}{interface}{$in_iface}{gateway} = "" if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{gateway}; - $anvil->data->{network}{$host}{interface}{$in_iface}{dns} = "" if not defined $anvil->data->{network}{$host}{interface}{$in_iface}{dns}; - $anvil->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}; + my $this_ip = $1; + my $this_interface = $2; + my $metric = $3; $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}, + 's1:this_ip' => $this_ip, + 's2:this_interface' => $this_interface, + 's3:metric' => $metric, + 's4:lowest_metric' => $lowest_metric, }}); - if ($in_iface ne "lo") + if ($metric < $lowest_metric) { - # 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/class/net/".$in_iface."/statistics/rx_bytes; - echo -n 'tx:'; - cat /sys/class/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}}).")", - }}); - } - } + $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, + }}); } } - next if not $in_iface; - if ($in_iface eq "lo") + } + $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) { - # We don't care about 'lo'. - delete $anvil->data->{network}{$host}{interface}{$in_iface}; - next; + # 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, + }}); } - if ($line =~ /inet (.*?)\/(.*?) /) + else { - 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)) + # 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+(.*)$/) { - # 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 }}); + $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 }}); } - $anvil->data->{network}{$host}{interface}{$in_iface}{ip} = $ip; - $anvil->data->{network}{$host}{interface}{$in_iface}{subnet_mask} = $subnet_mask; + 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: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}, + "s1:dns_hash->{$order}" => $dns_hash->{$order}, + "s2:dns_list" => $dns_list, }}); } - if ($line =~ /ether (.*?) /i) + $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}) { - 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 (.*)$/) + $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}) { - $mac_address = $1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { mac_address => $mac_address }}); + $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->data->{network}{$host}{interface}{$in_iface}{mac_address} = $mac_address; + } + } + + #$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_interfces + +This loads all network information for the given host UUID. + +The main difference from C<< ->load_ips() >> is that this method loads information about all interfaces, regardless of if they have an IP, as well as their link state and link information. + +The loaded data will be stored as: + +* C<< machine::::interface:::: + +Parameters; + +=head3 clear (optional, default '1') + +When set, any previously known information is cleared. Specifically, the C<< network::> >> hash is deleted prior to the load. To prevent this, set this to C<< 0 >>. + +=head3 host (optional, default is 'host_uuid' value) + +This is the optional C<< target >> string to use in the hash where the data is stored. + +=head3 host_uuid (optional, default 'sys::host_uuid') + +This is the C<< host_uuid >> of the hosts whose IP and interface data that you want to load. The default is to load the local machine's data. + +=cut +sub load_interfces +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Network->load_interfces()" }}); + + my $clear = defined $parameter->{clear} ? $parameter->{clear} : 1; + my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : $anvil->data->{sys}{host_uuid}; + my $host = defined $parameter->{host} ? $parameter->{host} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + clear => $clear, + host => $host, + host_uuid => $host_uuid, + }}); + + if (not $host_uuid) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->load_interfces()", parameter => "host_uuid" }}); + return(""); + } + + if (not $host) + { + $host = $host_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }}); + } + + if (($clear) && (exists $anvil->data->{network}{$host})) + { + delete $anvil->data->{network}{$host}; + } + + # Now load bridge info + my $query = " +SELECT + bridge_uuid, + bridge_name, + bridge_id, + bridge_mac_address, + bridge_mtu, + bridge_stp_enabled +FROM + bridges +WHERE + bridge_id != 'DELETED' +AND + bridge_host_uuid = ".$anvil->Database->quote($host_uuid)." +;"; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $bridge_uuid = defined $row->[0] ? $row->[0] : ""; + my $bridge_name = defined $row->[1] ? $row->[1] : ""; + my $bridge_id = defined $row->[2] ? $row->[2] : ""; + my $bridge_mac_address = defined $row->[3] ? $row->[3] : ""; + my $bridge_mtu = defined $row->[4] ? $row->[4] : ""; + my $bridge_stp_enabled = defined $row->[5] ? $row->[5] : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + bridge_uuid => $bridge_uuid, + bridge_name => $bridge_name, + bridge_id => $bridge_id, + bridge_mac_address => $bridge_mac_address, + bridge_mtu => $bridge_mtu, + bridge_stp_enabled => $bridge_stp_enabled, + }}); + + # Record the bridge_uuid -> name + $anvil->data->{network}{$host}{bridge_uuid}{$bridge_uuid}{name} = $bridge_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "network::${host}::bridge_uuid::${bridge_uuid}::name" => $anvil->data->{network}{$host}{bridge_uuid}{$bridge_uuid}{name}, + }}); + + # We'll initially load empty strings for what would be the IP information. Any interface with IPs will be populated when we call + $anvil->data->{network}{$host}{interface}{$bridge_name}{uuid} = $bridge_uuid; + $anvil->data->{network}{$host}{interface}{$bridge_name}{id} = $bridge_id; + $anvil->data->{network}{$host}{interface}{$bridge_name}{mac_address} = $bridge_mac_address; + $anvil->data->{network}{$host}{interface}{$bridge_name}{mtu} = $bridge_mtu; + $anvil->data->{network}{$host}{interface}{$bridge_name}{stp_enabled} = $bridge_stp_enabled; + $anvil->data->{network}{$host}{interface}{$bridge_name}{type} = "bridge"; + $anvil->data->{network}{$host}{interface}{$bridge_name}{interfaces} = []; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "network::${host}::interface::${bridge_name}::uuid" => $anvil->data->{network}{$host}{interface}{$bridge_name}{uuid}, + "network::${host}::interface::${bridge_name}::id" => $anvil->data->{network}{$host}{interface}{$bridge_name}{id}, + "network::${host}::interface::${bridge_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$bridge_name}{mac_address}, + "network::${host}::interface::${bridge_name}::mtu" => $anvil->data->{network}{$host}{interface}{$bridge_name}{mtu}, + "network::${host}::interface::${bridge_name}::stp_enabled" => $anvil->data->{network}{$host}{interface}{$bridge_name}{stp_enabled}, + "network::${host}::interface::${bridge_name}::type" => $anvil->data->{network}{$host}{interface}{$bridge_name}{type}, + }}); + } + + # Now load bond info + $query = " +SELECT + bond_uuid, + bond_name, + bond_mode, + bond_mtu, + bond_primary_interface, + bond_primary_reselect, + bond_active_interface, + bond_mii_polling_interval, + bond_up_delay, + bond_down_delay, + bond_mac_address, + bond_operational, + bond_bridge_uuid +FROM + bonds WHERE bond_mode != 'DELETED' +AND + bond_host_uuid = ".$anvil->Database->quote($host_uuid)." +;"; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }}); + $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $bond_uuid = defined $row->[0] ? $row->[0] : ""; + my $bond_name = defined $row->[1] ? $row->[1] : ""; + my $bond_mode = defined $row->[2] ? $row->[2] : ""; + my $bond_mtu = defined $row->[3] ? $row->[3] : ""; + my $bond_primary_interface = defined $row->[4] ? $row->[4] : ""; + my $bond_primary_reselect = defined $row->[5] ? $row->[5] : ""; + my $bond_active_interface = defined $row->[6] ? $row->[6] : ""; + my $bond_mii_polling_interval = defined $row->[7] ? $row->[7] : ""; + my $bond_up_delay = defined $row->[8] ? $row->[8] : ""; + my $bond_down_delay = defined $row->[9] ? $row->[9] : ""; + my $bond_mac_address = defined $row->[10] ? $row->[10] : ""; + my $bond_operational = defined $row->[11] ? $row->[11] : ""; + my $bond_bridge_uuid = defined $row->[12] ? $row->[12] : ""; + my $bridge_name = ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + bond_uuid => $bond_uuid, + bond_name => $bond_name, + bond_mode => $bond_mode, + bond_mtu => $bond_mtu, + bond_primary_interface => $bond_primary_interface, + bond_primary_reselect => $bond_primary_reselect, + bond_active_interface => $bond_active_interface, + bond_mii_polling_interval => $bond_mii_polling_interval, + bond_up_delay => $bond_up_delay, + bond_down_delay => $bond_down_delay, + bond_mac_address => $bond_mac_address, + bond_operational => $bond_operational, + bond_bridge_uuid => $bond_bridge_uuid, + }}); + + # If this bond is connected to a bridge, get the bridge name. + if (($bond_bridge_uuid) && (defined $anvil->data->{network}{$host}{bridge_uuid}{$bond_bridge_uuid}{name})) + { + $bridge_name = $anvil->data->{network}{$host}{bridge_uuid}{$bond_bridge_uuid}{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + bond_bridge_uuid => $bond_bridge_uuid, + bridge_name => $bridge_name, + }}); + push @{$anvil->data->{network}{$host}{interface}{$bridge_name}{interfaces}}, $bond_name; + } + + # Record the bond_uuid -> name + $anvil->data->{network}{$host}{bond_uuid}{$bond_uuid}{name} = $bond_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "network::${host}::bond_uuid::${bond_uuid}::name" => $anvil->data->{network}{$host}{bond_uuid}{$bond_uuid}{name}, + }}); + + # We'll initially load empty strings for what would be the IP information. Any interface with IPs will be populated when we call + $anvil->data->{network}{$host}{interface}{$bond_name}{uuid} = $bond_uuid; + $anvil->data->{network}{$host}{interface}{$bond_name}{mode} = $bond_mode; + $anvil->data->{network}{$host}{interface}{$bond_name}{mtu} = $bond_mtu; + $anvil->data->{network}{$host}{interface}{$bond_name}{primary_interface} = $bond_primary_interface; + $anvil->data->{network}{$host}{interface}{$bond_name}{primary_reselect} = $bond_primary_reselect; + $anvil->data->{network}{$host}{interface}{$bond_name}{active_interface} = $bond_active_interface; + $anvil->data->{network}{$host}{interface}{$bond_name}{mii_polling_interval} = $bond_mii_polling_interval; + $anvil->data->{network}{$host}{interface}{$bond_name}{up_delay} = $bond_up_delay; + $anvil->data->{network}{$host}{interface}{$bond_name}{down_delay} = $bond_down_delay; + $anvil->data->{network}{$host}{interface}{$bond_name}{mac_address} = $bond_mac_address; + $anvil->data->{network}{$host}{interface}{$bond_name}{operational} = $bond_operational; + $anvil->data->{network}{$host}{interface}{$bond_name}{bridge_uuid} = $bond_bridge_uuid; + $anvil->data->{network}{$host}{interface}{$bond_name}{type} = "bond"; + $anvil->data->{network}{$host}{interface}{$bond_name}{interfaces} = []; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "network::${host}::interface::${bond_name}::uuid" => $anvil->data->{network}{$host}{interface}{$bond_name}{uuid}, + "network::${host}::interface::${bond_name}::mode" => $anvil->data->{network}{$host}{interface}{$bond_name}{mode}, + "network::${host}::interface::${bond_name}::mtu" => $anvil->data->{network}{$host}{interface}{$bond_name}{mtu}, + "network::${host}::interface::${bond_name}::primary_interface" => $anvil->data->{network}{$host}{interface}{$bond_name}{primary_interface}, + "network::${host}::interface::${bond_name}::primary_reselect" => $anvil->data->{network}{$host}{interface}{$bond_name}{primary_reselect}, + "network::${host}::interface::${bond_name}::active_interface" => $anvil->data->{network}{$host}{interface}{$bond_name}{active_interface}, + "network::${host}::interface::${bond_name}::mii_polling_interval" => $anvil->data->{network}{$host}{interface}{$bond_name}{mii_polling_interval}, + "network::${host}::interface::${bond_name}::up_delay" => $anvil->data->{network}{$host}{interface}{$bond_name}{up_delay}, + "network::${host}::interface::${bond_name}::down_delay" => $anvil->data->{network}{$host}{interface}{$bond_name}{down_delay}, + "network::${host}::interface::${bond_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$bond_name}{mac_address}, + "network::${host}::interface::${bond_name}::operational" => $anvil->data->{network}{$host}{interface}{$bond_name}{operational}, + "network::${host}::interface::${bond_name}::bridge_uuid" => $anvil->data->{network}{$host}{interface}{$bond_name}{bridge}, + "network::${host}::interface::${bond_name}::type" => $anvil->data->{network}{$host}{interface}{$bond_name}{type}, + }}); + } + + # The order will allow us to show the order in which the interfaces were changed, which the user can + # use to track interfaces as they unplug and plug cables back in. + my $order = 1; + $query = " +SELECT + network_interface_uuid, + network_interface_mac_address, + network_interface_name, + network_interface_speed, + network_interface_mtu, + network_interface_link_state, + network_interface_operational, + network_interface_duplex, + network_interface_medium, + network_interface_bond_uuid, + network_interface_bridge_uuid +FROM + network_interfaces +WHERE + network_interface_operational != 'DELETED' +AND + network_interface_host_uuid = ".$anvil->Database->quote($host_uuid)." +ORDER BY + modified_date DESC +;"; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }}); + $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + my $changed_order = 1; + foreach my $row (@{$results}) + { + my $network_interface_uuid = defined $row->[0] ? $row->[0] : ""; + my $network_interface_mac_address = defined $row->[1] ? $row->[1] : ""; + my $network_interface_name = defined $row->[2] ? $row->[2] : ""; + my $network_interface_speed = defined $row->[3] ? $row->[3] : ""; + my $network_interface_mtu = defined $row->[4] ? $row->[4] : ""; + my $network_interface_link_state = defined $row->[5] ? $row->[5] : ""; + my $network_interface_operational = defined $row->[6] ? $row->[6] : ""; + my $network_interface_duplex = defined $row->[7] ? $row->[7] : ""; + my $network_interface_medium = defined $row->[8] ? $row->[8] : ""; + my $network_interface_bond_uuid = defined $row->[9] ? $row->[9] : ""; + my $network_interface_bridge_uuid = defined $row->[10] ? $row->[10] : ""; + my $bond_name = ""; + my $bridge_name = ""; + my $this_change_orger = 0; + if (($network_interface_name =~ /^virbr/) or ($network_interface_name =~ /^vnet/)) + { + # This isn't a physical NIC, so it doesn't get a changed order + } + else + { + $this_change_orger = $changed_order++; + } + if (($network_interface_bond_uuid) && (defined $anvil->data->{network}{$host}{bond_uuid}{$network_interface_bond_uuid}{name})) + { + $bond_name = $anvil->data->{network}{$host}{bond_uuid}{$network_interface_bond_uuid}{name}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "network::${host}::interface::${in_iface}::mac_address" => $anvil->data->{network}{$host}{interface}{$in_iface}{mac_address}, + network_interface_name => $network_interface_name, + bond_name => $bond_name, }}); - - # 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//bonding_slave/perm_hwaddr' exists and, if so, read the MAC - # address from there. If not, read the MAC address from - # '/sys/class/net//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}, - }}); - } - } + push @{$anvil->data->{network}{$host}{interface}{$bond_name}{interfaces}}, $network_interface_name; } - if ($line =~ /mtu (\d+) /i) + if (($network_interface_bridge_uuid) && (defined $anvil->data->{network}{$host}{bridge_uuid}{$network_interface_bridge_uuid}{name})) { - my $mtu = $1; - $anvil->data->{network}{$host}{interface}{$in_iface}{mtu} = $mtu; + $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::${host}::interface::${in_iface}::mtu" => $anvil->data->{network}{$host}{interface}{$in_iface}{mtu}, + network_interface_name => $network_interface_name, + bridge_name => $bridge_name, }}); + push @{$anvil->data->{network}{$host}{interface}{$bridge_name}{interfaces}}, $network_interface_name; } - } - - # 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, + 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, }}); - } - 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, - }); + # 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 => { - "s1:full_path" => $full_path, - "s2:file_body" => $file_body, + "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}, }}); - - # 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) + # Load the IPs + $anvil->Network->load_ips({ + debug => $debug, + host_uuid => $host_uuid, + host => $host, + clear => 0, + }); + + return(0); +} + + +=head2 load_ips + +This method loads and stores the same data as the C<< get_ips >> method, but does so by loading data from the database, instead of collecting it directly from the host. As such, it can also be used by C<< find_matches >>. + +C<< Note >>: IP addresses that have been deleted will be marked so by C<< ip >> being set to C<< DELETED >>. + +The loaded data will be stored as: + +* C<< network::::interface::::ip >> - If an IP address is set +* C<< network::::interface::::subnet_mask >> - If an IP is set +* C<< network::::interface::::mac >> - Always set. +* C<< network::::interface::::default_gateway >> = C<< 0 >> if not the default gateway, C<< 1 >> if so. +* C<< network::::interface::::gateway >> = If the default gateway, this is the gateway IP address. +* C<< network::::interface::::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::> >> hash is deleted prior to the load. To prevent this, set this to C<< 0 >>. + +=head3 host (optional, default is 'host_uuid' value) + +This is the optional C<< host >> string to use in the hash where the data is stored. + +=head3 host_uuid (optional, default 'sys::host_uuid') + +This is the C<< host_uuid >> of the hosts whose IP and interface data that you want to load. The default is to load the local machine's data. + +=cut +sub load_ips +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Network->load_ips()" }}); + + my $clear = defined $parameter->{clear} ? $parameter->{clear} : 1; + my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : $anvil->Get->host_uuid; + my $host = defined $parameter->{host} ? $parameter->{host} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + clear => $clear, + host => $host, + host_uuid => $host_uuid, + }}); + + if (not $host_uuid) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->load_ips()", parameter => "ip" }}); + return(""); + } + + if (not $host) { - # 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, - }}); + $host = $host_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }}); } - else + + if (($clear) && (exists $anvil->data->{network}{$host})) { - # 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, - }}); + delete $anvil->data->{network}{$host}; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0700", variables => { hash => "network::${host}" }}); } - foreach my $line (split/\n/, $output) + + # 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}) { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }}); - if ($line =~ /default via (.*?) dev (.*?) proto .*? metric (\d+)/i) + my $ip_address_address = $row->[0]; + my $ip_address_subnet_mask = $row->[1]; + my $ip_address_gateway = $row->[2]; + my $ip_address_default_gateway = $row->[3]; + my $ip_address_dns = $row->[4]; + my $ip_address_on_type = $row->[5]; + my $ip_address_on_uuid = $row->[6]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + ip_address_address => $ip_address_address, + ip_address_subnet_mask => $ip_address_subnet_mask, + ip_address_gateway => $ip_address_gateway, + ip_address_default_gateway => $ip_address_default_gateway, + ip_address_dns => $ip_address_dns, + ip_address_on_type => $ip_address_on_type, + ip_address_on_uuid => $ip_address_on_uuid, + }}); + + my $interface_name = ""; + my $interface_mac = ""; + if ($ip_address_on_type eq "interface") { - my $this_ip = $1; - my $this_interface = $2; - my $metric = $3; + my $query = " +SELECT + network_interface_uuid, + network_interface_name, + 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 + network_interface_uuid = ".$anvil->Database->quote($ip_address_on_uuid)." +AND + network_interface_operational != 'DELETED' +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - 's1:this_ip' => $this_ip, - 's2:this_interface' => $this_interface, - 's3:metric' => $metric, - 's4:lowest_metric' => $lowest_metric, + results => $results, + count => $count, }}); + next if not $count; - 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, - }}); - } + $interface_name = $results->[0]->[1]; + $interface_mac = $results->[0]->[2]; + $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}{network_interface_uuid} = $results->[0]->[0]; + $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->data->{network}{$host}{interface}{$interface_name}{speed} = $results->[0]->[3]; + $anvil->data->{network}{$host}{interface}{$interface_name}{mtu} = $results->[0]->[4]; + $anvil->data->{network}{$host}{interface}{$interface_name}{link_state} = $results->[0]->[5]; + $anvil->data->{network}{$host}{interface}{$interface_name}{operational} = $results->[0]->[6]; + $anvil->data->{network}{$host}{interface}{$interface_name}{duplex} = $results->[0]->[7]; + $anvil->data->{network}{$host}{interface}{$interface_name}{medium} = $results->[0]->[8]; + $anvil->data->{network}{$host}{interface}{$interface_name}{bond_uuid} = $results->[0]->[9]; + $anvil->data->{network}{$host}{interface}{$interface_name}{bridge_uuid} = $results->[0]->[10]; + $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}::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}, + }}); } - } - $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) + elsif ($ip_address_on_type eq "bond") { - # Local call. - ($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); + 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 => { - 's1:output' => $output, - 's2:return_code' => $return_code, + 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}, }}); } - else + elsif ($ip_address_on_type eq "bridge") { - # 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, - }); + 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 => { - 's1:output' => $output, - 's2:error' => $error, - 's3:return_code' => $return_code, + results => $results, + count => $count, }}); - } - 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 not $count; - next if $in_interface ne $route_interface; + $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, + }}); - 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->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 => { - "s1:dns_hash->{$order}" => $dns_hash->{$order}, - "s2:dns_list" => $dns_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}, }}); } - $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; +=head2 manage_firewall -=head3 interface (required) +This method manages a C<< firewalld >> firewall. -This is the name of the interface being evaluated. +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. -=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); -} +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. -=head2 is_ip_in_network +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. -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; -Parameters +=head3 task (optional, default 'check') -=head3 ip (required) +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. -This is the ip IP address being analyzed. +The default is C<< all >>, which checks the entire configuration, updating the active configuration as needed. -=head3 network (required) +=head3 port_number (required) -This is the IP address that will be paired with the subnet mask to see if the ip matches. +This is the port number to work on. -=head3 subnet_mask (required) +If not specified, C<< service >> is required. -This is the subnet mask paired against the IP address used to check the ip against. +=head3 protocol (optional) + +This can be c<< tcp >> or C<< upd >> and is used to specify what protocol to use with the C<< port >>, when specified. The default is C<< tcp >>. 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 is_ip_in_network +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->is_ip_in_network()" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Network->manage_firewall()" }}); - my $ip = defined $parameter->{ip} ? $parameter->{ip} : ""; - my $network = defined $parameter->{network} ? $parameter->{network} : ""; - my $subnet_mask = defined $parameter->{subnet_mask} ? $parameter->{subnet_mask} : ""; + 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} : "tcp"; + my $zone = defined $parameter->{zone} ? $parameter->{zone} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - ip => $ip, - network => $network, - subnet_mask => $subnet_mask, + task => $task, + port_number => $port_number, + protocol => $protocol, + zone => $zone }}); - if (not $network) + # 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 => 2, list => { firewalld_running => $firewalld_running }}); + if (not $firewalld_running) { - $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); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0669"}); + return(1); } - elsif (not $anvil->Validate->ipv4({ip => $network})) + + # 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, + }}); + if (($task eq "check") && ($port_number eq "")) { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "warning_0019", variables => { parameter => "network", network => $network }}); - return(0); + ### 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) + { + $reload = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { reload => $reload }}); + } + + # What zones do we need, and what zones do we have? + $anvil->Network->get_ips({target => $host_name}); + 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 => 2, 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 => 2, 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 => 2, 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 => 2, 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. + foreach my $zone (sort {$a cmp $b} keys %{$anvil->data->{firewalld}{zones}}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, 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}; + + # 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 => 2, 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 => 2, list => { + output => $output, + return_code => $return_code, + }}); + } + + # 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 => 2, 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 => 2, list => { + output => $output, + return_code => $return_code, + }}); + } + + # Does the long description need to be updated? + my $description = ""; + if ($zone =~ /^(.*?)(\d+)$/) + { + my $network = $1; + my $sequence = $2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, 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 => 2, 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 => 2, list => { + output => $output, + return_code => $return_code, + }}); + } + + # Now we need to decide what should be opened for each network. + } } - if (not $ip) + + + + + + # Don't do anything below here yet. + return(2); + + + # This will be set if the port is found to be open. + my $open = 0; + + # Checking the iptables rules in memory is very fast, relative to firewall-cmd. So we'll do an + # initial check there to see if the port in question is listed. + my $shell_call = $anvil->data->{path}{exe}{'iptables-save'}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }}); + + my ($iptables, $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); + foreach my $line (split/\n/, $iptables) { - $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); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }}); + if (($line =~ /-m $protocol /) && ($line =~ /--dport $port_number /) && ($line =~ /ACCEPT/)) + { + $open = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open }}); + last; + } } - elsif (not $anvil->Validate->ipv4({ip => $ip})) + + # If the port is open and the task is 'check' or 'open', we're done and can return now and save a lot + # of time. + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'task' => $task, 'open' => $open }}); + if ((($task eq "check") or ($task eq "open")) && ($open)) { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "warning_0019", variables => { parameter => "ip", network => $ip }}); - return(0); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open }}); + return($open); } - if (not $subnet_mask) + + + + # Before we do anything, what zone is active? + my $active_zone = ""; + if (not $active_zone) { - $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); + 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({debug => $debug, 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 !~ /\s/) + { + $active_zone = $line; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { active_zone => $active_zone }}); + } + last; + } } - elsif (not $anvil->Validate->subnet_mask({subnet_mask => $subnet_mask})) + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { active_zone => $active_zone }}); + + # If I still don't know what the active zone is, we're done. + if (not $active_zone) { - $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); + return("!!error!!"); } - my $match = 0; - my $block = Net::Netmask->new($network."/".$subnet_mask); - if ($block->match($ip)) + # If we have an active zone, see if the requested port is open. + my $zone_file = $anvil->data->{path}{directories}{firewalld_zones}."/".$active_zone.".xml"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { zone_file => $zone_file }}); + if (not -e $zone_file) { - # This is a match! - $match = 1; + #... + return($open); } - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { match => $match }}); - return($match); + # Read the XML to see what services are opened already and translate those into port numbers and + # protocols. + local $@; + my $open_services = []; + my $xml = XML::Simple->new(); + my $body = ""; + my $test = eval { $body = $xml->XMLin($zone_file, KeyAttr => { language => 'name', key => 'name' }, ForceArray => [ 'service' ]) }; + if (not $test) + { + chomp $@; + my $error = "[ Error ] - The was a problem reading: [$zone_file]. The error was:\n"; + $error .= "===========================================================\n"; + $error .= $@."\n"; + $error .= "===========================================================\n"; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", raw => $error}); + + # Clear the error so it doesn't propogate out to a future 'die' and confuse things. + $@ = ''; + } + else + { + # Parse the already-opened services + foreach my $hash_ref (@{$body->{service}}) + { + # Load the details of this service. + my $service = $hash_ref->{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { service => $service }}); + $anvil->System->_load_specific_firewalld_zone({service => $hash_ref->{name}}); + push @{$open_services}, $service; + } + + # Now loop through the open services, protocols and ports looking for the one passed in by + # the caller. If found, the port is already open. + foreach my $service (sort {$a cmp $b} @{$open_services}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { service => $service }}); + foreach my $this_protocol ("tcp", "udp") + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_protocol => $this_protocol }}); + foreach my $this_port (sort {$a cmp $b} @{$anvil->data->{firewalld}{zones}{by_name}{$service}{tcp}}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_port => $this_port }}); + if (($port_number eq $this_port) && ($this_protocol eq $protocol)) + { + # Opened already (as the recorded service). + $open = $service; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open }}); + last if $open; + } + last if $open; + } + last if $open; + } + last if $open; + } + } + + # We're done if we were just checking. However, if we've been asked to open a currently closed port, + # or vice versa, make the change before returning. + my $changed = 0; + if (($task eq "open") && (not $open)) + { + # Map the port to a service, if possible. + my $service = $anvil->System->_match_port_to_service({port => $port_number}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { service => $service }}); + + # Open the port + if ($service) + { + my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --permanent --add-service ".$service; + $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 }}); + if ($output eq "success") + { + $open = 1; + $changed = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open, changed => $changed }}); + } + else + { + # Something went wrong... + return("!!error!!"); + } + } + else + { + my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --permanent --add-port ".$port_number."/".$protocol; + $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 }}); + if ($output eq "success") + { + $open = 1; + $changed = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open, changed => $changed }}); + } + else + { + # Something went wrong... + return("!!error!!"); + } + } + } + elsif (($task eq "close") && ($open)) + { + # Map the port to a service, if possible. + my $service = $anvil->System->_match_port_to_service({port => $port_number}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { service => $service }}); + + # Close the port + if ($service) + { + my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --permanent --remove-service ".$service; + $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 }}); + if ($output eq "success") + { + $open = 0; + $changed = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open, changed => $changed }}); + } + else + { + # Something went wrong... + return("!!error!!"); + } + } + else + { + my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --permanent --remove-port ".$port_number."/".$protocol; + $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 }}); + if ($output eq "success") + { + $open = 0; + $changed = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open, changed => $changed }}); + } + else + { + # Something went wrong... + return("!!error!!"); + } + } + } + + # If we made a change, reload. + if ($changed) + { + $anvil->System->reload_daemon({daemon => $anvil->data->{sys}{daemon}{firewalld}}); + } + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open }}); + return($open); } + =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. @@ -3272,4 +3853,79 @@ sub read_nmcli # 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); +} + 1; diff --git a/Anvil/Tools/Storage.pm b/Anvil/Tools/Storage.pm index 7afbc752..0626d229 100644 --- a/Anvil/Tools/Storage.pm +++ b/Anvil/Tools/Storage.pm @@ -3607,7 +3607,7 @@ This is the name of the file to read. When reading from a remote machine, it mus =head3 force_read (optional, default '1') -This is an otpional parameter that, if set to C<< 0 >>, will allow an existing cached copy of the file to be used instead of actually reading the file from disk (again). +This is an optional parameter that, if set to C<< 0 >>, will allow an existing cached copy of the file to be used instead of actually reading the file from disk (again). =head3 password (optional) diff --git a/Anvil/Tools/System.pm b/Anvil/Tools/System.pm index 08f02524..3afc4c8b 100644 --- a/Anvil/Tools/System.pm +++ b/Anvil/Tools/System.pm @@ -3475,6 +3475,7 @@ sub check_firewall # Show live or permanent rules? Permanent is default my $permanent = defined $parameter->{permanent} ? $parameter->{permanent} : 1; + my $start = defined $parameter->{start} ? $parameter->{start} : 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { permanent => $permanent }}); # Read in /etc/firewalld/firewalld.conf and parse the 'DefaultZone' variable. @@ -3654,319 +3655,6 @@ sub manage_authorized_keys return(0); } -=head2 manage_firewall - -This method manages a firewalld firewall. - -B: This is pretty basic at this time. Capabilities will be added over time so please expect changes to this method. - -Parameters; - -=head3 task (optional) - -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 >>, the state of the given C<< port >> is returned. - -The default is C<< check >>. - -=head3 port_number (required) - -This is the port number to work on. - -If not specified, C<< service >> is required. - -=head3 protocol (optional) - -This can be c<< tcp >> or C<< upd >> and is used to specify what protocol to use with the C<< port >>, when specified. The default is C<< tcp >>. - -=cut -### TODO: This is slooooow. We need to be able to get more data per system call. -### - Getting better... -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 => "System->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} : "tcp"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - task => $task, - port_number => $port_number, - protocol => $protocol, - }}); - - ### NOTE: Disabled during development - return(0); - - # Make sure we have a port or service. - if (not $port_number) - { - # ... - return("!!error!!"); - } - if (($protocol ne "tcp") && ($protocol ne "udp")) - { - # Bad protocol - return("!!error!!"); - } - - # This will be set if the port is found to be open. - my $open = 0; - - # Checking the iptables rules in memory is very fast, relative to firewall-cmd. So we'll do an - # initial check there to see if the port in question is listed. - my $shell_call = $anvil->data->{path}{exe}{'iptables-save'}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }}); - - my ($iptables, $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); - foreach my $line (split/\n/, $iptables) - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }}); - if (($line =~ /-m $protocol /) && ($line =~ /--dport $port_number /) && ($line =~ /ACCEPT/)) - { - $open = 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open }}); - last; - } - } - - # If the port is open and the task is 'check' or 'open', we're done and can return now and save a lot - # of time. - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'task' => $task, 'open' => $open }}); - if ((($task eq "check") or ($task eq "open")) && ($open)) - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open }}); - return($open); - } - - # 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 (not $firewalld_running) - { - if ($anvil->data->{sys}{daemons}{restart_firewalld}) - { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", 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 - { - # We've been asked to leave it off. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0128"}); - return(0); - } - } - - - # Before we do anything, what zone is active? - my $active_zone = ""; - if (not $active_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({debug => $debug, 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 !~ /\s/) - { - $active_zone = $line; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { active_zone => $active_zone }}); - } - last; - } - } - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { active_zone => $active_zone }}); - - # If I still don't know what the active zone is, we're done. - if (not $active_zone) - { - return("!!error!!"); - } - - # If we have an active zone, see if the requested port is open. - my $zone_file = $anvil->data->{path}{directories}{firewalld_zones}."/".$active_zone.".xml"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { zone_file => $zone_file }}); - if (not -e $zone_file) - { - #... - return($open); - } - - # Read the XML to see what services are opened already and translate those into port numbers and - # protocols. - local $@; - my $open_services = []; - my $xml = XML::Simple->new(); - my $body = ""; - my $test = eval { $body = $xml->XMLin($zone_file, KeyAttr => { language => 'name', key => 'name' }, ForceArray => [ 'service' ]) }; - if (not $test) - { - chomp $@; - my $error = "[ Error ] - The was a problem reading: [$zone_file]. The error was:\n"; - $error .= "===========================================================\n"; - $error .= $@."\n"; - $error .= "===========================================================\n"; - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", raw => $error}); - - # Clear the error so it doesn't propogate out to a future 'die' and confuse things. - $@ = ''; - } - else - { - # Parse the already-opened services - foreach my $hash_ref (@{$body->{service}}) - { - # Load the details of this service. - my $service = $hash_ref->{name}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { service => $service }}); - $anvil->System->_load_specific_firewalld_zone({service => $hash_ref->{name}}); - push @{$open_services}, $service; - } - - # Now loop through the open services, protocols and ports looking for the one passed in by - # the caller. If found, the port is already open. - foreach my $service (sort {$a cmp $b} @{$open_services}) - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { service => $service }}); - foreach my $this_protocol ("tcp", "udp") - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_protocol => $this_protocol }}); - foreach my $this_port (sort {$a cmp $b} @{$anvil->data->{firewalld}{zones}{by_name}{$service}{tcp}}) - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_port => $this_port }}); - if (($port_number eq $this_port) && ($this_protocol eq $protocol)) - { - # Opened already (as the recorded service). - $open = $service; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open }}); - last if $open; - } - last if $open; - } - last if $open; - } - last if $open; - } - } - - # We're done if we were just checking. However, if we've been asked to open a currently closed port, - # or vice versa, make the change before returning. - my $changed = 0; - if (($task eq "open") && (not $open)) - { - # Map the port to a service, if possible. - my $service = $anvil->System->_match_port_to_service({port => $port_number}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { service => $service }}); - - # Open the port - if ($service) - { - my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --permanent --add-service ".$service; - $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 }}); - if ($output eq "success") - { - $open = 1; - $changed = 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open, changed => $changed }}); - } - else - { - # Something went wrong... - return("!!error!!"); - } - } - else - { - my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --permanent --add-port ".$port_number."/".$protocol; - $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 }}); - if ($output eq "success") - { - $open = 1; - $changed = 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open, changed => $changed }}); - } - else - { - # Something went wrong... - return("!!error!!"); - } - } - } - elsif (($task eq "close") && ($open)) - { - # Map the port to a service, if possible. - my $service = $anvil->System->_match_port_to_service({port => $port_number}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { service => $service }}); - - # Close the port - if ($service) - { - my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --permanent --remove-service ".$service; - $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 }}); - if ($output eq "success") - { - $open = 0; - $changed = 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open, changed => $changed }}); - } - else - { - # Something went wrong... - return("!!error!!"); - } - } - else - { - my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --permanent --remove-port ".$port_number."/".$protocol; - $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 }}); - if ($output eq "success") - { - $open = 0; - $changed = 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open, changed => $changed }}); - } - else - { - # Something went wrong... - return("!!error!!"); - } - } - } - - # If we made a change, reload. - if ($changed) - { - $anvil->System->reload_daemon({daemon => $anvil->data->{sys}{daemon}{firewalld}}); - } - - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'open' => $open }}); - return($open); -} - =head2 pids This parses C<< ps aux >> and stores the information about running programs in C<< pids:::: >>. If called against a remote host, the data is stored in C<< remote_pids:::: >>. diff --git a/notes b/notes index d63fd871..6d5aab04 100644 --- a/notes +++ b/notes @@ -7,6 +7,24 @@ dnf -y update && dnf -y install https://www.alteeve.com/an-repo/m3/anvil-release dnf -y update && dnf -y install https://www.alteeve.com/an-repo/m3/anvil-release-latest.noarch.rpm && alteeve-repo-setup -y && dnf -y install anvil-dr --allowerasing +### Currently set default zone; +# Doesn't seem to matter - /etc/firewalld/firewalld.conf:6:DefaultZone=public + +firewall-cmd --get-default-zone +# public +firewall-cmd --permanent --set-default-zone=IFN1 + + +firewall-cmd --permanent --new-zone="IFN1" +firewall-cmd --permanent --zone=IFN1 --set-description="Internet-Facing Network 1" +firewall-cmd --permanent --zone=IFN1 --set-short="IFN1" +firewall-cmd --permanent --zone=IFN1 --add-interface=ifn1_bond1 +firewall-cmd --permanent --zone=IFN1 --add-service=ssh +firewall-cmd --permanent --zone=IFN1 --add-service=postgresql +firewall-cmd --permanent --zone=IFN1 --add-port=22869/tcp +firewall-cmd --reload + + # Configure APC PDUs and UPSes tcpip -i 10.201.2.3 -s 255.255.0.0 -g 10.201.255.254 web -h enable diff --git a/scancore-agents/Makefile.am b/scancore-agents/Makefile.am index 42243270..6db8ee20 100644 --- a/scancore-agents/Makefile.am +++ b/scancore-agents/Makefile.am @@ -66,6 +66,11 @@ dist_lvm_DATA = \ dist_lvm_SCRIPTS = \ scan-lvm/scan-lvm +networkdir = ${targetdir}/scan-network +dist_network_DATA = \ + scan-network/scan-network.sql \ + scan-network/scan-network.xml + serverdir = ${targetdir}/scan-server dist_server_DATA = \ scan-server/scan-server.sql \ diff --git a/share/words.xml b/share/words.xml index ae1e4f2a..4797b566 100644 --- a/share/words.xml +++ b/share/words.xml @@ -2090,7 +2090,7 @@ The file: [#!variable!file!#] needs to be updated. The difference is: query() was asked to query the database with UUID: [#!variable!old_uuid!#] but there is no file handle open to the database. Switched the read to: [#!variable!new_uuid!#].]]> Opening the firewall zone: [#!variable!zone!#] to allow the service: [#!variable!service!#]. No password for the database on the host with UUID: [#!variable!uuid!#], skipping it. - The firewalld daemon isn't running, skipping firewall setup. + The firewalld daemon isn't running, skipping firewall setup. Is 'sys::daemon::firewalld' set to '0' in anvil.conf? The postgresql server is installed. The host: [#!variable!host_name!#] was powered off for an unknown reason, and 'feature::scancore::disable::boot-unknown-stop' is set to: [#!data!feature::scancore::disable::boot-unknown-stop!#]. Will not boot this host. The host: [#!variable!host_name!#] was powered off for an unknown reason, and 'feature::scancore::disable::boot-unknown-stop' is set to: [#!data!feature::scancore::disable::boot-unknown-stop!#]. If power and temperature looks good, we'll boot it. @@ -2128,6 +2128,7 @@ The file: [#!variable!file!#] needs to be updated. The difference is: read_state() was called but both the 'state_name' and 'state_uuid' parameters were not passed or both were empty.]]> Forcing the dailing resync and checking to clear records in the history schema no longer in public schema. Updating the OUI list will happen after the system has been up for at least an hour. You can force an update now by running 'striker-parse-oui --force' at the command line. + Updated: [#!data!path::configs::firewalld.conf!#] to disable 'AllowZoneDrifting'. See: https://firewalld.org/2020/01/allowzonedrifting The host name: [#!variable!target!#] does not resolve to an IP address. @@ -2549,6 +2550,11 @@ Available options; #!variable!cores!#c (#!variable!threads!#t) -=] Server Usage and Anvil! Node Resource Availability This program is currently disabled, please see NOTE in the header for more information. + # NOTE: This was added by the Anvil!, as per firewalld's warning below. +# WARNING: AllowZoneDrifting is enabled. This is considered an insecure +# configuration option. It will be removed in a future release. +# Please consider disabling it now. + Migration Network Saved the mail server information successfully! @@ -3226,6 +3232,17 @@ We will sleep a bit and try again. [ Warning ] - While evaluating database shutdown, the host UUID: [#!variable!host_uuid!#] was not yet found in the database on host: [#!variable!db_uuid!#]. DB shutdown will not happen until all hosts are in all DBs. [ Warning ] - While preparing to record the state: [#!variable!state_info!#], the host UUID: [#!variable!host_uuid!#] was not yet found in the database on host: [#!variable!db_uuid!#]. NOT recording the state! [ Warning ] - The daemon: [#!variable!daemon!#] was found running. It shouldn't be, and will now be stopped and disabled. + [ Warning ] - Failed to parse the firewall zone file: [#!variable!file!#]. The body of the file was: +======== +#!variable!body!# +======== + +The error was: + +======== +#!variable!error!# +======== + diff --git a/tools/Makefile.am b/tools/Makefile.am index 18dd6e5d..ca8eec50 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -17,8 +17,10 @@ dist_sbin_SCRIPTS = \ anvil-get-server-screenshot \ anvil-join-anvil \ anvil-maintenance-mode \ + anvil-manage-dr \ anvil-manage-files \ anvil-manage-firewall \ + anvil-manage-host \ anvil-manage-keys \ anvil-manage-power \ anvil-manage-server \ @@ -26,16 +28,25 @@ dist_sbin_SCRIPTS = \ anvil-parse-fence-agents \ anvil-provision-server \ anvil-rename-server \ + anvil-report-usage \ anvil-safe-start \ + anvil-safe-stop \ anvil-scan-network \ + anvil-show-local-ips \ anvil-shutdown-server \ anvil-sync-shared \ + anvil-test-alerts \ + anvil-update-definition \ anvil-update-issue \ anvil-update-states \ anvil-update-system \ anvil-watch-bonds \ scancore \ + striker-auto-initialize-all \ striker-boot-machine \ + striker-db-report \ + striker-db-status \ + striker-file-manager \ striker-get-peer-data \ striker-initialize-host \ striker-manage-install-target \ @@ -47,10 +58,8 @@ dist_sbin_SCRIPTS = \ striker-prep-database \ striker-purge-target \ striker-scan-network \ - striker-show-db-counts \ - striker-auto-initialize-all \ - striker-db-status - + striker-show-db-counts + fencedir = ${FASEXECPREFIX}/sbin dist_fence_SCRIPTS = \ diff --git a/tools/anvil-manage-firewall b/tools/anvil-manage-firewall index 64ba4075..ebfc042b 100755 --- a/tools/anvil-manage-firewall +++ b/tools/anvil-manage-firewall @@ -51,19 +51,23 @@ if (not $anvil->data->{sys}{manage}{firewall}) $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }}); # Read switches -$anvil->data->{switches}{'y'} = ""; +$anvil->data->{switches}{force} = ""; +$anvil->data->{switches}{'y'} = ""; $anvil->Get->switches; -# For now, we just disable the firewall, if it is enabled. -my $firewall_running = $anvil->System->check_daemon({daemon => "firewalld", debug => 3}); -$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { firewall_running => $firewall_running }}); -if ($firewall_running eq "1") +# Enable and start the firewall, if needed +my $firewall_running = $anvil->Network->check_firewall({debug => 2}); +if (not $firewall_running) { - # Disable it. - $anvil->System->stop_daemon({daemon => "firewalld", debug => 2}); - $anvil->System->disable_daemon({daemon => "firewalld", debug => 2}); + # It must be disabled, exit + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0669"}); + $anvil->nice_exit({exit_code => 0}); +} + +if (not $anvil->data->{switches}{force}) +{ + $anvil->nice_exit({exit_code => 0}); } -$anvil->nice_exit({exit_code => 0}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "message_0134"}); @@ -106,7 +110,7 @@ sub check_initial_setup foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$local_host}{interface}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface => $interface }}); - if ($interface =~ /^((bcn|ifn|sn)\d+)_/) + if ($interface =~ /^((bcn|ifn|sn|mn)\d+)_/) { # We'll use the start of the string (network type) as the zone, though it should # always be overridden by the ZONE="" variable in each interface's config. diff --git a/tools/striker-prep-database b/tools/striker-prep-database index 173604ec..f0889b70 100755 --- a/tools/striker-prep-database +++ b/tools/striker-prep-database @@ -613,7 +613,7 @@ sub configure_firewall # Is postgres open? if ((not exists $anvil->data->{firewall}{zone}{$in_zone}{service}{postgresql}) or (not $anvil->data->{firewall}{zone}{$in_zone}{service}{postgresql})) { - ### TODO: Switch this to System->manage_firewall(). + ### TODO: Switch this to Network->manage_firewall(). # Enable it. my $service = "postgresql"; $reload = 1;