#!/usr/bin/perl # # This is the master daemon that manages all periodically run processes on Striker dashboards and Anvil! # nodes. # use strict; use warnings; use Anvil::Tools; # 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->Storage->read_config({file => "/etc/anvil/anvil.conf"}); my $connections = $anvil->Database->connect({ sql_file => $anvil->data->{sys}{database}{schema}, test_table => "network_interfaces", }); print $THIS_FILE." ".__LINE__."; connections: [".$connections."]\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0132", variables => { connections => $connections }}); if (not $connections) { # No databases, exit. print $anvil->Words->string({key => "error_0003"}); $anvil->nice_exit({exit_code => 2}); } report_network($anvil); $anvil->nice_exit({exit_code => 0}); ############################################################################################################# # Functions # ############################################################################################################# # This reports the current network interface states, tracked by the MAC address. sub report_network { my ($anvil) = @_; # Run 'ip addr' to see what IPs are in use. The results will be stored in: $anvil->System->get_ips(); # We'll read through '/sys/class/net' looking for network interfaces. # * 'sys::network::interface::::ip' - If an IP address is set # * 'sys::network::interface::::subnet' - If an IP is set my $directory = "/sys/class/net"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { directory => $directory }}); 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"; next if $file =~ /virbr\d/; my $full_path = "$directory/$file"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, 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", debug => 2}) : 0; # Mbps (ie: 1000 = Gbps), gives a very high number for unplugged link my $media = "unknown"; my $type = "interface"; # These are variables that will be needed if this is a bond interface. my $ip_address = ""; my $subnet_mask = ""; my $bond_mode = ""; my $primary_slave = ""; my $primary_reselect = ""; my $active_slave = ""; my $mii_polling_interval = ""; my $up_delay = ""; my $down_delay = ""; if (exists $anvil->data->{sys}{network}{interface}{$interface}) { $ip_address = $anvil->data->{sys}{network}{interface}{$interface}{ip} ? $anvil->data->{sys}{network}{interface}{$interface}{ip} : ""; $subnet_mask = $anvil->data->{sys}{network}{interface}{$interface}{subnet} ? $anvil->data->{sys}{network}{interface}{$interface}{subnet} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ip_address => $ip_address, subnet_mask => $subnet_mask, }}); } # 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}); $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/) { ### Set some fake values. # Speed is "as fast as possible", so we'll record 100 Gbps, but that is really kind of arbitrary. $speed = 100000 if not $speed; $duplex = "full" if not $duplex; $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"; # Read the bond mode. $bond_mode = $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/mode"}); $bond_mode =~ s/\s.*//; $primary_slave = $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"}); $primary_reselect =~ s/\s.*//; $active_slave = $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"}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { active_slave => $active_slave, bond_mode => $bond_mode, mii_polling_interval => $mii_polling_interval, primary_reselect => $primary_reselect, primary_slave => $primary_slave, type => $type, }}); } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { active_slave => $active_slave, bond_mode => $bond_mode, 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_slave => $primary_slave, 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 => 2, list => { speed => $speed }}); } # Find the media, if possible. my $shell_call = $anvil->data->{path}{exe}{ethtool}." $interface"; my $ethtool = $anvil->System->call({shell_call => $shell_call}); 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 => 2, list => { media => $media }}); last; } } # Log $anvil->data->{network}{interfaces}{by_name}{$interface} = { active_slave => $active_slave, bond_mode => $bond_mode, down_delay => $down_delay, duplex => $duplex, 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_slave => $primary_slave, speed => $speed, subnet_mask => $subnet_mask, type => $type, up_delay => $up_delay, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "network::interfaces::by_name::${interface}::active_slave" => $anvil->data->{network}{interfaces}{by_name}{$interface}{active_slave}, "network::interfaces::by_name::${interface}::bond_mode" => $anvil->data->{network}{interfaces}{by_name}{$interface}{bond_mode}, "network::interfaces::by_name::${interface}::down_delay" => $anvil->data->{network}{interfaces}{by_name}{$interface}{down_delay}, "network::interfaces::by_name::${interface}::duplex" => $anvil->data->{network}{interfaces}{by_name}{$interface}{duplex}, "network::interfaces::by_name::${interface}::ip_address" => $anvil->data->{network}{interfaces}{by_name}{$interface}{ip_address}, "network::interfaces::by_name::${interface}::link_state" => $anvil->data->{network}{interfaces}{by_name}{$interface}{link_state}, "network::interfaces::by_name::${interface}::mac_address" => $anvil->data->{network}{interfaces}{by_name}{$interface}{mac_address}, "network::interfaces::by_name::${interface}::media" => $anvil->data->{network}{interfaces}{by_name}{$interface}{media}, "network::interfaces::by_name::${interface}::mii_polling_interval" => $anvil->data->{network}{interfaces}{by_name}{$interface}{mii_polling_interval}, "network::interfaces::by_name::${interface}::mtu" => $anvil->data->{network}{interfaces}{by_name}{$interface}{mtu}, "network::interfaces::by_name::${interface}::operational" => $anvil->data->{network}{interfaces}{by_name}{$interface}{operational}, "network::interfaces::by_name::${interface}::primary_reselect" => $anvil->data->{network}{interfaces}{by_name}{$interface}{primary_reselect}, "network::interfaces::by_name::${interface}::primary_slave" => $anvil->data->{network}{interfaces}{by_name}{$interface}{primary_slave}, "network::interfaces::by_name::${interface}::speed" => $anvil->data->{network}{interfaces}{by_name}{$interface}{speed}, "network::interfaces::by_name::${interface}::subnet_mask" => $anvil->data->{network}{interfaces}{by_name}{$interface}{subnet_mask}, "network::interfaces::by_name::${interface}::type" => $anvil->data->{network}{interfaces}{by_name}{$interface}{type}, "network::interfaces::by_name::${interface}::up_delay" => $anvil->data->{network}{interfaces}{by_name}{$interface}{up_delay}, }}); } } closedir(DIRECTORY); foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{interfaces}{by_name}}) { my $active_slave = $anvil->data->{network}{interfaces}{by_name}{$interface}{active_slave}; my $bond_mode = $anvil->data->{network}{interfaces}{by_name}{$interface}{bond_mode}; my $down_delay = $anvil->data->{network}{interfaces}{by_name}{$interface}{down_delay}; my $duplex = $anvil->data->{network}{interfaces}{by_name}{$interface}{duplex}; my $ip_address = $anvil->data->{network}{interfaces}{by_name}{$interface}{ip_address}; my $link_state = $anvil->data->{network}{interfaces}{by_name}{$interface}{link_state}; my $mac_address = $anvil->data->{network}{interfaces}{by_name}{$interface}{mac_address}; my $media = $anvil->data->{network}{interfaces}{by_name}{$interface}{media}; my $mii_polling_interval = $anvil->data->{network}{interfaces}{by_name}{$interface}{mii_polling_interval}; my $mtu = $anvil->data->{network}{interfaces}{by_name}{$interface}{mtu}; my $operational = $anvil->data->{network}{interfaces}{by_name}{$interface}{operational}; my $primary_reselect = $anvil->data->{network}{interfaces}{by_name}{$interface}{primary_reselect}; my $primary_slave = $anvil->data->{network}{interfaces}{by_name}{$interface}{primary_slave}; my $speed = $anvil->data->{network}{interfaces}{by_name}{$interface}{speed}; my $subnet_mask = $anvil->data->{network}{interfaces}{by_name}{$interface}{subnet_mask}; my $type = $anvil->data->{network}{interfaces}{by_name}{$interface}{type}; my $up_delay = $anvil->data->{network}{interfaces}{by_name}{$interface}{up_delay}; my $default_gateway = $anvil->data->{sys}{network}{interface}{$interface}{default_gateway}; my $gateway = $anvil->data->{sys}{network}{interface}{$interface}{gateway}; my $dns = $anvil->data->{sys}{network}{interface}{$interface}{dns}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { active_slave => $active_slave, bond_mode => $bond_mode, 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_slave => $primary_slave, speed => $speed, subnet_mask => $subnet_mask, type => $type, up_delay => $up_delay, }}); if ($type eq "interface") { my $network_interface_uuid = $anvil->Database->insert_or_update_network_interfaces({ file => $THIS_FILE, line => __LINE__, 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 => 2, list => { network_interface_uuid => $network_interface_uuid }}); if (($network_interface_uuid) && ($ip_address)) { my $ip_address_uuid = $anvil->Database->insert_or_update_ip_addresses({ debug => 2, 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, }); } } elsif ($type eq "bond") { my $bond_uuid = $anvil->Database->insert_or_update_bonds({ 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_slave => $primary_slave, bond_primary_reselect => $primary_reselect, bond_active_slave => $active_slave, bond_mii_polling_interval => $mii_polling_interval, bond_up_delay => $up_delay, bond_down_delay => $down_delay, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bond_uuid => $bond_uuid }}); if (($bond_uuid) && ($ip_address)) { my $ip_address_uuid = $anvil->Database->insert_or_update_ip_addresses({ debug => 2, 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, }); } } } # Write out the XML file and JSON file. my $order = 1; my $network_xml = "\n"; $network_xml .= "\n"; my $network_json = "{\"networks\":[\n"; my $query = " SELECT 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->data->{sys}{use_db_fh}->quote($anvil->Get->host_uuid)." ORDER BY modified_date DESC ;"; $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 $network_interface_mac_address = $row->[0]; my $network_interface_name = $row->[1]; my $network_interface_speed = $row->[2]; my $network_interface_mtu = defined $row->[3] ? $row->[3] : ""; my $network_interface_link_state = $row->[4]; my $network_interface_operational = $row->[5]; my $network_interface_duplex = $row->[6]; my $network_interface_medium = defined $row->[7] ? $row->[7] : ""; my $network_interface_bond_uuid = defined $row->[8] ? $row->[8] : ""; my $network_interface_bridge_uuid = defined $row->[9] ? $row->[9] : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network_interface_mac_address => $network_interface_mac_address, network_interface_name => $network_interface_name, network_interface_speed => $network_interface_speed, network_interface_mtu => $network_interface_mtu, network_interface_link_state => $network_interface_link_state, network_interface_operational => $network_interface_operational, network_interface_duplex => $network_interface_duplex, network_interface_medium => $network_interface_medium, network_interface_bond_uuid => $network_interface_bond_uuid, network_interface_bridge_uuid => $network_interface_bridge_uuid, order => $order, }}); $network_json .= " { \"name\":\"$network_interface_name\", \"mac\":\"$network_interface_mac_address\", \"link\":\"$network_interface_link_state\", \"speed\":\"$network_interface_speed\", \"mtu\":\"$network_interface_mtu\", \"duplex\":\"$network_interface_duplex\", \"state\":\"$network_interface_operational\", \"media\":\"$network_interface_medium\", \"bond\":\"$network_interface_bond_uuid\", \"bridge\":\"$network_interface_bridge_uuid\", \"order\":\"$order\" },\n"; $network_xml .= " \n"; $order++; } $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 }}); ### TODO: Set the 'status/network.json' name into 'anvil.conf' # Write the JSON file. 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({ 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({ file => $output_xml, body => $network_xml, overwrite => 1, mode => "0644", user => "apache", group => "apache" }); return(0); }