Merge pull request #190 from ClusterLabs/anvil-tools-dev

Anvil tools dev
main
digimer-bot 3 years ago committed by GitHub
commit ce06239a7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      Anvil/Tools/Convert.pm
  2. 11
      Anvil/Tools/DRBD.pm
  3. 2
      Anvil/Tools/Database.pm
  4. 13
      Anvil/Tools/Network.pm
  5. 35
      Anvil/Tools/Server.pm
  6. 87
      Anvil/Tools/Storage.pm
  7. 58
      Anvil/Tools/System.pm
  8. 6
      notes
  9. 31
      ocf/alteeve/server
  10. 14
      scancore-agents/scan-filesystems/scan-filesystems
  11. 25
      scancore-agents/scan-hardware/scan-hardware
  12. 178
      scancore-agents/scan-network/scan-network
  13. 5
      scancore-agents/scan-network/scan-network.xml
  14. 23
      share/words.xml
  15. 978
      tools/anvil-manage-dr
  16. 3
      tools/anvil-provision-server

@ -1197,6 +1197,9 @@ sub round
# Return if the user passed a double-dash.
return('--') if $number eq "--";
# Take out exponent notation
$number =~ s/e-\d+$//;
# Make a copy of the passed number that I can manipulate.
my $rounded_number = $number;

@ -761,6 +761,17 @@ sub gather_data
host2_ip_address => $host2_ip_address,
host2_tcp_port => $host2_tcp_port,
}});
$anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host1_ip_address} = $host1_ip_address;
$anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host1_tcp_port} = $host1_tcp_port;
$anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_ip_address} = $host2_ip_address;
$anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_tcp_port} = $host2_tcp_port;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host1_ip_address" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host1_ip_address},
"s2:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host1_tcp_port" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host1_tcp_port},
"s3:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host2_ip_address" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_ip_address},
"s4:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host2_tcp_port" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_tcp_port},
}});
}
# $peer = $this_host_name;

@ -1657,7 +1657,7 @@ sub connect
}
# Before we try to connect, see if this is a local database and, if so, make sure it's setup.
my $is_local = $anvil->Network->is_local({debug => 2, host => $host});
my $is_local = $anvil->Network->is_local({debug => $debug, host => $host});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
if ($is_local)
{

@ -2151,8 +2151,17 @@ fi";
}
if ($line =~ /ether (.*?) /i)
{
my $mac_address = $1;
$anvil->data->{network}{$host}{interface}{$in_iface}{mac_address} = $mac_address;
my $mac_address = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { mac_address => $mac_address }});
# Wireless interfaces have a 'permaddr' that is stable. The MAC address shown by 'ether' changes constantly, for some odd reason.
if ($line =~ /permaddr (.*)$/)
{
$mac_address = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { mac_address => $mac_address }});
}
$anvil->data->{network}{$host}{interface}{$in_iface}{mac_address} = $mac_address;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${in_iface}::mac_address" => $anvil->data->{network}{$host}{interface}{$in_iface}{mac_address},
}});

@ -864,37 +864,6 @@ sub map_network
return(0);
}
=head2 provision
This method creates a new (virtual) server on an Anvil! system.
Parameters;
=cut
sub provision
{
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 => "Server->provision()" }});
=cut
Provision order:
1. Create LVs and register the storage.
- NOTE: If the LV is already in the DB (from a past install) and the peer is not available and the local
DRBD resource doesn't show Consistent, abort. If the peer is alive but we can't contact it, it's
possible the peer is UpToDate.
2. Create the DRBD resource. If "Inconsistent" on both nodes, force up to date
3. Wait for install media/image to be ready
4. Provision VM and add to Pacemaker.
=cut
return(0);
}
=head2 migrate_virsh
@ -918,9 +887,9 @@ This is the host name (or IP) of the host that we're pulling the server from.
If set, the server will be pulled.
=head3 target (optional, defaukt is the full local host name)
=head3 target (optional, default is the full local host name)
This is the host name (or IP) Of the host that the server will be pushed to, if C<< source >> is not set. When this is not passed, the local full host name is used as default.
This is the host name (or IP) of the host that the server will be pushed to, if C<< source >> is not set. When this is not passed, the local full host name is used as default.
=cut
sub migrate_virsh

