You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4467 lines
204 KiB
4467 lines
204 KiB
#!/usr/bin/perl |
|
# |
|
# This scans the network, bridges, bonds and interfaces. |
|
# |
|
# Examples; |
|
# |
|
# Exit codes; |
|
# 0 = Normal exit. |
|
# 1 = Startup failure (not running as root, no DB, bad file read, etc) |
|
# |
|
# TODO: |
|
# - |
|
# |
|
|
|
use strict; |
|
use warnings; |
|
use Anvil::Tools; |
|
use Data::Dumper; |
|
|
|
# Disable buffering |
|
$| = 1; |
|
|
|
# Prevent a discrepency between UID/GID and EUID/EGID from throwing an error. |
|
$< = $>; |
|
$( = $); |
|
|
|
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; |
|
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; |
|
if (($running_directory =~ /^\./) && ($ENV{PWD})) |
|
{ |
|
$running_directory =~ s/^\./$ENV{PWD}/; |
|
} |
|
|
|
my $anvil = Anvil::Tools->new(); |
|
|
|
# Make sure we're running as 'root' |
|
# $< == real UID, $> == effective UID |
|
if (($< != 0) && ($> != 0)) |
|
{ |
|
# Not root |
|
print $anvil->Words->string({key => "error_0005"})."\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
# These are the threasholds for when to alert when swap is running out. |
|
$anvil->data->{switches}{force} = 0; |
|
|
|
#$anvil->Storage->read_config(); |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "log_0115", variables => { program => $THIS_FILE }}); |
|
|
|
# Read switches |
|
$anvil->Get->switches; |
|
|
|
# Handle start-up tasks |
|
my $problem = $anvil->ScanCore->agent_startup({agent => $THIS_FILE}); |
|
if ($problem) |
|
{ |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
if ($anvil->data->{switches}{purge}) |
|
{ |
|
# This can be called when doing bulk-database purges. |
|
my $schema_file = $anvil->data->{path}{directories}{scan_agents}."/".$THIS_FILE."/".$THIS_FILE.".sql"; |
|
$anvil->Database->purge_data({ |
|
debug => 2, |
|
tables => $anvil->Database->get_tables_from_schema({schema_file => $schema_file}), |
|
}); |
|
$anvil->nice_exit({exit_code => 0}); |
|
} |
|
|
|
# If there's no DB (or cached data isn't recorded to the database yet), this will store those records. |
|
$anvil->data->{cache}{new_file} = "# interface (nm_device),timestamp,mac_address,speed,link_state,operational,nm_uuid,nm_name\n"; |
|
process_interface_cache($anvil); |
|
|
|
# Read the data. |
|
collect_data($anvil); |
|
|
|
# Load stored data. |
|
read_last_scan($anvil); |
|
|
|
# Look for changes. |
|
find_changes($anvil); |
|
|
|
# Finally, process health weights. |
|
process_health($anvil); |
|
|
|
# This clears the TX and RX variable data for interfaces older than 'scancore::database::age_out'. |
|
clear_old_variables($anvil); |
|
|
|
# This removes network interfaces that have been marked as DELETED for a while now. |
|
clear_old_interfaces($anvil); |
|
|
|
# Write out the interface cache |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { |
|
"cache::new_file" => $anvil->data->{cache}{new_file}, |
|
"path::data::network_cache" => $anvil->data->{path}{data}{network_cache}, |
|
}}); |
|
$anvil->Storage->write_file({ |
|
body => $anvil->data->{cache}{new_file}, |
|
file => $anvil->data->{path}{data}{network_cache}, |
|
overwrite => 1, |
|
backup => 0, |
|
}); |
|
|
|
# Shut down. |
|
$anvil->ScanCore->agent_shutdown({agent => $THIS_FILE}); |
|
|
|
|
|
############################################################################################################# |
|
# Functions # |
|
############################################################################################################# |
|
|
|
# This reads in the interface cache file and looks for records that haven't been stored in the database yet. |
|
sub process_interface_cache |
|
{ |
|
my ($anvil) = @_; |
|
|
|
# Does the file exist? If so, read it in. |
|
if (-e $anvil->data->{path}{data}{network_cache}) |
|
{ |
|
my $body = $anvil->Storage->read_file({debug => 2, cache => 0, force_read => 1, file => $anvil->data->{path}{data}{network_cache}}); |
|
foreach my $line (split/\n/, $body) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); |
|
next if $line =~ /^#/; |
|
my ($nm_device, $timestamp, $mac_address, $speed, $link_state, $operational, $nm_uuid, $nm_name) = (split/,/, $line); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
timestamp => $timestamp, |
|
speed => $speed, |
|
mac_address => $mac_address, |
|
link_state => $link_state, |
|
operational => $operational, |
|
nm_uuid => $nm_uuid, |
|
nm_device => $nm_device, |
|
}}); |
|
|
|
if ($anvil->data->{sys}{database}{connections}) |
|
{ |
|
my ($network_interface_uuid) = $anvil->Database->insert_or_update_network_interfaces({ |
|
debug => 2, |
|
link_only => 1, |
|
timestamp => $timestamp, |
|
network_interface_name => $nm_device, |
|
network_interface_link_state => $link_state, |
|
network_interface_mac_address => $mac_address, |
|
network_interface_operational => $operational, |
|
network_interface_speed => $speed, |
|
network_interface_nm_uuid => $nm_uuid, |
|
network_interface_device => $nm_device, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network_interface_uuid => $network_interface_uuid }}); |
|
if (not $network_interface_uuid) |
|
{ |
|
# Failed to update, could be that we cached data for an interface not yet |
|
# seen. If so, the coming scan will add it and this cache should flush out |
|
# next time. |
|
$anvil->data->{cache}{new_file} .= $line."\n"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "cache::new_file" => $anvil->data->{cache}{new_file} }}); |
|
} |
|
} |
|
else |
|
{ |
|
# No database, re-cache |
|
$anvil->data->{cache}{new_file} .= $line."\n"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "cache::new_file" => $anvil->data->{cache}{new_file} }}); |
|
} |
|
} |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
# This removes network interfaces that have been marked as DELETED for a while now. |
|
sub clear_old_interfaces |
|
{ |
|
my ($anvil) = @_; |
|
|
|
# Read in all interfaces and for each, delete historical records over the age-out time. |
|
my $age = $anvil->data->{scancore}{database}{age_out}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { age => $age }}); |
|
|
|
if ($age =~ /\D/) |
|
{ |
|
# Age is not valid, set it to defaults. |
|
$age = 24; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { age => $age }}); |
|
} |
|
|
|
my $query = "SELECT now() - '".$age."h'::interval;"; |
|
my $old_timestamp = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
query => $query, |
|
old_timestamp => $old_timestamp, |
|
}}); |
|
|
|
# It is possible that a record exists on one DB, but not the other. Unsure how this happens, but this |
|
# cleans it up. |
|
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}}) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
uuid => $anvil->Database->get_host_from_uuid({debug => 2, short => 1, host_uuid => $uuid})." (".$uuid.")", |
|
}}); |
|
my $query = " |
|
SELECT |
|
ip_address_uuid, |
|
ip_address_address |
|
FROM |
|
ip_addresses |
|
WHERE |
|
ip_address_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." |
|
AND |
|
ip_address_note = 'DELETED' |
|
AND |
|
modified_date < '".$old_timestamp."' |
|
ORDER BY |
|
ip_address_address ASC |
|
;"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
my $results = $anvil->Database->query({uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__}); |
|
my $count = @{$results}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
foreach my $row (@{$results}) |
|
{ |
|
my $ip_address_uuid = $row->[0]; |
|
my $ip_address_address = $row->[1]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:ip_address_uuid' => $ip_address_uuid, |
|
's2:ip_address_address' => $ip_address_address, |
|
}}); |
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_network_log_0005", variables => { |
|
age => $age, |
|
ip => $ip_address_address, |
|
}}); |
|
|
|
my $queries = []; |
|
push @{$queries}, "DELETE FROM history.ip_addresses WHERE ip_address_uuid = '".$ip_address_uuid."';"; |
|
push @{$queries}, "DELETE FROM ip_addresses WHERE ip_address_uuid = '".$ip_address_uuid."';"; |
|
foreach my $query (@{$queries}) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
} |
|
# Write to both DBs. |
|
$anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__}); |
|
} |
|
|
|
# Remove interfaces |
|
$query = " |
|
SELECT |
|
network_interface_uuid, |
|
network_interface_mac_address, |
|
network_interface_name |
|
FROM |
|
network_interfaces |
|
WHERE |
|
network_interface_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." |
|
AND |
|
network_interface_operational = 'DELETED' |
|
AND |
|
modified_date < '".$old_timestamp."' |
|
ORDER BY |
|
network_interface_name ASC |
|
;"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
$results = $anvil->Database->query({uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__}); |
|
$count = @{$results}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
foreach my $row (@{$results}) |
|
{ |
|
my $network_interface_uuid = $row->[0]; |
|
my $network_interface_mac_address = $row->[1]; |
|
my $network_interface_name = $row->[2]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:network_interface_uuid' => $network_interface_uuid, |
|
's2:network_interface_mac_address' => $network_interface_mac_address, |
|
's3:network_interface_name' => $network_interface_name, |
|
}}); |
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_network_log_0002", variables => { |
|
age => $age, |
|
mac => $network_interface_mac_address, |
|
name => $network_interface_name, |
|
}}); |
|
|
|
my $queries = []; |
|
push @{$queries}, "DELETE FROM history.network_interfaces WHERE network_interface_uuid = '".$network_interface_uuid."';"; |
|
push @{$queries}, "DELETE FROM network_interfaces WHERE network_interface_uuid = '".$network_interface_uuid."';"; |
|
foreach my $query (@{$queries}) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
} |
|
# Write to both DBs. |
|
$anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__}); |
|
} |
|
|
|
# Delete old bonds |
|
$query = " |
|
SELECT |
|
bond_uuid, |
|
bond_name |
|
FROM |
|
bonds |
|
WHERE |
|
bond_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." |
|
AND |
|
bond_mode = 'DELETED' |
|
AND |
|
modified_date < '".$old_timestamp."' |
|
ORDER BY |
|
bond_name ASC |
|
;"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
$results = $anvil->Database->query({uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__}); |
|
$count = @{$results}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
foreach my $row (@{$results}) |
|
{ |
|
my $bond_uuid = $row->[0]; |
|
my $bond_name = $row->[1]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:bond_uuid' => $bond_uuid, |
|
's2:bond_name' => $bond_name, |
|
}}); |
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_network_log_0003", variables => { |
|
age => $age, |
|
name => $bond_name, |
|
}}); |
|
|
|
my $queries = []; |
|
push @{$queries}, "DELETE FROM history.bonds WHERE bond_uuid = '".$bond_uuid."';"; |
|
push @{$queries}, "DELETE FROM bonds WHERE bond_uuid = '".$bond_uuid."';"; |
|
foreach my $query (@{$queries}) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
} |
|
# Write to both DBs. |
|
$anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__}); |
|
} |
|
|
|
# Delete old bridges |
|
$query = " |
|
SELECT |
|
bridge_uuid, |
|
bridge_name |
|
FROM |
|
bridges |
|
WHERE |
|
bridge_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." |
|
AND |
|
bridge_id = 'DELETED' |
|
AND |
|
modified_date < '".$old_timestamp."' |
|
ORDER BY |
|
bridge_name ASC |
|
;"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
$results = $anvil->Database->query({uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__}); |
|
$count = @{$results}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
foreach my $row (@{$results}) |
|
{ |
|
my $bridge_uuid = $row->[0]; |
|
my $bridge_name = $row->[1]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:bridge_uuid' => $bridge_uuid, |
|
's2:bridge_name' => $bridge_name, |
|
}}); |
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_network_log_0004", variables => { |
|
age => $age, |
|
name => $bridge_name, |
|
}}); |
|
|
|
my $queries = []; |
|
push @{$queries}, "DELETE FROM history.bridges WHERE bridge_uuid = '".$bridge_uuid."';"; |
|
push @{$queries}, "DELETE FROM bridges WHERE bridge_uuid = '".$bridge_uuid."';"; |
|
foreach my $query (@{$queries}) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
} |
|
# Write to both DBs. |
|
$anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__}); |
|
} |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
# This clears the TX and RX variable data for interfaces older than 'scancore::database::age_out'. |
|
sub clear_old_variables |
|
{ |
|
my ($anvil) = @_; |
|
|
|
# Only Strikers run this. |
|
my $host_type = $anvil->Get->host_type(); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }}); |
|
if ($host_type ne "striker") |
|
{ |
|
return(0); |
|
} |
|
|
|
# Read in all interfaces and for each, delete historical records over the age-out time. |
|
my $age = $anvil->data->{scancore}{database}{age_out}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { age => $age }}); |
|
|
|
if ($age =~ /\D/) |
|
{ |
|
# Age is not valid, set it to defaults. |
|
$age = 24; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { age => $age }}); |
|
} |
|
|
|
# Get the timestamp to delete thermal and power records older than $age hours. |
|
my $query = "SELECT now() - '".$age."h'::interval;"; |
|
my $old_timestamp = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { |
|
query => $query, |
|
old_timestamp => $old_timestamp, |
|
}}); |
|
|
|
# Read in all interface RX and TX variables. |
|
foreach my $uuid (keys %{$anvil->data->{cache}{database_handle}}) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { |
|
uuid => $uuid, |
|
db_host => $anvil->Get->host_name_from_uuid({host_uuid => $uuid}), |
|
}}); |
|
my $queries = []; |
|
$query = " |
|
SELECT |
|
variable_uuid, |
|
variable_name |
|
FROM |
|
variables |
|
WHERE |
|
variable_name LIKE '%::tx_bytes' |
|
OR |
|
variable_name LIKE '%::rx_bytes' |
|
;"; |
|
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); |
|
my $count = @{$results}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
foreach my $row (@{$results}) |
|
{ |
|
my $variable_uuid = $row->[0]; |
|
my $variable_name = $row->[1]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:variable_name' => $variable_name, |
|
's2:variable_uuid' => $variable_uuid, |
|
}}); |
|
|
|
# Find out of there are any records to remove at all. |
|
my $query = "SELECT history_id FROM history.variables WHERE variable_uuid = ".$anvil->Database->quote($variable_uuid)." AND modified_date <= '".$old_timestamp."';"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
|
|
my $results = $anvil->Database->query({uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__}); |
|
my $count = @{$results}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
|
|
if ($count) |
|
{ |
|
# Find how many records will be left. If it's 0, we'll use an OFFSET 1. |
|
my $query = "SELECT history_id FROM history.variables WHERE variable_uuid = ".$anvil->Database->quote($variable_uuid)." AND modified_date > '".$old_timestamp."';"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
|
|
my $results = $anvil->Database->query({uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__}); |
|
my $count = @{$results}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
if ($count) |
|
{ |
|
# At least one record will be left, we can do a simple delete. |
|
my $query = "DELETE FROM history.variables WHERE variable_uuid = ".$anvil->Database->quote($variable_uuid)." AND modified_date <= '".$old_timestamp."';"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
push @{$queries}, $query; |
|
} |
|
else |
|
{ |
|
# This would delete everything, reserve at least one record. |
|
foreach my $row (@{$results}) |
|
{ |
|
my $history_id = $row->[0]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { history_id => $history_id }}); |
|
|
|
my $query = "DELETE FROM history.variables WHERE variable_uuid = ".$anvil->Database->quote($variable_uuid)." AND history_id = '".$history_id."';"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
push @{$queries}, $query; |
|
} |
|
} |
|
} |
|
} |
|
|
|
my $commits = @{$queries}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { commits => $commits }}); |
|
if ($commits) |
|
{ |
|
# Commit the DELETEs. |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_network_log_0001", variables => { |
|
age => $age, |
|
records => $commits, |
|
host => $anvil->Get->host_name_from_uuid({host_uuid => $uuid}), |
|
}}); |
|
$anvil->Database->write({debug => 2, uuid => $uuid, query => $queries, source => $THIS_FILE, line => __LINE__}); |
|
undef $queries; |
|
} |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
# This reads in all of the network data |
|
sub collect_data |
|
{ |
|
my ($anvil) = @_; |
|
|
|
# Read in data from Network Manager |
|
my $uptime = $anvil->Get->uptime; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { uptime => $uptime }}); |
|
if ($uptime < 600) |
|
{ |
|
# Check for / start down but configured interfaces. |
|
$anvil->Network->collect_data({up => 1, debug => 2}); |
|
} |
|
else |
|
{ |
|
# Just collect data. |
|
$anvil->Network->collect_data({debug => 2}); |
|
} |
|
|
|
### TODO: We can get the IPs from collect_data, we should phase this out. |
|
# The 'local_host' is needed to pull data recorded by Network->get_ips(); |
|
$anvil->Network->get_ips({debug => 2}); |
|
my $local_host = $anvil->Get->short_host_name(); |
|
my $directory = "/sys/class/net"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
local_host => $local_host, |
|
directory => $directory, |
|
}}); |
|
|
|
### TODO: Convert this to use Sys::Virt |
|
# Make sure there are no virsh bridges, removing any found. |
|
my $host_type = $anvil->Get->host_type(); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }}); |
|
if (($host_type eq "node") or ($host_type eq "dr")) |
|
{ |
|
my $default_network = "/etc/libvirt/qemu/networks/default.xml"; |
|
if (-e $default_network) |
|
{ |
|
my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." net-destroy default"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
|
|
$shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." net-undefine default"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
|
|
# Register an alert |
|
my $variables = { |
|
bridge => "default", |
|
}; |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_network_alert_0001", variables => $variables}); |
|
$anvil->Alert->register({ |
|
alert_level => "notice", |
|
message => "scan_network_alert_0001", |
|
variables => $variables, |
|
set_by => $THIS_FILE, |
|
}); |
|
} |
|
} |
|
|
|
# Collect data from nmcli |
|
$anvil->Network->collect_data({debug => 2}); |
|
|
|
# Walk through the sysfs files. |
|
local(*DIRECTORY); |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0018", variables => { directory => $directory }}); |
|
opendir(DIRECTORY, $directory); |
|
while(my $file = readdir(DIRECTORY)) |
|
{ |
|
next if $file eq "."; |
|
next if $file eq ".."; |
|
next if $file eq "lo"; |
|
my $full_path = $directory."/".$file; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { full_path => $full_path }}); |
|
if (-d $full_path) |
|
{ |
|
### NOTE: The 'interface' maps to the network manager 'GENERAL.IP-IFACE', which is |
|
### 'network_interface_device'. The 'network_interface_name' is the biosdevname. |
|
# Pull out the data I want. Note that some of these don't exist with virtio-net interfaces. |
|
my $interface = $file; |
|
my $link_state = -e $full_path."/carrier" ? $anvil->Storage->read_file({file => $full_path."/carrier"}) : 0; |
|
$link_state =~ s/\n$//; |
|
my $mtu = -e $full_path."/mtu" ? $anvil->Storage->read_file({file => $full_path."/mtu"}) : 0; |
|
$mtu =~ s/\n$//; |
|
my $duplex = -e $full_path."/duplex" ? $anvil->Storage->read_file({file => $full_path."/duplex"}) : "unknown"; # full or half? |
|
$duplex =~ s/\n$//; |
|
my $operational = -e $full_path."/operstate" ? $anvil->Storage->read_file({file => $full_path."/operstate"}) : "unknown"; # up or down |
|
$operational =~ s/\n$//; |
|
my $modalias = -e $full_path."/device/modalias" ? $anvil->Storage->read_file({file => $full_path."/device/modalias"}) : "unknown"; |
|
$modalias =~ s/\n$//; |
|
my $speed = $link_state ? $anvil->Storage->read_file({file => $full_path."/speed"}) : 0; # Mbps (ie: 1000 = Gbps), gives a very high number for unplugged link |
|
$speed =~ s/\n$//; |
|
my $media = "unknown"; |
|
my $type = "interface"; |
|
my $driver = ""; |
|
my $tx_bytes = 0; # How many bytes transmitted |
|
my $rx_bytes = 0; # How many bytes received |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
interface => $interface, |
|
link_state => $link_state, |
|
mtu => $mtu, |
|
duplex => $duplex, |
|
operational => $operational, |
|
speed => $speed, |
|
modalias => $modalias, |
|
}}); |
|
|
|
# Try to find the nm_uuid |
|
my $nm_uuid = ""; |
|
if ((exists $anvil->data->{nmcli}{name}{$interface}) && ($anvil->data->{nmcli}{name}{$interface}{uuid})) |
|
{ |
|
$nm_uuid = $anvil->data->{nmcli}{name}{$interface}{uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { nm_uuid => $nm_uuid }}); |
|
} |
|
elsif ((exists $anvil->data->{nmcli}{device}{$interface}) && ($anvil->data->{nmcli}{device}{$interface}{uuid})) |
|
{ |
|
$nm_uuid = $anvil->data->{nmcli}{device}{$interface}{uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { nm_uuid => $nm_uuid }}); |
|
} |
|
|
|
my $nm_device = ""; # biosdevname |
|
my $nm_name = ""; # ip name |
|
if ($nm_uuid) |
|
{ |
|
$nm_device = $anvil->data->{nmcli}{uuid}{$nm_uuid}{device}; |
|
$nm_name = $anvil->data->{nmcli}{uuid}{$nm_uuid}{name}; |
|
} |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
nm_device => $nm_device, |
|
nm_name => $nm_name, |
|
}}); |
|
|
|
### NOTE: This only parses virtio so far. |
|
# Pick out our driver. |
|
if ($modalias =~ /^virtio:/) |
|
{ |
|
$driver = "virtio"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { driver => $driver }}); |
|
} |
|
|
|
# The MAC address can faked by a number of ways, so we make an explicit call to 'ethtool' to get the permanent mac address. |
|
my $mac_address = ""; |
|
my $shell_call = $anvil->data->{path}{exe}{ethtool}." -P ".$interface; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
|
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
if ($output =~ /(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)$/) |
|
{ |
|
$mac_address = lc($1); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }}); |
|
} |
|
else |
|
{ |
|
# Get it by reading the address file. |
|
if (-e $full_path."/bonding_slave/perm_hwaddr") |
|
{ |
|
$mac_address = $anvil->Storage->read_file({file => $full_path."/bonding_slave/perm_hwaddr"}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }}); |
|
} |
|
elsif (-e $full_path."/address") |
|
{ |
|
$mac_address = $anvil->Storage->read_file({file => $full_path."/address"}); |
|
$mac_address =~ s/\n//; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }}); |
|
} |
|
} |
|
|
|
# These are variables that will be needed if this is a bond interface. |
|
my $ip_address = ""; |
|
my $subnet_mask = ""; |
|
my $bond_mode = ""; |
|
my $primary_interface = ""; |
|
my $primary_reselect = ""; |
|
my $active_interface = ""; |
|
my $mii_polling_interval = ""; |
|
my $up_delay = ""; |
|
my $down_delay = ""; |
|
my $bond_master = ""; |
|
|
|
# These are variables that will be needed if this is a bridge interface |
|
my $bridge_id = ""; |
|
my $bridge_stp_enabled = ""; |
|
|
|
# Explicitly check for the existing of the hash so that we don't auto-vivivate the interface. |
|
if (exists $anvil->data->{network}{$local_host}{interface}{$interface}) |
|
{ |
|
$ip_address = defined $anvil->data->{network}{$local_host}{interface}{$interface}{ip} ? $anvil->data->{network}{$local_host}{interface}{$interface}{ip} : ""; |
|
$subnet_mask = defined $anvil->data->{network}{$local_host}{interface}{$interface}{subnet_mask} ? $anvil->data->{network}{$local_host}{interface}{$interface}{subnet_mask} : ""; |
|
$type = defined $anvil->data->{network}{$local_host}{interface}{$interface}{type} ? $anvil->data->{network}{$local_host}{interface}{$interface}{type} : "interface"; |
|
$tx_bytes = defined $anvil->data->{network}{$local_host}{interface}{$interface}{tx_bytes} ? $anvil->data->{network}{$local_host}{interface}{$interface}{tx_bytes} : 0; |
|
$rx_bytes = defined $anvil->data->{network}{$local_host}{interface}{$interface}{rx_bytes} ? $anvil->data->{network}{$local_host}{interface}{$interface}{rx_bytes} : 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
ip_address => $ip_address, |
|
subnet_mask => $subnet_mask, |
|
type => $type, |
|
rx_bytes => $rx_bytes." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $rx_bytes}).")", |
|
tx_bytes => $tx_bytes." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $tx_bytes}).")", |
|
}}); |
|
} |
|
|
|
# If this interface is already a bond slave, the real mac address will be in a |
|
# sub-directory. |
|
my $mac_bond_file = $directory."/".$file."/bonding_slave/perm_hwaddr"; |
|
if (-e $mac_bond_file) |
|
{ |
|
# It's a slave. |
|
$mac_address = $anvil->Storage->read_file({file => $mac_bond_file}); |
|
$mac_address =~ s/\n$//; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }}); |
|
} |
|
|
|
# If this is a virtual interface, set some fake values that don't actually exist on |
|
# the system for the sake of a cleaner display. |
|
if (($mac_address =~ /^52:54:00/) or ($driver eq "virtio")) |
|
{ |
|
### Set some fake values. |
|
# Speed is "as fast as possible", so we'll record 100 Gbps, but that is really kind of arbitrary. |
|
if ((not $speed) or ($speed eq "-1")) |
|
{ |
|
$speed = 10000; |
|
} |
|
if ((not $duplex) or ($duplex eq "unknown")) |
|
{ |
|
$duplex = "full"; |
|
} |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
speed => $speed, |
|
duplex => $duplex, |
|
}}); |
|
} |
|
# If the state is 'down', set the speed to '0'. |
|
if (not $link_state) |
|
{ |
|
$speed = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { speed => $speed }}); |
|
} |
|
|
|
# Is this a bond interface? |
|
if (-e "/proc/net/bonding/".$interface) |
|
{ |
|
# Yup, we'll neet to dig into the bond proc files to get the proper slaved |
|
# interface MAC addresses. |
|
$type = "bond"; |
|
$nm_uuid = $anvil->data->{nmcli}{bond}{$interface}{uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
type => $type, |
|
nm_uuid => $nm_uuid, |
|
}}); |
|
|
|
# Read the bond mode. |
|
$bond_mode = $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/mode"}); |
|
$primary_interface = $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/primary"}); |
|
$primary_reselect = $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/primary_reselect"}); |
|
$active_interface = $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/active_slave"}); |
|
$mii_polling_interval = $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/miimon"}); |
|
$up_delay = $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/updelay"}); |
|
$down_delay = $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/downdelay"}); |
|
$bond_mode =~ s/\s.*//; |
|
$bond_mode =~ s/\n$//; |
|
$primary_interface =~ s/\n$//; |
|
$primary_reselect =~ s/\s.*//; |
|
$primary_reselect =~ s/\n$//; |
|
$active_interface =~ s/\n$//; |
|
$mii_polling_interval =~ s/\n$//; |
|
$up_delay =~ s/\n$//; |
|
$down_delay =~ s/\n$//; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
active_interface => $active_interface, |
|
bond_mode => $bond_mode, |
|
mii_polling_interval => $mii_polling_interval, |
|
primary_reselect => $primary_reselect, |
|
primary_interface => $primary_interface, |
|
type => $type, |
|
}}); |
|
} |
|
elsif ((-e $full_path."/master") && ($interface !~ /^vnet/)) |
|
{ |
|
# We're in a bond. |
|
my $target = readlink($full_path."/master"); |
|
$bond_master = ($target =~ /^.*\/(.*)$/)[0]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
target => $target, |
|
bond_master => $bond_master, |
|
}}); |
|
} |
|
elsif (-d $full_path."/bridge") |
|
{ |
|
# It's a bridge |
|
$type = "bridge"; |
|
$bridge_id = $anvil->Storage->read_file({debug => 3, file => $full_path."/bridge/bridge_id"}); |
|
$bridge_stp_enabled = $anvil->Storage->read_file({debug => 3, file => $full_path."/bridge/stp_state"}); |
|
$bridge_id =~ s/\n$//; |
|
$bridge_stp_enabled =~ s/\n$//; |
|
$speed = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
type => $type, |
|
bridge_id => $bridge_id, |
|
bridge_stp_enabled => $bridge_stp_enabled, |
|
}}); |
|
if ($bridge_stp_enabled eq "0") |
|
{ |
|
$bridge_stp_enabled = "disabled"; |
|
} |
|
elsif ($bridge_stp_enabled eq "1") |
|
{ |
|
$bridge_stp_enabled = "enabled_kernel"; |
|
} |
|
elsif ($bridge_stp_enabled eq "2") |
|
{ |
|
$bridge_stp_enabled = "enabled_userland"; |
|
} |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bridge_stp_enabled => $bridge_stp_enabled }}); |
|
} |
|
|
|
# If this is a 'vnet' device, set 'operational' to up |
|
if ($interface =~ /^vnet/) |
|
{ |
|
$operational = "up"; |
|
$media = "virtual"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
operational => $operational, |
|
media => $media, |
|
}}); |
|
} |
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
active_interface => $active_interface, |
|
bond_master => $bond_master, |
|
bond_mode => $bond_mode, |
|
bridge_id => $bridge_id, |
|
bridge_stp_enabled => $bridge_stp_enabled, |
|
down_delay => $down_delay, |
|
duplex => $duplex, |
|
interface => $interface, |
|
mac_address => $mac_address, |
|
mii_polling_interval => $mii_polling_interval, |
|
mtu => $mtu, |
|
operational => $operational, |
|
primary_reselect => $primary_reselect, |
|
primary_interface => $primary_interface, |
|
speed => $speed, |
|
subnet_mask => $subnet_mask, |
|
type => $type, |
|
up_delay => $up_delay, |
|
}}); |
|
|
|
# If the MAC address starts with '52:54:00', we've got a virtio NIC. |
|
if ((not defined $speed) or ($speed eq "")) |
|
{ |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_network_error_0001", variables => { file => $full_path."/speed" }}); |
|
next; |
|
} |
|
if ($speed =~ /\D/) |
|
{ |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_network_error_0002", variables => { |
|
file => $full_path."/speed", |
|
speed => $speed, |
|
}}); |
|
next; |
|
} |
|
if ($speed > 100000) |
|
{ |
|
# NOTE: This is probably 0 now... Though someday >100 Gbps will be reasonable |
|
# and we'll need to change this. |
|
$speed = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { speed => $speed }}); |
|
} |
|
|
|
# Find the media, if possible. |
|
(my $ethtool, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{ethtool}." ".$interface}); |
|
foreach my $line (split/\n/, $ethtool) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); |
|
if ($line =~ /Supported ports: \[ (.*?) \]/i) |
|
{ |
|
$media = lc($1); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { media => $media }}); |
|
|
|
# This can be 'tp mii', which breaks json. |
|
if ($media =~ /\t/) |
|
{ |
|
$media =~ s/\t/,/g; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { media => $media }}); |
|
} |
|
last; |
|
} |
|
} |
|
|
|
# Store new information we found. |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{nm_uuid} = $nm_uuid; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{nm_device} = $nm_device; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{nm_name} = $nm_name; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{active_interface} = $active_interface; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{bond_mode} = $bond_mode; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{bond_master} = $bond_master; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{bridge_id} = $bridge_id; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{bridge_stp_enabled} = $bridge_stp_enabled; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{down_delay} = $down_delay; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{duplex} = $duplex; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{ip} = $ip_address; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{link_state} = $link_state; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{mac_address} = $mac_address; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{media} = $media; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{mii_polling_interval} = $mii_polling_interval; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{mtu} = $mtu; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{operational} = $operational; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{primary_reselect} = $primary_reselect; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{primary_interface} = $primary_interface; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{speed} = $speed; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{subnet_mask} = $subnet_mask; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{type} = $type; |
|
$anvil->data->{network}{$local_host}{interface}{$interface}{up_delay} = $up_delay; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"network::${local_host}::interface::${interface}::nm_uuid" => $anvil->data->{network}{$local_host}{interface}{$interface}{nm_uuid}, |
|
"network::${local_host}::interface::${interface}::nm_device" => $anvil->data->{network}{$local_host}{interface}{$interface}{nm_device}, |
|
"network::${local_host}::interface::${interface}::nm_name" => $anvil->data->{network}{$local_host}{interface}{$interface}{nm_name}, |
|
"network::${local_host}::interface::${interface}::active_interface" => $anvil->data->{network}{$local_host}{interface}{$interface}{active_interface}, |
|
"network::${local_host}::interface::${interface}::bond_mode" => $anvil->data->{network}{$local_host}{interface}{$interface}{bond_mode}, |
|
"network::${local_host}::interface::${interface}::bond_master" => $anvil->data->{network}{$local_host}{interface}{$interface}{bond_master}, |
|
"network::${local_host}::interface::${interface}::bridge_id" => $anvil->data->{network}{$local_host}{interface}{$interface}{bridge_id}, |
|
"network::${local_host}::interface::${interface}::bridge_stp_enabled" => $anvil->data->{network}{$local_host}{interface}{$interface}{bridge_stp_enabled}, |
|
"network::${local_host}::interface::${interface}::down_delay" => $anvil->data->{network}{$local_host}{interface}{$interface}{down_delay}, |
|
"network::${local_host}::interface::${interface}::duplex" => $anvil->data->{network}{$local_host}{interface}{$interface}{duplex}, |
|
"network::${local_host}::interface::${interface}::ip" => $anvil->data->{network}{$local_host}{interface}{$interface}{ip}, |
|
"network::${local_host}::interface::${interface}::link_state" => $anvil->data->{network}{$local_host}{interface}{$interface}{link_state}, |
|
"network::${local_host}::interface::${interface}::mac_address" => $anvil->data->{network}{$local_host}{interface}{$interface}{mac_address}, |
|
"network::${local_host}::interface::${interface}::media" => $anvil->data->{network}{$local_host}{interface}{$interface}{media}, |
|
"network::${local_host}::interface::${interface}::mii_polling_interval" => $anvil->data->{network}{$local_host}{interface}{$interface}{mii_polling_interval}, |
|
"network::${local_host}::interface::${interface}::mtu" => $anvil->data->{network}{$local_host}{interface}{$interface}{mtu}, |
|
"network::${local_host}::interface::${interface}::operational" => $anvil->data->{network}{$local_host}{interface}{$interface}{operational}, |
|
"network::${local_host}::interface::${interface}::primary_reselect" => $anvil->data->{network}{$local_host}{interface}{$interface}{primary_reselect}, |
|
"network::${local_host}::interface::${interface}::primary_interface" => $anvil->data->{network}{$local_host}{interface}{$interface}{primary_interface}, |
|
"network::${local_host}::interface::${interface}::speed" => $anvil->data->{network}{$local_host}{interface}{$interface}{speed}, |
|
"network::${local_host}::interface::${interface}::subnet_mask" => $anvil->data->{network}{$local_host}{interface}{$interface}{subnet_mask}, |
|
"network::${local_host}::interface::${interface}::type" => $anvil->data->{network}{$local_host}{interface}{$interface}{type}, |
|
"network::${local_host}::interface::${interface}::up_delay" => $anvil->data->{network}{$local_host}{interface}{$interface}{up_delay}, |
|
}}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"network::${local_host}::interface::${interface}::link_state" => $anvil->data->{network}{$local_host}{interface}{$interface}{link_state}, |
|
"network::${local_host}::interface::${interface}::operational" => $anvil->data->{network}{$local_host}{interface}{$interface}{operational}, |
|
}}); |
|
|
|
# If this is a link and there's no database connections, cache the data. |
|
if (($type eq "interface") && (not $anvil->data->{sys}{database}{connections})) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
nm_name => $nm_name, |
|
nm_device => $nm_device, |
|
}}); |
|
# nm_device,timestamp,mac_address,speed,link_state,operational,nm_uuid,nm_name |
|
$anvil->data->{cache}{new_file} .= $nm_device.",".$anvil->Database->refresh_timestamp.",".$mac_address.",".$speed.",".$link_state.",".$operational.",".$nm_uuid.",".$nm_name."\n"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"cache::new_file" => $anvil->data->{cache}{new_file}, |
|
}}); |
|
} |
|
} |
|
} |
|
closedir(DIRECTORY); |
|
|
|
# Find what interfaces are connected to which bridges |
|
$anvil->Network->bridge_info({debug => 2}); |
|
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$local_host}{interface}}) |
|
{ |
|
my $ip_address = $anvil->data->{network}{$local_host}{interface}{$interface}{ip}; |
|
my $type = $anvil->data->{network}{$local_host}{interface}{$interface}{type}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
interface => $interface, |
|
ip_address => $ip_address, |
|
type => $type, |
|
}}); |
|
|
|
$anvil->data->{interface}{name_to_type}{$interface} = $type; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"interface::name_to_type::${interface}" => $anvil->data->{interface}{name_to_type}{$interface}, |
|
}}); |
|
|
|
if ($type eq "bridge") |
|
{ |
|
# Store the bridge |
|
$anvil->data->{new}{bridge}{$interface}{nm_uuid} = $anvil->data->{network}{$local_host}{interface}{$interface}{nm_uuid}; |
|
$anvil->data->{new}{bridge}{$interface}{id} = $anvil->data->{network}{$local_host}{interface}{$interface}{bridge_id}; |
|
$anvil->data->{new}{bridge}{$interface}{mac_address} = $anvil->data->{network}{$local_host}{interface}{$interface}{mac_address}; |
|
$anvil->data->{new}{bridge}{$interface}{mtu} = $anvil->data->{network}{$local_host}{interface}{$interface}{mtu}; |
|
$anvil->data->{new}{bridge}{$interface}{stp_enabled} = $anvil->data->{network}{$local_host}{interface}{$interface}{bridge_stp_enabled}; |
|
$anvil->data->{new}{bridge}{$interface}{tx_bytes} = $anvil->data->{network}{$local_host}{interface}{$interface}{tx_bytes}; |
|
$anvil->data->{new}{bridge}{$interface}{rx_bytes} = $anvil->data->{network}{$local_host}{interface}{$interface}{rx_bytes}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"new::bridge::${interface}::nm_uuid" => $anvil->data->{new}{bridge}{$interface}{nm_uuid}, |
|
"new::bridge::${interface}::id" => $anvil->data->{new}{bridge}{$interface}{id}, |
|
"new::bridge::${interface}::mac_address" => $anvil->data->{new}{bridge}{$interface}{mac_address}, |
|
"new::bridge::${interface}::mtu" => $anvil->data->{new}{bridge}{$interface}{mtu}, |
|
"new::bridge::${interface}::stp_enabled" => $anvil->data->{new}{bridge}{$interface}{stp_enabled}, |
|
"new::bridge::${interface}::tx_bytes" => $anvil->data->{new}{bridge}{$interface}{tx_bytes}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new}{bridge}{$interface}{tx_bytes}}).")", |
|
"new::bridge::${interface}::rx_bytes" => $anvil->data->{new}{bridge}{$interface}{rx_bytes}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new}{bridge}{$interface}{rx_bytes}}).")", |
|
}}); |
|
} |
|
elsif ($type eq "bond") |
|
{ |
|
# Store the bond |
|
$anvil->data->{new}{bond}{$interface}{nm_uuid} = $anvil->data->{network}{$local_host}{interface}{$interface}{nm_uuid}; |
|
$anvil->data->{new}{bond}{$interface}{mode} = $anvil->data->{network}{$local_host}{interface}{$interface}{bond_mode}; |
|
$anvil->data->{new}{bond}{$interface}{mtu} = $anvil->data->{network}{$local_host}{interface}{$interface}{mtu}; |
|
$anvil->data->{new}{bond}{$interface}{master} = $anvil->data->{network}{$local_host}{interface}{$interface}{bond_master}; |
|
$anvil->data->{new}{bond}{$interface}{link_state} = $anvil->data->{network}{$local_host}{interface}{$interface}{link_state}; |
|
$anvil->data->{new}{bond}{$interface}{operational} = $anvil->data->{network}{$local_host}{interface}{$interface}{operational}; |
|
$anvil->data->{new}{bond}{$interface}{mac_address} = $anvil->data->{network}{$local_host}{interface}{$interface}{mac_address}; |
|
$anvil->data->{new}{bond}{$interface}{primary_interface} = $anvil->data->{network}{$local_host}{interface}{$interface}{primary_interface}; |
|
$anvil->data->{new}{bond}{$interface}{primary_reselect} = $anvil->data->{network}{$local_host}{interface}{$interface}{primary_reselect}; |
|
$anvil->data->{new}{bond}{$interface}{active_interface} = $anvil->data->{network}{$local_host}{interface}{$interface}{active_interface}; |
|
$anvil->data->{new}{bond}{$interface}{mii_polling_interval} = $anvil->data->{network}{$local_host}{interface}{$interface}{mii_polling_interval}; |
|
$anvil->data->{new}{bond}{$interface}{up_delay} = $anvil->data->{network}{$local_host}{interface}{$interface}{up_delay}; |
|
$anvil->data->{new}{bond}{$interface}{down_delay} = $anvil->data->{network}{$local_host}{interface}{$interface}{down_delay}; |
|
$anvil->data->{new}{bond}{$interface}{bridge_uuid} = ""; # We'll dig his out later as the bridge might not be in the database yet. |
|
$anvil->data->{new}{bond}{$interface}{tx_bytes} = $anvil->data->{network}{$local_host}{interface}{$interface}{tx_bytes}; |
|
$anvil->data->{new}{bond}{$interface}{rx_bytes} = $anvil->data->{network}{$local_host}{interface}{$interface}{rx_bytes}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"new::bond::${interface}::nm_uuid" => $anvil->data->{new}{bond}{$interface}{nm_uuid}, |
|
"new::bond::${interface}::mode" => $anvil->data->{new}{bond}{$interface}{mode}, |
|
"new::bond::${interface}::mtu" => $anvil->data->{new}{bond}{$interface}{mtu}, |
|
"new::bond::${interface}::master" => $anvil->data->{new}{bond}{$interface}{master}, |
|
"new::bond::${interface}::link_state" => $anvil->data->{new}{bond}{$interface}{link_state}, |
|
"new::bond::${interface}::operational" => $anvil->data->{new}{bond}{$interface}{operational}, |
|
"new::bond::${interface}::mac_address" => $anvil->data->{new}{bond}{$interface}{mac_address}, |
|
"new::bond::${interface}::primary_interface" => $anvil->data->{new}{bond}{$interface}{primary_interface}, |
|
"new::bond::${interface}::primary_reselect" => $anvil->data->{new}{bond}{$interface}{primary_reselect}, |
|
"new::bond::${interface}::active_interface" => $anvil->data->{new}{bond}{$interface}{active_interface}, |
|
"new::bond::${interface}::mii_polling_interval" => $anvil->data->{new}{bond}{$interface}{mii_polling_interval}, |
|
"new::bond::${interface}::up_delay" => $anvil->data->{new}{bond}{$interface}{up_delay}, |
|
"new::bond::${interface}::down_delay" => $anvil->data->{new}{bond}{$interface}{down_delay}, |
|
"new::bond::${interface}::bridge_uuid" => $anvil->data->{new}{bond}{$interface}{bridge_uuid}, |
|
"new::bond::${interface}::tx_bytes" => $anvil->data->{new}{bond}{$interface}{tx_bytes}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new}{bond}{$interface}{tx_bytes}}).")", |
|
"new::bond::${interface}::rx_bytes" => $anvil->data->{new}{bond}{$interface}{rx_bytes}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new}{bond}{$interface}{rx_bytes}}).")", |
|
}}); |
|
} |
|
elsif ($type eq "interface") |
|
{ |
|
# Store the interface |
|
$anvil->data->{new}{interface}{$interface}{nm_uuid} = $anvil->data->{network}{$local_host}{interface}{$interface}{nm_uuid}; |
|
$anvil->data->{new}{interface}{$interface}{nm_device} = $anvil->data->{network}{$local_host}{interface}{$interface}{nm_device}; |
|
$anvil->data->{new}{interface}{$interface}{nm_name} = $anvil->data->{network}{$local_host}{interface}{$interface}{nm_name}; |
|
$anvil->data->{new}{interface}{$interface}{bond_uuid} = ""; |
|
$anvil->data->{new}{interface}{$interface}{bond_name} = $anvil->data->{network}{$local_host}{interface}{$interface}{bond_master}; |
|
$anvil->data->{new}{interface}{$interface}{bridge_uuid} = ""; |
|
$anvil->data->{new}{interface}{$interface}{bridge_name} = ""; |
|
$anvil->data->{new}{interface}{$interface}{duplex} = $anvil->data->{network}{$local_host}{interface}{$interface}{duplex}; |
|
$anvil->data->{new}{interface}{$interface}{link_state} = $anvil->data->{network}{$local_host}{interface}{$interface}{link_state}; |
|
$anvil->data->{new}{interface}{$interface}{operational} = $anvil->data->{network}{$local_host}{interface}{$interface}{operational}; |
|
$anvil->data->{new}{interface}{$interface}{mac_address} = $anvil->data->{network}{$local_host}{interface}{$interface}{mac_address}; |
|
$anvil->data->{new}{interface}{$interface}{medium} = $anvil->data->{network}{$local_host}{interface}{$interface}{media}; |
|
$anvil->data->{new}{interface}{$interface}{mtu} = $anvil->data->{network}{$local_host}{interface}{$interface}{mtu}; |
|
$anvil->data->{new}{interface}{$interface}{speed} = $anvil->data->{network}{$local_host}{interface}{$interface}{speed}; |
|
$anvil->data->{new}{interface}{$interface}{tx_bytes} = $anvil->data->{network}{$local_host}{interface}{$interface}{tx_bytes}; |
|
$anvil->data->{new}{interface}{$interface}{rx_bytes} = $anvil->data->{network}{$local_host}{interface}{$interface}{rx_bytes}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"new::interface::${interface}::nm_uuid" => $anvil->data->{new}{interface}{$interface}{nm_uuid}, |
|
"new::interface::${interface}::nm_device" => $anvil->data->{new}{interface}{$interface}{nm_device}, |
|
"new::interface::${interface}::nm_name" => $anvil->data->{new}{interface}{$interface}{nm_name}, |
|
"new::interface::${interface}::bond_uuid" => $anvil->data->{new}{interface}{$interface}{bond_uuid}, |
|
"new::interface::${interface}::bond_name" => $anvil->data->{new}{interface}{$interface}{bond_name}, |
|
"new::interface::${interface}::bridge_uuid" => $anvil->data->{new}{interface}{$interface}{bridge_uuid}, |
|
"new::interface::${interface}::bridge_name" => $anvil->data->{new}{interface}{$interface}{bridge_name}, |
|
"new::interface::${interface}::duplex" => $anvil->data->{new}{interface}{$interface}{duplex}, |
|
"new::interface::${interface}::link_state" => $anvil->data->{new}{interface}{$interface}{link_state}, |
|
"new::interface::${interface}::operational" => $anvil->data->{new}{interface}{$interface}{operational}, |
|
"new::interface::${interface}::mac_address" => $anvil->data->{new}{interface}{$interface}{mac_address}, |
|
"new::interface::${interface}::medium" => $anvil->data->{new}{interface}{$interface}{medium}, |
|
"new::interface::${interface}::mtu" => $anvil->data->{new}{interface}{$interface}{mtu}, |
|
"new::interface::${interface}::speed" => $anvil->data->{new}{interface}{$interface}{speed}, |
|
"new::interface::${interface}::tx_bytes" => $anvil->data->{new}{interface}{$interface}{tx_bytes}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new}{interface}{$interface}{tx_bytes}}).")", |
|
"new::interface::${interface}::rx_bytes" => $anvil->data->{new}{interface}{$interface}{rx_bytes}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new}{interface}{$interface}{rx_bytes}}).")", |
|
}}); |
|
} |
|
|
|
# Record the IP address info. |
|
if ($ip_address) |
|
{ |
|
$anvil->data->{new}{ip_address}{$ip_address}{on_interface} = $interface; |
|
$anvil->data->{new}{ip_address}{$ip_address}{subnet_mask} = $anvil->data->{network}{$local_host}{interface}{$interface}{subnet_mask}; |
|
$anvil->data->{new}{ip_address}{$ip_address}{gateway} = $anvil->data->{network}{$local_host}{interface}{$interface}{gateway}; |
|
$anvil->data->{new}{ip_address}{$ip_address}{default_gateway} = $anvil->data->{network}{$local_host}{interface}{$interface}{default_gateway}; |
|
$anvil->data->{new}{ip_address}{$ip_address}{dns} = $anvil->data->{network}{$local_host}{interface}{$interface}{dns}; |
|
$anvil->data->{new}{ip_address}{$ip_address}{on_uuid} = ""; |
|
$anvil->data->{new}{ip_address}{$ip_address}{note} = ""; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"new::ip_address::${ip_address}::on_interface" => $anvil->data->{new}{ip_address}{$ip_address}{on_interface}, |
|
"new::ip_address::${ip_address}::subnet_mask" => $anvil->data->{new}{ip_address}{$ip_address}{subnet_mask}, |
|
"new::ip_address::${ip_address}::gateway" => $anvil->data->{new}{ip_address}{$ip_address}{gateway}, |
|
"new::ip_address::${ip_address}::default_gateway" => $anvil->data->{new}{ip_address}{$ip_address}{default_gateway}, |
|
"new::ip_address::${ip_address}::dns" => $anvil->data->{new}{ip_address}{$ip_address}{dns}, |
|
"new::ip_address::${ip_address}::on_uuid" => $anvil->data->{new}{ip_address}{$ip_address}{on_uuid}, |
|
"new::ip_address::${ip_address}::note" => $anvil->data->{new}{ip_address}{$ip_address}{note}, |
|
}}); |
|
} |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
# This reads in the states from the last can |
|
sub read_last_scan |
|
{ |
|
my ($anvil) = @_; |
|
|
|
### NOTE: There is a bug somewhere where interfaces are periodically being added twice per host. This |
|
### checks for / cleans those up. Remove this when the core issue is resolved. |
|
clear_duplicates($anvil); |
|
|
|
# Read in the old bridge data. |
|
load_bridge_data($anvil); |
|
load_bond_data($anvil); |
|
load_interface_data($anvil); |
|
load_ip_address_data($anvil); |
|
|
|
return(0); |
|
} |
|
|
|
# There is a bug somewhere where interfaces, bridges and ip addresses are periodically being added twice per |
|
# host. This checks for / cleans those up. Remove this when the core issue is resolved. |
|
sub clear_duplicates |
|
{ |
|
my ($anvil) = @_; |
|
|
|
# Look for duplicate bridges. |
|
my $query = " |
|
SELECT |
|
bridge_uuid, |
|
bridge_name, |
|
bridge_id, |
|
bridge_mac_address |
|
FROM |
|
bridges |
|
WHERE |
|
bridge_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." |
|
ORDER BY |
|
bridge_name ASC, |
|
bridge_id DESC |
|
;"; |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 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 => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
foreach my $row (@{$results}) |
|
{ |
|
my $bridge_uuid = $row->[0]; |
|
my $bridge_name = $row->[1]; |
|
my $bridge_id = $row->[2]; |
|
my $bridge_mac_address = $row->[3]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
bridge_uuid => $bridge_uuid, |
|
bridge_name => $bridge_name, |
|
bridge_id => $bridge_id, |
|
bridge_mac_address => $bridge_mac_address, |
|
}}); |
|
|
|
if (not exists $anvil->data->{duplicate_bridges}{seen}{$bridge_name}) |
|
{ |
|
$anvil->data->{duplicate_bridges}{seen}{$bridge_name} = []; |
|
} |
|
push @{$anvil->data->{duplicate_bridges}{seen}{$bridge_name}}, $bridge_uuid; |
|
|
|
$anvil->data->{duplicate_bridges}{bridge_uuid}{$bridge_uuid}{bridge_name} = $bridge_name; |
|
$anvil->data->{duplicate_bridges}{bridge_uuid}{$bridge_uuid}{bridge_mac_address} = $bridge_mac_address; |
|
$anvil->data->{duplicate_bridges}{bridge_uuid}{$bridge_uuid}{bridge_id} = $bridge_id; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"duplicate_bridges::bridge_uuid::${bridge_uuid}::bridge_name" => $anvil->data->{duplicate_bridges}{bridge_uuid}{$bridge_uuid}{bridge_name}, |
|
"duplicate_bridges::bridge_uuid::${bridge_uuid}::bridge_id" => $anvil->data->{duplicate_bridges}{bridge_uuid}{$bridge_uuid}{bridge_id}, |
|
"duplicate_bridges::bridge_uuid::${bridge_uuid}::bridge_mac_address" => $anvil->data->{duplicate_bridges}{bridge_uuid}{$bridge_uuid}{bridge_mac_address}, |
|
}}); |
|
|
|
$anvil->data->{deleted_bridges}{$bridge_uuid} = 0; |
|
} |
|
foreach my $bridge_name (sort {$a cmp $b} keys %{$anvil->data->{duplicate_bridges}{seen}}) |
|
{ |
|
my $count = @{$anvil->data->{duplicate_bridges}{seen}{$bridge_name}}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:bridge_name' => $bridge_name, |
|
's2:count' => $count, |
|
}}); |
|
|
|
if ($count > 1) |
|
{ |
|
# Duplicate! Is one of them marked as DELETED? |
|
foreach my $bridge_uuid (@{$anvil->data->{duplicate_bridges}{seen}{$bridge_name}}) |
|
{ |
|
# Is this one deleted? |
|
my $bridge_mac_address = $anvil->data->{duplicate_bridges}{bridge_uuid}{$bridge_uuid}{bridge_mac_address}; |
|
my $bridge_id = $anvil->data->{duplicate_bridges}{bridge_uuid}{$bridge_uuid}{bridge_id}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
bridge_uuid => $bridge_uuid, |
|
bridge_mac_address => $anvil->data->{duplicate_bridges}{bridge_uuid}{$bridge_uuid}{bridge_mac_address}, |
|
bridge_id => $anvil->data->{duplicate_bridges}{bridge_uuid}{$bridge_uuid}{bridge_id}, |
|
}}); |
|
if ($bridge_id eq "DELETED") |
|
{ |
|
# Take this one out. |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0685", variables => { |
|
name => $bridge_name, |
|
uuid => $bridge_uuid, |
|
}}); |
|
|
|
# If there's a bond connected to this bridge, get it's bond_uuid so |
|
# we can remove any interfaces linked to it. |
|
my $bond_uuid = ""; |
|
my $query = "SELECT bond_uuid FROM bonds WHERE bond_bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 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 => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
if ($count) |
|
{ |
|
$bond_uuid = $results->[0]->[0]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bond_uuid => $bond_uuid }}); |
|
} |
|
|
|
my $queries = []; |
|
push @{$queries}, "DELETE FROM history.network_interfaces WHERE network_interface_bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
push @{$queries}, "DELETE FROM network_interfaces WHERE network_interface_bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
if ($bond_uuid) |
|
{ |
|
push @{$queries}, "DELETE FROM history.network_interfaces WHERE network_interface_bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
push @{$queries}, "DELETE FROM network_interfaces WHERE network_interface_bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
} |
|
push @{$queries}, "DELETE FROM history.bonds WHERE bond_bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
push @{$queries}, "DELETE FROM bonds WHERE bond_bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
push @{$queries}, "DELETE FROM history.bridges WHERE bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
push @{$queries}, "DELETE FROM bridges WHERE bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
|
|
# Write it out. |
|
$anvil->Database->write({debug => 2, query => $queries, source => $THIS_FILE, line => __LINE__}); |
|
|
|
$count--; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); |
|
|
|
$anvil->data->{deleted_bridges}{$bridge_uuid} = 1; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"deleted_bridges::${bridge_uuid}" => $anvil->data->{deleted_bridges}{$bridge_uuid}, |
|
}}); |
|
} |
|
last if $count == 1; |
|
} |
|
|
|
# If count is still > 1, we need to arbitrarily delete an interface. |
|
if ($count > 1) |
|
{ |
|
foreach my $bridge_uuid (@{$anvil->data->{duplicate_bridges}{seen}{$bridge_name}}) |
|
{ |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0685", variables => { |
|
name => $bridge_name, |
|
uuid => $bridge_uuid, |
|
}}); |
|
|
|
# If there's a bond connected to this bridge, get it's bond_uuid so |
|
# we can remove any interfaces linked to it. |
|
my $bond_uuid = ""; |
|
my $query = "SELECT bond_uuid FROM bonds WHERE bond_bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 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 => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
if ($count) |
|
{ |
|
$bond_uuid = $results->[0]->[0]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bond_uuid => $bond_uuid }}); |
|
} |
|
|
|
my $queries = []; |
|
push @{$queries}, "DELETE FROM history.network_interfaces WHERE network_interface_bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
push @{$queries}, "DELETE FROM network_interfaces WHERE network_interface_bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
if ($bond_uuid) |
|
{ |
|
push @{$queries}, "DELETE FROM history.network_interfaces WHERE network_interface_bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
push @{$queries}, "DELETE FROM network_interfaces WHERE network_interface_bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
} |
|
push @{$queries}, "DELETE FROM history.bonds WHERE bond_bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
push @{$queries}, "DELETE FROM bonds WHERE bond_bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
push @{$queries}, "DELETE FROM history.bridges WHERE bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
push @{$queries}, "DELETE FROM bridges WHERE bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; |
|
|
|
# Write it out. |
|
$anvil->Database->write({debug => 2, query => $queries, source => $THIS_FILE, line => __LINE__}); |
|
|
|
$count--; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); |
|
|
|
$anvil->data->{deleted_bridges}{$bridge_uuid} = 1; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"deleted_bridges::${bridge_uuid}" => $anvil->data->{deleted_bridges}{$bridge_uuid}, |
|
}}); |
|
} |
|
last if $count == 1; |
|
} |
|
} |
|
} |
|
delete $anvil->data->{duplicate_bridges}; |
|
|
|
# Load the bridges again. |
|
$anvil->Database->get_bridges({include_deleted => 1}); |
|
|
|
# Look for duplicate bonds. |
|
$query = " |
|
SELECT |
|
bond_uuid, |
|
bond_name, |
|
bond_operational, |
|
bond_mac_address, |
|
bond_bridge_uuid |
|
FROM |
|
bonds |
|
WHERE |
|
bond_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." |
|
ORDER BY |
|
bond_name ASC, |
|
bond_operational DESC |
|
;"; |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 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 => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
foreach my $row (@{$results}) |
|
{ |
|
my $bond_uuid = $row->[0]; |
|
my $bond_name = $row->[1]; |
|
my $bond_operational = $row->[2]; |
|
my $bond_mac_address = $row->[3]; |
|
my $bond_bridge_uuid = defined $row->[4] ? $row->[4] : ""; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
bond_uuid => $bond_uuid, |
|
bond_name => $bond_name, |
|
bond_operational => $bond_operational, |
|
bond_mac_address => $bond_mac_address, |
|
bond_bridge_uuid => $bond_bridge_uuid, |
|
}}); |
|
|
|
if (not exists $anvil->data->{duplicate_bonds}{seen}{$bond_name}) |
|
{ |
|
$anvil->data->{duplicate_bonds}{seen}{$bond_name} = []; |
|
} |
|
push @{$anvil->data->{duplicate_bonds}{seen}{$bond_name}}, $bond_uuid; |
|
|
|
$anvil->data->{duplicate_bonds}{bond_uuid}{$bond_uuid}{bond_name} = $bond_name; |
|
$anvil->data->{duplicate_bonds}{bond_uuid}{$bond_uuid}{bond_mac_address} = $bond_mac_address; |
|
$anvil->data->{duplicate_bonds}{bond_uuid}{$bond_uuid}{bond_operational} = $bond_operational; |
|
$anvil->data->{duplicate_bonds}{bond_uuid}{$bond_uuid}{bond_bridge_uuid} = $bond_bridge_uuid; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"duplicate_bonds::bond_uuid::${bond_uuid}::bond_name" => $anvil->data->{duplicate_bonds}{bond_uuid}{$bond_uuid}{bond_name}, |
|
"duplicate_bonds::bond_uuid::${bond_uuid}::bond_operational" => $anvil->data->{duplicate_bonds}{bond_uuid}{$bond_uuid}{bond_operational}, |
|
"duplicate_bonds::bond_uuid::${bond_uuid}::bond_mac_address" => $anvil->data->{duplicate_bonds}{bond_uuid}{$bond_uuid}{bond_mac_address}, |
|
"duplicate_bonds::bond_uuid::${bond_uuid}::bond_bridge_uuid" => $anvil->data->{duplicate_bonds}{bond_uuid}{$bond_uuid}{bond_bridge_uuid}, |
|
}}); |
|
|
|
$anvil->data->{deleted_bonds}{$bond_uuid} = 0; |
|
} |
|
foreach my $bond_name (sort {$a cmp $b} keys %{$anvil->data->{duplicate_bonds}{seen}}) |
|
{ |
|
my $count = @{$anvil->data->{duplicate_bonds}{seen}{$bond_name}}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:bond_name' => $bond_name, |
|
's2:count' => $count, |
|
}}); |
|
|
|
if ($count > 1) |
|
{ |
|
# Duplicate! Is one of them marked as DELETED? |
|
foreach my $bond_uuid (@{$anvil->data->{duplicate_bonds}{seen}{$bond_name}}) |
|
{ |
|
# Is this one deleted? |
|
my $bond_mac_address = $anvil->data->{duplicate_bonds}{bond_uuid}{$bond_uuid}{bond_mac_address}; |
|
my $bond_operational = $anvil->data->{duplicate_bonds}{bond_uuid}{$bond_uuid}{bond_operational}; |
|
my $bond_bridge_uuid = $anvil->data->{duplicate_bonds}{bond_uuid}{$bond_uuid}{bond_bridge_uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
bond_uuid => $bond_uuid, |
|
bond_mac_address => $bond_mac_address, |
|
bond_operational => $bond_operational, |
|
bond_bridge_uuid => $bond_bridge_uuid, |
|
}}); |
|
if ((($bond_bridge_uuid) && ($anvil->data->{deleted_bridges}{$bond_bridge_uuid})) or ($bond_operational eq "DELETED")) |
|
{ |
|
# Take this one out. |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0685", variables => { |
|
name => $bond_name, |
|
uuid => $bond_uuid, |
|
}}); |
|
|
|
my $queries = []; |
|
push @{$queries}, "DELETE FROM history.network_interfaces WHERE network_interface_bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
push @{$queries}, "DELETE FROM network_interfaces WHERE network_interface_bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
push @{$queries}, "DELETE FROM history.bonds WHERE bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
push @{$queries}, "DELETE FROM bonds WHERE bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
|
|
# Write it out. |
|
$anvil->Database->write({debug => 2, query => $queries, source => $THIS_FILE, line => __LINE__}); |
|
|
|
$count--; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); |
|
|
|
$anvil->data->{deleted_bonds}{$bond_uuid} = 1; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"deleted_bonds::${bond_uuid}" => $anvil->data->{deleted_bonds}{$bond_uuid}, |
|
}}); |
|
} |
|
} |
|
|
|
# If count is still > 1, we need to arbitrarily delete an interface. |
|
if ($count > 1) |
|
{ |
|
foreach my $bond_uuid (@{$anvil->data->{duplicate_bonds}{seen}{$bond_name}}) |
|
{ |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0685", variables => { |
|
name => $bond_name, |
|
uuid => $bond_uuid, |
|
}}); |
|
|
|
my $queries = []; |
|
push @{$queries}, "DELETE FROM history.network_interfaces WHERE network_interface_bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
push @{$queries}, "DELETE FROM network_interfaces WHERE network_interface_bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
push @{$queries}, "DELETE FROM history.bonds WHERE bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
push @{$queries}, "DELETE FROM bonds WHERE bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; |
|
|
|
# Write it out. |
|
$anvil->Database->write({debug => 2, query => $queries, source => $THIS_FILE, line => __LINE__}); |
|
|
|
$count--; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); |
|
|
|
$anvil->data->{deleted_bonds}{$bond_uuid} = 1; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"deleted_bonds::${bond_uuid}" => $anvil->data->{deleted_bonds}{$bond_uuid}, |
|
}}); |
|
} |
|
last if $count == 1; |
|
} |
|
} |
|
} |
|
delete $anvil->data->{duplicate_bonds}; |
|
|
|
|
|
# Look for duplicate network interfaces |
|
$query = " |
|
SELECT |
|
network_interface_uuid, |
|
network_interface_name, |
|
network_interface_mac_address, |
|
network_interface_operational, |
|
network_interface_bond_uuid, |
|
network_interface_bridge_uuid |
|
FROM |
|
network_interfaces |
|
WHERE |
|
network_interface_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." |
|
ORDER BY |
|
network_interface_name ASC |
|
;"; |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 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 => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
foreach my $row (@{$results}) |
|
{ |
|
my $network_interface_uuid = $row->[0]; |
|
my $network_interface_name = $row->[1]; |
|
my $network_interface_mac_address = $row->[2]; |
|
my $network_interface_operational = $row->[3]; |
|
my $network_interface_bond_uuid = defined $row->[4] ? $row->[4] : ""; |
|
my $network_interface_bridge_uuid = defined $row->[5] ? $row->[5] : ""; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
network_interface_uuid => $network_interface_uuid, |
|
network_interface_name => $network_interface_name, |
|
network_interface_mac_address => $network_interface_mac_address, |
|
network_interface_operational => $network_interface_operational, |
|
network_interface_bond_uuid => $network_interface_bond_uuid, |
|
network_interface_bridge_uuid => $network_interface_bridge_uuid, |
|
}}); |
|
|
|
if (not exists $anvil->data->{duplicate_nics}{seen}{$network_interface_name}) |
|
{ |
|
$anvil->data->{duplicate_nics}{seen}{$network_interface_name} = []; |
|
} |
|
push @{$anvil->data->{duplicate_nics}{seen}{$network_interface_name}}, $network_interface_uuid; |
|
|
|
$anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_name} = $network_interface_name; |
|
$anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_mac_address} = $network_interface_mac_address; |
|
$anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_operational} = $network_interface_operational; |
|
$anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_bond_uuid} = $network_interface_bond_uuid; |
|
$anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_bridge_uuid} = $network_interface_bridge_uuid; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"duplicate_nics::network_interface_uuid::${network_interface_uuid}::network_interface_name" => $anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_name}, |
|
"duplicate_nics::network_interface_uuid::${network_interface_uuid}::network_interface_mac_address" => $anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_mac_address}, |
|
"duplicate_nics::network_interface_uuid::${network_interface_uuid}::network_interface_operational" => $anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_operational}, |
|
"duplicate_nics::network_interface_uuid::${network_interface_uuid}::network_interface_bond_uuid" => $anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_bond_uuid}, |
|
"duplicate_nics::network_interface_uuid::${network_interface_uuid}::network_interface_bridge_uuid" => $anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_bridge_uuid}, |
|
}}); |
|
} |
|
foreach my $network_interface_name (sort {$a cmp $b} keys %{$anvil->data->{duplicate_nics}{seen}}) |
|
{ |
|
my $count = @{$anvil->data->{duplicate_nics}{seen}{$network_interface_name}}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:network_interface_name' => $network_interface_name, |
|
's2:count' => $count, |
|
}}); |
|
|
|
if ($count > 1) |
|
{ |
|
# Duplicate! Is one of them marked as DELETED? |
|
foreach my $network_interface_uuid (@{$anvil->data->{duplicate_nics}{seen}{$network_interface_name}}) |
|
{ |
|
# Is this one deleted? |
|
my $network_interface_mac_address = $anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_mac_address}; |
|
my $network_interface_operational = $anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_operational}; |
|
my $network_interface_bond_uuid = $anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_bond_uuid}; |
|
my $network_interface_bridge_uuid = $anvil->data->{duplicate_nics}{network_interface_uuid}{$network_interface_uuid}{network_interface_bridge_uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
network_interface_uuid => $network_interface_uuid, |
|
network_interface_mac_address => $network_interface_mac_address, |
|
network_interface_operational => $network_interface_operational, |
|
network_interface_bond_uuid => $network_interface_bond_uuid, |
|
network_interface_bridge_uuid => $network_interface_bridge_uuid, |
|
}}); |
|
if ((($network_interface_bond_uuid) && ($anvil->data->{deleted_bonds}{$network_interface_bond_uuid})) or |
|
(($network_interface_bridge_uuid) && ($anvil->data->{deleted_bridges}{$network_interface_bridge_uuid})) or |
|
($network_interface_operational eq "DELETED")) |
|
{ |
|
# Take this one out. |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0647", variables => { |
|
name => $network_interface_name, |
|
uuid => $network_interface_uuid, |
|
}}); |
|
|
|
my $queries = []; |
|
push @{$queries}, "DELETE FROM history.network_interfaces WHERE network_interface_uuid = ".$anvil->Database->quote($network_interface_uuid).";"; |
|
push @{$queries}, "DELETE FROM network_interfaces WHERE network_interface_uuid = ".$anvil->Database->quote($network_interface_uuid).";"; |
|
foreach my $query (@{$queries}) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
} |
|
$anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__}); |
|