* Updated Log->variables to add dots when printing a variable/value pair per line so that variables line up vertically.

* Renamed tools/anvil-configure-network to tools/anvil-configure-striker given that it will also now update system passwords.
* Started working on tools/anvil-update-states to properly handle a Striker with already-configured networking.
* Cleaned up tools/anvil-change-password.
* Fixed a bug in Storage->update_config to set the ownership of anvil.conf to 'apache:apache' so that the web server can read it.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 7 years ago
parent 2163739b93
commit 4e6f492c4f
  1. 29
      Anvil/Tools/Log.pm
  2. 4
      Anvil/Tools/Storage.pm
  3. 277
      Anvil/Tools/System.pm
  4. 2
      cgi-bin/home
  5. 12
      share/words.xml
  6. 69
      tools/anvil-change-password
  7. 14
      tools/anvil-configure-striker
  8. 169
      tools/anvil-update-states

@ -608,20 +608,43 @@ sub variables
}
else
{
# Put all the entries on their own line.
# Put all the entries on their own line. We'll loop twice; the first time to get the
# longest variable, and the second loop to print them (with dots to make the values
# all line up).
my $length = 0;
foreach my $key (sort {$a cmp $b} keys %{$list})
{
if (length($key) > $length)
{
$length = length($key);
}
}
# We add '1' to account for the colon we append.
$length++;
$raw .= $anvil->Words->string({key => "log_0019"})."\n";
foreach my $key (sort {$a cmp $b} keys %{$list})
{
# Strip a leading 'sX:' in case the user is sorting the output.
my $say_key = $key;
$say_key =~ s/^s(\d+)://;
$say_key .= ":";
my $difference = $length - length($say_key);
if ($difference)
{
$say_key .= " ";
for (2..$difference)
{
$say_key .= ".";
}
}
if ($entry ne $entries)
{
$raw .= "|- $say_key: [".$list->{$key}."]\n";
$raw .= "|- $say_key [".$list->{$key}."]\n";
}
else
{
$raw .= "\\- $say_key: [".$list->{$key}."]\n";
$raw .= "\\- $say_key [".$list->{$key}."]\n";
}
$entry++;
}

@ -1949,11 +1949,11 @@ sub update_config
body => $new_file,
debug => $debug,
file => $anvil->data->{path}{configs}{'anvil.conf'},
group => "admin",
group => "apache",
mode => "0640",
overwrite => 1,
secure => 1,
user => "admin",
user => "apache",
password => $password,
port => $port,
target => $target,

@ -15,7 +15,6 @@ my $THIS_FILE = "System.pm";
### Methods;
# call
# (disabled) change_apache_password
# change_shell_user_password
# check_daemon
# check_memory
@ -197,262 +196,6 @@ sub call
return($output);
}
# =head2 change_apache_password
#
# NOTE: Likely to be removed.
#
# This changes the password used to connet to Striker's web interface. If the C<< .htpasswd >> file isn't found, this method will effectively enable the password feature.
#
# The return code will be C<< 255 >> on internal error. Otherwise, it will be the code returned from the C<< passwd >> call.
#
# Parameters;
#
# =head3 new_password (required)
#
# This is the new password to set. The user should be encouraged to select a good (long) password.
#
# =head3 password (optional)
#
# If you are changing the apache password on a remote machine, this is the password used to connect to that machine. If not passed, an attempt to connect with passwordless SSH will be made (but this won't be the case in most instances). Ignored if C<< target >> is not given.
#
# =head3 port (optional, default 22)
#
# This is the TCP port number to use if connecting to a remote machine over SSH. Ignored if C<< target >> is not given.
#
# =head3 remote_user (optional, default root)
#
# If C<< target >> is set and we're changing the password for a remote user, this is the user we B<< log into >> the remote machine as, B<< not >> the user whose password we will change.
#
# =head3 target (optional)
#
# This is the IP address or (resolvable) host name of the target machine whose user account you want to change the password
#
# =head3 user (optional, default 'sys::apache::user' or 'admin')
#
# This is the apache user name to use. If another name existed before in C<< .htpasswd >>, that old user name will be removed.
#
# =cut
# sub change_apache_password
# {
# my $self = shift;
# my $parameter = shift;
# my $anvil = $self->parent;
# my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
#
# my $new_password = defined $parameter->{new_password} ? $parameter->{new_password} : "";
# my $password = defined $parameter->{password} ? $parameter->{password} : "";
# my $port = defined $parameter->{port} ? $parameter->{port} : "";
# my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "";
# my $target = defined $parameter->{target} ? $parameter->{target} : "";
# my $user = defined $parameter->{user} ? $parameter->{user} : $anvil->data->{sys}{apache}{user};
# my $return_code = 255;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
# user => $user,
# target => $target,
# port => $port,
# remote_user => $remote_user,
# new_password => $anvil->Log->secure ? $new_password : "--",
# password => $anvil->Log->secure ? $password : "--",
# }});
#
# # Set the user to 'admin' if it's not set.
# if (not $user)
# {
# $user = "admin";
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { user => $user }});
# }
#
# # OK, what about a password?
# if (not $new_password)
# {
# # Um...
# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Systeme->change_apache_password()", parameter => "new_password" }});
# return($return_code);
# }
#
# # Only the root user can do this!
# # $< == real UID, $> == effective UID
# if (($< != 0) && ($> != 0))
# {
# # Not root
# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0156", variables => { method => "Systeme->change_apache_password()" }});
# return($return_code);
# }
#
# # Read httpd.conf and make sure apache is configured for .htpasswd.
# my $httpd_conf = $anvil->Storage->read_file({
# file => $anvil->data->{path}{configs}{'httpd.conf'},
# debug => $debug,
# target => $target,
# port => $port,
# remote_user => $remote_user,
# password => $password,
# });
# if ($httpd_conf eq "!!error!!")
# {
# # We're done.
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { httpd_conf => $httpd_conf }});
# return($return_code);
# }
# my $rewrite = 0;
# my $new_file = "";
# my $in_html_directory = 0;
# my $in_cgi_directory = 0;
# my $cgi_first_line = 0;
# foreach my $line (split/\n/, $httpd_conf)
# {
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { line => $line, in_cgi_directory => $in_cgi_directory }});
#
# if ($in_html_directory)
# {
# if ($line =~ /^(\s+)AllowOverride None/i)
# {
# # We need to update.
# my $space = $1;
# $rewrite = 1;
# $new_file .= $space."AllowOverride AuthConfig\n";
# next;
# }
# elsif ($line eq "</Directory>")
# {
# $in_html_directory = 0;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_html_directory => $in_html_directory }});
# }
# }
# elsif ($line eq '<Directory "/var/www/html">')
# {
# $in_html_directory = 1;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_html_directory => $in_html_directory }});
# }
#
# if ($in_cgi_directory)
# {
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { cgi_first_line => $cgi_first_line, line => $line }});
# if ((not $cgi_first_line) && ($line =~ /^(\s+)AllowOverride None/i))
# {
# # We need to update.
# my $space = $1;
# $rewrite = 1;
# $new_file .= $space."# Password login\n";
# $new_file .= $space."AuthType Basic\n";
# $new_file .= $space."AuthName \"Striker - The Anvil! Dashboard\"\n";
# $new_file .= $space."AuthUserFile ".$anvil->data->{path}{data}{'.htpasswd'}."\n";
# $new_file .= $space."Require valid-user \n";
#
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { space => $space, rewrite => $rewrite }});
# }
# elsif ($line eq "</Directory>")
# {
# $in_cgi_directory = 0;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_cgi_directory => $in_cgi_directory }});
# }
# elsif ($line =~ /Require all granted/)
# {
# # We don't want this line.
# $rewrite = 1;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { rewrite => $rewrite }});
# next;
# }
# $cgi_first_line = 1;
# }
# elsif ($line eq '<Directory "/var/www/cgi-bin">')
# {
# $in_cgi_directory = 1;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_cgi_directory => $in_cgi_directory }});
# }
#
# $new_file .= $line."\n";
# }
#
# # Update the file if needed.
# if ($rewrite)
# {
# # Back it up first.
# my $backup_file = $anvil->Storage->backup({
# file => $anvil->data->{path}{configs}{'httpd.conf'},
# debug => $debug,
# target => $target,
# port => $port,
# remote_user => $remote_user,
# password => $password,
# });
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { backup_file => $backup_file }});
#
# if ($backup_file)
# {
# # Proceed.
# $anvil->Storage->write_file({
# body => $new_file,
# debug => $debug,
# file => $anvil->data->{path}{configs}{'httpd.conf'},
# overwrite => 1,
# secure => 0,
# target => $target,
# port => $port,
# remote_user => $remote_user,
# password => $password,
# });
# }
# }
#
# # Generate the htpasswd md5 (special flavour) hash string.
# # See: https://httpd.apache.org/docs/2.4/misc/password_encryptions.html
#
# # <user>:$apr1$<salt>$<hash>
# #admin:$apr1$Upbg/ujf$SJNCufHfq76uo2t0rZTZI/
#
# # my $salt = $anvil->System->call({debug => $debug, shell_call => $anvil->data->{path}{exe}{openssl}." rand 1000 | ".$anvil->data->{path}{exe}{strings}." | ".$anvil->data->{path}{exe}{'grep'}." -io [0-9A-Za-z\.\/] | ".$anvil->data->{path}{exe}{head}." -n 16 | ".$anvil->data->{path}{exe}{'tr'}." -d '\n'" });
# # my $new_hash = crypt($new_password,"\$6\$".$salt."\$");
# # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
# # salt => $salt,
# # new_hash => $new_hash,
# # }});
#
# # htpasswd -cb /etc/httpd/.htpasswd admin Initial2
# # chown apache:apache /etc/httpd/.htpasswd
# # chmod 0660 /etc/httpd/.htpasswd
#
#
# # (re)write the htpasswd file.
# ### TODO: Temporary!
# $new_password =~ s/"/\"/g;
# my $shell_call = $anvil->data->{path}{exe}{htpasswd}." -nb ".$user." \"".$new_password."\"";
# my $output = $anvil->System->call({debug => $debug, secure => 1, shell_call => $shell_call});
# my $hash_string = "";
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { output => $output }});
# foreach my $line (split/\n/, $output)
# {
# $hash_string = $line;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { hash_string => $hash_string }});
# last;
# }
#
# # Write out the file.
# $return_code = $anvil->Storage->write_file({
# body => $hash_string."\n\n",
# debug => $debug,
# file => $anvil->data->{path}{data}{'.htpasswd'},
# group => "apache",
# mode => "0660",
# overwrite => 1,
# secure => 0,
# target => $target,
# user => "apache",
# port => $port,
# remote_user => $remote_user,
# password => $password,
# });
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { return_code => $return_code }});
#
# if (not $return_code)
# {
# # Restart apache
# $anvil->System->reload_daemon({debug => $debug, daemon => "httpd"});
# }
#
# return($return_code);
# }
=head2 change_shell_user_password
@ -793,9 +536,9 @@ sub get_ips
$in_iface = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { in_iface => $in_iface }});
$anvil->data->{sys}{networks}{$in_iface}{ip} = "" if not defined $anvil->data->{sys}{networks}{$in_iface}{ip};
$anvil->data->{sys}{networks}{$in_iface}{subnet} = "" if not defined $anvil->data->{sys}{networks}{$in_iface}{subnet};
$anvil->data->{sys}{networks}{$in_iface}{mac} = "" if not defined $anvil->data->{sys}{networks}{$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};
}
next if not $in_iface;
next if $in_iface eq "lo";
@ -813,20 +556,20 @@ sub get_ips
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { subnet => $subnet }});
}
$anvil->data->{sys}{networks}{$in_iface}{ip} = $ip;
$anvil->data->{sys}{networks}{$in_iface}{subnet} = $subnet;
$anvil->data->{sys}{network}{interface}{$in_iface}{ip} = $ip;
$anvil->data->{sys}{network}{interface}{$in_iface}{subnet} = $subnet;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:sys::networks::${in_iface}::ip" => $anvil->data->{sys}{networks}{$in_iface}{ip},
"s2:sys::networks::${in_iface}::subnet" => $anvil->data->{sys}{networks}{$in_iface}{subnet},
"s1:sys::network::interface::${in_iface}::ip" => $anvil->data->{sys}{network}{interface}{$in_iface}{ip},
"s2:sys::network::interface::${in_iface}::subnet" => $anvil->data->{sys}{network}{interface}{$in_iface}{subnet},
}});
}
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;
$anvil->data->{sys}{networks}{$in_iface}{mac} = $mac;
$anvil->data->{sys}{network}{interface}{$in_iface}{mac} = $mac;
$anvil->data->{sys}{mac}{$mac}{iface} = $in_iface;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"sys::networks::${in_iface}::mac" => $anvil->data->{sys}{networks}{$in_iface}{mac},
$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},
}});
}