@ -22,6 +22,7 @@ my $THIS_FILE = "Storage.pm";
# check_md5sums
# compress
# copy_file
# copy_device
# delete_file
# find
# get_file_stats
@ -1263,6 +1264,92 @@ fi";
}
=head3 copy_device
This uses the C<< dd >> system call, possibly over ssh, to create a copy of the source on the destination. Being based on C<< dd >>, this works with raw block devices and to or from files.
B<< Warning >>: This must be used carefully! Calling this backwards could destroy data!
B<< Note >>: The caller is responsible for ensuring the data on the soure will not change during the copy. If the source is a server, make sure it's off. If the source is a file system, make sure it's unmounted.
B<< Note >>: If the C<< source >> or C<< destination >> is a remote host, passwordless SSH must be configured for this to work!
Parameters;
=head3 block_size (optional, default '4M')
This is the block size to be used for the copy. Specifically, this transtes into 'read <size> bytes, copy, read <size> bytes, copy'. This should match the size of the logical extents, block size or similar where needed. Most LVM logical extents are 4 MiB, so the default of C<< 4M >> should be fine in most cases.
B<< Note >>: See C<< man dd >> for valid formatting of this option.
=head3 calculate_sums (Optional, default '0')
If set to C<< 1 >>, the C<< md5sum >> of the source and destination are calculated and returned. If this is not used, the returned sum fields will be an empty string.
B<< Note >>: Calculating sums is highly advised, but can increase the time it takes for the copy to complete!
=head3 destination (required)
This is the full path to the destination (copy to) file or device. If the source is remote, used the format C<< <remote_user>@target:/path/to/file >>.
B<< Note >>: Only the source OR the destination can be remote, not both!
=head3 source (required)
This is the full path to the source (copy from) file or device. If the source is remote, used the format C<< <remote_user>@target:/path/to/file >>.
B<< Note >>: Only the source OR the destination can be remote, not both!
=head3 status_file (required)
This is the path to the status file used to record the progress of the copy. This will contain a parsed version of the C<< dd ... --status=progress >> output. When the copy is done, if C<< calculate_sums >> is set, then the C<< source=<sum> >> and C<< destination=<sum> >> will be recorded, marking the completion of the copy. If not set, those same variables will be written without a value, still marking the end of the copy. If there is a problem, the last line of the file be C<< failed=<reason> >>.
=cut
sub copy_device
{
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 => "Storage->copy_device()" }});
my $block_size = defined $parameter->{block_size} ? $parameter->{block_size} : "";
my $calculate_sums = defined $parameter->{calculate_sums} ? $parameter->{calculate_sums} : "";
my $destination = defined $parameter->{destination} ? $parameter->{destination} : "";
my $source = defined $parameter->{source} ? $parameter->{source} : "";
my $status_file = defined $parameter->{status_file} ? $parameter->{status_file} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
block_size => $block_size,
calculate_sums => $calculate_sums,
destination => $destination,
source => $source,
status_file => $status_file,
}});
if (not $source)
{
# No source passed.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Storage->copy_device()", parameter => "source" }});
return('!!error!!');
}
if (not $destination)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Storage->copy_device()", parameter => "destination" }});
return('!!error!!');
}
if (not $block_size)
{
$block_size = "4M";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { block_size => $block_size }});
}
# Verify that the source exists.
return("");
}
=head3 delete_file
This deletes a file. Pretty much what it says on the tin. When run locally, it uses C<< unlink >>. When run on a remote machine, it uses C<< rm -f >>. As such, this will not delete directories, nor will it delete recursively.

