#!/usr/bin/perl # # This updates things like the current network configuration, shared file data and writes it out to a json file. # use strict; use warnings; use Anvil::Tools; use Data::Dumper; # Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete. $| = 1; 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(); $anvil->Log->level({set => 2}); $anvil->Log->secure({set => 0}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }}); $anvil->Get->switches; # 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,timestamp,mac_address,speed,link_state,operational\n"; $anvil->Database->connect({debug => 3}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"}); if (not $anvil->data->{sys}{database}{connections}) { # No database, but we need to keep going. If the user unplugged the only cable connecting us to the # network, we'll lose all DBs, but we still need to record the order the NICs went up and down. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "warning_0016"}); # Generate our own time stamp $anvil->data->{sys}{database}{timestamp} = $anvil->Get->date_and_time({use_utc => 1}); $anvil->data->{sys}{database}{timestamp} =~ s/\//-/g; } process_interface_cache($anvil); update_network($anvil); # Write out the interface cache $anvil->Storage->write_file({ debug => 3, body => $anvil->data->{cache}{new_file}, file => $anvil->data->{path}{data}{network_cache}, overwrite => 1, backup => 0, }); $anvil->nice_exit({exit_code => 0}); ############################################################################################################# # 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 => 3, 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 => 3, list => { line => $line }}); next if $line =~ /^#/; my ($interface, $timestamp, $mac_address, $speed, $link_state, $operational) = (split/,/, $line); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { interface => $interface, timestamp => $timestamp, speed => $speed, mac_address => $mac_address, link_state => $link_state, operational => $operational, }}); if ($anvil->data->{sys}{database}{connections}) { my ($network_interface_uuid) = $anvil->Database->insert_or_update_network_interfaces({ debug => 3, link_only => 1, timestamp => $timestamp, network_interface_name => $interface, network_interface_link_state => $link_state, network_interface_mac_address => $mac_address, network_interface_operational => $operational, network_interface_speed => $speed, }); $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 reports the current network interface states, tracked by the MAC address. sub update_network { my ($anvil) = @_; # Run 'ip addr' to see what IPs are in use. $anvil->Network->get_ips(); # We'll read through '/sys/class/net' looking for network interfaces. # * 'network::${local_host}::interface::::ip' - If an IP address is set # * 'network::${local_host}::interface::::subnet_mask' - If an IP is set my $local_host = $anvil->Get->short_host_name(); my $directory = "/sys/class/net"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { directory => $directory }}); # We'll need to know what interfaces to remove, if any. This will store the interfaces we've seen and # any others will be removed. $anvil->data->{seen} = { interface => {}, bond => {}, bridge => {}, ip => {}, }; # Walk through the sysfs files. local(*DIRECTORY); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, 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 => 3, list => { full_path => $full_path }}); if (-d $full_path) { # Pull out the data I want. Note that some of these don't exist with virtio-net interfaces. my $interface = $file; my $mac_address = -e $full_path."/address" ? $anvil->Storage->read_file({file => $full_path."/address"}) : ""; my $link_state = -e $full_path."/carrier" ? $anvil->Storage->read_file({file => $full_path."/carrier"}) : 0; my $mtu = -e $full_path."/mtu" ? $anvil->Storage->read_file({file => $full_path."/mtu"}) : 0; my $duplex = -e $full_path."/duplex" ? $anvil->Storage->read_file({file => $full_path."/duplex"}) : "unknown"; # full or half? my $operational = -e $full_path."/operstate" ? $anvil->Storage->read_file({file => $full_path."/operstate"}) : "unknown"; # up or down 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 my $media = "unknown"; my $type = "interface"; # If the NIC is a bond member, the MAC address could be virtual. if (-e $full_path."/bonding_slave/perm_hwaddr") { $mac_address = $anvil->Storage->read_file({file => $full_path."/bonding_slave/perm_hwaddr"}); } # Clean up some newlines. $mac_address =~ s/\n$//; $link_state =~ s/\n$//; $mtu =~ s/\n$//; $duplex =~ s/\n$//; $operational =~ s/\n$//; $speed =~ s/\n$//; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { interface => $interface, speed => $speed, 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"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { ip_address => $ip_address, subnet_mask => $subnet_mask, type => $type, }}); } # 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 => 3, 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/) { ### Set some fake values. # Speed is "as fast as possible", so we'll record 100 Gbps, but that is really kind of arbitrary. $speed = 1000 if ((not $speed) or ($speed eq "-1")); $duplex = "full" if not $duplex; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, 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 => 3, 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"; # 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 => 3, 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") { # No, but it's slaved to one. my $target = readlink($full_path."/master"); $bond_master = ($target =~ /^.*\/(.*)$/)[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, 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 => 3, list => { bridge_id => $bridge_id, bridge_stp_enabled => $bridge_stp_enabled, type => $type, }}); 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 => 3, list => { bridge_stp_enabled => $bridge_stp_enabled }}); } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, 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 "")) { die $THIS_FILE." ".__LINE__."; No speed for: [".$full_path."/speed]\n"; } if ($speed =~ /\D/) { die $THIS_FILE." ".__LINE__."; Speed: [$speed] isn't numeric for: [".$full_path."/speed]\n"; } 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 => 3, 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 => 3, list => { line => $line }}); if ($line =~ /Supported ports: \[ (.*?) \]/i) { $media = lc($1); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { media => $media }}); last; } } # Record this interface $anvil->data->{seen}{$type}{$interface} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "seen::${type}::${interface}" => $anvil->data->{seen}{$type}{$interface} }}); # Record the IP, if set. if ($ip_address) { $anvil->data->{seen}{ip}{$ip_address} = $interface; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "seen::ip::${ip_address}" => $anvil->data->{seen}{ip}{$ip_address} }}); } # Store new information we found. $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 => 3, list => { "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 => 3, 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->data->{cache}{new_file} .= $interface.",".$anvil->data->{sys}{database}{timestamp}.",".$mac_address.",".$speed.",".$link_state.",".$operational."\n"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "cache::new_file" => $anvil->data->{cache}{new_file}, }}); } } } closedir(DIRECTORY); # Find what interfaces are connected to which bridges $anvil->Network->bridge_info({debug => 3}); delete $anvil->data->{interface_to_bridge} if exists $anvil->data->{interface_to_bridge}; foreach my $bridge_name (sort {$a cmp $b} keys %{$anvil->data->{bridge}{$local_host}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bridge_name => $bridge_name }}); foreach my $interface_name (sort {$a cmp $b} @{$anvil->data->{bridge}{$local_host}{$bridge_name}{interfaces}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { interface_name => $interface_name }}); $anvil->data->{interface_to_bridge}{$interface_name} = $bridge_name; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "interface_to_bridge::${interface_name}" => $anvil->data->{interface_to_bridge}{$interface_name}, }}); } } # We need to record bridges first so we know their UUIDs when looking at bonds and interfaces that # might be connected to them. Then we need to look at bonds so that their UUIDs are available when # recording interfaces. foreach my $processing ("bridge", "bond", "interface") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { processing => $processing }}); foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$local_host}{interface}}) { # Skip if this isn't the device type we're working on. next if not defined $anvil->data->{network}{$local_host}{interface}{$interface}{type}; my $type = $anvil->data->{network}{$local_host}{interface}{$interface}{type}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 's1:interface' => $interface, 's2:type' => $type, 's3:processing' => $processing, }}); next if $processing ne $anvil->data->{network}{$local_host}{interface}{$interface}{type}; my $active_interface = $anvil->data->{network}{$local_host}{interface}{$interface}{active_interface}; my $bond_mode = $anvil->data->{network}{$local_host}{interface}{$interface}{bond_mode}; my $bond_master = $anvil->data->{network}{$local_host}{interface}{$interface}{bond_master}; my $bridge_id = $anvil->data->{network}{$local_host}{interface}{$interface}{bridge_id}; my $bridge_stp_enabled = $anvil->data->{network}{$local_host}{interface}{$interface}{bridge_stp_enabled}; my $down_delay = $anvil->data->{network}{$local_host}{interface}{$interface}{down_delay}; my $duplex = $anvil->data->{network}{$local_host}{interface}{$interface}{duplex}; my $ip_address = $anvil->data->{network}{$local_host}{interface}{$interface}{ip}; my $link_state = $anvil->data->{network}{$local_host}{interface}{$interface}{link_state}; my $mac_address = $anvil->data->{network}{$local_host}{interface}{$interface}{mac_address}; my $media = $anvil->data->{network}{$local_host}{interface}{$interface}{media}; my $mii_polling_interval = $anvil->data->{network}{$local_host}{interface}{$interface}{mii_polling_interval}; my $mtu = $anvil->data->{network}{$local_host}{interface}{$interface}{mtu}; my $operational = $anvil->data->{network}{$local_host}{interface}{$interface}{operational}; my $primary_reselect = $anvil->data->{network}{$local_host}{interface}{$interface}{primary_reselect}; my $primary_interface = $anvil->data->{network}{$local_host}{interface}{$interface}{primary_interface}; my $speed = $anvil->data->{network}{$local_host}{interface}{$interface}{speed}; my $subnet_mask = $anvil->data->{network}{$local_host}{interface}{$interface}{subnet_mask}; my $up_delay = $anvil->data->{network}{$local_host}{interface}{$interface}{up_delay}; my $default_gateway = $anvil->data->{network}{$local_host}{interface}{$interface}{default_gateway}; my $gateway = $anvil->data->{network}{$local_host}{interface}{$interface}{gateway}; my $dns = $anvil->data->{network}{$local_host}{interface}{$interface}{dns}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { active_interface => $active_interface, bond_mode => $bond_mode, bond_master => $bond_master, default_gateway => $default_gateway, down_delay => $down_delay, dns => $dns, duplex => $duplex, gateway => $gateway, interface => $interface, ip_address => $ip_address, link_state => $link_state, mac_address => $mac_address, media => $media, mii_polling_interval => $mii_polling_interval, mtu => $mtu, operational => $operational, primary_reselect => $primary_reselect, primary_interface => $primary_interface, speed => $speed, subnet_mask => $subnet_mask, up_delay => $up_delay, }}); if (($type eq $processing) && ($type eq "bridge")) { if ($anvil->data->{sys}{database}{connections}) { my $bridge_uuid = $anvil->Database->insert_or_update_bridges({ debug => 3, file => $THIS_FILE, line => __LINE__, bridge_name => $interface, bridge_id => $bridge_id, bridge_mac_address => $mac_address, bridge_mtu => $mtu, bridge_stp_enabled => $bridge_stp_enabled, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bridge_uuid => $bridge_uuid }}); $anvil->data->{bridge_by_name}{$interface} = $bridge_uuid; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "bridge_by_name::${interface}" => $anvil->data->{bridge_by_name}{$interface} }}); if (($bridge_uuid) && ($ip_address)) { my $ip_address_uuid = $anvil->Database->insert_or_update_ip_addresses({ debug => 3, file => $THIS_FILE, line => __LINE__, ip_address_on_type => $type, ip_address_on_uuid => $bridge_uuid, ip_address_address => $ip_address, ip_address_subnet_mask => $subnet_mask, ip_address_gateway => $gateway, ip_address_default_gateway => $default_gateway, ip_address_dns => $dns, ip_address_note => "", }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { ip_address_uuid => $ip_address_uuid }}); } } } if (($type eq $processing) && ($type eq "bond")) { # Is this bond connected to a bridge? my $bond_bridge_uuid = ""; my $bond_on_bridge = exists $anvil->data->{interface_to_bridge}{$interface} ? $anvil->data->{interface_to_bridge}{$interface} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bond_on_bridge => $bond_on_bridge }}); if ($bond_on_bridge) { $bond_bridge_uuid = $anvil->data->{bridge_by_name}{$bond_on_bridge}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bond_bridge_uuid => $bond_bridge_uuid }}); } if ($anvil->data->{sys}{database}{connections}) { my $bond_uuid = $anvil->Database->insert_or_update_bonds({ debug => 3, file => $THIS_FILE, line => __LINE__, bond_name => $interface, bond_mode => $bond_mode, bond_mtu => $mtu, bond_link_state => $link_state, bond_operational => $operational, bond_mac_address => $mac_address, bond_primary_interface => $primary_interface, bond_primary_reselect => $primary_reselect, bond_active_interface => $active_interface, bond_mii_polling_interval => $mii_polling_interval, bond_up_delay => $up_delay, bond_down_delay => $down_delay, bond_bridge_uuid => $bond_bridge_uuid, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bond_uuid => $bond_uuid }}); $anvil->data->{bond_by_name}{$interface} = $bond_uuid; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "bond_by_name::${interface}" => $anvil->data->{bond_by_name}{$interface} }}); if (($bond_uuid) && ($ip_address)) { my $ip_address_uuid = $anvil->Database->insert_or_update_ip_addresses({ debug => 3, file => $THIS_FILE, line => __LINE__, ip_address_on_type => $type, ip_address_on_uuid => $bond_uuid, ip_address_address => $ip_address, ip_address_subnet_mask => $subnet_mask, ip_address_gateway => $gateway, ip_address_default_gateway => $default_gateway, ip_address_dns => $dns, ip_address_note => "", }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { ip_address_uuid => $ip_address_uuid }}); } } } if (($type eq $processing) && ($type eq "interface")) { # Is this interface connected to a bridge? my $network_interface_bridge_uuid = ""; my $network_interface_on_bridge = exists $anvil->data->{interface_to_bridge}{$interface} ? $anvil->data->{interface_to_bridge}{$interface} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network_interface_on_bridge => $network_interface_on_bridge }}); if ($network_interface_on_bridge) { $network_interface_bridge_uuid = $anvil->data->{bridge_by_name}{$network_interface_on_bridge}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network_interface_bridge_uuid => $network_interface_bridge_uuid }}); } my $say_bond_uuid = ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bond_master => $bond_master }}); if (($bond_master) && ($anvil->data->{bond_by_name}{$bond_master})) { $say_bond_uuid = $anvil->data->{bond_by_name}{$bond_master}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "bond_by_name::${bond_master}" => $anvil->data->{bond_by_name}{$bond_master}, say_bond_uuid => $say_bond_uuid, }}); } if ($anvil->data->{sys}{database}{connections}) { my $network_interface_uuid = $anvil->Database->insert_or_update_network_interfaces({ debug => 3, file => $THIS_FILE, line => __LINE__, network_interface_bond_uuid => $say_bond_uuid, network_interface_bridge_uuid => $network_interface_bridge_uuid, network_interface_name => $interface, network_interface_duplex => $duplex, network_interface_link_state => $link_state, network_interface_operational => $operational, network_interface_mac_address => $mac_address, network_interface_medium => $media, network_interface_mtu => $mtu, network_interface_speed => $speed, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network_interface_uuid => $network_interface_uuid }}); $anvil->data->{interface_by_name}{$interface} = $network_interface_uuid; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "interface_by_name::${interface}" => $anvil->data->{interface_by_name}{$interface} }}); if (($network_interface_uuid) && ($ip_address)) { my $ip_address_uuid = $anvil->Database->insert_or_update_ip_addresses({ debug => 3, file => $THIS_FILE, line => __LINE__, ip_address_on_type => $type, ip_address_on_uuid => $network_interface_uuid, ip_address_address => $ip_address, ip_address_subnet_mask => $subnet_mask, ip_address_gateway => $gateway, ip_address_default_gateway => $default_gateway, ip_address_dns => $dns, ip_address_note => "", }); } } } } } if (not $anvil->data->{sys}{database}{connections}) { return(0); } # I need to read back in the interface and bridge data and splice it together before I write out the # XML and JSON files. my $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 FROM bonds WHERE bond_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." AND bond_mode != 'DELETED' ;"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, 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 => 3, list => { results => $results, count => $count, }}); foreach my $row (@{$results}) { my $bond_uuid = $row->[0]; my $bond_name = $row->[1]; $anvil->data->{bonds}{$bond_uuid} = { bond_name => $bond_name, bond_mode => $row->[2], bond_mtu => $row->[3], bond_primary_interface => $row->[4], bond_primary_reselect => $row->[5], bond_active_interface => $row->[6], bond_mii_polling_interval => $row->[7], bond_up_delay => $row->[8], bond_down_delay => $row->[9], bond_mac_address => $row->[10], bond_operational => $row->[11], }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "bonds::${bond_uuid}::bond_name" => $anvil->data->{bonds}{$bond_uuid}{bond_name}, "bonds::${bond_uuid}::bond_mode" => $anvil->data->{bonds}{$bond_uuid}{bond_mode}, "bonds::${bond_uuid}::bond_mtu" => $anvil->data->{bonds}{$bond_uuid}{bond_mtu}, "bonds::${bond_uuid}::bond_primary_interface" => $anvil->data->{bonds}{$bond_uuid}{bond_primary_interface}, "bonds::${bond_uuid}::bond_primary_reselect" => $anvil->data->{bonds}{$bond_uuid}{bond_primary_reselect}, "bonds::${bond_uuid}::bond_active_interface" => $anvil->data->{bonds}{$bond_uuid}{bond_active_interface}, "bonds::${bond_uuid}::bond_mii_polling_interval" => $anvil->data->{bonds}{$bond_uuid}{bond_mii_polling_interval}, "bonds::${bond_uuid}::bond_up_delay" => $anvil->data->{bonds}{$bond_uuid}{bond_up_delay}, "bonds::${bond_uuid}::bond_down_delay" => $anvil->data->{bonds}{$bond_uuid}{bond_down_delay}, "bonds::${bond_uuid}::bond_mac_address" => $anvil->data->{bonds}{$bond_uuid}{bond_mac_address}, "bonds::${bond_uuid}::bond_operational" => $anvil->data->{bonds}{$bond_uuid}{bond_operational}, }}); # Make sure I've seen this interface in this scan and, if not, update this entry to remove it. if ((not exists $anvil->data->{seen}{bond}{$bond_name}) or (not $anvil->data->{seen}{bond}{$bond_name})) { # Mark it as deleted. my $query = "UPDATE bonds SET bond_mode = 'DELETED' WHERE bond_uuid = ".$anvil->Database->quote($bond_uuid).";"; $anvil->Database->write({debug => 3, query => $query, source => $THIS_FILE, line => __LINE__}); # Remove it from the hash so we don't add it to the .json and .xml files. delete $anvil->data->{bonds}{$bond_uuid}; } } $query = " SELECT bridge_uuid, bridge_name, bridge_id, bridge_mac_address, bridge_mtu, bridge_stp_enabled FROM bridges WHERE bridge_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." AND bridge_id != 'DELETED' ;"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, 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 => 3, 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]; my $bridge_mtu = $row->[4]; my $bridge_stp_enabled = $row->[5]; $anvil->data->{bridges}{$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, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "bridges::${bridge_uuid}::bridge_name" => $anvil->data->{bridges}{$bridge_uuid}{bridge_name}, "bridges::${bridge_uuid}::bridge_id" => $anvil->data->{bridges}{$bridge_uuid}{bridge_id}, "bridges::${bridge_uuid}::bridge_mac_address" => $anvil->data->{bridges}{$bridge_uuid}{bridge_mac_address}, "bridges::${bridge_uuid}::bridge_mtu" => $anvil->data->{bridges}{$bridge_uuid}{bridge_mtu}, "bridges::${bridge_uuid}::bridge_stp_enabled" => $anvil->data->{bridges}{$bridge_uuid}{bridge_stp_enabled}, }}); # Make sure I've seen this interface in this scan and, if not, update this entry to remove it. if ((not exists $anvil->data->{seen}{bridge}{$bridge_name}) or (not $anvil->data->{seen}{bridge}{$bridge_name})) { # Mark it as deleted. my $query = "UPDATE bridges SET bridge_id = 'DELETED' WHERE bridge_uuid = ".$anvil->Database->quote($bridge_uuid).";"; $anvil->Database->write({debug => 3, query => $query, source => $THIS_FILE, line => __LINE__}); # Remove it from the hash so we don't add it to the .json and .xml files. delete $anvil->data->{bridges}{$bridge_uuid}; } } # Process interfaces $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_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." ORDER BY modified_date DESC ;"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, 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 => 3, list => { results => $results, count => $count, }}); # The order will track the order the interfaces were last modified, which is a way to determine when # they came up. my $network_xml = "\n"; $network_xml .= "\n"; my $network_json = "{\"networks\":[\n"; my $order = 1; foreach my $row (@{$results}) { my $network_interface_uuid = $row->[0]; my $network_interface_name = $row->[2]; $anvil->data->{network_interfaces}{$network_interface_uuid} = { network_interface_mac_address => $row->[1], network_interface_name => $network_interface_name, network_interface_speed => $row->[3], network_interface_mtu => $row->[4], network_interface_link_state => $row->[5], network_interface_operational => $row->[6], network_interface_duplex => $row->[7], network_interface_medium => $row->[8], network_interface_bond_uuid => defined $row->[9] ? $row->[9] : 'NULL', network_interface_bridge_uuid => defined $row->[10] ? $row->[10] : 'NULL', }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "network_interfaces::${network_interface_uuid}::network_interface_mac_address" => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_mac_address}, "network_interfaces::${network_interface_uuid}::network_interface_name" => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_name}, "network_interfaces::${network_interface_uuid}::network_interface_speed" => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_speed}, "network_interfaces::${network_interface_uuid}::network_interface_mtu" => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_mtu}, "network_interfaces::${network_interface_uuid}::network_interface_link_state" => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_link_state}, "network_interfaces::${network_interface_uuid}::network_interface_operational" => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_operational}, "network_interfaces::${network_interface_uuid}::network_interface_duplex" => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_duplex}, "network_interfaces::${network_interface_uuid}::network_interface_medium" => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_medium}, "network_interfaces::${network_interface_uuid}::network_interface_bond_uuid" => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_bond_uuid}, "network_interfaces::${network_interface_uuid}::network_interface_bridge_uuid" => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_bridge_uuid}, order => $order, }}); # Make sure I've seen this interface in this scan and, if not, update this entry to remove it. if (not exists $anvil->data->{network}{$local_host}{interface}{$network_interface_name}) { if ($anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_operational} ne "DELETED") { # Mark it as deleted. my $query = "UPDATE network_interfaces SET network_interface_operational = 'DELETED' WHERE network_interface_uuid = ".$anvil->Database->quote($network_interface_uuid).";"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0124", variables => { query => $query }}); $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); } # Remove it from the hash so we don't add it to the .json and .xml files. delete $anvil->data->{interface}{$network_interface_uuid}; # Loop so we don't try to process any further. next; } my $say_bond = ""; my $network_interface_bond_uuid = $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_bond_uuid}; my $network_interface_bridge_uuid = $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_bridge_uuid}; if (($network_interface_bond_uuid) && ($network_interface_bond_uuid ne 'NULL')) { $say_bond = $anvil->data->{bonds}{$network_interface_bond_uuid}{bond_name}; } my $say_bridge = ""; if (($network_interface_bridge_uuid) && ($network_interface_bridge_uuid ne 'NULL')) { $say_bridge = $anvil->data->{bridges}{$network_interface_bridge_uuid}{bridge_name}; } $network_json .= " { \"name\":\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_name}."\", \"mac\":\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_mac_address}."\", \"link\":\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_link_state}."\", \"speed\":\"".$anvil->Convert->add_commas({number => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_speed}})."\", \"mtu\":\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_mtu}."\", \"duplex\":\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_duplex}."\", \"state\":\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_operational}."\", \"media\":\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_medium}."\", \"bond\":\"".$say_bond."\", \"bridge\":\"".$say_bridge."\", \"order\":\"".$order."\" },\n"; $network_xml .= " data->{network_interfaces}{$network_interface_uuid}{network_interface_name}."\" mac=\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_mac_address}."\" link=\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_link_state}."\" speed=\"".$anvil->Convert->add_commas({number => $anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_speed}})."\" mtu=\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_mtu}."\" duplex=\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_duplex}."\" state=\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_operational}."\" media=\"".$anvil->data->{network_interfaces}{$network_interface_uuid}{network_interface_medium}."\" bond=\"".$say_bond."\" bridge=\"".$say_bridge."\" order=\"".$order."\" />\n"; $order++; } $network_json =~ s/,$//s; $network_json .= "],\n"; $network_json .= "\"ips\":[\n"; # Now record the IPs. $query = " SELECT ip_address_uuid, ip_address_on_type, ip_address_on_uuid, ip_address_address, ip_address_subnet_mask, ip_address_gateway, ip_address_default_gateway, ip_address_dns, ip_address_note FROM ip_addresses WHERE ip_address_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." ;"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, 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 => 3, list => { results => $results, count => $count, }}); foreach my $row (@{$results}) { my $ip_address_uuid = $row->[0]; my $ip_address_on_type = $row->[1]; my $ip_address_on_uuid = $row->[2]; my $ip_address_address = $row->[3]; my $ip_address_subnet_mask = $row->[4]; my $ip_address_gateway = $row->[5]; my $ip_address_default_gateway = $row->[6]; my $ip_address_dns = $row->[7]; my $ip_address_note = $row->[8]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { ip_address_on_type => $ip_address_on_type, ip_address_on_uuid => $ip_address_on_uuid, 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_note => $ip_address_note, }}); if ((not exists $anvil->data->{seen}{ip}{$ip_address_address}) or (not $anvil->data->{seen}{ip}{$ip_address_address})) { # This IP address no longer exists on this host, removing it. my $ip_address_uuid = $anvil->Database->insert_or_update_ip_addresses({ file => $THIS_FILE, line => __LINE__, 'delete' => 1, ip_address_uuid => $ip_address_uuid, }); next; } my $say_on = ""; if ($ip_address_on_type eq "interface") { $say_on = $anvil->data->{network_interfaces}{$ip_address_on_uuid}{network_interface_name}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_on => $say_on }}); } elsif ($ip_address_on_type eq "bond") { $say_on = $anvil->data->{bonds}{$ip_address_on_uuid}{bond_name}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_on => $say_on }}); } elsif ($ip_address_on_type eq "bridge") { $say_on = $anvil->data->{bridges}{$ip_address_on_uuid}{bridge_name}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_on => $say_on }}); } $network_json .= " { \"address\":\"$ip_address_address\", \"on\":\"$say_on\", \"subnet_mask\":\"$ip_address_subnet_mask\", \"gateway\":\"$ip_address_gateway\", \"default_gateway\":\"$ip_address_default_gateway\", \"dns\":\"$ip_address_dns\" },\n"; $network_xml .= " \n"; } $network_json =~ s/,$//s; $network_json .= "]}\n"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network_json => $network_json }}); $network_xml .= "\n"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network_xml => $network_xml }}); # Write the JSON file, if we're a dashboard. Nodes and DR hosts don't have a WebUI, so they're not # needed. if ($anvil->Get->host_type eq "striker") { my $output_json = $anvil->data->{path}{directories}{html}."/status/network.json"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output_xml => $output_json }}); $anvil->Storage->write_file({ backup => 0, file => $output_json, body => $network_json, overwrite => 1, mode => "0644", user => "apache", group => "apache" }); # Write the XML file. my $output_xml = $anvil->data->{path}{directories}{html}."/status/network.xml"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output_xml => $output_xml }}); $anvil->Storage->write_file({ backup => 0, file => $output_xml, body => $network_xml, overwrite => 1, mode => "0644", user => "apache", group => "apache" }); } return(0); }