diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index 6d55f511..8c87048b 100755 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -23,6 +23,7 @@ my $THIS_FILE = "Database.pm"; # get_local_uuid # initialize # insert_or_update_hosts +# insert_or_update_ip_addresses # insert_or_update_jobs # insert_or_update_network_interfaces # insert_or_update_states @@ -1387,7 +1388,7 @@ sub initialize =head2 insert_or_update_hosts -This updates (or inserts) a record in the 'hosts' table. +This updates (or inserts) a record in the 'hosts' table. The C<< host_uuid >> UUID will be returned. If there is an error, an empty string is returned. @@ -1524,7 +1525,304 @@ WHERE } $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0126", variables => { method => "Database->insert_or_update_hosts()" }}); - return(0); + return($host_uuid); +} + +=head2 insert_or_update_ip_addresses + +This updates (or inserts) a record in the 'ip_addresses' table. The C<< ip_address_uuid >> referencing the database row will be returned. + +If there is an error, an empty string is returned. + +Parameters; + +=head3 uuid (optional) + +If set, only the corresponding database will be written to. + +=head3 file (optional) + +If set, this is the file name logged as the source of any INSERTs or UPDATEs. + +=head3 line (optional) + +If set, this is the file line number logged as the source of any INSERTs or UPDATEs. + +=head2 ip_address_uuid (optional) + +If not passed, a check will be made to see if an existing entry is found for C<< ip_address_address >>. If found, that entry will be updated. If not found, a new record will be inserted. + +=head2 ip_address_host_uuid (optional) + +This is the host that the IP address is on. If not passed, the local C<< sys::host_uuid >> will be used (indicating it is a local IP address). + +=head2 ip_address_on_type (required) + +This indicates what type of interface the IP address is on. This must be either C<< interface >>, C<< bond >> or C<< bridge >>. + +=head2 ip_address_on_uuid (required) + +This is the UUID of the bridge, bond or interface that this IP address is on. + +=head2 ip_address_address (required) + +This is the acual IP address. It's tested with IPv4 addresses in dotted-decimal format, though it can also store IPv6 addresses. If this is set to C<< 0 >>, it will be treated as deleted and will be ignored (unless a new IP is assigned to the same interface in the future). + +=head2 ip_address_subnet (required) + +This is the subnet mask for the IP address. It is tested with IPv4 in dotted decimal format, though it can also store IPv6 format subnet masks. + +=head2 ip_address_default_gateway (optional, default '0') + +If a gateway address is set, and this is set to C<< 1 >>, the associated interface will be the default gateway for the host. + +=head2 ip_address_gateway (optional) + +This is an option gateway IP address for this interface. + +=head2 ip_address_dns (optional) + +This is a comma-separated list of DNS servers used to resolve host names. This is recorded, but ignored unless C<< ip_address_gateway >> is set. Example format is C<< 8.8.8.8 >> or C<< 8.8.8.8,4.4.4.4 >>. + +=cut +sub insert_or_update_ip_addresses +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->insert_or_update_ip_addresses()" }}); + + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; + my $file = defined $parameter->{file} ? $parameter->{file} : ""; + my $line = defined $parameter->{line} ? $parameter->{line} : ""; + my $ip_address_uuid = defined $parameter->{ip_address_uuid} ? $parameter->{ip_address_uuid} : ""; + my $ip_address_host_uuid = defined $parameter->{ip_address_host_uuid} ? $parameter->{ip_address_host_uuid} : $anvil->data->{sys}{host_uuid}; + my $ip_address_on_type = defined $parameter->{ip_address_on_type} ? $parameter->{ip_address_on_type} : ""; + my $ip_address_on_uuid = defined $parameter->{ip_address_on_uuid} ? $parameter->{ip_address_on_uuid} : ""; + my $ip_address_address = defined $parameter->{ip_address_address} ? $parameter->{ip_address_address} : ""; + my $ip_address_subnet = defined $parameter->{ip_address_subnet} ? $parameter->{ip_address_subnet} : ""; + my $ip_address_gateway = defined $parameter->{ip_address_gateway} ? $parameter->{ip_address_gateway} : "NULL"; + my $ip_address_default_gateway = defined $parameter->{ip_address_default_gateway} ? $parameter->{ip_address_default_gateway} : 0; + my $ip_address_dns = defined $parameter->{ip_address_dns} ? $parameter->{ip_address_dns} : "NULL"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + uuid => $uuid, + file => $file, + line => $line, + ip_address_uuid => $ip_address_uuid, + ip_address_host_uuid => $ip_address_host_uuid, + 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 => $ip_address_subnet, + ip_address_gateway => $ip_address_gateway, + ip_address_default_gateway => $ip_address_default_gateway, + ip_address_dns => $ip_address_dns, + }}); + + if (not $ip_address_on_type) + { + # Throw an error and exit. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_ip_addresses()", parameter => "ip_address_on_type" }}); + return(""); + } + if (not $ip_address_on_uuid) + { + # Throw an error and exit. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_ip_addresses()", parameter => "ip_address_on_uuid" }}); + return(""); + } + if (not $ip_address_address) + { + # Throw an error and exit. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_ip_addresses()", parameter => "ip_address_address" }}); + return(""); + } + if (not $ip_address_subnet) + { + # Throw an error and exit. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_ip_addresses()", parameter => "ip_address_subnet" }}); + return(""); + } + + # If we don't have a UUID, see if we can find one for the given ip_address server name. + if (not $ip_address_uuid) + { + my $query = " +SELECT + ip_address_uuid +FROM + ip_addresses +WHERE + ip_address_address = ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_address)." +AND + ip_address_host_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_host_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + + my $results = $anvil->Database->query({query => $query, source => $file ? $file : $THIS_FILE, line => $line ? $line : __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + if ($count) + { + $ip_address_uuid = $results->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ip_address_uuid => $ip_address_uuid }}); + } + } + + # default gateway is a boolean, so translate it. + my $say_ip_address_default_gateway = (($ip_address_default_gateway eq "1") or ($ip_address_default_gateway =~ /true/i)) ? "TRUE" : "FALSE"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_ip_address_default_gateway => $say_ip_address_default_gateway }}); + + # If I still don't have an ip_address_uuid, we're INSERT'ing . + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ip_address_uuid => $ip_address_uuid }}); + if (not $ip_address_uuid) + { + # It's possible that this is called before the host is recorded in the database. So to be + # safe, we'll return without doing anything if there is no host_uuid in the database. + my $hosts = $anvil->Database->get_hosts(); + my $found = 0; + foreach my $hash_ref (@{$hosts}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "hash_ref->{host_uuid}" => $hash_ref->{host_uuid}, + "sys::host_uuid" => $anvil->data->{sys}{host_uuid}, + }}); + if ($hash_ref->{host_uuid} eq $anvil->data->{sys}{host_uuid}) + { + $found = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { found => $found }}); + } + } + if (not $found) + { + # We're out. + return(""); + } + + # INSERT + $ip_address_uuid = $anvil->Get->uuid(); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ip_address_uuid => $ip_address_uuid }}); + + my $query = " +INSERT INTO + ip_addresses +( + ip_address_uuid, + ip_address_host_uuid, + ip_address_on_type, + ip_address_on_uuid, + ip_address_address, + ip_address_subnet, + ip_address_gateway, + ip_address_default_gateway, + ip_address_dns, + modified_date +) VALUES ( + ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_uuid).", + ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_host_uuid).", + ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_on_type).", + ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_on_uuid).", + ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_address).", + ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_subnet).", + ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_gateway).", + $say_ip_address_default_gateway, + ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_dns).", + ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})." +); +"; + $query =~ s/'NULL'/NULL/g; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $file ? $file : $THIS_FILE, line => $line ? $line : __LINE__}); + } + else + { + # Query the rest of the values and see if anything changed. + my $query = " +SELECT + ip_address_host_uuid, + ip_address_on_type, + ip_address_on_uuid, + ip_address_address, + ip_address_subnet, + ip_address_gateway, + ip_address_default_gateway, + ip_address_dns +FROM + ip_addresses +WHERE + ip_address_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + + my $results = $anvil->Database->query({query => $query, source => $file ? $file : $THIS_FILE, line => $line ? $line : __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $old_ip_address_host_uuid = $row->[0]; + my $old_ip_address_on_type = $row->[1]; + my $old_ip_address_on_uuid = $row->[2]; + my $old_ip_address_address = $row->[3]; + my $old_ip_address_subnet = $row->[4]; + my $old_ip_address_gateway = defined $row->[5] ? $row->[5] : "NULL"; + my $old_ip_address_default_gateway = $row->[6]; + my $old_ip_address_dns = defined $row->[7] ? $row->[7] : "NULL"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + old_ip_address_host_uuid => $old_ip_address_host_uuid, + old_ip_address_on_type => $old_ip_address_on_type, + old_ip_address_on_uuid => $old_ip_address_on_uuid, + old_ip_address_address => $old_ip_address_address, + old_ip_address_subnet => $old_ip_address_subnet, + old_ip_address_gateway => $old_ip_address_gateway, + old_ip_address_default_gateway => $old_ip_address_default_gateway, + old_ip_address_dns => $old_ip_address_dns, + }}); + + my $say_old_ip_address_default_gateway = (($old_ip_address_default_gateway eq "1") or ($old_ip_address_default_gateway =~ /true/i)) ? "TRUE" : "FALSE"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_old_ip_address_default_gateway => $say_old_ip_address_default_gateway }}); + + # Anything change? + if (($old_ip_address_host_uuid ne $ip_address_host_uuid) or + ($old_ip_address_on_type ne $ip_address_on_type) or + ($old_ip_address_on_uuid ne $ip_address_on_uuid) or + ($old_ip_address_address ne $ip_address_address) or + ($old_ip_address_subnet ne $ip_address_subnet) or + ($old_ip_address_gateway ne $ip_address_gateway) or + ($say_old_ip_address_default_gateway ne $say_ip_address_default_gateway) or + ($old_ip_address_dns ne $ip_address_dns)) + { + # Something changed, save. + my $query = " +UPDATE + ip_addresses +SET + ip_address_host_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_host_uuid).", + ip_address_on_type = ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_on_type).", + ip_address_on_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_on_uuid).", + ip_address_address = ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_address).", + ip_address_subnet = ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_subnet).", + ip_address_gateway = ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_gateway).", + ip_address_default_gateway = $say_ip_address_default_gateway, + ip_address_dns = ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_dns).", + modified_date = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})." +WHERE + ip_address_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($ip_address_uuid)." +"; + $query =~ s/'NULL'/NULL/g; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $file ? $file : $THIS_FILE, line => $line ? $line : __LINE__}); + } + } + } + + return($ip_address_uuid); } diff --git a/Anvil/Tools/System.pm b/Anvil/Tools/System.pm index 26ff6b8c..28b6edd1 100755 --- a/Anvil/Tools/System.pm +++ b/Anvil/Tools/System.pm @@ -511,9 +511,12 @@ sub enable_daemon This method checks the local system for interfaces and stores them in: -* C<< sys::network::interface::::ip >> - If an IP address is set -* C<< sys::network::interface::::subnet >> - If an IP is set -* C<< sys::network::interface::::mac >> - Always set. +* C<< sys::network::interface::::ip >> - If an IP address is set +* C<< sys::network::interface::::subnet >> - If an IP is set +* C<< sys::network::interface::::mac >> - Always set. +* C<< sys::network::interface::::default_gateway >> = C<< 0 >> if not the default gateway, C<< 1 >> if so. +* C<< sys::network::interface::::gateway >> = If the default gateway, this is the gateway IP address. +* C<< sys::network::interface::::dns >> = If the default gateway, this is the comma-separated list of active DNS servers. To aid in look-up by MAC address, C<< sys::mac::::iface >> is also set. @@ -527,7 +530,7 @@ sub get_ips $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "System->get_ips()" }}); my $in_iface = ""; - my $ip_addr = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{ip}." addr list"}); + my $ip_addr = $anvil->System->call({debug => $debug, shell_call => $anvil->data->{path}{exe}{ip}." addr list"}); foreach my $line (split/\n/, $ip_addr) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }}); @@ -536,9 +539,12 @@ sub get_ips $in_iface = $1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { in_iface => $in_iface }}); - $anvil->data->{sys}{network}{interface}{$in_iface}{ip} = "" if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{ip}; - $anvil->data->{sys}{network}{interface}{$in_iface}{subnet} = "" if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{subnet}; - $anvil->data->{sys}{network}{interface}{$in_iface}{mac} = "" if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{mac}; + $anvil->data->{sys}{network}{interface}{$in_iface}{ip} = "" if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{ip}; + $anvil->data->{sys}{network}{interface}{$in_iface}{subnet} = "" if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{subnet}; + $anvil->data->{sys}{network}{interface}{$in_iface}{mac} = "" if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{mac}; + $anvil->data->{sys}{network}{interface}{$in_iface}{default_gateway} = 0 if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{default_gateway}; + $anvil->data->{sys}{network}{interface}{$in_iface}{gateway} = "" if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{gateway}; + $anvil->data->{sys}{network}{interface}{$in_iface}{dns} = "" if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{dns}; } next if not $in_iface; next if $in_iface eq "lo"; @@ -565,9 +571,9 @@ sub get_ips } if ($line =~ /ether ([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}) /i) { - my $mac = $1; + my $mac = $1; $anvil->data->{sys}{network}{interface}{$in_iface}{mac} = $mac; - $anvil->data->{sys}{mac}{$mac}{iface} = $in_iface; + $anvil->data->{sys}{mac}{$mac}{iface} = $in_iface; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sys::network::interface::${in_iface}::mac" => $anvil->data->{sys}{network}{interface}{$in_iface}{mac}, "sys::mac::${mac}::iface" => $anvil->data->{sys}{mac}{$mac}{iface}, @@ -575,6 +581,83 @@ sub get_ips } } + # Get the routing info. + my $lowest_metric = 99999999; + my $route_interface = ""; + my $route_ip = ""; + my $ip_route = $anvil->System->call({debug => $debug, shell_call => $anvil->data->{path}{exe}{ip}." route show"}); + foreach my $line (split/\n/, $ip_route) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }}); + if ($line =~ /default via (.*?) dev (.*?) proto static metric (\d+)/i) + { + my $this_ip = $1; + my $this_interface = $2; + my $metric = $3; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + this_ip => $this_ip, + this_interface => $this_interface, + metric => $metric, + }}); + + if ($metric < $lowest_metric) + { + $lowest_metric = $metric; + $route_interface = $this_interface; + $route_ip = $this_ip; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + lowest_metric => $lowest_metric, + route_interface => $route_interface, + route_ip => $route_ip, + }}); + } + } + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + route_interface => $route_interface, + route_ip => $route_ip, + }}); + + # If I got a route, get the DNS. + if ($route_interface) + { + my $dns_list = ""; + my $dns_hash = {}; + my $ip_route = $anvil->System->call({debug => $debug, shell_call => $anvil->data->{path}{exe}{nmcli}." dev show"}); + foreach my $line (split/\n/, $ip_route) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }}); + if ($line =~ /IP4.DNS\[(\d+)\]:\s+(.*)/i) + { + my $order = $1; + my $ip = $2; + + $dns_hash->{$order} = $ip; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "dns_hash->{$order}" => $dns_hash->{$order} }}); + } + } + + foreach my $order (sort {$a cmp $b} keys %{$dns_hash}) + { + $dns_list .= $dns_hash->{$order}.","; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "s1:dns_hash->{$order}" => $dns_hash->{$order}, + "s2:dns_list" => $dns_list, + }}); + } + $dns_list =~ s/,$//; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { dns_list => $dns_list }}); + + $anvil->data->{sys}{network}{interface}{$route_interface}{default_gateway} = 1; + $anvil->data->{sys}{network}{interface}{$route_interface}{gateway} = $route_ip; + $anvil->data->{sys}{network}{interface}{$route_interface}{dns} = $dns_list; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "sys::network::interface::${route_interface}::default_gateway" => $anvil->data->{sys}{network}{interface}{$route_interface}{default_gateway}, + "sys::network::interface::${route_interface}::gateway" => $anvil->data->{sys}{network}{interface}{$route_interface}{gateway}, + "sys::network::interface::${route_interface}::dns" => $anvil->data->{sys}{network}{interface}{$route_interface}{dns}, + }}); + } + return(0); } diff --git a/tools/anvil-update-states b/tools/anvil-update-states index 0a33d5c0..28978e71 100755 --- a/tools/anvil-update-states +++ b/tools/anvil-update-states @@ -81,8 +81,7 @@ sub report_network my $subnet_mask = ""; 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 $is_bond = 0; - my $is_slave = 0; + my $type = "interface"; if (exists $anvil->data->{sys}{network}{interface}{$interface}) { @@ -100,12 +99,8 @@ sub report_network if (-e $mac_bond_file) { # It's a slave. - $is_slave = 1; $mac_address = $anvil->Storage->read_file({file => $mac_bond_file}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - is_slave => $is_slave, - mac_address => $mac_address, - }}); + $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 @@ -133,19 +128,18 @@ sub report_network { # Yup, we'll neet to dig into the bond proc files to get the proper slaved # interface MAC addresses. - $is_bond = 1; + $type = "bond"; } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { duplex => $duplex, interface => $interface, - ip_address => $ip_address, - link_state => $link_state, mac_address => $mac_address, mtu => $mtu, operational => $operational, speed => $speed, subnet_mask => $subnet_mask, + type => $type, }}); # If the MAC address starts with '52:54:00', we've got a virtio NIC. @@ -183,8 +177,6 @@ sub report_network $anvil->data->{network}{interfaces}{by_name}{$interface} = { duplex => $duplex, ip_address => $ip_address, - is_bond => $is_bond, - is_slave => $is_slave, link_state => $link_state, mac_address => $mac_address, media => $media, @@ -192,12 +184,11 @@ sub report_network operational => $operational, speed => $speed, subnet_mask => $subnet_mask, + type => $type, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "network::interfaces::by_name::${interface}::duplex" => $duplex, "network::interfaces::by_name::${interface}::ip_address" => $ip_address, - "network::interfaces::by_name::${interface}::is_bond" => $is_bond, - "network::interfaces::by_name::${interface}::is_slave" => $is_slave, "network::interfaces::by_name::${interface}::link_state" => $link_state, "network::interfaces::by_name::${interface}::mac_address" => $mac_address, "network::interfaces::by_name::${interface}::media" => $media, @@ -205,6 +196,7 @@ sub report_network "network::interfaces::by_name::${interface}::operational" => $operational, "network::interfaces::by_name::${interface}::speed" => $speed, "network::interfaces::by_name::${interface}::subnet_mask" => $subnet_mask, + "network::interfaces::by_name::${interface}::type" => $type, }}); } } @@ -212,33 +204,37 @@ sub report_network foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{interfaces}{by_name}}) { - my $duplex = $anvil->data->{network}{interfaces}{by_name}{$interface}{duplex}; - my $ip_address = $anvil->data->{network}{interfaces}{by_name}{$interface}{ip_address}; - my $is_bond = $anvil->data->{network}{interfaces}{by_name}{$interface}{is_bond}; - my $is_slave = $anvil->data->{network}{interfaces}{by_name}{$interface}{is_slave}; - 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 $mtu = $anvil->data->{network}{interfaces}{by_name}{$interface}{mtu}; - my $operational = $anvil->data->{network}{interfaces}{by_name}{$interface}{operational}; - my $speed = $anvil->data->{network}{interfaces}{by_name}{$interface}{speed}; - my $subnet_mask = $anvil->data->{network}{interfaces}{by_name}{$interface}{subnet_mask}; + 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 $mtu = $anvil->data->{network}{interfaces}{by_name}{$interface}{mtu}; + my $operational = $anvil->data->{network}{interfaces}{by_name}{$interface}{operational}; + 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 $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 => { - duplex => $duplex, - interface => $interface, - ip_address => $ip_address, - is_bond => $is_bond, - is_slave => $is_slave, - link_state => $link_state, - mac_address => $mac_address, - media => $media, - mtu => $mtu, - operational => $operational, - speed => $speed, - subnet_mask => $subnet_mask, + default_gateway => $default_gateway, + dns => $dns, + duplex => $duplex, + gateway => $gateway, + interface => $interface, + ip_address => $ip_address, + link_state => $link_state, + mac_address => $mac_address, + media => $media, + mtu => $mtu, + operational => $operational, + speed => $speed, + subnet_mask => $subnet_mask, + type => $type, }}); -# $anvil->Database->insert_or_update_network_interfaces({ +# my $network_interface_uuid = $anvil->Database->insert_or_update_network_interfaces({ # file => $THIS_FILE, # line => __LINE__, # network_interface_name => $interface, @@ -249,6 +245,16 @@ sub report_network # network_interface_medium => $media, # network_interface_mtu => $mtu, # network_interface_speed => $speed, +# }); +# +# my $ip_address_uuid = $anvil->Database->insert_or_update_ip_addresses({ +# 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 => $ip_address_subnet, +# ip_address_gateway => $ip_address_gateway, +# ip_address_default_gateway => $ip_address_default_gateway, +# ip_address_dns => $ip_address_dns, # }); } diff --git a/tools/anvil.sql b/tools/anvil.sql index dbcde29f..ba2eb794 100644 --- a/tools/anvil.sql +++ b/tools/anvil.sql @@ -655,6 +655,76 @@ CREATE TRIGGER trigger_bridges FOR EACH ROW EXECUTE PROCEDURE history_bridges(); +-- This stores information about network ip addresss. +CREATE TABLE ip_addresses ( + ip_address_uuid uuid not null primary key, + ip_address_host_uuid uuid not null, + ip_address_on_type text not null, -- Either 'interface', 'bond' or 'bridge' + ip_address_on_uuid uuid not null, -- This is the UUID of the interface, bond or bridge that has this IP + ip_address_address text not null, -- The actual IP address + ip_address_subnet text not null, -- The subnet mask (in dotted decimal format) + ip_address_gateway text, -- If set, this is the gateway IP for this subnet + ip_address_default_gateway boolean default FALSE, -- If true, the gateway will be the default for the host. + ip_address_dns text, -- If set, this is a comma-separated list of DNS IPs to use (in the order given) + modified_date timestamp with time zone not null, + + FOREIGN KEY(ip_address_host_uuid) REFERENCES hosts(host_uuid) +); +ALTER TABLE ip_addresses OWNER TO #!variable!user!#; + +CREATE TABLE history.ip_addresses ( + history_id bigserial, + ip_address_uuid uuid, + ip_address_host_uuid uuid, + ip_address_on_type text, + ip_address_on_uuid uuid, + ip_address_address text, + ip_address_subnet text, + ip_address_gateway text, + ip_address_default_gateway text, + ip_address_dns text, + modified_date timestamp with time zone not null +); +ALTER TABLE history.ip_addresses OWNER TO #!variable!user!#; + +CREATE FUNCTION history_ip_addresses() RETURNS trigger +AS $$ +DECLARE + history_ip_addresses RECORD; +BEGIN + SELECT INTO history_ip_addresses * FROM ip_addresses WHERE ip_address_uuid = new.ip_address_uuid; + INSERT INTO history.ip_addresses + (ip_address_uuid, + ip_address_host_uuid, + ip_address_on_type, + ip_address_on_uuid, + ip_address_address, + ip_address_subnet, + ip_address_gateway, + ip_address_default_gateway, + ip_address_dns, + modified_date) + VALUES + (history_ip_addresses.ip_address_uuid, + history_ip_addresses.ip_address_host_uuid, + history_ip_addresses.ip_address_on_type, + history_ip_addresses.ip_address_on_uuid, + history_ip_addresses.ip_address_address, + history_ip_addresses.ip_address_subnet, + history_ip_addresses.ip_address_gateway, + history_ip_addresses.ip_address_default_gateway, + history_ip_addresses.ip_address_dns, + history_ip_addresses.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_ip_addresses() OWNER TO #!variable!user!#; + +CREATE TRIGGER trigger_ip_addresses + AFTER INSERT OR UPDATE ON ip_addresses + FOR EACH ROW EXECUTE PROCEDURE history_ip_addresses(); + -- ------------------------------------------------------------------------------------------------------- -- -- These are special tables with no history or tracking UUIDs that simply record transient information. --