@ -4902,7 +4902,6 @@ sub update_hosts
# Read in the existing hosts file
my $add_header = 1;
my $changes = 0;
my $added_lo_ipv4 = 0;
my $added_lo_ipv6 = 0;
my $new_body = "";
@ -4912,6 +4911,35 @@ sub update_hosts
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { old_body => $old_body }});
# Look for duplicate empty lines and clear them.
my $last_line_blank = 0;
my $cleaned_body = "";
foreach my $line (split/\n/, $old_body)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
if ($line)
{
$last_line_blank = 0;
$cleaned_body .= $line."\n";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { last_line_blank => $last_line_blank }});
}
elsif (not $last_line_blank)
{
$last_line_blank = 1;
$cleaned_body .= $line."\n";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { last_line_blank => $last_line_blank }});
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { last_line_blank => $last_line_blank }});
my $difference = diff \$old_body, \$cleaned_body, { STYLE => 'Unified' };
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { difference => $difference }});
if ($difference)
{
$old_body = $cleaned_body;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { old_body => $old_body }});
}
# This will track the IPs we've seen. We'll only write these out once, and skip any futher entries
# that may be found.
my $written_ips = {};
@ -4955,9 +4983,6 @@ sub update_hosts
{
if ($line ne "127.0.0.1\tlocalhost localhost.localdomain localhost4 localhost4.localdomain4")
{
$changes = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { changes => $changes }});
if (not $added_lo_ipv4)
{
$new_body .= "127.0.0.1\tlocalhost localhost.localdomain localhost4 localhost4.localdomain4\n";
@ -4977,9 +5002,6 @@ sub update_hosts
{
if ($line ne "::1\t\tlocalhost localhost.localdomain localhost6 localhost6.localdomain6")
{
$changes = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { changes => $changes }});
if (not $added_lo_ipv6)
{
$new_body .= "::1\t\tlocalhost localhost.localdomain localhost6 localhost6.localdomain6\n";
@ -5019,8 +5041,6 @@ sub update_hosts
if (exists $written_ips->{$ip_address})
{
# Skipping at least one line, rewrite the file.
$changes = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { changes => $changes }});
next;
}
$written_ips->{$ip_address} = 1;
@ -5049,8 +5069,6 @@ sub update_hosts
new_ip => $ip_address,
host => $name,
}});
$changes = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { changes => $changes }});
next;
}
}
@ -5092,11 +5110,9 @@ sub update_hosts
my $header = $anvil->Words->string({key => "message_0177"});
$header =~ s/^\n//;
$new_body = $header.$new_body;
$changes = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:changes' => $changes,
's2:header' => $header,
's3:new_body' => $new_body,
's1:header' => $header,
's2:new_body' => $new_body,
}});
}
@ -5132,9 +5148,6 @@ sub update_hosts
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { new_line_count => $new_line_count }});
if ($new_line_count)
{
$changes = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { changes => $changes }});
$new_body .= "\n";
#$new_body .= "\n# ".$anvil->Words->string({key => "message_0178", variables => { date => $anvil->Get->date_and_time({debug => $debug}) }})."\n";
foreach my $ip_address (@{$ip_order})
@ -5144,11 +5157,10 @@ sub update_hosts
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:changes' => $changes,
's2:new_body' => $new_body,
}});
if ($changes)
$difference = "";
$difference = diff \$old_body, \$new_body, { STYLE => 'Unified' };
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { difference => $difference }});
if ($difference)
{
# Write the new file.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { new_body => $new_body }});

@ -6,7 +6,7 @@ TODO:
- host_health is a duplicate of 'health'
============
# Dump
su - postgres -c "pg_dump anvil > /var/lib/pgsql/anvil.out"
su - postgres -c "pg_dump --schema-only anvil > /var/lib/pgsql/anvil_schema.out"
@ -934,7 +934,7 @@ OS10(conf-vlt-1)# discovery-interface ethernet 1/1/25-1/1/26
# Configure the same MAC address to the VLT on both switches:
OS10# configure terminal
OS10(config)# vlt-domain 1
OS10(conf-vlt-1)# vlt-mac 00:00:00:00:00:02
OS10(conf-vlt-1)# vlt-mac 00:00:00:00:00:02 # Set once per VLT domain, not per switch
OS10# show vlt 1 mismatch
(If no issues, VLT is OK)
@ -958,7 +958,7 @@ OS10(config)# write memory
OS10(config)# hostname zo-switch01
zo-switch01(config)# interface vlan 100
zo-switch01(conf-if-vl-100)# description BCN1
zo-switch01(conf-if-vl-100)# interface range ethernet 1/1/1-1/1/10
zo-switch01(conf-if-vl-100)# interface range ethernet 1/1/1-1/1/14
zo-switch01(conf-range-eth1/1/1-1/1/10)# switchport access vlan 100
zo-switch01(conf-range-eth1/1/1-1/1/10)# no shutdown
zo-switch01(conf-range-eth1/1/1-1/1/10)# exit

@ -1260,6 +1260,35 @@ sub migrate_server
target_host => $target,
}});
# If there is a '<target>.mnX' (migration network X) entry that can be resolved, we'll change the
# target to use that. This is a dedicated, usually back-to-back network used in nodes specifically
# for migration.
my $test_target = $target;
$test_target =~ s/\..*$//;
$test_target .= "mn1"; # Might want to make this a loop to support MN2+ later
my $test_ip = $anvil->Convert->host_name_to_ip({debug => 2, host_name => $test_target});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
test_target => $test_target,
test_ip => $test_ip,
}});
if ($test_ip)
{
# Can we access the peer with this?
my ($access) = $anvil->Remote->test_access({debug => 3, target => $test_ip});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { access => $access }});
# Did we get access?
if ($access)
{
# Yup! Switch the target.
$target = $test_ip;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0663", variables => {
target => $test_target,
ip => $target,
}});
}
}
# Before migrating, make sure the daemons are running on the peer.
check_daemons($anvil, "start");
@ -1422,7 +1451,7 @@ sub migrate_server
debug => 2,
server => $server,
source => $source,
target => $target
target => $target,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { migrated => $migrated }});
}