@ -206,7 +206,7 @@ sub save_job
debug => 2,
file => $THIS_FILE,
line => __LINE__,
job_command => "anvil-configure-network",
job_command => "anvil-configure-striker",
job_data => "form::config_step2",
job_name => "configure::network",
job_title => "job_0001",

@ -45,6 +45,16 @@ Author: Madison Kelly <mkelly@alteeve.ca>
<key name="message_0021">Proceed? [y/N]</key>
<key name="message_0022">Aborting.</key>
<key name="message_0023">Auto-approved by command line switch, proceeding.</key>
<key name="message_0024">Updating the Striker user: [#!variable!user!#] password... </key>
<key name="message_0025">Done.</key>
<key name="message_0026">Updating the database user: [#!variable!user!#] password... </key>
<key name="message_0027">Updating the local config file: [#!variable!file!#] database password... </key>
<key name="message_0028">Updating the shell user: [#!variable!user!#] password... </key>
<key name="message_0029">Finished!
NOTE: You must update the password of any other system using this host's
database manually!
</key>
<!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key>
@ -352,7 +362,7 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st
<key name="error_0005">This program must run with 'root' level privileges.</key>
<key name="error_0006">No password was given, exiting.</key>
<key name="error_0007">The passwords don't match, exiting.</key>
<key name="error_0008">Failed to read the file: [#!variable!file!#]. Please see the logs for details</key>
<key name="error_0008">Failed to read the file: [#!variable!file!#]. It doesn't appear to exist.</key>
<key name="error_0009">Failed to add the target: [#!variable!target!#]:[#!variable!port!#]'s RSA fingerprint to: [#!variable!user!#]'s list of known hosts.</key>
<key name="error_0010">There was a problem adding the local machine to the: [#!data!path::configs::anvil.conf!#] file. Please see the log for details.</key>

@ -75,7 +75,7 @@ if ($anvil->data->{switches}{'password-file'})
else
{
# The file doesn't exist.
print $anvil->Words->string({key => "error_0008", variables => { file => $anvil->data->{switches}{'password-file'} }});
print $anvil->Words->string({key => "error_0008", variables => { file => $anvil->data->{switches}{'password-file'} }})."\n";
$anvil->nice_exit({exit_code => 4});
}
}
@ -136,40 +136,6 @@ else
{
print $anvil->Words->string({key => "message_0023"})."\n";
update_local_passwords($anvil);
# Update the user password.
my $user = "admin";
my $user_uuid = $anvil->Database->insert_or_update_users({
debug => 2,
user_name => $user,
user_password_hash => $anvil->data->{switches}{'new-password'},
user_is_admin => 1,
user_is_experienced => 1,
user_is_trusted => 1,
});
# Validate
my $valid = $anvil->Account->validate_password({
debug => 2,
user => $user,
password => $anvil->data->{switches}{'new-password'},
});
# Update the database password.
# foreach my $user ("postgres", $database_user)
# {
# my $update_output = $anvil->System->call({secure => 1, shell_call => $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{psql}." template1 -c \\\"ALTER ROLE $user WITH PASSWORD '".$anvil->data->{database}{$local_uuid}{password}."';\\\"\"", source => $THIS_FILE, line => __LINE__});
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 1, list => { update_output => $update_output }});
# foreach my $line (split/\n/, $user_list)
# {
# if ($line =~ /ALTER ROLE/)
# {
# # Password set
# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0100", variables => { user => $user }});
# }
# }
# }
}
else
{
@ -201,7 +167,29 @@ sub update_local_passwords
{
my ($anvil) = @_;
# Update the 'admin' user password in the database.
my $user = "admin";
print $anvil->Words->string({key => "message_0024", variables => { user => $user }});
my $user_uuid = $anvil->Database->insert_or_update_users({
debug => 2,
user_name => $user,
user_password_hash => $anvil->data->{switches}{'new-password'},
user_is_admin => 1,
user_is_experienced => 1,
user_is_trusted => 1,
});
print $anvil->Words->string({key => "message_0025"})."\n";
# Validate
my $valid = $anvil->Account->validate_password({
debug => 2,
user => $user,
password => $anvil->data->{switches}{'new-password'},
});
### NOTE: We directly connect to the local 'template1' database as
# Update the database passwords
my $host_uuid = $anvil->data->{sys}{host_uuid};
my $old_password = $anvil->data->{database}{$host_uuid}{password};
my $dbh = DBI->connect("DBI:Pg:dbname=template1;host=localhost;port=5432", "postgres", $old_password, {
@ -229,30 +217,39 @@ sub update_local_passwords
my $owner_name = $results->[0]->[1];
foreach my $user ("postgres", $owner_name)
{
print $anvil->Words->string({key => "message_0026", variables => { user => $user }});
my $query = "ALTER ROLE ".$user." WITH PASSWORD ".$dbh->quote($anvil->data->{switches}{'new-password'});
$dbh->do($query) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0090", variables => {
query => $anvil->Log->secure ? $query : "--",
server => "localhost",
db_error => $DBI::errstr,
}});
print $anvil->Words->string({key => "message_0025"})."\n";
}
# Update our database password in anvil.conf
print $anvil->Words->string({key => "message_0027", variables => { file => $anvil->data->{switches}{'new-password'} }});
$anvil->Storage->update_config({
debug => 2,
secure => 1,
variable => "database::${host_uuid}::password",
value => $anvil->data->{switches}{'new-password'},
});
print $anvil->Words->string({key => "message_0025"})."\n";
### TODO: Loop through any other dashboards and nodes we know about and call the above with 'target'
### (and password, port and remote_user) set.
# Update the local users.
foreach my $user ("admin", "root")
{
print "Updating: [$user] with password: [".$anvil->data->{switches}{'new-password'}."]\n";
print $anvil->Words->string({key => "message_0028", variables => { user => $user }});
$anvil->System->change_shell_user_password({debug => 2, user => $user, new_password => $anvil->data->{switches}{'new-password'}});
print $anvil->Words->string({key => "message_0025"})."\n";
}
# All done!
print $anvil->Words->string({key => "message_0029"})."\n";
return(0);
}

@ -1,6 +1,6 @@
#!/usr/bin/perl
#
# This is called when the local network needs to be reconfigured.
# This is called when striker needs to configure the local network and user accounts.
#
# Exit codes;
# 0 = Normal exit.
@ -85,6 +85,18 @@ sub update_passwords
{
my ($anvil) = @_;
# Write the password into a temporary file.
my $error = $anvil->Storage->write_file({
body => ,
debug => $debug,
file => $temp_file,
group => $group,
mode => $mode,
overwrite => 1,
secure => $secure,
user => $user,
});
return(0);
}

@ -48,11 +48,17 @@ sub report_network
{
my ($anvil) = @_;
# Write out the data in json format.
# 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::<iface_name>::ip' - If an IP address is set
# * 'sys::network::interface::<iface_name>::subnet' - If an IP is set
my $directory = $anvil->data->{path}{sysfs}{network_interfaces};
#print $THIS_FILE." ".__LINE__."; directory: [".$directory."]\n";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { directory => $directory }});
local(*DIRECTORY);
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0018", variables => { directory => $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))
{
@ -61,7 +67,7 @@ sub report_network
next if $file eq "lo";
next if $file =~ /virbr\d/;
my $full_path = "$directory/$file";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { full_path => $full_path }});
$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.
@ -71,48 +77,96 @@ sub report_network
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 $ip_address = "";
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;
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.
$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,
}});
}
# 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.
$is_bond = 1;
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
duplex => $duplex,
interface => $interface,
mac_address => $mac_address,
ip_address => $ip_address,
link_state => $link_state,
mac_address => $mac_address,
mtu => $mtu,
duplex => $duplex,
operational => $operational,
speed => $speed,
subnet_mask => $subnet_mask,
}});
# If the MAC address starts with '52:54:00', we've got a virtio NIC.
die $THIS_FILE." ".__LINE__."; No speed for: [".$full_path."/speed]\n" if ((not defined $speed) or ($speed eq ""));
die $THIS_FILE." ".__LINE__."; Speed: [$speed] isn't numeric for: [".$full_path."/speed]\n" if $speed =~ /\D/;
#
# 100000
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 }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { speed => $speed }});
}
# Find the media, if possible.
my $media = "unknown";
my $shell_call = $anvil->data->{path}{exe}{ethtool}." $interface";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { shell_call => $shell_call }});
my $ethtool = $anvil->System->call({shell_call => $shell_call});
foreach my $line (split/\n/, $ethtool)
{
@ -120,44 +174,83 @@ sub report_network
if ($line =~ /Supported ports: \[ (.*?) \]/i)
{
$media = lc($1);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { media => $media }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { media => $media }});
last;
}
}
# Log
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
interface => $interface,
mac_address => $mac_address,
$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,
mtu => $mtu,
duplex => $duplex,
operational => $operational,
speed => $speed,
media => $media,
subnet_mask => $subnet_mask,
};
$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,
"network::interfaces::by_name::${interface}::mtu" => $mtu,
"network::interfaces::by_name::${interface}::operational" => $operational,
"network::interfaces::by_name::${interface}::speed" => $speed,
"network::interfaces::by_name::${interface}::subnet_mask" => $subnet_mask,
}});
$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,
});
}
}
closedir(DIRECTORY);
### TODO: Create $anvil "ip" table and record IPs on this system, linking back to $anvil interface, bond or
### bridge.
# Run 'ip addr' to see what IPs are in use.
$anvil->System->get_ips;
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};
$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,
}});
# $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,
# });
}
# Write out the XML file and JSON file.
my $order = 1;

Loading…
Cancel
Save