@ -486,6 +486,11 @@ INSERT INTO
if ($changed)
{
# First time we've fallen under 5%
my $alert_level = "warning";
if ($new_mount_point eq "<swap>")
{
$alert_level = "notice";
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0002", variables => $variables});
$anvil->Alert->register({
alert_level => "warning",
@ -537,10 +542,15 @@ INSERT INTO
}});
if (($very_low_changed) or ($low_changed))
{
# Clear the alert
# Clear the alert. If this is swap, make it a notice level alert.
my $alert_level = $very_low_changed ? "warning" : "notice";
if ($new_mount_point eq "<swap>")
{
$alert_level = "notice";
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0004", variables => $variables});
$anvil->Alert->register({
alert_level => $very_low_changed ? "warning" : "notice",
alert_level => $alert_level,
clear_alert => 1,
message => "scan_filesystem_alert_0004",
sort_position => 1,

@ -632,6 +632,11 @@ sub find_changes
my $new_scan_hardware_memory_free = $anvil->data->{summary}{ram}{proc}{memory_free};
my $new_scan_hardware_swap_total = $anvil->data->{summary}{ram}{proc}{swap_total};
my $new_scan_hardware_swap_free = $anvil->data->{summary}{ram}{proc}{swap_free};
my $new_scan_hardware_swap_used = $new_scan_hardware_swap_total - $new_scan_hardware_swap_free;
my $new_swap_used_percentage = $anvil->Convert->round({
number => (($new_scan_hardware_swap_used / $new_scan_hardware_swap_total) * 100),
places => 0,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"new_scan_hardware_cpu_model" => $new_scan_hardware_cpu_model,
"new_scan_hardware_cpu_cores" => $new_scan_hardware_cpu_cores,
@ -646,6 +651,8 @@ sub find_changes
"new_scan_hardware_memory_free" => $anvil->Convert->add_commas({number => $new_scan_hardware_memory_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_free}).")",
"new_scan_hardware_swap_total" => $anvil->Convert->add_commas({number => $new_scan_hardware_swap_total})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total}).")",
"new_scan_hardware_swap_free" => $anvil->Convert->add_commas({number => $new_scan_hardware_swap_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_free}).")",
"new_scan_hardware_swap_used" => $anvil->Convert->add_commas({number => $new_scan_hardware_swap_used})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_used}).")",
"new_swap_used_percentage" => $new_swap_used_percentage."%",
}});
# The LED status needs to be translated.
@ -901,12 +908,16 @@ sub find_changes
if ($new_scan_hardware_memory_free ne $old_scan_hardware_memory_free)
{
# This always changes, so it's an info-level alert
$update = 1;
$update = 1;
my $say_new_scan_hardware_memory_free = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_free})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_memory_free})." #!string!scan_hardware_unit_0001!#)";
my $say_old_scan_hardware_memory_free = $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_memory_free})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_memory_free})." #!string!scan_hardware_unit_0001!#)";
$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "info", message => "scan_hardware_alert_0018,!!new!".$say_new_scan_hardware_memory_free."!!,!!old!".$say_old_scan_hardware_memory_free."!!"});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_hardware_alert_0018", variables => { new => $say_new_scan_hardware_memory_free, old => $say_old_scan_hardware_memory_free}});
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
new_scan_hardware_swap_free => $new_scan_hardware_swap_free,
old_scan_hardware_swap_free => $old_scan_hardware_swap_free,
}});
if ($new_scan_hardware_swap_free ne $old_scan_hardware_swap_free)
{
$update = 1;
@ -932,7 +943,11 @@ sub find_changes
# Check if swap has gone over the high threshold or dropped below the clear
# threashold.
if ($new_scan_hardware_swap_free > $anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold})
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
new_scan_hardware_swap_free => $new_scan_hardware_swap_free,
"scancore::scan-hardware::swap::high_threshold" => $anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold},
}});
if ($new_swap_used_percentage > $anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold})
{
# It's high
my $changed = $anvil->Alert->check_alert_sent({
@ -952,10 +967,10 @@ sub find_changes
swap_percent => $new_swap_percent_used,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 0, level => 1, key => "scan_hardware_alert_0020", variables => $variables});
$anvil->Alert->register({alert_level => "warning", message => "scan_hardware_alert_0020", variables => $variables, set_by => $THIS_FILE });
$anvil->Alert->register({alert_level => "notice", message => "scan_hardware_alert_0020", variables => $variables, set_by => $THIS_FILE });
}
}
elsif ($new_scan_hardware_swap_free < $anvil->data->{scancore}{'scan-hardware'}{swap}{clear_threshold})
elsif ($new_swap_used_percentage < $anvil->data->{scancore}{'scan-hardware'}{swap}{clear_threshold})
{
# It's low
my $changed = $anvil->Alert->check_alert_sent({
@ -976,7 +991,7 @@ sub find_changes
swap_percent => $new_swap_percent_used,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 0, level => 1, key => "scan_hardware_alert_0021", variables => $variables});
$anvil->Alert->register({alert_level => "warning", message => "scan_hardware_alert_0021", variables => $variables, set_by => $THIS_FILE });
$anvil->Alert->register({alert_level => "notice", message => "scan_hardware_alert_0021", variables => $variables, set_by => $THIS_FILE });
}
}
}

@ -82,7 +82,10 @@ find_changes($anvil);
# Finally, process health weights.
process_health($anvil);
# Shut down.
# This clears the TX and RX variable data for interfaces older than 'scancore::database::age_out'.
clear_old_variables($anvil);
# Shut down.
$anvil->ScanCore->agent_shutdown({agent => $THIS_FILE});
@ -90,6 +93,136 @@ $anvil->ScanCore->agent_shutdown({agent => $THIS_FILE});
# Functions #
#############################################################################################################
# This clears the TX and RX variable data for interfaces older than 'scancore::database::age_out'.
sub clear_old_variables
{
my ($anvil) = @_;
# Only Strikers run this.
my $host_type = $anvil->Get->host_type();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }});
if ($host_type ne "striker")
{
return(0);
}
# Read in all interfaces and for each, delete historical records over the age-out time.
my $age = $anvil->data->{scancore}{database}{age_out};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { age => $age }});
if ($age =~ /\D/)
{
# Age is not valid, set it to defaults.
$age = 24;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { age => $age }});
}
# Get the timestamp to delete thermal and power records older than $age hours.
my $query = "SELECT now() - '".$age."h'::interval;";
my $old_timestamp = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
query => $query,
old_timestamp => $old_timestamp,
}});
# Read in all interface RX and TX variables.
foreach my $uuid (keys %{$anvil->data->{cache}{database_handle}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
uuid => $uuid,
db_host => $anvil->Get->host_name_from_uuid({host_uuid => $uuid}),
}});
my $queries = [];
$query = "
SELECT
variable_uuid,
variable_name
FROM
variables
WHERE
variable_name LIKE '%::tx_bytes'
OR
variable_name LIKE '%::rx_bytes'
;";
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
my $variable_uuid = $row->[0];
my $variable_name = $row->[1];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:variable_name' => $variable_name,
's2:variable_uuid' => $variable_uuid,
}});
# Find out of there are any records to remove at all.
my $query = "SELECT history_id FROM history.variables WHERE variable_uuid = ".$anvil->Database->quote($variable_uuid)." AND modified_date <= '".$old_timestamp."';";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
my $results = $anvil->Database->query({uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
results => $results,
count => $count,
}});
if ($count)
{
# Find how many records will be left. If it's 0, we'll use an OFFSET 1.
my $query = "SELECT history_id FROM history.variables WHERE variable_uuid = ".$anvil->Database->quote($variable_uuid)." AND modified_date > '".$old_timestamp."';";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
my $results = $anvil->Database->query({uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
results => $results,
count => $count,
}});
if ($count)
{
# At least one record will be left, we can do a simple delete.
my $query = "DELETE FROM history.variables WHERE variable_uuid = ".$anvil->Database->quote($variable_uuid)." AND modified_date <= '".$old_timestamp."';";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
push @{$queries}, $query;
}
else
{
# This would delete everything, reserve at least one record.
foreach my $row (@{$results})
{
my $history_id = $row->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { history_id => $history_id }});
my $query = "DELETE FROM history.variables WHERE variable_uuid = ".$anvil->Database->quote($variable_uuid)." AND history_id = '".$history_id."';";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
push @{$queries}, $query;
}
}
}
}
my $commits = @{$queries};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { commits => $commits }});
if ($commits)
{
# Commit the DELETEs.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_network_log_0001", variables => {
age => $age,
records => $commits,
host => $anvil->Get->host_name_from_uuid({host_uuid => $uuid}),
}});
$anvil->Database->write({debug => 2, uuid => $uuid, query => $queries, source => $THIS_FILE, line => __LINE__});
undef $queries;
}
}
return(0);
}
# This reads in all of the network data
sub collect_data
{
@ -177,7 +310,6 @@ sub collect_data
{
# 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?
@ -188,14 +320,7 @@ sub collect_data
my $tx_bytes = 0; # How many bytes transmitted
my $rx_bytes = 0; # How many bytes received
# 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$//;
@ -203,7 +328,6 @@ sub collect_data
$speed =~ s/\n$//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
interface => $interface,
mac_address => $mac_address,
link_state => $link_state,
mtu => $mtu,
duplex => $duplex,
@ -211,6 +335,37 @@ sub collect_data
speed => $speed,
}});
# The MAC address can faked by a number of ways, so we make an explicit call to 'ethtool' to get the permanent mac address.
my $mac_address = "";
my $shell_call = $anvil->data->{path}{exe}{ethtool}." -P ".$interface;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
if ($output =~ /(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)$/)
{
$mac_address = lc($1);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }});
}
else
{
# Get it by reading the address file.
if (-e $full_path."/bonding_slave/perm_hwaddr")
{
$mac_address = $anvil->Storage->read_file({file => $full_path."/bonding_slave/perm_hwaddr"});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }});
}
elsif (-e $full_path."/address")
{
$mac_address = $anvil->Storage->read_file({file => $full_path."/address"});
$mac_address =~ s/\n//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }});
}
}
# These are variables that will be needed if this is a bond interface.
my $ip_address = "";
my $subnet_mask = "";
@ -402,7 +557,7 @@ sub collect_data
}
# Find the media, if possible.
my ($ethtool, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{ethtool}." $interface"});
(my $ethtool, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{ethtool}." $interface"});
foreach my $line (split/\n/, $ethtool)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
@ -3156,6 +3311,7 @@ AND
source_name => $source_name,
}});
### TODO: Don't set / clear interfaces that appear down but aren't named ifn/bcn/sn as they're probably unconfigured/unusued interfaces.
my $problem = 0;
if ((not $new_link_state) or ($new_operational eq "down") or ($new_duplex ne "full"))
{

@ -214,9 +214,6 @@ Prerequisites:
This mode is NOT supported by the Anvil! Intelligent Availability™ platform!
</key>
<key name="scan_network_log_0001">Aging out RX and TX data under: [#!variable!records!#] interfaces. These have 1 or more historical records older than: [#!variable!age!#] hours old from the database host: [#!variable!host!#].</key>
</language>
</words>

@ -1267,6 +1267,28 @@ It will take time for it to initialize, please be patient.</key>
<key name="job_0383">The job has been recorded with the UUID: [#!variable!job_uuid!#], it will start in just a moment if anvil-daemon is running.</key>
<key name="job_0384">Manage DR tasks for a given server</key>
<key name="job_0385">This job can protect, remove (unprotect), connect, disconnect or update (connect, sync, disconnect) a given server.</key>
<key name="job_0386">
Do you want to connect the DR host for the server: [#!variable!server!#]?
Note: Depending on the disk write load and storage network speed to the DR host,
this could cause reduced disk write performance.</key>
<key name="job_0387">About to connect the DR resource for the server: [#!variable!server!#].</key>
<key name="job_0388">Brought up the connection locally. Now checking that the resource is up on the nodes.</key>
<key name="job_0389">Making sure the resource is up on: [#!variable!host_name!#].</key>
<key name="job_0390">Waiting now for the our resource to connect.</key>
<key name="job_0391">Done! The server: [#!variable!server!#] is now connected.</key>
<key name="job_0392">
Do you want to disconnect the DR host for the server: [#!variable!server!#]?
Note: Once down, no further changes will be written to the DR host.</key>
<key name="job_0393">About to disconnect the DR resource for the server: [#!variable!server!#].</key>
<key name="job_0394">Done! The server: [#!variable!server!#] is now disconnected.</key>
<key name="job_0395">
Do you want to update the DR host for the server: [#!variable!server!#]?
Note: This will connect the DR host until the disk(s) on DR are (all) UpToDate.
Depending on the disk write load and storage network speed to the DR host,
this could cause reduced disk write performance.</key>
<key name="job_0396">Still sync'ing from: [#!variable!sync_source!#] at a rate of: [#!variable!sync_speed!#/sec]. Estimated time remaining is: [#!variable!time_to_sync!#].</key>
<key name="job_0397">Sync'ed! Bringing the resource back down now.</key>
<key name="job_0398">Waiting for the connection to come up...</key>
<!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key>
@ -2031,6 +2053,7 @@ The file: [#!variable!file!#] needs to be updated. The difference is:
<key name="log_0660">Stopped the postgresql daemon as a peer is currently primary.</key>
<key name="log_0661">Our most recent database dump is newer than any from our peers. As such, we'll just start the database without a load.</key>
<key name="log_0662">Retrying to connect to the database.</key>
<key name="log_0663">The target can be reached on the dedicated migration network: [#!variable!target!#] via the IP address: [#!variable!ip!#], switching to use that for the RAM copy.</key>
<!-- Messages for users (less technical than log entries), though sometimes used for logs, too. -->
<key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key>

File diff suppressed because it is too large Load Diff

@ -44,6 +44,7 @@ $anvil->data->{switches}{uuid} = "";
$anvil->data->{switches}{ram} = "";
$anvil->data->{switches}{'storage-group'} = "";
$anvil->data->{switches}{'storage-size'} = "";
$anvil->data->{switches}{'use-image'} = "";
$anvil->Get->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
@ -487,7 +488,7 @@ sub provision_server
$shell_call .= " --network bridge=ifn1_bridge1".$nic_model." \\\n";
$shell_call .= " --graphics vnc \\\n";
$shell_call .= " --sound ich9 \\\n";
$shell_call .= " --clock offset=".$clock_offset." \\\n"; # We may want to support ',rtc_tickpolicy=catchup'
$shell_call .= " --clock offset=".$clock_offset.",rtc_tickpolicy=catchup \\\n";
$shell_call .= " --boot menu=on \\\n";
$shell_call .= " --disk path=/dev/drbd/by-res/".$server."/0".$disk_bus.",driver.io=threads,cache=writeback,driver.discard=unmap,boot.order=1 \\\n";
$shell_call .= " --disk path=".$anvil->data->{job}{install_iso_path}.",device=cdrom,shareable=on,boot.order=2 \\\n";

Loading…
Cancel
Save