Merge branch 'main' into dependabot/npm_and_yarn/striker-ui/json5-1.0.2

main
Digimer 2 years ago committed by GitHub
commit 6225bb7f67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      Anvil/Tools.pm
  2. 218
      Anvil/Tools/Cluster.pm
  3. 25
      Anvil/Tools/Convert.pm
  4. 56
      Anvil/Tools/DRBD.pm
  5. 383
      Anvil/Tools/Database.pm
  6. 300
      Anvil/Tools/Get.pm
  7. 20
      Anvil/Tools/ScanCore.pm
  8. 83
      Anvil/Tools/Server.pm
  9. 12
      Anvil/Tools/Storage.pm
  10. 63
      Anvil/Tools/System.pm
  11. 4
      anvil.conf
  12. 186
      cgi-bin/striker
  13. 7
      html/skins/alteeve/anvil.html
  14. 6
      notes
  15. 37
      ocf/alteeve/server
  16. 78
      scancore-agents/scan-apc-pdu/scan-apc-pdu
  17. 1
      scancore-agents/scan-apc-pdu/scan-apc-pdu.xml
  18. 2
      scancore-agents/scan-cluster/scan-cluster.sql
  19. 1
      scancore-agents/scan-drbd/scan-drbd
  20. 4
      scancore-agents/scan-network/scan-network
  21. 72
      scancore-agents/scan-server/scan-server
  22. 3
      scancore-agents/scan-server/scan-server.xml
  23. 53
      share/anvil.sql
  24. 842
      share/words.xml
  25. 4
      tools/Makefile.am
  26. 1
      tools/anvil-boot-server
  27. 27
      tools/anvil-configure-host
  28. 193
      tools/anvil-daemon
  29. 6
      tools/anvil-join-anvil
  30. 2
      tools/anvil-manage-files
  31. 19
      tools/anvil-manage-server
  32. 563
      tools/anvil-provision-server
  33. 56
      tools/anvil-report-usage
  34. 411
      tools/anvil-version-changes
  35. 296
      tools/fence_pacemaker
  36. 2
      tools/striker-get-peer-data
  37. 652
      tools/unfence_pacemaker

@ -925,7 +925,7 @@ sub _set_defaults
# This is the list of OSes short in the user's short list of OS types to # This is the list of OSes short in the user's short list of OS types to
# optimize for. The full the list is available by running: # optimize for. The full the list is available by running:
# /usr/bin/osinfo-query os # /usr/bin/osinfo-query os
os_short_list => "rhel5.11, rhel6.10, rhel7.9, rhel8.3, win10, win2k16, win2k19", os_short_list => "rhel8.7,rhel9.1,win10,win2k19,win2k22",
}, },
terminal => { terminal => {
columns => 80, columns => 80,
@ -1056,6 +1056,7 @@ sub _set_paths
'httpd.conf' => "/etc/httpd/conf/httpd.conf", 'httpd.conf' => "/etc/httpd/conf/httpd.conf",
'journald_anvil' => "/etc/systemd/journald.conf.d/anvil.conf", 'journald_anvil' => "/etc/systemd/journald.conf.d/anvil.conf",
'journald.conf' => "/etc/systemd/journald.conf", 'journald.conf' => "/etc/systemd/journald.conf",
'logind.conf' => "/etc/systemd/logind.conf",
'lvm.conf' => "/etc/lvm/lvm.conf", 'lvm.conf' => "/etc/lvm/lvm.conf",
'pg_hba.conf' => "/var/lib/pgsql/data/pg_hba.conf", 'pg_hba.conf' => "/var/lib/pgsql/data/pg_hba.conf",
'postgresql.conf' => "/var/lib/pgsql/data/postgresql.conf", 'postgresql.conf' => "/var/lib/pgsql/data/postgresql.conf",
@ -1074,10 +1075,11 @@ sub _set_paths
'.htpasswd' => "/etc/httpd/.htpasswd", '.htpasswd' => "/etc/httpd/.htpasswd",
'chrony.conf' => "/etc/chrony.conf", 'chrony.conf' => "/etc/chrony.conf",
group => "/etc/group", group => "/etc/group",
issue => "/etc/issue",
httpd_conf => "/etc/httpd/conf/httpd.conf", httpd_conf => "/etc/httpd/conf/httpd.conf",
host_configured => "/etc/anvil/host.configured",
host_ssh_key => "/etc/ssh/ssh_host_ecdsa_key.pub", host_ssh_key => "/etc/ssh/ssh_host_ecdsa_key.pub",
host_uuid => "/etc/anvil/host.uuid", host_uuid => "/etc/anvil/host.uuid",
issue => "/etc/issue",
network_cache => "/tmp/network_cache.anvil", network_cache => "/tmp/network_cache.anvil",
passwd => "/etc/passwd", passwd => "/etc/passwd",
'redhat-release' => "/etc/redhat-release", 'redhat-release' => "/etc/redhat-release",
@ -1151,6 +1153,7 @@ sub _set_paths
'anvil-update-files' => "/usr/sbin/anvil-update-files", 'anvil-update-files' => "/usr/sbin/anvil-update-files",
'anvil-update-states' => "/usr/sbin/anvil-update-states", 'anvil-update-states' => "/usr/sbin/anvil-update-states",
'anvil-update-system' => "/usr/sbin/anvil-update-system", 'anvil-update-system' => "/usr/sbin/anvil-update-system",
'anvil-version-changes' => "/usr/sbin/anvil-version-changes",
augtool => "/usr/bin/augtool", augtool => "/usr/bin/augtool",
base64 => "/usr/bin/base64", base64 => "/usr/bin/base64",
blockdev => "/usr/sbin/blockdev", blockdev => "/usr/sbin/blockdev",
@ -1167,6 +1170,7 @@ sub _set_paths
createdb => "/usr/bin/createdb", createdb => "/usr/bin/createdb",
createrepo_c => "/usr/bin/createrepo_c", createrepo_c => "/usr/bin/createrepo_c",
createuser => "/usr/bin/createuser", createuser => "/usr/bin/createuser",
crm_attribute => "/usr/sbin/crm_attribute",
crm_error => "/usr/sbin/crm_error", crm_error => "/usr/sbin/crm_error",
crm_resource => "/usr/sbin/crm_resource", crm_resource => "/usr/sbin/crm_resource",
crm_mon => "/usr/sbin/crm_mon", crm_mon => "/usr/sbin/crm_mon",
@ -1250,6 +1254,7 @@ sub _set_paths
rpm => "/usr/bin/rpm", rpm => "/usr/bin/rpm",
rsync => "/usr/bin/rsync", rsync => "/usr/bin/rsync",
sed => "/usr/bin/sed", sed => "/usr/bin/sed",
setsid => "/usr/bin/setsid", # See: https://serverfault.com/questions/1105733/virsh-command-hangs-when-script-runs-in-the-background
'shutdown' => "/usr/sbin/shutdown", 'shutdown' => "/usr/sbin/shutdown",
snmpget => "/usr/bin/snmpget", snmpget => "/usr/bin/snmpget",
snmpset => "/usr/bin/snmpset", snmpset => "/usr/bin/snmpset",
@ -1280,6 +1285,7 @@ sub _set_paths
tput => "/usr/bin/tput", tput => "/usr/bin/tput",
'tr' => "/usr/bin/tr", 'tr' => "/usr/bin/tr",
uname => "/usr/bin/uname", uname => "/usr/bin/uname",
unfence_pacemaker => "/usr/sbin/unfence_pacemaker",
unzip => "/usr/bin/unzip", unzip => "/usr/bin/unzip",
useradd => "/usr/sbin/useradd", useradd => "/usr/sbin/useradd",
usermod => "/usr/sbin/usermod", usermod => "/usr/sbin/usermod",

@ -8,6 +8,7 @@ use warnings;
use Data::Dumper; use Data::Dumper;
use Scalar::Util qw(weaken isweak); use Scalar::Util qw(weaken isweak);
use String::ShellQuote; use String::ShellQuote;
use Text::Diff;
use XML::LibXML; use XML::LibXML;
use XML::Simple qw(:strict); use XML::Simple qw(:strict);
@ -21,6 +22,7 @@ my $THIS_FILE = "Cluster.pm";
# check_node_status # check_node_status
# check_server_constraints # check_server_constraints
# check_stonith_config # check_stonith_config
# configure_logind
# delete_server # delete_server
# get_fence_methods # get_fence_methods
# get_anvil_name # get_anvil_name
@ -646,6 +648,8 @@ sub boot_server
} }
} }
### TODO: If we don't have a node, pick the node with the most VMs already running (by total RAM
### count)
if ($node) if ($node)
{ {
$anvil->Cluster->_set_server_constraint({ $anvil->Cluster->_set_server_constraint({
@ -654,6 +658,8 @@ sub boot_server
}); });
} }
### TODO: Make sure that the drbd fence rule exists in pacemaker and add it, if not.
# Now boot the server. # Now boot the server.
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $anvil->data->{path}{exe}{pcs}." resource enable ".$server}); my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $anvil->data->{path}{exe}{pcs}." resource enable ".$server});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
@ -1622,6 +1628,109 @@ sub check_stonith_config
} }
=head2 configure_logind
This configures logind to ensure it doesn't try to do a graceful shutdown when being fenced via acpid power-button events.
See: https://access.redhat.com/solutions/1578823
This method takes no parameters
=cut
sub configure_logind
{
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 => "Cluster->configure_logind()" }});
# Only run this on nodes.
my $host_type = $anvil->Get->host_type({debug => $debug});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_type => $host_type }});
if ($host_type ne "node")
{
return(0);
}
# Read in the file.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
'path::configs::logind.conf' => $anvil->data->{path}{configs}{'logind.conf'},
}});
if (not -e $anvil->data->{path}{configs}{'logind.conf'})
{
# wtf?
return(0);
}
my $added = 0;
my $new_body = "";
my $old_body = $anvil->Storage->read_file({debug => $debug, file => $anvil->data->{path}{configs}{'logind.conf'}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { old_body => $old_body }});
if ($old_body eq "!!error!!")
{
return(0);
}
# If we don't see 'HandlePowerKey=ignore', we need to add it.
foreach my $line (split/\n/, $old_body)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { old_body => $old_body }});
$new_body .= $line."\n";
if ($line =~ /^HandlePowerKey=(.*)$/)
{
# It's been set. No matter how it's set, we don't change it again.
my $set_to = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { set_to => $set_to }});
return(0);
}
if ($line =~ /^#HandlePowerKey=/)
{
# Add line under the commented out one.
$new_body .= "HandlePowerKey=ignore\n";
$added = 1;
}
}
if (not $added)
{
# Append it.
$new_body .= "HandlePowerKey=ignore\n";
$added = 1;
}
# Still here? We almost certainly want to save then, but lets look for a difference just the same.
my $difference = diff \$old_body, \$new_body, { STYLE => 'Unified' };
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
added => $added,
difference => $difference,
}});
if ($added)
{
# Write it out.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0732"});
$anvil->Storage->write_file({
file => $anvil->data->{path}{configs}{'logind.conf'},
body => $new_body,
backup => 1,
overwrite => 1,
});
sleep 1;
# Restart the daemon.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0733", variables => { daemon => "systemd-logind.service" }});
$anvil->System->restart_daemon({
debug => $debug,
daemon => "systemd-logind.service",
});
}
return(0);
}
=head2 delete_server =head2 delete_server
This takes a server (resource) name and deletes it from pacemaker. If there is a problem, C<< !!error!! >> is returned. Otherwise, C<< 0 >> is removed either once the resource is deleted, or if the resource didn't exist in the first place. This takes a server (resource) name and deletes it from pacemaker. If there is a problem, C<< !!error!! >> is returned. Otherwise, C<< 0 >> is removed either once the resource is deleted, or if the resource didn't exist in the first place.
@ -3079,6 +3188,7 @@ sub parse_cib
foreach my $node ($dom->findnodes('/cib/configuration/nodes/node')) foreach my $node ($dom->findnodes('/cib/configuration/nodes/node'))
{ {
my $node_id = $node->{id}; my $node_id = $node->{id};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { node_id => $node_id }});
foreach my $variable (sort {$a cmp $b} keys %{$node}) foreach my $variable (sort {$a cmp $b} keys %{$node})
{ {
next if $variable eq "id"; next if $variable eq "id";
@ -3111,6 +3221,7 @@ sub parse_cib
foreach my $instance_attributes ($node->findnodes('./instance_attributes')) foreach my $instance_attributes ($node->findnodes('./instance_attributes'))
{ {
my $instance_attributes_id = $instance_attributes->{id}; my $instance_attributes_id = $instance_attributes->{id};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { instance_attributes_id => $instance_attributes_id }});
foreach my $nvpair ($instance_attributes->findnodes('./nvpair')) foreach my $nvpair ($instance_attributes->findnodes('./nvpair'))
{ {
my $id = $nvpair->{id}; my $id = $nvpair->{id};
@ -3180,14 +3291,37 @@ sub parse_cib
foreach my $constraint ($dom->findnodes('/cib/configuration/constraints/rsc_location')) foreach my $constraint ($dom->findnodes('/cib/configuration/constraints/rsc_location'))
{ {
my $id = $constraint->{id}; my $id = $constraint->{id};
$anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{node} = $constraint->{node}; $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{node} = $constraint->{node} ? $constraint->{node} : "";
$anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{resource} = $constraint->{rsc}; $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{resource} = $constraint->{rsc};
$anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{score} = $constraint->{score}; $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{score} = $constraint->{score} ? $constraint->{score} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"cib::parsed::configuration::constraints::location::${id}::node" => $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{node}, "cib::parsed::configuration::constraints::location::${id}::node" => $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{node},
"cib::parsed::configuration::constraints::location::${id}::resource" => $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{resource}, "cib::parsed::configuration::constraints::location::${id}::resource" => $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{resource},
"cib::parsed::configuration::constraints::location::${id}::score" => $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{score}, "cib::parsed::configuration::constraints::location::${id}::score" => $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{score},
}}); }});
# If there's no 'node', this is probably a drbd fence constraint.
if (not $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{node})
{
foreach my $rule_id ($constraint->findnodes('./rule'))
{
my $constraint_id = $rule_id->{id};
$anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint}{$constraint_id}{score} = $rule_id->{score};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"cib::parsed::configuration::constraints::location::${id}::constraint::${constraint_id}::score" => $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint}{$constraint_id}{score},
}});
foreach my $expression_id ($rule_id->findnodes('./expression'))
{
my $attribute = $expression_id->{attribute};
$anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint}{$constraint_id}{attribute}{$attribute}{operation} = $expression_id->{operation};
$anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint}{$constraint_id}{attribute}{$attribute}{value} = $expression_id->{value};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"cib::parsed::configuration::constraints::location::${id}::constraint::${constraint_id}::attribute::${attribute}::operation" => $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint}{$constraint_id}{attribute}{$attribute}{operation},
"cib::parsed::configuration::constraints::location::${id}::constraint::${constraint_id}::attribute::${attribute}::value" => $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint}{$constraint_id}{attribute}{$attribute}{value},
}});
}
}
}
} }
foreach my $node_state ($dom->findnodes('/cib/status/node_state')) foreach my $node_state ($dom->findnodes('/cib/status/node_state'))
{ {
@ -3529,17 +3663,17 @@ sub parse_cib
foreach my $lrm_resource_id (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}}) foreach my $lrm_resource_id (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}})
{ {
my $lrm_resource_operations_count = keys %{$anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}{$lrm_resource_id}{lrm_rsc_op_id}}; my $lrm_resource_operations_count = keys %{$anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}{$lrm_resource_id}{lrm_rsc_op_id}};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { lrm_resource_operations_count => $lrm_resource_operations_count }});
foreach my $lrm_rsc_op_id (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}{$lrm_resource_id}{lrm_rsc_op_id}}) foreach my $lrm_rsc_op_id (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}{$lrm_resource_id}{lrm_rsc_op_id}})
{ {
my $type = $anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}{$lrm_resource_id}{type}; my $type = $anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}{$lrm_resource_id}{type};
my $class = $anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}{$lrm_resource_id}{class}; my $class = $anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}{$lrm_resource_id}{class};
my $operation = $anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}{$lrm_resource_id}{lrm_rsc_op_id}{$lrm_rsc_op_id}{operation}; my $operation = $anvil->data->{cib}{parsed}{cib}{status}{node_state}{$id}{lrm_id}{$lrm_id}{lrm_resource}{$lrm_resource_id}{lrm_rsc_op_id}{$lrm_rsc_op_id}{operation};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
lrm_resource_operations_count => $lrm_resource_operations_count, 's1:lrm_rsc_op_id' => $lrm_rsc_op_id,
type => $type, 's2:type' => $type,
class => $class, 's3:class' => $class,
operation => $operation, 's4:operation' => $operation,
lrm_rsc_op_id => $lrm_rsc_op_id,
}}); }});
# Skip unless it's a server. # Skip unless it's a server.
@ -3569,6 +3703,76 @@ sub parse_cib
"cib::parsed::data::server::${lrm_resource_id}::role" => $anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{role}, "cib::parsed::data::server::${lrm_resource_id}::role" => $anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{role},
}}); }});
} }
# Do we have a DRBD fence rule?
$anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{'exists'} = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"cib::parsed::data::server::${lrm_resource_id}::drbd_fence_rule::exists" => $anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{'exists'},
}});
foreach my $id (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{configuration}{constraints}{location}})
{
my $node = $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{node};
my $resource = $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{resource};
my $score = $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{score};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:id" => $id,
"s2:node" => $node,
"s3:resource" => $resource,
"s4:score" => $score,
}});
# Is this the server?
next if $resource ne $lrm_resource_id;
next if not exists $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint};
foreach my $constraint_id (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint}})
{
my $score = $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint}{$constraint_id}{score};
foreach my $attribute (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint}{$constraint_id}{attribute}})
{
my $operation = $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint}{$constraint_id}{attribute}{$attribute}{operation};
my $value = $anvil->data->{cib}{parsed}{configuration}{constraints}{location}{$id}{constraint}{$constraint_id}{attribute}{$attribute}{value};
my $test_key = "location-".$resource."-rule";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:constraint_id' => $constraint_id,
's2:score' => $score,
's3:attribute' => $attribute,
's4:operation' => $operation,
's5:value' => $value,
's6:test_key' => $test_key,
}});
if ($constraint_id eq $test_key)
{
$anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{'exists'} = 1;
$anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{attribute} = $attribute;
$anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{operation} = $operation;
$anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{value} = $value;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:cib::parsed::data::server::${lrm_resource_id}::drbd_fence_rule::exists" => $anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{'exists'},
"s2:cib::parsed::data::server::${lrm_resource_id}::drbd_fence_rule::attribute" => $anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{attribute},
"s3:cib::parsed::data::server::${lrm_resource_id}::drbd_fence_rule::operation" => $anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{operation},
"s4:cib::parsed::data::server::${lrm_resource_id}::drbd_fence_rule::value" => $anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{value},
}});
# Is this refereneced by any node attributes?
foreach my $node_id (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{cib}{node_state}})
{
my $node_name = $anvil->data->{cib}{parsed}{configuration}{nodes}{$node_id}{uname};
my $value = defined $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{$attribute} ? $anvil->data->{cib}{parsed}{cib}{node_state}{$node_id}{$attribute} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:node_id" => $node_id,
"s2:node_name" => $node_name,
"s3:value" => $value,
}});
}
}
}
last if $anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{'exists'};
}
# Did we find it?
last if $anvil->data->{cib}{parsed}{data}{server}{$lrm_resource_id}{drbd_fence_rule}{'exists'};
}
} }
} }
} }

@ -238,9 +238,26 @@ sub bytes_to_human_readable
# Die if either the 'time' or 'float' has a non-digit character in it. # Die if either the 'time' or 'float' has a non-digit character in it.
if ($human_readable_size =~ /\D/) if ($human_readable_size =~ /\D/)
{ {
# See if this is already human readable.
my $bytes = $anvil->Convert->human_readable_to_bytes({
debug => $debug,
size => $human_readable_size,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'bytes' => $bytes }});
if ($bytes =~ /^\d+$/)
{
# This is fine, convert to our standard size and return.
my $new_human_readable_size = $anvil->Convert->bytes_to_human_readable({
debug => 2,
'bytes' => $bytes,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { new_human_readable_size => $new_human_readable_size }});
return($new_human_readable_size);
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0116", variables => { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0116", variables => {
method => "Convert->bytes_to_human_readable()", method => "Convert->bytes_to_human_readable()",
parameter => "hostnmae", parameter => "bytes",
value => $human_readable_size, value => $human_readable_size,
}}); }});
return ("!!error!!"); return ("!!error!!");
@ -982,6 +999,12 @@ sub human_readable_to_bytes
type => $type, type => $type,
}}); }});
# If we were passed nothing, return nothing.
if ($size eq "")
{
return("");
}
# Start cleaning up the variables. # Start cleaning up the variables.
my $value = $size; my $value = $size;
$size =~ s/ //g; $size =~ s/ //g;

@ -1396,9 +1396,9 @@ SELECT
FROM FROM
scan_drbd scan_drbd
WHERE WHERE
scan_drbd_host_uuid = '618e8007-3a0b-4bbf-a616-a64fd7d2dc30' scan_drbd_host_uuid = ".$anvil->Database->quote($node1_host_uuid)."
OR OR
scan_drbd_host_uuid = '75070e21-a0e3-4ba5-b4f7-476bf5d08107' scan_drbd_host_uuid = ".$anvil->Database->quote($node2_host_uuid)."
ORDER BY modified_date DESC ORDER BY modified_date DESC
LIMIT 1 LIMIT 1
;"; ;";
@ -1547,9 +1547,9 @@ LIMIT 1
### TODO: Handle external metadata ### TODO: Handle external metadata
my $this_host = $host_href->{name}; my $this_host = $host_href->{name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
this_host => $this_host, this_host => $this_host,
'$anvil->Get->host_name' => $anvil->Get->host_name, 'Get->host_name' => $anvil->Get->host_name,
'$anvil->Get->short_host_name' => $anvil->Get->short_host_name, 'Get->short_host_name' => $anvil->Get->short_host_name,
}}); }});
if (($this_host eq $anvil->Get->host_name) or ($this_host eq $anvil->Get->short_host_name)) if (($this_host eq $anvil->Get->host_name) or ($this_host eq $anvil->Get->short_host_name))
{ {
@ -2755,6 +2755,7 @@ sub update_global_common
my $usage_count_seen = 0; my $usage_count_seen = 0;
my $udev_always_use_vnr_seen = 0; my $udev_always_use_vnr_seen = 0;
my $fence_peer_seen = 0; my $fence_peer_seen = 0;
my $unfence_peer_seen = 0;
my $auto_promote_seen = 0; my $auto_promote_seen = 0;
my $disk_flushes_seen = 0; my $disk_flushes_seen = 0;
my $md_flushes_seen = 0; my $md_flushes_seen = 0;
@ -2778,6 +2779,7 @@ sub update_global_common
# These values will be used to track where we are in processing the config file and what values are needed. # These values will be used to track where we are in processing the config file and what values are needed.
my $say_usage_count = $usage_count ? "yes" : "no"; my $say_usage_count = $usage_count ? "yes" : "no";
my $say_fence_peer = $anvil->data->{path}{exe}{fence_pacemaker}; my $say_fence_peer = $anvil->data->{path}{exe}{fence_pacemaker};
my $say_unfence_peer = $anvil->data->{path}{exe}{unfence_pacemaker};
my $say_auto_promote = "yes"; my $say_auto_promote = "yes";
my $say_flushes = $use_flushes ? "yes" : "no"; my $say_flushes = $use_flushes ? "yes" : "no";
my $say_allow_two_primaries = "no"; my $say_allow_two_primaries = "no";
@ -2865,6 +2867,17 @@ sub update_global_common
$new_global_common .= $new_line."\n"; $new_global_common .= $new_line."\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0518", variables => { file => $anvil->data->{path}{configs}{'global-common.conf'}, line => $line }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0518", variables => { file => $anvil->data->{path}{configs}{'global-common.conf'}, line => $line }});
} }
if (not $unfence_peer_seen)
{
$update = 1;
my $new_line = "\t\tunfence-peer ".$say_unfence_peer.";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:update' => $update,
's2:new_line' => $new_line,
}});
$new_global_common .= $new_line."\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0518", variables => { file => $anvil->data->{path}{configs}{'global-common.conf'}, line => $line }});
}
} }
elsif ($in_startup) elsif ($in_startup)
{ {
@ -3091,7 +3104,37 @@ sub update_global_common
} }
if ($in_handlers) if ($in_handlers)
{ {
if ($line =~ /(\s*)fence-peer(\s+)(.*?)(;.*)$/) if ($line =~ /(\s*)unfence-peer(\s+)(.*?)(;.*)$/)
{
my $left_space = $1;
my $middle_space = $2;
my $value = $3;
my $right_side = $4;
$unfence_peer_seen = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:left_space' => $left_space,
's2:middle_space' => $middle_space,
's3:value' => $value,
's4:right_side' => $right_side,
's5:unfence_peer_seen' => $unfence_peer_seen,
's6:say_unfence_peer' => $say_unfence_peer,
}});
if ($value ne $say_unfence_peer)
{
$update = 1;
my $new_line = $left_space."unfence-peer".$middle_space.$say_unfence_peer.$right_side;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:update' => $update,
's2:new_line' => $new_line,
}});
$new_global_common .= $new_line.$comment."\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0518", variables => { file => $anvil->data->{path}{configs}{'global-common.conf'}, line => $line }});
next;
}
}
elsif ($line =~ /(\s*)fence-peer(\s+)(.*?)(;.*)$/)
{ {
my $left_space = $1; my $left_space = $1;
my $middle_space = $2; my $middle_space = $2;
@ -3104,6 +3147,7 @@ sub update_global_common
's3:value' => $value, 's3:value' => $value,
's4:right_side' => $right_side, 's4:right_side' => $right_side,
's5:fence_peer_seen' => $fence_peer_seen, 's5:fence_peer_seen' => $fence_peer_seen,
's6:say_fence_peer' => $say_fence_peer,
}}); }});
if ($value ne $say_fence_peer) if ($value ne $say_fence_peer)

@ -29,6 +29,7 @@ my $THIS_FILE = "Database.pm";
# get_alerts # get_alerts
# get_anvils # get_anvils
# get_bridges # get_bridges
# get_dr_links
# get_fences # get_fences
# get_file_locations # get_file_locations
# get_files # get_files
@ -53,6 +54,7 @@ my $THIS_FILE = "Database.pm";
# insert_or_update_anvils # insert_or_update_anvils
# insert_or_update_bridges # insert_or_update_bridges
# insert_or_update_bonds # insert_or_update_bonds
# insert_or_update_dr_links
# insert_or_update_fences # insert_or_update_fences
# insert_or_update_file_locations # insert_or_update_file_locations
# insert_or_update_files # insert_or_update_files
@ -1849,11 +1851,13 @@ sub connect
} }
# If we're a striker and no connections were found, start our database. # If we're a striker and no connections were found, start our database.
my $configured_databases = keys %{$anvil->data->{database}};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
local_host_type => $local_host_type, local_host_type => $local_host_type,
"sys::database::connections" => $anvil->data->{sys}{database}{connections}, "sys::database::connections" => $anvil->data->{sys}{database}{connections},
configured_databases => $configured_databases,
}}); }});
if (($local_host_type eq "striker") && (not $anvil->data->{sys}{database}{connections})) if (($local_host_type eq "striker") && (not $anvil->data->{sys}{database}{connections}) && ($configured_databases > 2))
{ {
# Tell the user we're going to try to load and start. # Tell the user we're going to try to load and start.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "log_0650"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "log_0650"});
@ -2648,6 +2652,9 @@ sub get_anvils
$anvil->Database->get_files({debug => $debug}); $anvil->Database->get_files({debug => $debug});
$anvil->Database->get_file_locations({debug => $debug}); $anvil->Database->get_file_locations({debug => $debug});
# Also pull in DRs so we can link them.
$anvil->Database->get_dr_links({debug => $debug});
my $query = " my $query = "
SELECT SELECT
anvil_uuid, anvil_uuid,
@ -2757,6 +2764,7 @@ WHERE
"anvils::host_uuid::${anvil_node2_host_uuid}::role" => $anvil->data->{anvils}{host_uuid}{$anvil_node2_host_uuid}{role}, "anvils::host_uuid::${anvil_node2_host_uuid}::role" => $anvil->data->{anvils}{host_uuid}{$anvil_node2_host_uuid}{role},
}}); }});
} }
### TODO: Remove this once the switch over to 'dr_links' is done.
if ($anvil_dr1_host_uuid) if ($anvil_dr1_host_uuid)
{ {
$anvil->data->{anvils}{host_uuid}{$anvil_dr1_host_uuid}{anvil_name} = $anvil_name; $anvil->data->{anvils}{host_uuid}{$anvil_dr1_host_uuid}{anvil_name} = $anvil_name;
@ -2810,6 +2818,32 @@ WHERE
"anvils::anvil_uuid::${anvil_uuid}::file_name::${file_name}::file_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid}, "anvils::anvil_uuid::${anvil_uuid}::file_name::${file_name}::file_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid},
}}); }});
} }
# Process DR hosts this Anvil! is allowed to use.
if (exists $anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid})
{
foreach my $dr_link_host_uuid (sort {$a cmp $b} keys %{$anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}{dr_link_host_uuid}})
{
my $dr_link_uuid = $anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}{dr_link_host_uuid}{$dr_link_host_uuid}{dr_link_uuid};
my $dr_link_note = $anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_note};
my $dr_link_short_host_name = $anvil->data->{hosts}{host_uuid}{$dr_link_host_uuid}{short_host_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:dr_link_host_uuid" => $dr_link_host_uuid,
"s2:dr_link_uuid" => $dr_link_uuid,
"s3:dr_link_note" => $dr_link_note,
"s4:dr_link_short_host_name" => $dr_link_short_host_name,
}});
next if $dr_link_note eq "DELETED";
$anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{dr_host}{$dr_link_host_uuid}{dr_link_uuid} = $dr_link_uuid;
$anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{dr_host}{$dr_link_host_uuid}{short_host_name} = $dr_link_short_host_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:anvils::anvil_uuid::${anvil_uuid}::dr_host::${dr_link_host_uuid}::dr_link_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{dr_host}{$dr_link_host_uuid}{dr_link_uuid},
"s2:anvils::anvil_uuid::${anvil_uuid}::dr_host::${dr_link_host_uuid}::short_host_name" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{dr_host}{$dr_link_host_uuid}{short_host_name},
}});
}
}
} }
return(0); return(0);
@ -2925,6 +2959,112 @@ WHERE
} }
=head2 get_dr_links
This loads the known dr_link devices into the C<< anvil::data >> hash at:
* dr_links::dr_link_uuid::<dr_link_uuid>::dr_link_host_uuid
* dr_links::dr_link_uuid::<dr_link_uuid>::dr_link_anvil_uuid
* dr_links::dr_link_uuid::<dr_link_uuid>::dr_link_note
* dr_links::dr_link_uuid::<dr_link_uuid>::modified_date
To simplify finding links by host or Anvil! UUID, links to C<< dr_link_uuid >> are stored in these hashes;
* dr_links::by_anvil_uuid::<dr_link_anvil_uuid>::dr_link_host_uuid::<dr_link_host_uuid>::dr_link_uuid
* dr_links::by_host_uuid::<dr_link_host_uuid>::dr_link_anvil_uuid::<dr_link_anvil_uuid>::dr_link_uuid
If the hash was already populated, it is cleared before repopulating to ensure no stale data remains.
B<<Note>>: Deleted links (ones where C<< dr_link_note >> is set to C<< DELETED >>) are ignored. See the C<< include_deleted >> parameter to include them.
Parameters;
=head3 include_deleted (Optional, default 0)
If set to C<< 1 >>, deleted links are included when loading the data. When C<< 0 >> is set, the default, any dr_link agent with C<< dr_link_note >> set to C<< DELETED >> is ignored.
=cut
sub get_dr_links
{
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->get_dr_links()" }});
my $include_deleted = defined $parameter->{include_deleted} ? $parameter->{include_deleted} : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
include_deleted => $include_deleted,
}});
if (exists $anvil->data->{dr_links})
{
delete $anvil->data->{dr_links};
}
my $query = "
SELECT
dr_link_uuid,
dr_link_host_uuid,
dr_link_anvil_uuid,
dr_link_note,
modified_date
FROM
dr_links ";
if (not $include_deleted)
{
$query .= "
WHERE
dr_link_note != 'DELETED'";
}
$query .= "
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
my $dr_link_uuid = $row->[0];
my $dr_link_host_uuid = $row->[1];
my $dr_link_anvil_uuid = $row->[2];
my $dr_link_note = defined $row->[3] ? $row->[3] : "";
my $modified_date = $row->[4];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
dr_link_uuid => $dr_link_uuid,
dr_link_host_uuid => $dr_link_host_uuid,
dr_link_anvil_uuid => $dr_link_anvil_uuid,
dr_link_note => $dr_link_note,
modified_date => $modified_date,
}});
# Record the data in the hash, too.
$anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_host_uuid} = $dr_link_host_uuid;
$anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_anvil_uuid} = $dr_link_anvil_uuid;
$anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_note} = $dr_link_note;
$anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{modified_date} = $modified_date;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"dr_links::dr_link_uuid::${dr_link_uuid}::dr_link_host_uuid" => $anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_host_uuid},
"dr_links::dr_link_uuid::${dr_link_uuid}::dr_link_anvil_uuid" => $anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_anvil_uuid},
"dr_links::dr_link_uuid::${dr_link_uuid}::dr_link_note" => $anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_note},
"dr_links::dr_link_uuid::${dr_link_uuid}::modified_date" => $anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{modified_date},
}});
$anvil->data->{dr_links}{by_anvil_uuid}{$dr_link_anvil_uuid}{dr_link_host_uuid}{$dr_link_host_uuid}{dr_link_uuid} = $dr_link_uuid;
$anvil->data->{dr_links}{by_host_uuid}{$dr_link_host_uuid}{dr_link_anvil_uuid}{$dr_link_anvil_uuid}{dr_link_uuid} = $dr_link_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"dr_links::by_anvil_uuid::${dr_link_anvil_uuid}::dr_link_host_uuid::${dr_link_host_uuid}::dr_link_uuid" => $anvil->data->{dr_links}{by_anvil_uuid}{$dr_link_anvil_uuid}{dr_link_host_uuid}{$dr_link_host_uuid}{dr_link_uuid},
"dr_links::by_host_uuid::${dr_link_host_uuid}::dr_link_anvil_uuid::${dr_link_anvil_uuid}::dr_link_uuid" => $anvil->data->{dr_links}{by_host_uuid}{$dr_link_host_uuid}{dr_link_anvil_uuid}{$dr_link_anvil_uuid}{dr_link_uuid},
}});
}
return(0);
}
=head2 get_fences =head2 get_fences
This loads the known fence devices into the C<< anvil::data >> hash at: This loads the known fence devices into the C<< anvil::data >> hash at:
@ -7168,6 +7308,241 @@ WHERE
} }
=head2 insert_or_update_dr_links
This updates (or inserts) a record in the 'dr_links' table. The C<< dr_link_uuid >> UUID 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.
=head3 delete (optional)
If this is set to C<< 1 >>, the record will be deleted. Specifiically, C<< dr_link_note >> is set to C<< DELETED >>.
=head3 dr_link_uuid (optional, usually)
This is the specific record to update. If C<< delete >> is set, then either this OR both C<< dr_link_host_uuid >> and C<< dr_link_anvil_uuid >> are required.
=head3 dr_link_host_uuid (required, must by a host_type -> dr)
This is the DR host's c<< hosts >> -> C<< host_uuid >>. The host_type is checked and only hosts with C<< host_type >> = C<< dr >> are allowed.
=head3 dr_link_anvil_uuid (required)
This is the C<< anvils >> -> C<< anvil_uuid >> that will be allowed to use this DR host.
=head3 dr_link_note (optional)
This is an optional note that can be used to store anything. If this is set to C<< DELETED >>, the DR to Anvil! link is severed.
=cut
sub insert_or_update_dr_links
{
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_dr_links()" }});
my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : "";
my $file = defined $parameter->{file} ? $parameter->{file} : "";
my $line = defined $parameter->{line} ? $parameter->{line} : "";
my $delete = defined $parameter->{'delete'} ? $parameter->{'delete'} : "";
my $dr_link_uuid = defined $parameter->{dr_link_uuid} ? $parameter->{dr_link_uuid} : "";
my $dr_link_host_uuid = defined $parameter->{dr_link_host_uuid} ? $parameter->{dr_link_host_uuid} : "";
my $dr_link_anvil_uuid = defined $parameter->{dr_link_anvil_uuid} ? $parameter->{dr_link_anvil_uuid} : "";
my $dr_link_note = defined $parameter->{dr_link_note} ? $parameter->{dr_link_note} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
uuid => $uuid,
file => $file,
line => $line,
dr_link_host_uuid => $dr_link_host_uuid,
dr_link_anvil_uuid => $dr_link_anvil_uuid,
dr_link_note => $dr_link_note,
}});
# Make sure that the UUIDs are valid.
$anvil->Database->get_hosts({deubg => $debug});
$anvil->Database->get_dr_links({debug => $debug});
# If deleting, and if we have a valid 'dr_link_uuid' UUID, delete now and be done,
if ($delete)
{
# Do we have a valid dr_link_uuid?
if ($dr_link_uuid)
{
#
if (not exists $anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid})
{
# Invalid, can't delete.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0397", variables => { uuid => $dr_link_uuid }});
return("");
}
# If we're here, delete it if it isn't already.
if ($anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_note} ne "DELETED")
{
my $query = "
UPDATE
dr_links
SET
dr_link_node = 'DELETED',
modified_date = ".$anvil->Database->quote($anvil->Database->refresh_timestamp)."
WHERE
dr_link_uuid = ".$anvil->Database->quote($dr_link_uuid)."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
$anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
}
return($dr_link_uuid)
}
}
# Still here? Make sure we've got sane parameters
if (not $dr_link_host_uuid)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_dr_links()", parameter => "dr_link_host_uuid" }});
return("");
}
if (not $dr_link_anvil_uuid)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_dr_links()", parameter => "dr_link_anvil_uuid" }});
return("");
}
# We've got UUIDs, but are they valid?
if (not exists $anvil->data->{hosts}{host_uuid}{$dr_link_host_uuid})
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0394", variables => { uuid => $dr_link_host_uuid }});
return("");
}
elsif ($anvil->data->{hosts}{host_uuid}{$dr_link_host_uuid}{host_type} ne "dr")
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0395", variables => {
uuid => $dr_link_host_uuid,
name => $anvil->data->{hosts}{host_uuid}{$dr_link_host_uuid}{host_name},
type => $anvil->data->{hosts}{host_uuid}{$dr_link_host_uuid}{host_type},
}});
return("");
}
if (not exists $anvil->data->{anvils}{anvil_uuid}{$dr_link_anvil_uuid})
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0396", variables => { uuid => $dr_link_anvil_uuid }});
return("");
}
my $dr_host_name = $anvil->data->{hosts}{host_uuid}{$dr_link_host_uuid}{host_name};
my $anvil_name = $anvil->data->{anvils}{anvil_uuid}{$dr_link_anvil_uuid}{anvil_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
dr_host_name => $dr_host_name,
anvil_name => $anvil_name,
}});
# Get the dr_link_uuid, if one exists.
if (not $dr_link_uuid)
{
if ((exists $anvil->data->{dr_links}{by_anvil_uuid}{$dr_link_anvil_uuid}) &&
(exists $anvil->data->{dr_links}{by_anvil_uuid}{$dr_link_anvil_uuid}{dr_link_host_uuid}{$dr_link_host_uuid}))
{
$dr_link_uuid = $anvil->data->{dr_links}{by_anvil_uuid}{$dr_link_anvil_uuid}{dr_link_host_uuid}{$dr_link_host_uuid}{dr_link_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { dr_link_uuid => $dr_link_uuid }});
}
}
# If we're deleting and we found a dr_link_uuid, DELETE now and return.
if ($delete)
{
if (($dr_link_uuid) && ($anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_note} ne "DELETED"))
{
my $query = "
UPDATE
dr_links
SET
dr_link_node = 'DELETED',
modified_date = ".$anvil->Database->quote($anvil->Database->refresh_timestamp)."
WHERE
dr_link_uuid = ".$anvil->Database->quote($dr_link_uuid)."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
$anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
}
return($dr_link_uuid)
}
# Do we have a UUID?
if ($dr_link_uuid)
{
# Yup. Has something changed?
my $old_dr_link_anvil_uuid = $anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_anvil_uuid};
my $old_dr_link_host_uuid = $anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_host_uuid};
my $old_dr_link_note = $anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_note};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
old_dr_link_anvil_uuid => $old_dr_link_anvil_uuid,
old_dr_link_host_uuid => $old_dr_link_host_uuid,
old_dr_link_note => $old_dr_link_note,
}});
if (($old_dr_link_anvil_uuid ne $dr_link_anvil_uuid) or
($old_dr_link_host_uuid ne $dr_link_host_uuid) or
($old_dr_link_note ne $dr_link_note))
{
# Clear the stop data.
my $query = "
UPDATE
dr_links
SET
dr_link_host_uuid = ".$anvil->Database->quote($dr_link_host_uuid).",
dr_link_anvil_uuid = ".$anvil->Database->quote($dr_link_anvil_uuid).",
dr_link_note = ".$anvil->Database->quote($dr_link_note).",
modified_date = ".$anvil->Database->quote($anvil->Database->refresh_timestamp)."
WHERE
dr_link_uuid = ".$anvil->Database->quote($dr_link_uuid)."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
$anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
}
}
else
{
# No, INSERT.
$dr_link_uuid = $anvil->Get->uuid();
my $query = "
INSERT INTO
dr_links
(
dr_link_uuid,
dr_link_host_uuid,
dr_link_anvil_uuid,
dr_link_note,
modified_date
) VALUES (
".$anvil->Database->quote($dr_link_uuid).",
".$anvil->Database->quote($dr_link_host_uuid).",
".$anvil->Database->quote($dr_link_anvil_uuid).",
".$anvil->Database->quote($dr_link_note).",
".$anvil->Database->quote($anvil->Database->refresh_timestamp)."
);
";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
$anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
}
return($dr_link_uuid);
}
=head2 insert_or_update_fences =head2 insert_or_update_fences
This updates (or inserts) a record in the 'fences' table. The C<< fence_uuid >> UUID will be returned. This updates (or inserts) a record in the 'fences' table. The C<< fence_uuid >> UUID will be returned.
@ -11826,9 +12201,9 @@ WHERE
{ {
my $difference = diff \$old_server_definition_xml, \$server_definition_xml, { STYLE => 'Unified' }; my $difference = diff \$old_server_definition_xml, \$server_definition_xml, { STYLE => 'Unified' };
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0556", variables => { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0556", variables => {
server_name => $server_name, server_name => $server_name,
server_definition_server_uuid => $server_definition_server_uuid, server_definition_server_uuid => $server_definition_server_uuid,
difference => $difference, difference => $difference,
}}); }});
} }
@ -14828,7 +15203,7 @@ INSERT INTO
{ {
### NOTE: There is no history schema for states. ### NOTE: There is no history schema for states.
# The lock is stale. # The lock is stale.
my $query = "DELETE FROM states WHERE state_uuid = ".$state_uuid.";"; my $query = "DELETE FROM states WHERE state_uuid = ".$anvil->Database->quote($state_uuid).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
$anvil->Database->write({debug => $debug, query => $query, source => $THIS_FILE, line => __LINE__}); $anvil->Database->write({debug => $debug, query => $query, source => $THIS_FILE, line => __LINE__});
} }

@ -42,6 +42,8 @@ my $THIS_FILE = "Get.pm";
# uptime # uptime
# users_home # users_home
# uuid # uuid
# virsh_list_net
# virsh_list_os
# _salt # _salt
# _wrap_to # _wrap_to
@ -466,12 +468,9 @@ Data is store in the following hashes;
anvil_resources::<anvil_uuid>::ram::allocated anvil_resources::<anvil_uuid>::ram::allocated
anvil_resources::<anvil_uuid>::ram::hardware anvil_resources::<anvil_uuid>::ram::hardware
anvil_resources::<anvil_uuid>::bridges::<bridge_name>::on_nodes anvil_resources::<anvil_uuid>::bridges::<bridge_name>::on_nodes
anvil_resources::<anvil_uuid>::bridges::<bridge_name>::on_dr
anvil_resources::<anvil_uuid>::storage_group::<storage_group_uuid>::group_name anvil_resources::<anvil_uuid>::storage_group::<storage_group_uuid>::group_name
anvil_resources::<anvil_uuid>::storage_group::<storage_group_uuid>::vg_size anvil_resources::<anvil_uuid>::storage_group::<storage_group_uuid>::vg_size
anvil_resources::<anvil_uuid>::storage_group::<storage_group_uuid>::free_size anvil_resources::<anvil_uuid>::storage_group::<storage_group_uuid>::free_size
anvil_resources::<anvil_uuid>::storage_group::<storage_group_uuid>::vg_size_on_dr
anvil_resources::<anvil_uuid>::storage_group::<storage_group_uuid>::available_on_dr
All sizes are stored in bytes. All sizes are stored in bytes.
@ -517,8 +516,7 @@ sub available_resources
SELECT SELECT
anvil_name, anvil_name,
anvil_node1_host_uuid, anvil_node1_host_uuid,
anvil_node2_host_uuid, anvil_node2_host_uuid
anvil_dr1_host_uuid
FROM FROM
anvils anvils
WHERE WHERE
@ -539,20 +537,13 @@ WHERE
} }
# Get the details. # Get the details.
my $anvil_name = $results->[0]->[0]; my $anvil_name = $results->[0]->[0];
my $node1_host_uuid = $results->[0]->[1]; my $node1_host_uuid = $results->[0]->[1];
my $node2_host_uuid = $results->[0]->[2]; my $node2_host_uuid = $results->[0]->[2];
my $dr1_host_uuid = defined $results->[0]->[3] ? $results->[0]->[3] : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
anvil_name => $anvil_name, anvil_name => $anvil_name,
node1_host_uuid => $node1_host_uuid, node1_host_uuid => $node1_host_uuid,
node2_host_uuid => $node2_host_uuid, node2_host_uuid => $node2_host_uuid,
dr1_host_uuid => $dr1_host_uuid,
}});
$anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} = $dr1_host_uuid ? 1 : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"anvil_resources::${anvil_uuid}::has_dr" => $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr},
}}); }});
# Load hosts and network bridges # Load hosts and network bridges
@ -570,13 +561,13 @@ WHERE
$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads} = 0; $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads} = 0;
$anvil->data->{anvil_resources}{$anvil_uuid}{ram}{hardware} = 0; $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{hardware} = 0;
foreach my $host_uuid ($node1_host_uuid, $node2_host_uuid, $dr1_host_uuid) foreach my $host_uuid ($node1_host_uuid, $node2_host_uuid)
{ {
# If DR isn't defined, it'll be blank.
next if not $host_uuid;
my $this_is = "node1"; my $this_is = "node1";
if ($host_uuid eq $node2_host_uuid) { $this_is = "node2"; } if ($host_uuid eq $node2_host_uuid)
elsif ($host_uuid eq $dr1_host_uuid) { $this_is = "dr1"; } {
$this_is = "node2";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_is => $this_is }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_is => $this_is }});
# Start collecting data. # Start collecting data.
@ -650,34 +641,30 @@ WHERE
"anvil_resources::${anvil_uuid}::host_uuid::${host_uuid}::ram::hardware" => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$host_uuid}{ram}{hardware}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$host_uuid}{ram}{hardware}}).")", "anvil_resources::${anvil_uuid}::host_uuid::${host_uuid}::ram::hardware" => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$host_uuid}{ram}{hardware}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$host_uuid}{ram}{hardware}}).")",
}}); }});
# For available resources, we only care about nodes. # How many cores?
if ($this_is !~ /^dr/) if ((not $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores}) or
($scan_hardware_cpu_cores < $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores}))
{ {
# How many cores? $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores} = $scan_hardware_cpu_cores;
if ((not $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores}) or $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
($scan_hardware_cpu_cores < $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores})) "anvil_resources::${anvil_uuid}::cpu::cores" => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores},
{ }});
$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores} = $scan_hardware_cpu_cores; }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { if ((not $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}) or
"anvil_resources::${anvil_uuid}::cpu::cores" => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores}, ($scan_hardware_cpu_threads < $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}))
}}); {
} $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads} = $scan_hardware_cpu_threads;
if ((not $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}) or $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
($scan_hardware_cpu_threads < $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads})) "anvil_resources::${anvil_uuid}::cpu::threads" => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads},
{ }});
$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads} = $scan_hardware_cpu_threads; }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { if ((not $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available}) or
"anvil_resources::${anvil_uuid}::cpu::threads" => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}, ($scan_hardware_ram_total < $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{hardware}))
}}); {
} $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available} = $scan_hardware_ram_total;
if ((not $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available}) or $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
($scan_hardware_ram_total < $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{hardware})) "anvil_resources::${anvil_uuid}::ram::available" => $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available}}).")",
{ }});
$anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available} = $scan_hardware_ram_total;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"anvil_resources::${anvil_uuid}::ram::available" => $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available}}).")",
}});
}
} }
} }
@ -750,7 +737,6 @@ ORDER BY
foreach my $bridge_name (sort {$a cmp $b} keys %{$anvil->data->{anvil_resources}{$anvil_uuid}{bridges}}) foreach my $bridge_name (sort {$a cmp $b} keys %{$anvil->data->{anvil_resources}{$anvil_uuid}{bridges}})
{ {
$anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on_nodes} = 0; $anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on_nodes} = 0;
$anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on_dr} = 0;
if (($anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on}{$node1_host_uuid}) && if (($anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on}{$node1_host_uuid}) &&
($anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on}{$node2_host_uuid})) ($anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on}{$node2_host_uuid}))
{ {
@ -759,13 +745,6 @@ ORDER BY
"anvil_resources::${anvil_uuid}::bridges::${bridge_name}::on_nodes" => $anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on_nodes}, "anvil_resources::${anvil_uuid}::bridges::${bridge_name}::on_nodes" => $anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on_nodes},
}}); }});
} }
if ($anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on}{$dr1_host_uuid})
{
$anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on_dr} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"anvil_resources::${anvil_uuid}::bridges::${bridge_name}::on_dr" => $anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on_dr},
}});
}
} }
foreach my $storage_group_uuid (keys %{$anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}}) foreach my $storage_group_uuid (keys %{$anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}})
@ -779,8 +758,6 @@ ORDER BY
my $node1_vg_free = 0; my $node1_vg_free = 0;
my $node2_vg_size = 0; my $node2_vg_size = 0;
my $node2_vg_free = 0; my $node2_vg_free = 0;
my $dr1_vg_size = 0;
my $dr1_vg_free = 0;
if (exists $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$node1_host_uuid}) if (exists $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$node1_host_uuid})
{ {
$node1_vg_size = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$node1_host_uuid}{vg_size}; $node1_vg_size = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$node1_host_uuid}{vg_size};
@ -799,25 +776,12 @@ ORDER BY
node2_vg_free => $node2_vg_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $node2_vg_free}).")", node2_vg_free => $node2_vg_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $node2_vg_free}).")",
}}); }});
} }
if (exists $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$dr1_host_uuid})
{
$dr1_vg_size = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$dr1_host_uuid}{vg_size};
$dr1_vg_free = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$dr1_host_uuid}{vg_free};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
dr1_vg_size => $dr1_vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $dr1_vg_size}).")",
dr1_vg_free => $dr1_vg_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $dr1_vg_free}).")",
}});
}
$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size} = $node2_vg_size < $node1_vg_size ? $node2_vg_size : $node1_vg_size; $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size} = $node2_vg_size < $node1_vg_size ? $node2_vg_size : $node1_vg_size;
$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size} = $node2_vg_free < $node1_vg_free ? $node2_vg_free : $node1_vg_free; $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size} = $node2_vg_free < $node1_vg_free ? $node2_vg_free : $node1_vg_free;
$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size_on_dr} = $dr1_vg_size;
$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{available_on_dr} = $dr1_vg_free;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"anvil_resources::${anvil_uuid}::storage_group::${storage_group_uuid}::vg_size" => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}}).")", "anvil_resources::${anvil_uuid}::storage_group::${storage_group_uuid}::vg_size" => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}}).")",
"anvil_resources::${anvil_uuid}::storage_group::${storage_group_uuid}::free_size" => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size}}).")", "anvil_resources::${anvil_uuid}::storage_group::${storage_group_uuid}::free_size" => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size}}).")",
"anvil_resources::${anvil_uuid}::storage_group::${storage_group_uuid}::vg_size_on_dr" => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size_on_dr}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size_on_dr}}).")",
"anvil_resources::${anvil_uuid}::storage_group::${storage_group_uuid}::available_on_dr" => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{available_on_dr}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{available_on_dr}}).")",
}}); }});
# Make it easy to sort by group name # Make it easy to sort by group name
@ -2691,6 +2655,194 @@ sub uuid
return($uuid); return($uuid);
} }
=head2 virsh_list_net
This parses the output from C<< osinfo-query device class=net >> and populated the hash;
osinfo::net::<name>::vendor = Company name
osinfo::net::<name>::vendor_id = Vendor ID hex value
osinfo::net::<name>::product = Device friendly name
osinfo::net::<name>::product_id = Product ID hex value
This method takes no parameters.
=cut
sub virsh_list_net
{
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 => "Get->virsh_list_net()" }});
my $shell_call = $anvil->data->{path}{exe}{'osinfo-query'}." device class=net";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, 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 => $debug, list => {
output => $output,
return_code => $return_code,
}});
if (exists $anvil->data->{osinfo}{net})
{
delete $anvil->data->{osinfo}{net};
}
my $last_vendor = "";
foreach my $line (split/\n/, $output)
{
$line = $anvil->Words->clean_spaces({string => $line});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
next if $line =~ /Vendor \| Vendor ID/;
next if $line =~ /----+----/;
if ($line =~ /(.*?) \| (.*?) \| (.*?) \| (.*?) \| (.*?) \| (.*?) \| (.*?) \| (.*)$/)
{
my $vendor = $1;
my $vendor_id = $2;
my $product = $3;
my $product_id = $4;
my $name = $5;
my $class = $6;
my $bus = $7;
my $id = $8;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:vendor' => $vendor,
's2:vendor_id' => $vendor_id,
's3:product' => $product,
's4:product_id' => $product_id,
's5:name' => $name,
's6:class' => $class,
's7:bus' => $bus,
's8:id' => $id,
}});
if ($vendor)
{
$last_vendor = $vendor;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { last_vendor => $last_vendor }});
}
else
{
$vendor = $last_vendor;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { vendor => $vendor }});
}
next if $bus eq "xen";
$anvil->data->{osinfo}{net}{$name}{vendor} = $vendor;
$anvil->data->{osinfo}{net}{$name}{vendor_id} = $vendor_id;
$anvil->data->{osinfo}{net}{$name}{product} = $product;
$anvil->data->{osinfo}{net}{$name}{product_id} = $product_id;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:osinfo::net::${name}::vendor" => $anvil->data->{osinfo}{net}{$name}{vendor},
"s2:osinfo::net::${name}::vendor_id" => $anvil->data->{osinfo}{net}{$name}{vendor_id},
"s3:osinfo::net::${name}::product" => $anvil->data->{osinfo}{net}{$name}{product},
"s4:osinfo::net::${name}::product_id" => $anvil->data->{osinfo}{net}{$name}{product_id},
}});
}
}
# If there's only a 'virtio-net', create a 'virtio' alias.
if ((not exists $anvil->data->{osinfo}{net}{virtio}) && (exists $anvil->data->{osinfo}{net}{'virtio-net'}))
{
$anvil->data->{osinfo}{net}{virtio}{vendor} = $anvil->data->{osinfo}{net}{'virtio-net'}{vendor};
$anvil->data->{osinfo}{net}{virtio}{vendor_id} = $anvil->data->{osinfo}{net}{'virtio-net'}{vendor_id};
$anvil->data->{osinfo}{net}{virtio}{product} = $anvil->data->{osinfo}{net}{'virtio-net'}{product};
$anvil->data->{osinfo}{net}{virtio}{product_id} = $anvil->data->{osinfo}{net}{'virtio-net'}{product_id};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:osinfo::net::virtio::vendor" => $anvil->data->{osinfo}{net}{virtio}{vendor},
"s2:osinfo::net::virtio::vendor_id" => $anvil->data->{osinfo}{net}{virtio}{vendor_id},
"s3:osinfo::net::virtio::product" => $anvil->data->{osinfo}{net}{virtio}{product},
"s4:osinfo::net::virtio::product_id" => $anvil->data->{osinfo}{net}{virtio}{product_id},
}});
}
return(0);
}
=head2 virsh_list_os
This parses the output from C<< osinfo-query os >> and populates the hash;
osinfo::os-list::<name>::name = Operating System name
osinfo::os-list::<name>::version = OS Version
osinfo::os-list::<name>::id = ID URL
It also loads the OSes into the strings as 'os_list_<short_name>' = OS
This method takes no parameters.
=cut
sub virsh_list_os
{
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 => "Get->virsh_list_os()" }});
my $shell_call = $anvil->data->{path}{exe}{'osinfo-query'}." os";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, 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 => $debug, list => {
output => $output,
return_code => $return_code,
}});
if (exists $anvil->data->{osinfo}{'os-list'})
{
delete $anvil->data->{osinfo}{'os-list'};
}
my $last_vendor = "";
foreach my $line (split/\n/, $output)
{
$line = $anvil->Words->clean_spaces({string => $line});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
next if $line =~ /Short ID \| Name/;
next if $line =~ /----+----/;
if ($line =~ /(.*?) \| (.*?) \| (.*?) \| (.*)$/)
{
my $short_id = $1;
my $name = $2;
my $version = $3;
my $id = $4;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:short_id' => $short_id,
's2:name' => $name,
's3:version' => $version,
's4:id' => $id,
}});
$version = "unknown" if $version eq "";
$anvil->data->{osinfo}{'os-list'}{$short_id}{name} = $name;
$anvil->data->{osinfo}{'os-list'}{$short_id}{version} = $version;
$anvil->data->{osinfo}{'os-list'}{$short_id}{id} = $id;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:osinfo::os-list::${short_id}::name" => $anvil->data->{osinfo}{'os-list'}{$short_id}{name},
"s2:osinfo::os-list::${short_id}::version" => $anvil->data->{osinfo}{'os-list'}{$short_id}{version},
"s3:osinfo::os-list::${short_id}::id" => $anvil->data->{osinfo}{'os-list'}{$short_id}{id},
}});
my $key = "os_list_".$short_id;
foreach my $file (sort {$a cmp $b} keys %{$anvil->data->{words}})
{
foreach my $language (sort {$a cmp $b} keys %{$anvil->data->{words}{$file}{language}})
{
$anvil->data->{words}{$file}{language}{$language}{key}{$key}{content} = $name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"words::${file}::language::${language}::key::${key}::content" => $anvil->data->{words}{$file}{language}{$language}{key}{$key}{content},
}});
}
}
}
}
return(0);
}
# =head3 # =head3
# #
# Private Functions; # Private Functions;

@ -1843,7 +1843,7 @@ sub post_scan_analysis_node
$anvil->Email->send_alerts(); $anvil->Email->send_alerts();
# Pull the server. # Pull the server.
my $shell_call = $anvil->data->{path}{exe}{'anvil-migate-server'}." --target local --server all".$anvil->Log->switches; my $shell_call = $anvil->data->{path}{exe}{'anvil-migrate-server'}." --target local --server all".$anvil->Log->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }});
$anvil->System->call({shell_call => $shell_call}); $anvil->System->call({shell_call => $shell_call});
@ -1967,7 +1967,7 @@ sub post_scan_analysis_node
$anvil->Email->send_alerts(); $anvil->Email->send_alerts();
# Pull the server. # Pull the server.
my $shell_call = $anvil->data->{path}{exe}{'anvil-migate-server'}." --target local --server all".$anvil->Log->switches; my $shell_call = $anvil->data->{path}{exe}{'anvil-migrate-server'}." --target local --server all".$anvil->Log->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }});
$anvil->System->call({shell_call => $shell_call}); $anvil->System->call({shell_call => $shell_call});
@ -2122,7 +2122,7 @@ sub post_scan_analysis_node
} }
$anvil->Email->send_alerts(); $anvil->Email->send_alerts();
my $shell_call = $anvil->data->{path}{exe}{'anvil-migate-server'}." --target local --server all".$anvil->Log->switches; my $shell_call = $anvil->data->{path}{exe}{'anvil-migrate-server'}." --target local --server all".$anvil->Log->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }});
$anvil->System->call({shell_call => $shell_call}); $anvil->System->call({shell_call => $shell_call});
@ -2185,7 +2185,7 @@ sub post_scan_analysis_node
} }
$anvil->Email->send_alerts(); $anvil->Email->send_alerts();
my $shell_call = $anvil->data->{path}{exe}{'anvil-migate-server'}." --target local --server all".$anvil->Log->switches; my $shell_call = $anvil->data->{path}{exe}{'anvil-migrate-server'}." --target local --server all".$anvil->Log->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }});
$anvil->System->call({shell_call => $shell_call}); $anvil->System->call({shell_call => $shell_call});
@ -2220,7 +2220,7 @@ sub post_scan_analysis_node
} }
$anvil->Email->send_alerts(); $anvil->Email->send_alerts();
my $shell_call = $anvil->data->{path}{exe}{'anvil-migate-server'}." --target local --server all".$anvil->Log->switches; my $shell_call = $anvil->data->{path}{exe}{'anvil-migrate-server'}." --target local --server all".$anvil->Log->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }});
$anvil->System->call({shell_call => $shell_call}); $anvil->System->call({shell_call => $shell_call});
@ -2272,7 +2272,7 @@ sub post_scan_analysis_node
} }
$anvil->Email->send_alerts(); $anvil->Email->send_alerts();
my $shell_call = $anvil->data->{path}{exe}{'anvil-migate-server'}." --target local --server all".$anvil->Log->switches; my $shell_call = $anvil->data->{path}{exe}{'anvil-migrate-server'}." --target local --server all".$anvil->Log->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0011", variables => { shell_call => $shell_call }});
$anvil->System->call({shell_call => $shell_call}); $anvil->System->call({shell_call => $shell_call});
@ -2488,7 +2488,12 @@ LIMIT 1;";
} }
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { check_power => $check_power }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
check_power => $check_power,
short_host_name => $short_host_name,
host_ipmi => $host_ipmi,
host_status => $host_status,
}});
if (not $check_power) if (not $check_power)
{ {
next; next;
@ -2681,7 +2686,6 @@ LIMIT 1;";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0672", variables => { host_name => $host_name }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0672", variables => { host_name => $host_name }});
# Check power # Check power
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0567", variables => { host_name => $host_name }});
my ($power_health, $shortest_time_on_batteries, $highest_charge_percentage, $estimated_hold_up_time) = $anvil->ScanCore->check_power({ my ($power_health, $shortest_time_on_batteries, $highest_charge_percentage, $estimated_hold_up_time) = $anvil->ScanCore->check_power({
debug => $debug, debug => $debug,
anvil_uuid => $anvil_uuid, anvil_uuid => $anvil_uuid,

@ -24,6 +24,31 @@ my $THIS_FILE = "Server.pm";
# migrate_virsh # migrate_virsh
# shutdown_virsh # shutdown_virsh
=cut TODO
Move all virsh calls over to using Sys::Virt;
Example;
#!/usr/bin/perl
use strict;
use warnings;
use Sys::Virt;
# https://metacpan.org/pod/Sys::Virt::Domain
# https://libvirt.org/api.html
my $uri = "qemu:///system";
my $connection = Sys::Virt->new(uri => $uri);
my @domains = $connection->list_domains();
foreach my $domain (@domains)
{
print $log_fh "Domain: [".$domain->get_name."], UUID: [".$domain->get_uuid_string()."]\n";
print $log_fh "Definition: [".$domain->get_xml_description."]\n";
}
=cut
=pod =pod
=encoding utf8 =encoding utf8
@ -173,7 +198,7 @@ sub boot_virsh
# Is this a local call or a remote call? # Is this a local call or a remote call?
my ($output, $return_code) = $anvil->System->call({ my ($output, $return_code) = $anvil->System->call({
debug => $debug, debug => $debug,
shell_call => $anvil->data->{path}{exe}{virsh}." create ".$definition, shell_call => $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." create ".$definition,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
output => $output, output => $output,
@ -286,7 +311,7 @@ sub count_servers
my $count = 0; my $count = 0;
if (-e $anvil->data->{path}{exe}{virsh}) if (-e $anvil->data->{path}{exe}{virsh})
{ {
my $shell_call = $anvil->data->{path}{exe}{virsh}." list"; my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." list";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, debug => $debug}); my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, debug => $debug});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
@ -396,12 +421,13 @@ sub find
my $host_type = $anvil->Get->host_type({debug => $debug}); my $host_type = $anvil->Get->host_type({debug => $debug});
my $host_name = $anvil->Get->host_name; my $host_name = $anvil->Get->host_name;
my $virsh_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." list --all";
my $virsh_output = ""; my $virsh_output = "";
my $return_code = ""; my $return_code = "";
if ($anvil->Network->is_local({host => $target})) if ($anvil->Network->is_local({host => $target}))
{ {
# Local call # Local call
($virsh_output, my $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." list --all"}); ($virsh_output, my $return_code) = $anvil->System->call({shell_call => $virsh_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
virsh_output => $virsh_output, virsh_output => $virsh_output,
return_code => $return_code, return_code => $return_code,
@ -425,7 +451,7 @@ sub find
($virsh_output, $error, $return_code) = $anvil->Remote->call({ ($virsh_output, $error, $return_code) = $anvil->Remote->call({
debug => 2, debug => 2,
password => $password, password => $password,
shell_call => $anvil->data->{path}{exe}{virsh}." list --all", shell_call => $virsh_call,
target => $target, target => $target,
remote_user => "root", remote_user => "root",
}); });
@ -661,12 +687,19 @@ sub get_status
}); });
# Is this a local call or a remote call? # Is this a local call or a remote call?
my $shell_call = $anvil->data->{path}{exe}{virsh}." dumpxml --inactive ".$server; my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." dumpxml --inactive ".$server;
my $this_host = $anvil->Get->short_host_name; my $this_host = $anvil->Get->short_host_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
shell_call => $shell_call,
this_host => $this_host,
}});
if ($anvil->Network->is_local({host => $target})) if ($anvil->Network->is_local({host => $target}))
{ {
# Local. # Local.
($anvil->data->{server}{$host}{$server}{from_virsh}{xml}, $anvil->data->{server}{$host}{$server}{from_virsh}{return_code}) = $anvil->System->call({shell_call => $shell_call}); ($anvil->data->{server}{$host}{$server}{from_virsh}{xml}, $anvil->data->{server}{$host}{$server}{from_virsh}{return_code}) = $anvil->System->call({
debug => $debug,
shell_call => $shell_call,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"server::${host}::${server}::from_virsh::xml" => $anvil->data->{server}{$host}{$server}{from_virsh}{xml}, "server::${host}::${server}::from_virsh::xml" => $anvil->data->{server}{$host}{$server}{from_virsh}{xml},
"server::${host}::${server}::from_virsh::return_code" => $anvil->data->{server}{$host}{$server}{from_virsh}{return_code}, "server::${host}::${server}::from_virsh::return_code" => $anvil->data->{server}{$host}{$server}{from_virsh}{return_code},
@ -865,7 +898,7 @@ sub map_network
# NOTE: We don't use 'Server->find' as the hassle of tracking hosts to target isn't worth it. # NOTE: We don't use 'Server->find' as the hassle of tracking hosts to target isn't worth it.
# Get a list of servers. # Get a list of servers.
my $shell_call = $anvil->data->{path}{exe}{virsh}." list"; my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." list";
my $output = ""; my $output = "";
if ($anvil->Network->is_local({host => $target})) if ($anvil->Network->is_local({host => $target}))
{ {
@ -1032,6 +1065,9 @@ sub migrate_virsh
### NOTE: This method is called by ocf:alteeve:server, which operates without database access. As ### NOTE: This method is called by ocf:alteeve:server, which operates without database access. As
### such, queries need to be run only if we've got one or more DB connections. ### such, queries need to be run only if we've got one or more DB connections.
# Mark this server as being in a migration state. # Mark this server as being in a migration state.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"sys::database::connections" => $anvil->data->{sys}{database}{connections},
}});
if ($anvil->data->{sys}{database}{connections}) if ($anvil->data->{sys}{database}{connections})
{ {
$anvil->Database->get_servers({debug => 2}); $anvil->Database->get_servers({debug => 2});
@ -1039,14 +1075,24 @@ sub migrate_virsh
my $migation_started = time; my $migation_started = time;
my $server_uuid = ""; my $server_uuid = "";
my $old_server_state = ""; my $old_server_state = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { migation_started => $migation_started }});
foreach my $this_server_uuid (keys %{$anvil->data->{servers}{server_uuid}}) foreach my $this_server_uuid (keys %{$anvil->data->{servers}{server_uuid}})
{ {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
this_server_uuid => $this_server_uuid,
"servers::server_uuid::${this_server_uuid}::server_name" => $anvil->data->{servers}{server_uuid}{$this_server_uuid}{server_name},
}});
if ($server eq $anvil->data->{servers}{server_uuid}{$this_server_uuid}{server_name}) if ($server eq $anvil->data->{servers}{server_uuid}{$this_server_uuid}{server_name})
{ {
$server_uuid = $this_server_uuid; $server_uuid = $this_server_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { server_uuid => $server_uuid }});
last; last;
} }
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
server_uuid => $server_uuid,
"sys::database::connections" => $anvil->data->{sys}{database}{connections},
}});
if (($server_uuid) && ($anvil->data->{sys}{database}{connections})) if (($server_uuid) && ($anvil->data->{sys}{database}{connections}))
{ {
if ($anvil->data->{servers}{server_uuid}{$server_uuid}{server_state} ne "migrating") if ($anvil->data->{servers}{server_uuid}{$server_uuid}{server_state} ne "migrating")
@ -1066,12 +1112,15 @@ WHERE
} }
} }
# The virsh command switches host names to IPs and needs to have both the source and target IPs in # We default to live migrations, but will remove that switch if it's been set to false.
# the known_hosts file to work. my $live_migrate = "--live";
my $live_migrate = ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
if (($server_uuid) && ($anvil->data->{servers}{server_uuid}{$server_uuid}{server_live_migration})) server_uuid => $server_uuid,
"servers::server_uuid::${server_uuid}::server_live_migration" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_live_migration},
}});
if (($server_uuid) && (not $anvil->data->{servers}{server_uuid}{$server_uuid}{server_live_migration}))
{ {
$live_migrate = "--live"; $live_migrate = "";
} }
my $target_ip = $anvil->Convert->host_name_to_ip({debug => $debug, host_name => $target}); my $target_ip = $anvil->Convert->host_name_to_ip({debug => $debug, host_name => $target});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
@ -1087,7 +1136,7 @@ WHERE
}); });
} }
my $migration_command = $anvil->data->{path}{exe}{virsh}." migrate --undefinesource --tunnelled --p2p ".$live_migrate." ".$server." qemu+ssh://".$target."/system"; my $migration_command = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." migrate --undefinesource --tunnelled --p2p ".$live_migrate." ".$server." qemu+ssh://".$target."/system";
if ($source) if ($source)
{ {
my $source_ip = $anvil->Convert->host_name_to_ip({debug => $debug, host_name => $source}); my $source_ip = $anvil->Convert->host_name_to_ip({debug => $debug, host_name => $source});
@ -1101,7 +1150,7 @@ WHERE
}); });
} }
$migration_command = $anvil->data->{path}{exe}{virsh}." -c qemu+ssh://root\@".$source."/system migrate --undefinesource --tunnelled --p2p ".$live_migrate." ".$server." qemu+ssh://".$target."/system"; $migration_command = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." -c qemu+ssh://root\@".$source."/system migrate --undefinesource --tunnelled --p2p ".$live_migrate." ".$server." qemu+ssh://".$target."/system";
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { migration_command => $migration_command }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { migration_command => $migration_command }});
@ -1962,7 +2011,7 @@ sub shutdown_virsh
### TODO: No, don't do this! The server might be migrating ### TODO: No, don't do this! The server might be migrating
# The server is paused. Resume it, wait a few, then proceed with the shutdown. # The server is paused. Resume it, wait a few, then proceed with the shutdown.
# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0314", variables => { server => $server }}); # $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0314", variables => { server => $server }});
# my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." resume $server"}); # my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." resume $server"});
# if ($return_code) # if ($return_code)
# { # {
# # Looks like virsh isn't running. # # Looks like virsh isn't running.
@ -1980,7 +2029,7 @@ sub shutdown_virsh
{ {
# The server is suspended. Resume it, wait a few, then proceed with the shutdown. # The server is suspended. Resume it, wait a few, then proceed with the shutdown.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0317", variables => { server => $server }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0317", variables => { server => $server }});
my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." dompmwakeup $server"}); my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." dompmwakeup $server"});
if ($return_code) if ($return_code)
{ {
# Looks like virsh isn't running. # Looks like virsh isn't running.
@ -2062,7 +2111,7 @@ WHERE
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0520", variables => { server => $server }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0520", variables => { server => $server }});
my ($output, $return_code) = $anvil->System->call({ my ($output, $return_code) = $anvil->System->call({
debug => $debug, debug => $debug,
shell_call => $anvil->data->{path}{exe}{virsh}." ".$task." ".$server, shell_call => $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." ".$task." ".$server,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
output => $output, output => $output,

@ -4359,7 +4359,13 @@ sub search_directories
# Set a default if nothing was passed. # Set a default if nothing was passed.
my $array = defined $parameter->{directories} ? $parameter->{directories} : ""; my $array = defined $parameter->{directories} ? $parameter->{directories} : "";
my $initialize = defined $parameter->{initialize} ? $parameter->{initialize} : ""; my $initialize = defined $parameter->{initialize} ? $parameter->{initialize} : "";
# If PATH isn't set, set it (could have been scrubbed by a caller).
if (not $ENV{PATH})
{
$ENV{PATH} = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin";
}
# If the array is a CSV of directories, convert it now. # If the array is a CSV of directories, convert it now.
if ($array =~ /,/) if ($array =~ /,/)
{ {
@ -5269,12 +5275,12 @@ sub _wait_if_changing
if (not $delay) if (not $delay)
{ {
$delay = 2; $delay = 10;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { delay => $delay }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { delay => $delay }});
} }
elsif (($delay =~ /\D/) or ($delay == 0)) elsif (($delay =~ /\D/) or ($delay == 0))
{ {
$delay = 2; $delay = 10;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { delay => $delay }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { delay => $delay }});
} }

@ -294,6 +294,7 @@ sub call
}}); }});
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => { timeout => $timeout }});
if ($timeout) if ($timeout)
{ {
# Prepend a timeout. # Prepend a timeout.
@ -301,6 +302,7 @@ sub call
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => { shell_call => $shell_call }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => { shell_call => $shell_call }});
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => { background => $background }});
if ($background) if ($background)
{ {
# Prepend '/tmp/' to STDOUT and/or STDERR output files, if needed. # Prepend '/tmp/' to STDOUT and/or STDERR output files, if needed.
@ -350,8 +352,10 @@ sub call
} }
else else
{ {
$output = ""; $output = "";
open (my $file_handle, $shell_call.$redirect."; ".$anvil->data->{path}{exe}{echo}." return_code:\$? |") or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => $secure, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }}); my $call_string = $shell_call.$redirect."; ".$anvil->data->{path}{exe}{echo}." return_code:\$? |";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => { call_string => $call_string }});
open (my $file_handle, $call_string) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => $secure, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }});
while(<$file_handle>) while(<$file_handle>)
{ {
chomp; chomp;
@ -605,6 +609,55 @@ sub check_if_configured
$configured = 0 if not defined $configured; $configured = 0 if not defined $configured;
$configured = 0 if $configured eq ""; $configured = 0 if $configured eq "";
if ((not $configured) && (-f $anvil->data->{path}{data}{host_configured}))
{
# See if there's a configured file.
my $body = $anvil->Storage->read_file({debug => $debug, file => $anvil->data->{path}{data}{host_configured}});
foreach my $line (split/\n/, $body)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
if ($line =~ /^(.*)=(.*)$/)
{
my $variable = $anvil->Words->clean_spaces({string => $1});
my $value = $anvil->Words->clean_spaces({string => $2});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
variable => $variable,
value => $value,
}});
if (($variable eq "system::configured") && ($value eq "1"))
{
# Write the database entry.
my $variable_uuid = $anvil->Database->insert_or_update_variables({
variable_name => "system::configured",
variable_value => 1,
variable_default => "",
variable_description => "striker_0048",
variable_section => "system",
variable_source_uuid => $anvil->data->{sys}{host_uuid},
variable_source_table => "hosts",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { variable_uuid => $variable_uuid }});
# mark it as configured.
$configured = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { configured => $configured }});
}
}
}
}
if (($configured) && (not -f $anvil->data->{path}{data}{host_configured}))
{
my $failed = $anvil->Storage->write_file({
debug => $debug,
file => $anvil->data->{path}{data}{host_configured},
body => "system::configured = 1\n",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { failed => $failed }});
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { configured => $configured }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { configured => $configured }});
return($configured); return($configured);
} }
@ -1458,6 +1511,12 @@ sub collect_ipmi_data
return('!!error!!'); return('!!error!!');
} }
# Take the double-quotes off the password.
$ipmi_password =~ s/^"(.*)"$/$1/;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
ipmi_password => $anvil->Log->is_secure($ipmi_password),
}});
my $read_start_time = time; my $read_start_time = time;
# If there is a password, write it to a temp file. # If there is a password, write it to a temp file.

@ -196,8 +196,8 @@ sys::manage::firewall = 1
### Server related options ### Server related options
# This is the "short list" of servers shown when provisioning a new server. To see the full list of options, # This is the "short list" of servers shown when provisioning a new server. To see the full list of options,
# run '/usr/bin/osinfo-query os' on any machine in the Anvil!. # run '/usr/bin/osinfo-query os' and use here the 'Short ID' entries on any machine in the Anvil!.
#sys::servers::os_short_list = debian10,fedora32,freebsd12.1,gentoo,macosx10.7,msdos6.22,openbsd6.7,opensuse15.2,rhel5.11,rhel6.10,rhel7.9,rhel8.3,sles12sp5,solaris11,ubuntu20.04,win10,win2k16,win2k19 #sys::servers::os_short_list = rhel8.7,rhel9.1,win10,win2k19,win2k22
### Scan agent options ### Scan agent options

@ -2017,10 +2017,12 @@ sub run_manifest
my $bcn_count = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{bcn}; my $bcn_count = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{bcn};
my $sn_count = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{sn}; my $sn_count = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{sn};
my $ifn_count = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{ifn}; my $ifn_count = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{ifn};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { my $mn_count = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{mn};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
bcn_count => $bcn_count, bcn_count => $bcn_count,
sn_count => $sn_count, sn_count => $sn_count,
ifn_count => $ifn_count, ifn_count => $ifn_count,
mn_count => $mn_count,
}}); }});
# If confirmed, run! # If confirmed, run!
@ -2355,7 +2357,7 @@ sub run_manifest
# Show the networks. # Show the networks.
my $networks = ""; my $networks = "";
my $default_seen = 0; my $default_seen = 0;
foreach my $network ("bcn", "sn", "ifn") foreach my $network ("bcn", "sn", "mn", "ifn")
{ {
my $count = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{$network}; my $count = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{$network};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
@ -2389,6 +2391,7 @@ sub run_manifest
my $network_key = "header_0036"; my $network_key = "header_0036";
if ($network eq "sn") { $network_key = "header_0037"; } if ($network eq "sn") { $network_key = "header_0037"; }
elsif ($network eq "ifn") { $network_key = "header_0038"; } elsif ($network eq "ifn") { $network_key = "header_0038"; }
elsif ($network eq "mn") { $network_key = "header_0106"; }
$networks .= $anvil->Template->get({file => "anvil.html", name => "run-manifest-network", variables => { $networks .= $anvil->Template->get({file => "anvil.html", name => "run-manifest-network", variables => {
name => $anvil->Words->string({key => $network_key, variables => { number => $i }}), name => $anvil->Words->string({key => $network_key, variables => { number => $i }}),
network => $network_range, network => $network_range,
@ -2402,7 +2405,7 @@ sub run_manifest
# Pull out the IPs that will be assigned to servers. # Pull out the IPs that will be assigned to servers.
my $machine_ips = ""; my $machine_ips = "";
foreach my $network ("bcn", "sn", "ifn") foreach my $network ("bcn", "sn", "mn", "ifn")
{ {
if ($network eq "sn") if ($network eq "sn")
{ {
@ -2440,6 +2443,7 @@ sub run_manifest
my $network_key = "header_0036"; my $network_key = "header_0036";
if ($network eq "sn") { $network_key = "header_0037"; } if ($network eq "sn") { $network_key = "header_0037"; }
elsif ($network eq "ifn") { $network_key = "header_0038"; } elsif ($network eq "ifn") { $network_key = "header_0038"; }
elsif ($network eq "mn") { $network_key = "header_0106"; }
$machine_ips .= $anvil->Template->get({file => "anvil.html", name => "run-manifest-ip", variables => { $machine_ips .= $anvil->Template->get({file => "anvil.html", name => "run-manifest-ip", variables => {
name => $anvil->Words->string({key => $network_key, variables => { number => $i }}), name => $anvil->Words->string({key => $network_key, variables => { number => $i }}),
node1 => $node1_ip ? $node1_ip : "--", node1 => $node1_ip ? $node1_ip : "--",
@ -2533,6 +2537,8 @@ sub handle_manifest
$anvil->data->{cgi}{bcn_count}{alert} = 0 if not defined $anvil->data->{cgi}{bcn_count}{alert}; $anvil->data->{cgi}{bcn_count}{alert} = 0 if not defined $anvil->data->{cgi}{bcn_count}{alert};
$anvil->data->{cgi}{sn_count}{value} = 0 if not defined $anvil->data->{cgi}{sn_count}{value}; $anvil->data->{cgi}{sn_count}{value} = 0 if not defined $anvil->data->{cgi}{sn_count}{value};
$anvil->data->{cgi}{sn_count}{alert} = 0 if not defined $anvil->data->{cgi}{sn_count}{alert}; $anvil->data->{cgi}{sn_count}{alert} = 0 if not defined $anvil->data->{cgi}{sn_count}{alert};
$anvil->data->{cgi}{mn_count}{value} = 0 if not defined $anvil->data->{cgi}{mn_count}{value};
$anvil->data->{cgi}{mn_count}{alert} = 0 if not defined $anvil->data->{cgi}{mn_count}{alert};
$anvil->data->{cgi}{ifn_count}{value} = 0 if not defined $anvil->data->{cgi}{ifn_count}{value}; $anvil->data->{cgi}{ifn_count}{value} = 0 if not defined $anvil->data->{cgi}{ifn_count}{value};
$anvil->data->{cgi}{ifn_count}{alert} = 0 if not defined $anvil->data->{cgi}{ifn_count}{alert}; $anvil->data->{cgi}{ifn_count}{alert} = 0 if not defined $anvil->data->{cgi}{ifn_count}{alert};
$anvil->data->{cgi}{dns}{value} = "8.8.8.8,8.8.4.4" if not defined $anvil->data->{cgi}{dns}{value}; $anvil->data->{cgi}{dns}{value} = "8.8.8.8,8.8.4.4" if not defined $anvil->data->{cgi}{dns}{value};
@ -2549,6 +2555,7 @@ sub handle_manifest
"cgi::sequence::value" => $anvil->data->{cgi}{sequence}{value}, "cgi::sequence::value" => $anvil->data->{cgi}{sequence}{value},
"cgi::bcn_count::value" => $anvil->data->{cgi}{bcn_count}{value}, "cgi::bcn_count::value" => $anvil->data->{cgi}{bcn_count}{value},
"cgi::sn_count::value" => $anvil->data->{cgi}{sn_count}{value}, "cgi::sn_count::value" => $anvil->data->{cgi}{sn_count}{value},
"cgi::mn_count::value" => $anvil->data->{cgi}{mn_count}{value},
"cgi::ifn_count::value" => $anvil->data->{cgi}{ifn_count}{value}, "cgi::ifn_count::value" => $anvil->data->{cgi}{ifn_count}{value},
"cgi::dns::value" => $anvil->data->{cgi}{dns}{value}, "cgi::dns::value" => $anvil->data->{cgi}{dns}{value},
"cgi::ntp::value" => $anvil->data->{cgi}{ntp}{value}, "cgi::ntp::value" => $anvil->data->{cgi}{ntp}{value},
@ -2749,6 +2756,11 @@ sub handle_manifest
$anvil->data->{cgi}{sn_count}{value} = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{sn}; $anvil->data->{cgi}{sn_count}{value} = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{sn};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::sn_count::value" => $anvil->data->{cgi}{sn_count}{value} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::sn_count::value" => $anvil->data->{cgi}{sn_count}{value} }});
} }
if (not $anvil->data->{cgi}{mn_count}{value})
{
$anvil->data->{cgi}{mn_count}{value} = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{mn};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::mn_count::value" => $anvil->data->{cgi}{mn_count}{value} }});
}
if (not $anvil->data->{cgi}{bcn_count}{value}) if (not $anvil->data->{cgi}{bcn_count}{value})
{ {
$anvil->data->{cgi}{bcn_count}{value} = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{bcn}; $anvil->data->{cgi}{bcn_count}{value} = $anvil->data->{manifests}{manifest_uuid}{$manifest_uuid}{parsed}{networks}{count}{bcn};
@ -2773,7 +2785,7 @@ sub handle_manifest
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::mtu::value" => $anvil->data->{cgi}{mtu}{value} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::mtu::value" => $anvil->data->{cgi}{mtu}{value} }});
} }
foreach my $network ("bcn", "sn", "ifn") foreach my $network ("bcn", "sn", "mn", "ifn")
{ {
my $count_key = $network."_count"; my $count_key = $network."_count";
foreach my $i (1..$anvil->data->{cgi}{$count_key}{value}) foreach my $i (1..$anvil->data->{cgi}{$count_key}{value})
@ -2812,7 +2824,7 @@ sub handle_manifest
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::${ipmi_ip_key}::value" => $anvil->data->{cgi}{$ipmi_ip_key}{value} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::${ipmi_ip_key}::value" => $anvil->data->{cgi}{$ipmi_ip_key}{value} }});
} }
foreach my $network ("bcn", "sn", "ifn") foreach my $network ("bcn", "sn", "mn", "ifn")
{ {
my $count_key = $network."_count"; my $count_key = $network."_count";
foreach my $i (1..$anvil->data->{cgi}{$count_key}{value}) foreach my $i (1..$anvil->data->{cgi}{$count_key}{value})
@ -2921,6 +2933,11 @@ sub handle_manifest
$anvil->data->{cgi}{sn_count}{value} = 1; $anvil->data->{cgi}{sn_count}{value} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::sn_count::value" => $anvil->data->{cgi}{sn_count}{value} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::sn_count::value" => $anvil->data->{cgi}{sn_count}{value} }});
} }
if (not $anvil->data->{cgi}{mn_count}{value})
{
$anvil->data->{cgi}{mn_count}{value} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::mn_count::value" => $anvil->data->{cgi}{mn_count}{value} }});
}
if (not $anvil->data->{cgi}{bcn_count}{value}) if (not $anvil->data->{cgi}{bcn_count}{value})
{ {
$anvil->data->{cgi}{bcn_count}{value} = 1; $anvil->data->{cgi}{bcn_count}{value} = 1;
@ -2941,9 +2958,11 @@ sub handle_manifest
sequence_class => $anvil->data->{cgi}{sequence}{alert} ? "input_alert" : "", sequence_class => $anvil->data->{cgi}{sequence}{alert} ? "input_alert" : "",
ifn_count => $anvil->data->{cgi}{ifn_count}{value}, ifn_count => $anvil->data->{cgi}{ifn_count}{value},
ifn_count_class => $anvil->data->{cgi}{ifn_count}{alert} ? "input_alert" : "", ifn_count_class => $anvil->data->{cgi}{ifn_count}{alert} ? "input_alert" : "",
sn_count => $anvil->data->{cgi}{sn_count}{value}, sn_count => $anvil->data->{cgi}{sn_count}{value},
sn_count_class => $anvil->data->{cgi}{sn_count}{alert} ? "input_alert" : "", sn_count_class => $anvil->data->{cgi}{sn_count}{alert} ? "input_alert" : "",
bcn_count => $anvil->data->{cgi}{bcn_count}{value}, mn_count => $anvil->data->{cgi}{mn_count}{value},
mn_count_class => $anvil->data->{cgi}{mn_count}{alert} ? "input_alert" : "",
bcn_count => $anvil->data->{cgi}{bcn_count}{value},
bcn_count_class => $anvil->data->{cgi}{bcn_count}{alert} ? "input_alert" : "", bcn_count_class => $anvil->data->{cgi}{bcn_count}{alert} ? "input_alert" : "",
}}); }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'form::body' => $anvil->data->{form}{body} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'form::body' => $anvil->data->{form}{body} }});
@ -3000,29 +3019,29 @@ sub handle_manifest
subnet => $select, subnet => $select,
}}); }});
} }
# There's only ever 1 SN. Just in case we change our mind later, we'll set it up as if it's # There's only ever 1 SN. Just in case we change our mind later, we'll set it up as if it's
# variable. # variable.
foreach my $i (1..$anvil->data->{cgi}{bcn_count}{value}) foreach my $i (1..$anvil->data->{cgi}{sn_count}{value})
{ {
my $say_sn = $anvil->Words->string({key => "striker_0020", variables => { number => '1' }}); my $say_sn = $anvil->Words->string({key => "striker_0020", variables => { number => '1' }});
my $network_key = "sn".$i."_network"; my $network_key = "sn".$i."_network";
my $subnet_key = "sn".$i."_subnet"; my $subnet_key = "sn".$i."_subnet";
my $gateway_key = "sn".$i."_gateway"; my $gateway_key = "sn".$i."_gateway";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
say_sn => $say_sn, say_sn => $say_sn,
network_key => $network_key, network_key => $network_key,
subnet_key => $subnet_key, subnet_key => $subnet_key,
gateway_key => $gateway_key, gateway_key => $gateway_key,
}}); }});
$anvil->data->{cgi}{$network_key}{value} = "10.10".$i.".0.0" if not defined $anvil->data->{cgi}{$network_key}{value}; $anvil->data->{cgi}{$network_key}{value} = "10.10".$i.".0.0" if not defined $anvil->data->{cgi}{$network_key}{value};
$anvil->data->{cgi}{$network_key}{alert} = 0 if not defined $anvil->data->{cgi}{$network_key}{alert}; $anvil->data->{cgi}{$network_key}{alert} = 0 if not defined $anvil->data->{cgi}{$network_key}{alert};
$anvil->data->{cgi}{$subnet_key}{value} = "255.255.0.0" if not defined $anvil->data->{cgi}{$subnet_key}{value}; $anvil->data->{cgi}{$subnet_key}{value} = "255.255.0.0" if not defined $anvil->data->{cgi}{$subnet_key}{value};
$anvil->data->{cgi}{$subnet_key}{alert} = 0 if not defined $anvil->data->{cgi}{$subnet_key}{alert}; $anvil->data->{cgi}{$subnet_key}{alert} = 0 if not defined $anvil->data->{cgi}{$subnet_key}{alert};
$anvil->data->{cgi}{$gateway_key}{value} = "" if not defined $anvil->data->{cgi}{$gateway_key}{value}; $anvil->data->{cgi}{$gateway_key}{value} = "" if not defined $anvil->data->{cgi}{$gateway_key}{value};
$anvil->data->{cgi}{$gateway_key}{alert} = 0 if not defined $anvil->data->{cgi}{$gateway_key}{alert}; $anvil->data->{cgi}{$gateway_key}{alert} = 0 if not defined $anvil->data->{cgi}{$gateway_key}{alert};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"cgi::${network_key}::value" => $anvil->data->{cgi}{$network_key}{value}, "cgi::${network_key}::value" => $anvil->data->{cgi}{$network_key}{value},
"cgi::${network_key}::alert" => $anvil->data->{cgi}{$network_key}{alert}, "cgi::${network_key}::alert" => $anvil->data->{cgi}{$network_key}{alert},
"cgi::${subnet_key}::value" => $anvil->data->{cgi}{$subnet_key}{value}, "cgi::${subnet_key}::value" => $anvil->data->{cgi}{$subnet_key}{value},
@ -3030,17 +3049,60 @@ sub handle_manifest
"cgi::${gateway_key}::value" => $anvil->data->{cgi}{$gateway_key}{value}, "cgi::${gateway_key}::value" => $anvil->data->{cgi}{$gateway_key}{value},
"cgi::${gateway_key}::alert" => $anvil->data->{cgi}{$gateway_key}{alert}, "cgi::${gateway_key}::alert" => $anvil->data->{cgi}{$gateway_key}{alert},
}}); }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::sn1_network::value" => $anvil->data->{cgi}{sn1_network}{value} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::sn1_network::value" => $anvil->data->{cgi}{sn1_network}{value} }});
$network_form .= $anvil->Template->get({file => "anvil.html", name => "manifest-step2-network-entry", variables => { $network_form .= $anvil->Template->get({file => "anvil.html", name => "manifest-step2-network-entry", variables => {
network => $say_sn, network => $say_sn,
network_name => $network_key, network_name => $network_key,
network_class => $anvil->data->{cgi}{$network_key}{alert} ? "input_alert" : "", network_class => $anvil->data->{cgi}{$network_key}{alert} ? "input_alert" : "",
network_value => $anvil->data->{cgi}{$network_key}{value}, network_value => $anvil->data->{cgi}{$network_key}{value},
subnet => '255.255.0.0 <input type="hidden" name="'.$network_key.'" id="'.$network_key.'" value="'.$anvil->data->{cgi}{sn1_subnet}{value}.'" />', subnet => '255.255.0.0 <input type="hidden" name="'.$network_key.'" id="'.$network_key.'" value="'.$anvil->data->{cgi}{sn1_subnet}{value}.'" />',
}}); }});
} }
# There's only ever 1 MN. Just in case we change our mind later, we'll set it up as if it's
# variable.
if ($anvil->data->{cgi}{mn_count}{value})
{
foreach my $i (1..$anvil->data->{cgi}{mn_count}{value})
{
my $say_mn = $anvil->Words->string({key => "striker_0299", variables => { number => '1' }});
my $network_key = "mn".$i."_network";
my $subnet_key = "mn".$i."_subnet";
my $gateway_key = "mn".$i."_gateway";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
say_mn => $say_mn,
network_key => $network_key,
subnet_key => $subnet_key,
gateway_key => $gateway_key,
}});
$anvil->data->{cgi}{$network_key}{value} = "10.199.0.0" if not defined $anvil->data->{cgi}{$network_key}{value};
$anvil->data->{cgi}{$network_key}{alert} = 0 if not defined $anvil->data->{cgi}{$network_key}{alert};
$anvil->data->{cgi}{$subnet_key}{value} = "255.255.0.0" if not defined $anvil->data->{cgi}{$subnet_key}{value};
$anvil->data->{cgi}{$subnet_key}{alert} = 0 if not defined $anvil->data->{cgi}{$subnet_key}{alert};
$anvil->data->{cgi}{$gateway_key}{value} = "" if not defined $anvil->data->{cgi}{$gateway_key}{value};
$anvil->data->{cgi}{$gateway_key}{alert} = 0 if not defined $anvil->data->{cgi}{$gateway_key}{alert};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"cgi::${network_key}::value" => $anvil->data->{cgi}{$network_key}{value},
"cgi::${network_key}::alert" => $anvil->data->{cgi}{$network_key}{alert},
"cgi::${subnet_key}::value" => $anvil->data->{cgi}{$subnet_key}{value},
"cgi::${subnet_key}::alert" => $anvil->data->{cgi}{$subnet_key}{alert},
"cgi::${gateway_key}::value" => $anvil->data->{cgi}{$gateway_key}{value},
"cgi::${gateway_key}::alert" => $anvil->data->{cgi}{$gateway_key}{alert},
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::mn1_network::value" => $anvil->data->{cgi}{mn1_network}{value} }});
$network_form .= $anvil->Template->get({file => "anvil.html", name => "manifest-step2-network-entry", variables => {
network => $say_mn,
network_name => $network_key,
network_class => $anvil->data->{cgi}{$network_key}{alert} ? "input_alert" : "",
network_value => $anvil->data->{cgi}{$network_key}{value},
subnet => '255.255.0.0 <input type="hidden" name="'.$network_key.'" id="'.$network_key.'" value="'.$anvil->data->{cgi}{mn1_subnet}{value}.'" />',
}});
}
}
# Now IFNs # Now IFNs
foreach my $i (1..$anvil->data->{cgi}{ifn_count}{value}) foreach my $i (1..$anvil->data->{cgi}{ifn_count}{value})
{ {
@ -3091,7 +3153,7 @@ sub handle_manifest
gateway_value => $anvil->data->{cgi}{$gateway_key}{value}, gateway_value => $anvil->data->{cgi}{$gateway_key}{value},
}}); }});
} }
my $back_link = $anvil->data->{sys}{cgi_string}; my $back_link = $anvil->data->{sys}{cgi_string};
$back_link =~ s/step=2/step=1/; $back_link =~ s/step=2/step=1/;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { back_link => $back_link}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { back_link => $back_link}});
@ -3125,7 +3187,7 @@ sub handle_manifest
my $network_form = ""; my $network_form = "";
my $network_note = ""; my $network_note = "";
foreach my $network ("bcn", "sn", "ifn") foreach my $network ("bcn", "sn", "mn", "ifn")
{ {
if ($network eq "sn") if ($network eq "sn")
{ {
@ -3146,6 +3208,7 @@ sub handle_manifest
my $say_network_code = "striker_0018"; my $say_network_code = "striker_0018";
if ($network eq "sn") { $say_network_code = "striker_0020"; } if ($network eq "sn") { $say_network_code = "striker_0020"; }
elsif ($network eq "ifn") { $say_network_code = "striker_0022"; } elsif ($network eq "ifn") { $say_network_code = "striker_0022"; }
elsif ($network eq "ifn") { $say_network_code = "striker_0299"; }
my $say_network = $anvil->Words->string({key => $say_network_code, variables => { number => $i }}); my $say_network = $anvil->Words->string({key => $say_network_code, variables => { number => $i }});
my $network_key = $network.$i."_network"; my $network_key = $network.$i."_network";
@ -3423,7 +3486,7 @@ sub sanity_check_manifest_step3
{ {
# It's a valid IP. Does it match any BCN or IFN network? # It's a valid IP. Does it match any BCN or IFN network?
my $match_found = 0; my $match_found = 0;
foreach my $network ("bcn", "sn", "ifn") foreach my $network ("bcn", "sn", "mn", "ifn")
{ {
my $count_key = $network."_count"; my $count_key = $network."_count";
foreach my $i (1..$anvil->data->{cgi}{$count_key}{value}) foreach my $i (1..$anvil->data->{cgi}{$count_key}{value})
@ -3464,7 +3527,7 @@ sub sanity_check_manifest_step3
} }
# Now check that the IPs are sane. # Now check that the IPs are sane.
foreach my $network ("bcn", "sn", "ifn") foreach my $network ("bcn", "sn", "mn", "ifn")
{ {
my $count_key = $network."_count"; my $count_key = $network."_count";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
@ -3477,6 +3540,7 @@ sub sanity_check_manifest_step3
my $say_network_code = "striker_0018"; my $say_network_code = "striker_0018";
if ($network eq "sn") { $say_network_code = "striker_0020"; } if ($network eq "sn") { $say_network_code = "striker_0020"; }
elsif ($network eq "ifn") { $say_network_code = "striker_0022"; } elsif ($network eq "ifn") { $say_network_code = "striker_0022"; }
elsif ($network eq "mn") { $say_network_code = "striker_0299"; }
my $say_network = $anvil->Words->string({key => $say_network_code, variables => { number => $i }}); my $say_network = $anvil->Words->string({key => $say_network_code, variables => { number => $i }});
my $network_key = $network.$i."_network"; my $network_key = $network.$i."_network";
@ -3598,7 +3662,18 @@ sub sanity_check_manifest_step2
$sane = check_network($anvil, $sane, $say_sn, "sn1_network", "sn1_subnet", "sn1_gateway"); $sane = check_network($anvil, $sane, $say_sn, "sn1_network", "sn1_subnet", "sn1_gateway");
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sane => $sane }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sane => $sane }});
# There's only ever 1 MN
my $say_mn = $anvil->Words->string({key => "striker_0299", variables => { number => '1' }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_mn => $say_mn }});
# The migration network always uses the subnet /16 and has no gateway.
$anvil->data->{cgi}{mn1_subnet}{value} = "255.255.0.0" if not defined $anvil->data->{cgi}{mn1_subnet}{value};
$anvil->data->{cgi}{mn1_gateway}{value} = "" if not defined $anvil->data->{cgi}{mn1_gateway}{value};
$sane = check_network($anvil, $sane, $say_mn, "mn1_network", "mn1_subnet", "mn1_gateway");
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sane => $sane }});
# Now IFNs # Now IFNs
foreach my $i (1..$anvil->data->{cgi}{ifn_count}{value}) foreach my $i (1..$anvil->data->{cgi}{ifn_count}{value})
{ {
@ -5356,10 +5431,13 @@ sub process_prep_network
process_anvil_menu($anvil); process_anvil_menu($anvil);
return(0); return(0);
} }
my $host_uuid = $anvil->data->{cgi}{host_uuid}{value};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_uuid => $host_uuid }});
# Pull the host's data out of the JSON file. # Pull the host's data out of the JSON file.
$anvil->Striker->parse_all_status_json(); $anvil->Striker->parse_all_status_json();
my $host_type = "";
my $host_name = ""; my $host_name = "";
foreach my $host (sort {$a cmp $b} keys %{$anvil->data->{json}{all_status}{hosts}}) foreach my $host (sort {$a cmp $b} keys %{$anvil->data->{json}{all_status}{hosts}})
{ {
@ -5371,7 +5449,11 @@ sub process_prep_network
{ {
# Found it. # Found it.
$host_name = $host; $host_name = $host;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { host_name => $host_name }}); $host_type = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_name => $host_name,
host_type => $host_type,
}});
last; last;
} }
} }
@ -5404,7 +5486,7 @@ sub process_prep_network
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{interface}}) foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{json}{all_status}{hosts}{$host_name}{network_interface}{interface}})
{ {
# if any interfaces are called 'virbrX-nic', ignore it as it will be removed. Likewise, ignore any 'vnetX' devices. # if any interfaces are called 'virbrX-nic', ignore it as it will be removed. Likewise, ignore any 'vnetX' devices.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { interface => $interface }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface => $interface }});
if (($interface =~ /^virbr\d+-nic/) or ($interface =~ /^vnet\d+/)) if (($interface =~ /^virbr\d+-nic/) or ($interface =~ /^vnet\d+/))
{ {
# Ignore it. # Ignore it.
@ -5417,35 +5499,37 @@ sub process_prep_network
# Store the mac address . # Store the mac address .
$interfaces->{$interface} = $mac_address; $interfaces->{$interface} = $mac_address;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "interfaces->{".$interface."}" => $interfaces->{$interface} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "interfaces->{".$interface."}" => $interfaces->{$interface} }});
} }
# Get the interface count # Get the interface count
my $interface_count = keys %{$interfaces}; my $interface_count = keys %{$interfaces};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { interface_count => $interface_count }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface_count => $interface_count }});
if ($interface_count < 6) if (($host_type eq "node") && ($interface_count < 6))
{ {
# Not enough interfaces. # Not enough interfaces.
$anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "warning_0015", variables => { interface_count => $interface_count } }) }}); $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "warning_0015", variables => { interface_count => $interface_count } }) }});
$anvil->data->{cgi}{task}{value} = ""; $anvil->data->{cgi}{task}{value} = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"cgi::task::value" => $anvil->data->{cgi}{task}{value}, "cgi::task::value" => $anvil->data->{cgi}{task}{value},
"form::error_massage" => $anvil->data->{form}{error_massage}, "form::error_massage" => $anvil->data->{form}{error_massage},
}}); }});
process_anvil_menu($anvil); process_anvil_menu($anvil);
} }
# TODO: For now, we're only allowing one BCN and SN. So at this time, we'll show one BCN pair, one SN # TODO: For now, we're only allowing one BCN, SN and MN. So at this time, we'll show one BCN pair,
# pair and N-IFN pairs. # one SN pair, one MN pair and N-IFN pairs.
my $bcn_pair_count = 1; my $bcn_pair_count = 1;
my $sn_pair_count = 1; my $sn_pair_count = 1;
my $ifn_pair_count = int(($interface_count - 4) / 2); my $mn_pair_count = $interface_count > 6 ? 1 : 0;
my $ifn_pair_count = int(($interface_count - 6) / 2);
$ifn_pair_count = 1 if $ifn_pair_count < 1; $ifn_pair_count = 1 if $ifn_pair_count < 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
bcn_pair_count => $bcn_pair_count, bcn_pair_count => $bcn_pair_count,
sn_pair_count => $sn_pair_count, sn_pair_count => $sn_pair_count,
ifn_pair_count => $ifn_pair_count, ifn_pair_count => $ifn_pair_count,
mn_pair_count => $mn_pair_count,
}}); }});
### NOTE: The weird 'form::config_step2::<x>::value is from reusing the logic used back when ### NOTE: The weird 'form::config_step2::<x>::value is from reusing the logic used back when
@ -5548,7 +5632,7 @@ sub process_prep_network
}); });
} }
} }
foreach my $network ("bcn", "sn", "ifn") foreach my $network ("bcn", "sn", "mn", "ifn")
{ {
my $count_key = $network."_count"; my $count_key = $network."_count";
my $loops = $anvil->data->{cgi}{$count_key}{value}; my $loops = $anvil->data->{cgi}{$count_key}{value};
@ -5578,6 +5662,10 @@ sub process_prep_network
{ {
$say_network = $anvil->Words->string({key => "striker_0020", variables => { number => $i }}); $say_network = $anvil->Words->string({key => "striker_0020", variables => { number => $i }});
} }
elsif ($network eq "mn")
{
$say_network = $anvil->Words->string({key => "striker_0299", variables => { number => $i }});
}
elsif ($network eq "ifn") elsif ($network eq "ifn")
{ {
$say_network = $anvil->Words->string({key => "striker_0022", variables => { number => $i }}); $say_network = $anvil->Words->string({key => "striker_0022", variables => { number => $i }});
@ -5737,7 +5825,7 @@ sub process_prep_network
if ((not $anvil->data->{cgi}{$link1_key}{value}) && (not $anvil->data->{cgi}{$link2_key}{value})) if ((not $anvil->data->{cgi}{$link1_key}{value}) && (not $anvil->data->{cgi}{$link2_key}{value}))
{ {
# If this is network 1, both are required. # If this is network 1, both are required.
if ($i == 1) if (($host_type eq "node") && ($i == 1))
{ {
# Required. # Required.
my $error_message = $anvil->Words->string({key => "warning_0021"}); my $error_message = $anvil->Words->string({key => "warning_0021"});
@ -5896,7 +5984,8 @@ sub process_prep_network
bcn_count => $bcn_pair_count, bcn_count => $bcn_pair_count,
sn_count => $sn_pair_count, sn_count => $sn_pair_count,
ifn_count => $ifn_pair_count, ifn_count => $ifn_pair_count,
gateway => $anvil->data->{cgi}{gateway}{value}, mn_count => $mn_pair_count,
gateway => $anvil->data->{cgi}{gateway}{value},
dns => $anvil->data->{cgi}{dns}{value}, dns => $anvil->data->{cgi}{dns}{value},
}}); }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'form::body' => $anvil->data->{form}{body} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'form::body' => $anvil->data->{form}{body} }});
@ -5921,7 +6010,7 @@ sub process_prep_network
my $interface_form = ""; my $interface_form = "";
# NOTE: We don't assign IPs at this point, unless the user manually sets one. We'll set all to 'dhcp' # NOTE: We don't assign IPs at this point, unless the user manually sets one. We'll set all to 'dhcp'
# until set during the Anvil! build later. # until set during the Anvil! build later.
foreach my $network ("bcn", "sn", "ifn") foreach my $network ("bcn", "sn", "mn", "ifn")
{ {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network => $network }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network => $network }});
my $name_key = ""; my $name_key = "";
@ -5945,7 +6034,13 @@ sub process_prep_network
$description_key = "striker_0023"; $description_key = "striker_0023";
$count = $ifn_pair_count; $count = $ifn_pair_count;
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { elsif ($network eq "mn")
{
$name_key = "striker_0299";
$description_key = "striker_0300";
$count = $mn_pair_count;
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
name_key => $name_key, name_key => $name_key,
description_key => $description_key, description_key => $description_key,
count => $count, count => $count,
@ -6006,7 +6101,7 @@ sub process_prep_network
iface1_select => $this_iface1_form, iface1_select => $this_iface1_form,
iface2_select => $this_iface2_form, iface2_select => $this_iface2_form,
network_name => $network.$i, network_name => $network.$i,
create_bridge => $network eq "sn" ? 0 : 1, create_bridge => (($network eq "sn") or ($network eq "mn")) ? 0 : 1,
}}); }});
} }
} }
@ -6064,6 +6159,7 @@ sub process_prep_network
bcn_count => $bcn_pair_count, bcn_count => $bcn_pair_count,
sn_count => $sn_pair_count, sn_count => $sn_pair_count,
ifn_count => $ifn_pair_count, ifn_count => $ifn_pair_count,
mn_count => $mn_pair_count,
host_uuid => $anvil->data->{cgi}{host_uuid}{value}, host_uuid => $anvil->data->{cgi}{host_uuid}{value},
host_name => $host_name, # This is the current host name, used to find the data in the all_status.json host_name => $host_name, # This is the current host name, used to find the data in the all_status.json
}}); }});
@ -8674,6 +8770,8 @@ x = Network;
- SN = 100 + network - SN = 100 + network
ie: SN1 = 10.101.y.z ie: SN1 = 10.101.y.z
SN2 = 10.102.y.z SN2 = 10.102.y.z
- MN = 199
ie: MN1 = 10.199.y.z
y = Device Type. y = Device Type.
Foudation Pack; Foudation Pack;

@ -558,6 +558,7 @@
<!-- Note: Currently, the user can't choose the number of BCN and SNs. However, this may change later, so we already setup for it. --> <!-- Note: Currently, the user can't choose the number of BCN and SNs. However, this may change later, so we already setup for it. -->
<input type="hidden" name="sn_count" id="sn_count" value="#!variable!sn_count!#" /> <input type="hidden" name="sn_count" id="sn_count" value="#!variable!sn_count!#" />
<input type="hidden" name="bcn_count" id="bcn_count" value="#!variable!bcn_count!#" /> <input type="hidden" name="bcn_count" id="bcn_count" value="#!variable!bcn_count!#" />
<input type="hidden" name="mn_count" id="mn_count" value="#!variable!mn_count!#" />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -775,8 +776,9 @@
<input type="hidden" name="domain" id="domain" value="#!data!cgi::domain::value!#"> <input type="hidden" name="domain" id="domain" value="#!data!cgi::domain::value!#">
<input type="hidden" name="sequence" id="sequence" value="#!data!cgi::sequence::value!#"> <input type="hidden" name="sequence" id="sequence" value="#!data!cgi::sequence::value!#">
<input type="hidden" name="bcn_count" id="bcn_count" value="#!data!cgi::bcn_count::value!#"> <input type="hidden" name="bcn_count" id="bcn_count" value="#!data!cgi::bcn_count::value!#">
<input type="hidden" name="sn_count" id="sn_count" value="#!data!cgi::bcn_count::value!#"> <input type="hidden" name="sn_count" id="sn_count" value="#!data!cgi::sn_count::value!#">
<input type="hidden" name="ifn_count" id="ifn_count" value="#!data!cgi::ifn_count::value!#"> <input type="hidden" name="ifn_count" id="ifn_count" value="#!data!cgi::ifn_count::value!#">
<input type="hidden" name="mn_count" id="mn_count" value="#!data!cgi::mn_count::value!#">
<input type="hidden" name="step" id="step" value="3"> <input type="hidden" name="step" id="step" value="3">
<input type="hidden" name="anvil" id="anvil" value="true"> <input type="hidden" name="anvil" id="anvil" value="true">
<input type="hidden" name="task" id="task" value="manifests"> <input type="hidden" name="task" id="task" value="manifests">
@ -1104,6 +1106,7 @@
<input type="hidden" name="bcn_count" id="bcn_count" value="#!data!cgi::bcn_count::value!#"> <input type="hidden" name="bcn_count" id="bcn_count" value="#!data!cgi::bcn_count::value!#">
<input type="hidden" name="sn_count" id="sn_count" value="#!data!cgi::sn_count::value!#"> <input type="hidden" name="sn_count" id="sn_count" value="#!data!cgi::sn_count::value!#">
<input type="hidden" name="ifn_count" id="ifn_count" value="#!data!cgi::ifn_count::value!#"> <input type="hidden" name="ifn_count" id="ifn_count" value="#!data!cgi::ifn_count::value!#">
<input type="hidden" name="mn_count" id="mn_count" value="#!data!cgi::mn_count::value!#">
<input type="hidden" name="dns" id="dns" value="#!data!cgi::dns::value!#" /> <input type="hidden" name="dns" id="dns" value="#!data!cgi::dns::value!#" />
<input type="hidden" name="ntp" id="ntp" value="#!data!cgi::ntp::value!#" /> <input type="hidden" name="ntp" id="ntp" value="#!data!cgi::ntp::value!#" />
<input type="hidden" name="mtu" id="mtu" value="#!data!cgi::mtu::value!#" /> <input type="hidden" name="mtu" id="mtu" value="#!data!cgi::mtu::value!#" />
@ -2455,6 +2458,7 @@
<input type="hidden" name="bcn_count" id="bcn_count" value="#!variable!bcn_count!#"> <input type="hidden" name="bcn_count" id="bcn_count" value="#!variable!bcn_count!#">
<input type="hidden" name="sn_count" id="sn_count" value="#!variable!sn_count!#"> <input type="hidden" name="sn_count" id="sn_count" value="#!variable!sn_count!#">
<input type="hidden" name="ifn_count" id="ifn_count" value="#!variable!ifn_count!#"> <input type="hidden" name="ifn_count" id="ifn_count" value="#!variable!ifn_count!#">
<input type="hidden" name="mn_count" id="mn_count" value="#!variable!mn_count!#">
<input type="hidden" name="save" id="save" value="true"> <input type="hidden" name="save" id="save" value="true">
<input type="hidden" name="anvil" id="anvil" value="true"> <input type="hidden" name="anvil" id="anvil" value="true">
<input type="hidden" name="task" id="task" value="prep-network"> <input type="hidden" name="task" id="task" value="prep-network">
@ -2557,6 +2561,7 @@
<input type="hidden" name="bcn_count" id="bcn_count" value="#!variable!bcn_count!#"> <input type="hidden" name="bcn_count" id="bcn_count" value="#!variable!bcn_count!#">
<input type="hidden" name="sn_count" id="sn_count" value="#!variable!sn_count!#"> <input type="hidden" name="sn_count" id="sn_count" value="#!variable!sn_count!#">
<input type="hidden" name="ifn_count" id="ifn_count" value="#!variable!ifn_count!#"> <input type="hidden" name="ifn_count" id="ifn_count" value="#!variable!ifn_count!#">
<input type="hidden" name="mn_count" id="mn_count" value="#!variable!mn_count!#">
<input type="hidden" name="dns" id="dns" value="#!variable!dns!#"> <input type="hidden" name="dns" id="dns" value="#!variable!dns!#">
<input type="hidden" name="gateway" id="gateway" value="#!variable!gateway!#"> <input type="hidden" name="gateway" id="gateway" value="#!variable!gateway!#">
<input type="hidden" name="gateway_interface" id="gateway_interface" value="#!variable!gateway_interface!#"> <input type="hidden" name="gateway_interface" id="gateway_interface" value="#!variable!gateway_interface!#">

@ -1,13 +1,15 @@
Add 'lsof' and 'strace' to Required
When pairing Striker, make sure new config goes to all known nodes! When pairing Striker, make sure new config goes to all known nodes!
Immediately set drbdadm to 'secondary' after 'primary --force'
dnf -y update && dnf -y install https://www.alteeve.com/an-repo/m3/anvil-release-latest.noarch.rpm && alteeve-repo-setup -y && dnf -y install anvil-striker --allowerasing dnf -y update && dnf -y install https://www.alteeve.com/an-repo/m3/anvil-release-latest.noarch.rpm && alteeve-repo-setup -y && dnf -y install anvil-striker --allowerasing
dnf -y update && dnf -y install https://www.alteeve.com/an-repo/m3/anvil-release-latest.noarch.rpm && alteeve-repo-setup -y && dnf -y install anvil-node --allowerasing dnf -y update && dnf -y install https://www.alteeve.com/an-repo/m3/anvil-release-latest.noarch.rpm && alteeve-repo-setup -y && dnf -y install anvil-node --allowerasing
dnf -y update && dnf -y install https://www.alteeve.com/an-repo/m3/anvil-release-latest.noarch.rpm && alteeve-repo-setup -y && dnf -y install anvil-dr --allowerasing dnf -y update && dnf -y install https://www.alteeve.com/an-repo/m3/anvil-release-latest.noarch.rpm && alteeve-repo-setup -y && dnf -y install anvil-dr --allowerasing
### Currently set default zone; ### Currently set default zone;
# Doesn't seem to matter - /etc/firewalld/firewalld.conf:6:DefaultZone=public # Doesn't seem to matter - /etc/firewalld/firewalld.conf:6:DefaultZone=public

@ -3,7 +3,7 @@
# This is the resource agent used to manage servers on the Anvil! Intelligent Availability platform. # This is the resource agent used to manage servers on the Anvil! Intelligent Availability platform.
# #
# License: GNU General Public License (GPL) v2+ # License: GNU General Public License (GPL) v2+
# (c) 1997-2021 - Alteeve's Niche! Inc. # (c) 1997-2023 - Alteeve's Niche! Inc.
# #
# WARNING: This is a pretty purpose-specific resource agent. No effort was made to test this on an rgmanager # WARNING: This is a pretty purpose-specific resource agent. No effort was made to test this on an rgmanager
# cluster or on any configuration outside how the Anvil! m3 uses it. If you plan to adapt it to # cluster or on any configuration outside how the Anvil! m3 uses it. If you plan to adapt it to
@ -125,10 +125,18 @@ $anvil->data->{environment}{OCF_ROOT} = defined $ENV{O
$anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_source} = defined $ENV{OCF_RESKEY_CRM_meta_migrate_source} ? $ENV{OCF_RESKEY_CRM_meta_migrate_source} : ""; $anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_source} = defined $ENV{OCF_RESKEY_CRM_meta_migrate_source} ? $ENV{OCF_RESKEY_CRM_meta_migrate_source} : "";
$anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_target} = defined $ENV{OCF_RESKEY_CRM_meta_migrate_target} ? $ENV{OCF_RESKEY_CRM_meta_migrate_target} : ""; $anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_target} = defined $ENV{OCF_RESKEY_CRM_meta_migrate_target} ? $ENV{OCF_RESKEY_CRM_meta_migrate_target} : "";
$anvil->data->{environment}{OCF_RESKEY_CRM_meta_record_pending} = defined $ENV{OCF_RESKEY_CRM_meta_record_pending} ? $ENV{OCF_RESKEY_CRM_meta_record_pending} : ""; $anvil->data->{environment}{OCF_RESKEY_CRM_meta_record_pending} = defined $ENV{OCF_RESKEY_CRM_meta_record_pending} ? $ENV{OCF_RESKEY_CRM_meta_record_pending} : "";
# When run from 'pcs', environment variables are scrubbed, which can cause us problems.
if (not $ENV{PATH})
{
$ENV{PATH} = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin";
}
# Any variable=value arguments in the resource are set under 'OCF_RESKEY_CRM_meta_' # Any variable=value arguments in the resource are set under 'OCF_RESKEY_CRM_meta_'
foreach my $key (sort {$a cmp $b} keys %ENV) foreach my $key (sort {$a cmp $b} keys %ENV)
{ {
next if $key !~ /^OCF_RESKEY_CRM_meta_/; #$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { "ENV{".$key."}" => $ENV{$key} }});
next if $key !~ /^OCF_/;
$anvil->data->{environment}{$key} = $ENV{$key}; $anvil->data->{environment}{$key} = $ENV{$key};
} }
@ -199,8 +207,8 @@ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 0, level
foreach my $key (sort {$a cmp $b} keys %{$anvil->data->{environment}}) foreach my $key (sort {$a cmp $b} keys %{$anvil->data->{environment}})
{ {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"environment::${key}" => $anvil->data->{environment}{$key}, "environment::${key}" => $anvil->data->{environment}{$key},
}}); }});
} }
foreach my $key (sort {$a cmp $b} keys %{$anvil->data->{switches}}) foreach my $key (sort {$a cmp $b} keys %{$anvil->data->{switches}})
@ -218,6 +226,14 @@ if (($anvil->data->{switches}{server}) && (not $anvil->data->{environment}{OCF_R
"environment::OCF_RESKEY_name" => $anvil->data->{environment}{OCF_RESKEY_name}, "environment::OCF_RESKEY_name" => $anvil->data->{environment}{OCF_RESKEY_name},
}}); }});
} }
elsif ((not $anvil->data->{switches}{server}) && ($anvil->data->{environment}{OCF_RESKEY_name}))
{
$anvil->data->{switches}{server} =$anvil->data->{environment}{OCF_RESKEY_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"switches::server" => $anvil->data->{switches}{server},
}});
}
if (($anvil->data->{switches}{migrate_to}) && (not $anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_target})) if (($anvil->data->{switches}{migrate_to}) && (not $anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_target}))
{ {
$anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_target} = $anvil->data->{switches}{migrate_to}; $anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_target} = $anvil->data->{switches}{migrate_to};
@ -1059,7 +1075,7 @@ sub stop_server
# Read in an parse the server's XML. # Read in an parse the server's XML.
$anvil->System->check_storage(); $anvil->System->check_storage();
$anvil->Server->get_status({server => $server}); $anvil->Server->get_status({debug => 2, server => $server});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0313", variables => { server => $server }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0313", variables => { server => $server }});
my $success = $anvil->Server->shutdown_virsh({server => $server}); my $success = $anvil->Server->shutdown_virsh({server => $server});
@ -1281,7 +1297,14 @@ pmsuspended - The domain has been suspended by guest power management, e.g. ente
sub migrate_server sub migrate_server
{ {
my ($anvil) = @_; my ($anvil) = @_;
# Do a sensitive, quick DB connect.
$anvil->Database->connect({
check_for_resync => 0,
retry => 0,
sensitive => 1,
});
### NOTE: For now, we're not going to block if the target is not UpToDate. There are times when a ### NOTE: For now, we're not going to block if the target is not UpToDate. There are times when a
### user might want to do this (ie: sync will be done soon and the need to evacuate the node ### user might want to do this (ie: sync will be done soon and the need to evacuate the node
### ASAP is high). Maybe we'll enforce this and require a '--force' switch later? ### ASAP is high). Maybe we'll enforce this and require a '--force' switch later?
@ -1594,7 +1617,7 @@ sub validate_all
source => $source, source => $source,
target => $target, target => $target,
}}); }});
# Log what we're doing. # Log what we're doing.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0581", variables => { server => $server }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0581", variables => { server => $server }});

@ -162,14 +162,6 @@ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "
# Read switches # Read switches
$anvil->Get->switches; $anvil->Get->switches;
# Too many connections cause the UPS to lag out, so we only run on Strikers.
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") && (not $anvil->data->{switches}{force}))
{
$anvil->nice_exit({exit_code => 1});
}
# Handle start-up tasks # Handle start-up tasks
my $problem = $anvil->ScanCore->agent_startup({agent => $THIS_FILE}); my $problem = $anvil->ScanCore->agent_startup({agent => $THIS_FILE});
if ($problem) if ($problem)
@ -177,6 +169,17 @@ if ($problem)
$anvil->nice_exit({exit_code => 1}); $anvil->nice_exit({exit_code => 1});
} }
# The PDUs don't allow multiple connections at the same time. This causes a lot of false alerts when many
# machines try to scan. As such, only Striker dashboards watch APC PDUs.
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") && (not $anvil->data->{switches}{force}))
{
# Exit.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_apc_pdu_message_0041", variables => { program => $THIS_FILE }});
$anvil->nice_exit({exit_code => 0});
}
if ($anvil->data->{switches}{purge}) if ($anvil->data->{switches}{purge})
{ {
# This can be called when doing bulk-database purges. # This can be called when doing bulk-database purges.
@ -1173,15 +1176,26 @@ WHERE
# Have any outlets changed? # Have any outlets changed?
foreach my $scan_apc_pdu_outlet_number (sort {$a cmp $b} keys %{$anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}}) foreach my $scan_apc_pdu_outlet_number (sort {$a cmp $b} keys %{$anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}})
{ {
my $scan_apc_pdu_serial_number = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu}{scan_apc_pdu_serial_number};
my $new_scan_apc_pdu_outlet_name = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_name}; my $new_scan_apc_pdu_outlet_name = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_name};
my $new_scan_apc_pdu_outlet_on_phase = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_on_phase}; my $new_scan_apc_pdu_outlet_on_phase = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_on_phase};
my $new_scan_apc_pdu_outlet_state = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state}; my $new_scan_apc_pdu_outlet_state = $anvil->data->{pdu}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_state};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
scan_apc_pdu_serial_number => $scan_apc_pdu_serial_number,
new_scan_apc_pdu_outlet_name => $new_scan_apc_pdu_outlet_name, new_scan_apc_pdu_outlet_name => $new_scan_apc_pdu_outlet_name,
new_scan_apc_pdu_outlet_on_phase => $new_scan_apc_pdu_outlet_on_phase, new_scan_apc_pdu_outlet_on_phase => $new_scan_apc_pdu_outlet_on_phase,
new_scan_apc_pdu_outlet_state => $new_scan_apc_pdu_outlet_state, new_scan_apc_pdu_outlet_state => $new_scan_apc_pdu_outlet_state,
}}); }});
if (($new_scan_apc_pdu_outlet_on_phase ne "!!no_connection!!") or ($new_scan_apc_pdu_outlet_on_phase ne "#!no_value!#"))
{
$anvil->Alert->check_condition_age({clear => 1, name => "scan_apc_pdu::pdu::".$scan_apc_pdu_serial_number."::phase_lost::".$scan_apc_pdu_outlet_number});
}
if (($new_scan_apc_pdu_outlet_state eq "!!no_connection!!") or ($new_scan_apc_pdu_outlet_state eq "#!no_value!#"))
{
$anvil->Alert->check_condition_age({clear => 1, name => "scan_apc_pdu::pdu::".$scan_apc_pdu_serial_number."::outlet_lost::".$scan_apc_pdu_outlet_number});
}
# Do I know about this outlet? # Do I know about this outlet?
if ($anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_uuid}) if ($anvil->data->{sql}{scan_apc_pdu_uuid}{$scan_apc_pdu_uuid}{scan_apc_pdu_outlets}{$scan_apc_pdu_outlet_number}{scan_apc_pdu_outlet_uuid})
{ {
@ -1241,7 +1255,20 @@ WHERE
} }
if ($new_scan_apc_pdu_outlet_on_phase ne $old_scan_apc_pdu_outlet_on_phase) if ($new_scan_apc_pdu_outlet_on_phase ne $old_scan_apc_pdu_outlet_on_phase)
{ {
# Phase changed, but why tho? # Phase changed. If the new phase is '!!no_connection!!', it
# could be contention, so check if this has been the case for
# at least five minutes.
if (($new_scan_apc_pdu_outlet_on_phase eq "!!no_connection!!") or ($new_scan_apc_pdu_outlet_on_phase eq "#!no_value!#"))
{
my $age = $anvil->Alert->check_condition_age({name => "scan_apc_pdu::pdu::".$scan_apc_pdu_serial_number."::phase_lost::".$scan_apc_pdu_outlet_number});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { age => $age }});
if ($age < 600)
{
# Ignore it for now.
next;
}
}
$changes = 1; $changes = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }});
@ -1262,6 +1289,19 @@ WHERE
} }
if ($new_scan_apc_pdu_outlet_state ne $old_scan_apc_pdu_outlet_state) if ($new_scan_apc_pdu_outlet_state ne $old_scan_apc_pdu_outlet_state)
{ {
# If we had a contention and the new value is '!!no_connection!!'.
if (($new_scan_apc_pdu_outlet_state eq "!!no_connection!!") or ($new_scan_apc_pdu_outlet_state eq "#!no_value!#"))
{
my $age = $anvil->Alert->check_condition_age({name => "scan_apc_pdu::pdu::".$scan_apc_pdu_serial_number."::outlet_lost::".$scan_apc_pdu_outlet_number});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { age => $age }});
if ($age < 600)
{
# Ignore it for now.
next;
}
}
# This is likely from a fence action, so we make it critical # This is likely from a fence action, so we make it critical
$changes = 1; $changes = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changes => $changes }});
@ -1658,11 +1698,11 @@ sub clear_phase_low_warning
if ($changed) if ($changed)
{ {
# Register an alert-cleared event. # Register an alert-cleared event.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0029"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0029", variables => { phase => $scan_apc_pdu_phase_number, name => $pdu_host_name }});
$anvil->Alert->register({ $anvil->Alert->register({
alert_level => "notice", alert_level => "notice",
clear => 1, clear => 1,
message => "scan_apc_pdu_message_0029", message => "scan_apc_pdu_message_0029,!!phase!".$scan_apc_pdu_phase_number."!!,!!name!".$pdu_host_name."!!",
set_by => $THIS_FILE, set_by => $THIS_FILE,
}); });
} }
@ -1683,11 +1723,11 @@ sub set_phase_high_warning
if ($changed) if ($changed)
{ {
# Register an alert-cleared event. # Register an alert-cleared event.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0027"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0027", variables => { phase => $scan_apc_pdu_phase_number, name => $pdu_host_name }});
$anvil->Alert->register({ $anvil->Alert->register({
alert_level => "notice", alert_level => "notice",
clear => 1, clear => 1,
message => "scan_apc_pdu_message_0027", message => "scan_apc_pdu_message_0027,!!phase!".$scan_apc_pdu_phase_number."!!,!!name!".$pdu_host_name."!!",
set_by => $THIS_FILE, set_by => $THIS_FILE,
}); });
} }
@ -1709,11 +1749,11 @@ sub clear_phase_high_warning
if ($changed) if ($changed)
{ {
# Register an alert-cleared event. # Register an alert-cleared event.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0028"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0028", variables => { phase => $scan_apc_pdu_phase_number, name => $pdu_host_name }});
$anvil->Alert->register({ $anvil->Alert->register({
alert_level => "notice", alert_level => "notice",
clear => 1, clear => 1,
message => "scan_apc_pdu_message_0028", message => "scan_apc_pdu_message_0028,!!phase!".$scan_apc_pdu_phase_number."!!,!!name!".$pdu_host_name."!!",
set_by => $THIS_FILE, set_by => $THIS_FILE,
}); });
} }
@ -1734,11 +1774,11 @@ sub set_phase_high_critical
if ($changed) if ($changed)
{ {
# Register an alert-cleared event. # Register an alert-cleared event.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0025"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0025", variables => { phase => $scan_apc_pdu_phase_number, name => $pdu_host_name }});
$anvil->Alert->register({ $anvil->Alert->register({
alert_level => "notice", alert_level => "notice",
clear => 1, clear => 1,
message => "scan_apc_pdu_message_0025", message => "scan_apc_pdu_message_0025,!!phase!".$scan_apc_pdu_phase_number."!!,!!name!".$pdu_host_name."!!",
set_by => $THIS_FILE, set_by => $THIS_FILE,
}); });
} }
@ -1760,11 +1800,11 @@ sub clear_phase_high_critical
if ($changed) if ($changed)
{ {
# Register an alert-cleared event. # Register an alert-cleared event.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0026"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0026", variables => { phase => $scan_apc_pdu_phase_number, name => $pdu_host_name }});
$anvil->Alert->register({ $anvil->Alert->register({
alert_level => "notice", alert_level => "notice",
clear => 1, clear => 1,
message => "scan_apc_pdu_message_0026", message => "scan_apc_pdu_message_0026,!!phase!".$scan_apc_pdu_phase_number."!!,!!name!".$pdu_host_name."!!",
set_by => $THIS_FILE, set_by => $THIS_FILE,
}); });
} }

@ -81,6 +81,7 @@ A new PDU: [#!variable!name!#] has been found
<key name="scan_apc_pdu_message_0038">- Phase: [#!variable!phase!#] current amperage draw: [#!variable!amps!#].</key> <key name="scan_apc_pdu_message_0038">- Phase: [#!variable!phase!#] current amperage draw: [#!variable!amps!#].</key>
<key name="scan_apc_pdu_message_0039">- Outlet: [#!variable!outlet!#], on phase: [#!variable!on_phase!#] is: [#!variable!state!#] (name: [#!variable!name!#]).</key> <key name="scan_apc_pdu_message_0039">- Outlet: [#!variable!outlet!#], on phase: [#!variable!on_phase!#] is: [#!variable!state!#] (name: [#!variable!name!#]).</key>
<key name="scan_apc_pdu_message_0040">The PDU model: [#!variable!model!#] at the IP address: [#!variable!ip_address!#] has vanished! Did the network cable come unplugged?</key> <key name="scan_apc_pdu_message_0040">The PDU model: [#!variable!model!#] at the IP address: [#!variable!ip_address!#] has vanished! Did the network cable come unplugged?</key>
<key name="scan_apc_pdu_message_0041">APC PDUs only allow one connection at a time. To avoid contention, only Striker dashboards scan APC PDUs. If you want this to run, you can use '--force'. Exiting.</key>
<!-- Units --> <!-- Units -->
<key name="scan_apc_pdu_unit_0001">Unknown</key> <key name="scan_apc_pdu_unit_0001">Unknown</key>

@ -127,7 +127,7 @@ CREATE TRIGGER trigger_scan_cluster_nodes
-- TODO: We may want to track this data in the future. For now, we're not going to bother as we can always -- TODO: We may want to track this data in the future. For now, we're not going to bother as we can always
-- dig through the historical cib.xml.X files on the nodes. -- dig through the historical cib.xml.X files on the nodes.
-- --
-- -- Constraints; Useful for tracking when servers are asked to migate. -- -- Constraints; Useful for tracking when servers are asked to migrate.
-- CREATE TABLE scan_cluster_constraints ( -- CREATE TABLE scan_cluster_constraints (
-- scan_cluster_constraint_uuid uuid primary key, -- scan_cluster_constraint_uuid uuid primary key,
-- scan_cluster_constraint_scan_cluster_uuid uuid not null, -- The parent scan_cluster_uuid. -- scan_cluster_constraint_scan_cluster_uuid uuid not null, -- The parent scan_cluster_uuid.

@ -154,6 +154,7 @@ sub check_config
} }
my $updated = $anvil->DRBD->update_global_common({ my $updated = $anvil->DRBD->update_global_common({
debug => 2,
usage_count => $anvil->data->{sys}{privacy}{strong} ? 0 : 1, usage_count => $anvil->data->{sys}{privacy}{strong} ? 0 : 1,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { updated => $updated }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { updated => $updated }});

@ -1054,12 +1054,14 @@ sub collect_data
'state' => $state, 'state' => $state,
}}); }});
if ($state ne "active") # This could be 'active' or 'activated'
if ($state !~ /activ/)
{ {
# Try brinding the interface up. # Try brinding the interface up.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "warning_0147", variables => { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "warning_0147", variables => {
interface => $interface, interface => $interface,
uptime => $uptime, uptime => $uptime,
'state' => $state,
}}); }});
my $shell_call = $anvil->data->{path}{exe}{ifup}." ".$name; my $shell_call = $anvil->data->{path}{exe}{ifup}." ".$name;

@ -91,6 +91,9 @@ record_migration_times($anvil);
# Check if we need to update the websocket stuff. # Check if we need to update the websocket stuff.
check_vnc($anvil); check_vnc($anvil);
# Check that there's a DRBD fence rule for each server.
check_drbd_fence_rules($anvil);
# Shut down. # Shut down.
$anvil->ScanCore->agent_shutdown({agent => $THIS_FILE}); $anvil->ScanCore->agent_shutdown({agent => $THIS_FILE});
@ -99,6 +102,73 @@ $anvil->ScanCore->agent_shutdown({agent => $THIS_FILE});
# Functions # # Functions #
############################################################################################################# #############################################################################################################
# Check that there's a DRBD fence rule for each server.
sub check_drbd_fence_rules
{
my ($anvil) = @_;
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 "node")
{
return(0);
}
my $problem = $anvil->Cluster->parse_cib();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }});
if ($problem)
{
return(0);
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"cib::parsed::local::ready" => $anvil->data->{cib}{parsed}{'local'}{ready},
}});
if (not $anvil->data->{cib}{parsed}{'local'}{ready})
{
return(0);
}
foreach my $server_name (sort {$a cmp $b} keys %{$anvil->data->{'scan-server'}{server_name}})
{
my $drbd_fence_rule_exists = $anvil->data->{cib}{parsed}{data}{server}{$server_name}{drbd_fence_rule}{'exists'};
my $drbd_fence_rule_attribute = $anvil->data->{cib}{parsed}{data}{server}{$server_name}{drbd_fence_rule}{attribute};
my $drbd_fence_rule_operation = $anvil->data->{cib}{parsed}{data}{server}{$server_name}{drbd_fence_rule}{operation};
my $drbd_fence_rule_value = $anvil->data->{cib}{parsed}{data}{server}{$server_name}{drbd_fence_rule}{value};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:server_name' => $server_name,
's2:drbd_fence_rule_exists' => $drbd_fence_rule_exists,
's3:drbd_fence_rule_attribute' => $drbd_fence_rule_attribute,
's4:drbd_fence_rule_operation' => $drbd_fence_rule_operation,
's5:drbd_fence_rule_value' => $drbd_fence_rule_value,
}});
### TODO: Verify that the other values are correct.
# If it's missing, add it
if (not $drbd_fence_rule_exists)
{
# Create it.
my $variables = {
server => $server_name,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0019", variables => $variables});
$anvil->Alert->register({alert_level => "notice", message => "scan_server_alert_0019", variables => $variables, set_by => $THIS_FILE});
#
my $shell_call = $anvil->data->{path}{exe}{pcs}." constraint location ".$server_name." rule score=-INFINITY drbd-fenced_".$server_name." eq 1";
$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, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
}
}
return(0);
}
# #
sub check_vnc sub check_vnc
{ {
@ -755,7 +825,7 @@ DELETED - Marks a server as no longer existing
} }
if ($server_host_uuid ne $old_server_host_uuid) if ($server_host_uuid ne $old_server_host_uuid)
{ {
# Server migated (to the peer or to a new Anvil!) # Server migrated (to the peer or to a new Anvil!)
my $variables = { my $variables = {
server => $server_name, server => $server_name,
old_host_name => $old_server_host_uuid eq "NULL" ? "NULL" : $anvil->Get->host_name_from_uuid({host_uuid => $old_server_host_uuid}), old_host_name => $old_server_host_uuid eq "NULL" ? "NULL" : $anvil->Get->host_name_from_uuid({host_uuid => $old_server_host_uuid}),

@ -110,6 +110,9 @@ The definition for the server: [#!variable!server!#] was changed in the database
#!variable!new_difference!# #!variable!new_difference!#
====================== ======================
</key> </key>
<key name="scan_server_alert_0019">
There was no DRBD fence rule for the: [#!variable!server!#] in the pacemaker configuration. Adding it now.
</key>
<!-- Log entries --> <!-- Log entries -->
<key name="scan_server_log_0001">Starting: [#!variable!program!#].</key> <key name="scan_server_log_0001">Starting: [#!variable!program!#].</key>

@ -408,6 +408,59 @@ CREATE TRIGGER trigger_anvils
FOR EACH ROW EXECUTE PROCEDURE history_anvils(); FOR EACH ROW EXECUTE PROCEDURE history_anvils();
-- This is the new method of tracking DR hosts and while Anvil! node pairs they can back up. This allows DR
-- hosts to protect multiple nodes, and allow multiple DRs to protect one node pair.
CREATE TABLE dr_links (
dr_link_uuid uuid not null primary key,
dr_link_host_uuid uuid not null,
dr_link_anvil_uuid uuid not null,
dr_link_note text, -- Set to 'DELETE' when no longer used.
modified_date timestamp with time zone not null,
FOREIGN KEY(dr_link_host_uuid) REFERENCES hosts(host_uuid),
FOREIGN KEY(dr_link_anvil_uuid) REFERENCES anvils(anvil_uuid)
);
ALTER TABLE dr_links OWNER TO admin;
CREATE TABLE history.dr_links (
history_id bigserial,
dr_link_uuid uuid,
dr_link_host_uuid uuid,
dr_link_anvil_uuid uuid,
dr_link_note text,
modified_date timestamp with time zone not null
);
ALTER TABLE history.dr_links OWNER TO admin;
CREATE FUNCTION history_dr_links() RETURNS trigger
AS $$
DECLARE
history_dr_links RECORD;
BEGIN
SELECT INTO history_dr_links * FROM dr_links WHERE dr_link_uuid = new.dr_link_uuid;
INSERT INTO history.dr_links
(dr_link_uuid,
dr_link_host_uuid,
dr_link_anvil_uuid,
dr_link_note,
modified_date)
VALUES
(history_dr_links.dr_link_uuid,
history_dr_links.dr_link_host_uuid,
history_dr_links.dr_link_anvil_uuid,
history_dr_links.dr_link_note,
history_dr_links.modified_date);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
ALTER FUNCTION history_dr_links() OWNER TO admin;
CREATE TRIGGER trigger_dr_links
AFTER INSERT OR UPDATE ON dr_links
FOR EACH ROW EXECUTE PROCEDURE history_dr_links();
-- This stores alerts coming in from various sources -- This stores alerts coming in from various sources
CREATE TABLE alerts ( CREATE TABLE alerts (
alert_uuid uuid not null primary key, alert_uuid uuid not null primary key,

@ -16,7 +16,7 @@ Author: Madison Kelly <mkelly@alteeve.ca>
<key name="brand_0002">Anvil!</key> <key name="brand_0002">Anvil!</key>
<key name="brand_0003">Striker</key> <key name="brand_0003">Striker</key>
<key name="brand_0004">ScanCore</key> <key name="brand_0004">ScanCore</key>
<key name="brand_0005"><![CDATA[&copy; 1997 - 2022 <a href="https://alteeve.com/" target="_new">Alteeve's Niche! Inc.</a>, Toronto, Ontario, Canada]]></key> <key name="brand_0005"><![CDATA[&copy; 1997 - 2023 <a href="https://alteeve.com/" target="_new">Alteeve's Niche! Inc.</a>, Toronto, Ontario, Canada]]></key>
<key name="brand_0006"><![CDATA[<i>Anvil!</i>]]></key> <key name="brand_0006"><![CDATA[<i>Anvil!</i>]]></key>
<key name="brand_0007">Node</key> <key name="brand_0007">Node</key>
<key name="brand_0008">DR Host</key> <key name="brand_0008">DR Host</key>
@ -566,7 +566,13 @@ The definition data passed in was:
* 2 or "warning" * 2 or "warning"
* 3 or "notice" * 3 or "notice"
* 4 or "info"</key> * 4 or "info"</key>
<key name="error_0394">[ Error ] - The host UUID: [#!variable!uuid!#] was not found.</key>
<key name="error_0395">[ Error ] - The host UUID: [#!variable!uuid!#], with the host name: [#!variable!name!#] is of host type: [#!variable!type!#]. This must be a type 'dr'.</key>
<key name="error_0396">[ Error ] - The Anvil! UUID: [#!variable!uuid!#] was not found.</key>
<key name="error_0397">[ Error ] - The DR link UUID: [#!variable!uuid!#] was not found.</key>
<key name="error_0398">[ Error ] - There was a problem processing the requested network: [#!variable!network!#]. Details should be logged.</key>
<key name="error_0399">[ Error ] - It looks like the new device: [#!variable!resource!#] failed to appear. Unable to proceed.</key>
<!-- Files templates --> <!-- Files templates -->
<!-- NOTE: Translating these files requires an understanding of which lines are translatable --> <!-- NOTE: Translating these files requires an understanding of which lines are translatable -->
<!-- comments and which are command lines that can't be changed! --> <!-- comments and which are command lines that can't be changed! -->
@ -956,7 +962,7 @@ resource #!variable!server!# {
<key name="header_0075">RAM Used</key> <key name="header_0075">RAM Used</key>
<key name="header_0076">RAM Free</key> <key name="header_0076">RAM Free</key>
<key name="header_0077">Bridges</key> <key name="header_0077">Bridges</key>
<key name="header_0078">Storage Group</key> <key name="header_0078">#!free!#</key>
<key name="header_0079">Used</key> <key name="header_0079">Used</key>
<key name="header_0080">Free</key> <key name="header_0080">Free</key>
<key name="header_0081">Anvil! Node</key> <key name="header_0081">Anvil! Node</key>
@ -984,7 +990,8 @@ resource #!variable!server!# {
<key name="header_0103">Host Type</key> <key name="header_0103">Host Type</key>
<key name="header_0104">Host UUID</key> <key name="header_0104">Host UUID</key>
<key name="header_0105">Machines</key> <key name="header_0105">Machines</key>
<key name="header_0106">MN link #!variable!number!#</key>
<!-- Strings used by jobs --> <!-- Strings used by jobs -->
<key name="job_0001">Configure Network</key> <key name="job_0001">Configure Network</key>
<key name="job_0002">The network configuration will be updated based on the variables stored in the database. Reconnecting to the machine using the new IP address may be required.</key> <key name="job_0002">The network configuration will be updated based on the variables stored in the database. Reconnecting to the machine using the new IP address may be required.</key>
@ -1153,7 +1160,7 @@ Failure! The return code: [#!variable!return_code!#] was received ('0' was expec
<key name="job_0161">* Please enter a number between 1 and #!variable!max_cores!#.</key> <key name="job_0161">* Please enter a number between 1 and #!variable!max_cores!#.</key>
<key name="job_0162">-=] Available cores / threads: [#!variable!cores!# / #!variable!threads!#]</key> <key name="job_0162">-=] Available cores / threads: [#!variable!cores!# / #!variable!threads!#]</key>
<key name="job_0163"> - Node #!variable!core!# CPU Model: [#!variable!model!#]</key> <key name="job_0163"> - Node #!variable!core!# CPU Model: [#!variable!model!#]</key>
<key name="job_0164"> - DR Host CPU: .... [#!variable!model!#], [#!variable!cores!#c]/[#!variable!threads!#t]</key> <key name="job_0164">#!free!#</key>
<key name="job_0165">RAM: ........... [#!variable!ram!#]</key> <key name="job_0165">RAM: ........... [#!variable!ram!#]</key>
<key name="job_0166">* Please enter a valid amount up to: [#!variable!ram_total!# / #!variable!ram_available!#].</key> <key name="job_0166">* Please enter a valid amount up to: [#!variable!ram_total!# / #!variable!ram_available!#].</key>
<key name="job_0167">-=] Available RAM: [#!variable!ram_available!#] <key name="job_0167">-=] Available RAM: [#!variable!ram_available!#]
@ -1161,9 +1168,8 @@ Failure! The return code: [#!variable!return_code!#] was received ('0' was expec
- Allocated to servers: [#!variable!ram_allocated!#] - Allocated to servers: [#!variable!ram_allocated!#]
- Node 1 RAM (total): . [#!variable!ram_node1!#] - Node 1 RAM (total): . [#!variable!ram_node1!#]
- Node 2 RAM (total): . [#!variable!ram_node2!#]</key> - Node 2 RAM (total): . [#!variable!ram_node2!#]</key>
<key name="job_0168"> - DR Host RAM (total): [#!variable!ram_available!#]</key> <key name="job_0168">#!free!#</key>
<key name="job_0169"> Available on Anvil!: [#!variable!vg_free!#], Total: [#!variable!vg_size!#] <key name="job_0169"> Available on Anvil!: [#!variable!vg_free!#], Total: [#!variable!vg_size!#]</key>
Available on DR: ... [#!variable!dr_free!#], Total: [#!variable!dr_size!#]</key>
<key name="job_0170">Storage Group: . [#!variable!storage_group!#]</key> <key name="job_0170">Storage Group: . [#!variable!storage_group!#]</key>
<key name="job_0171">* Please enter a number beside the storage group you want to use.</key> <key name="job_0171">* Please enter a number beside the storage group you want to use.</key>
<key name="job_0172">-=] Storage groups</key> <key name="job_0172">-=] Storage groups</key>
@ -1197,7 +1203,9 @@ It should be provisioned in the next minute or two.</key>
<key name="job_0194">The LV(s) behind the resource: [#!variable!resource!#] already existed, and the DRBD resource is not in the disk state 'UpToDate'. As such, we'll keep waiting before provisioning the server.</key> <key name="job_0194">The LV(s) behind the resource: [#!variable!resource!#] already existed, and the DRBD resource is not in the disk state 'UpToDate'. As such, we'll keep waiting before provisioning the server.</key>
<key name="job_0195">The resource needs to be forced to UpToDate as it is brand now, doing that now.</key> <key name="job_0195">The resource needs to be forced to UpToDate as it is brand now, doing that now.</key>
<key name="job_0196">-=] OS Short List</key> <key name="job_0196">-=] OS Short List</key>
<key name="job_0197">* Please enter an OS key that is closest to your target OS. Run 'osinfo-query os' for a full list.</key> <key name="job_0197">* Please enter an OS key that is closest to your target OS.
From any machine in the Anvil!, run 'osinfo-query os'.
Use the 'Short ID' that best matches your OS.</key>
<key name="job_0198">Optimize for: .. [#!variable!os!#]</key> <key name="job_0198">Optimize for: .. [#!variable!os!#]</key>
<key name="job_0199">Ready to provision the server! Please be patient, this could take a moment. The call to create the server will be: <key name="job_0199">Ready to provision the server! Please be patient, this could take a moment. The call to create the server will be:
==== ====
@ -1221,7 +1229,7 @@ It should be provisioned in the next minute or two.</key>
<key name="job_0215">The server has been flagged as deleted now.</key> <key name="job_0215">The server has been flagged as deleted now.</key>
<key name="job_0216">The server delete is complete on this host!</key> <key name="job_0216">The server delete is complete on this host!</key>
<key name="job_0217">It looks like ScanCore has not yet run on one or both nodes in this Anvil! system. Missing resource data, so unable to proceed.</key> <key name="job_0217">It looks like ScanCore has not yet run on one or both nodes in this Anvil! system. Missing resource data, so unable to proceed.</key>
<key name="job_0218">Manually calling 'scan-drbd' to ensure that the new agent is recorded.</key> <key name="job_0218">Manually calling 'scan-drbd' to ensure that the new resource is recorded.</key>
<key name="job_0219">The server name: [#!variable!server_name!#] is already used by another server.</key> <key name="job_0219">The server name: [#!variable!server_name!#] is already used by another server.</key>
<key name="job_0220">Deleting the server's definition file: [#!variable!file!#]...</key> <key name="job_0220">Deleting the server's definition file: [#!variable!file!#]...</key>
<key name="job_0221">The server: [#!variable!server_name!#] was not found in the cluster configuration. This can happen if a server was partially deleted and we're trying again.</key> <key name="job_0221">The server: [#!variable!server_name!#] was not found in the cluster configuration. This can happen if a server was partially deleted and we're trying again.</key>
@ -1470,7 +1478,45 @@ Note: This is a permanent action! If you protect this server again later, a full
<key name="job_0428">The server: [#!variable!server!#] is still running two minutes after asking it to stop. It might have woken up on the first press and ignored the shutdown request (Hi Windows). Pressing the poewr button again.</key> <key name="job_0428">The server: [#!variable!server!#] is still running two minutes after asking it to stop. It might have woken up on the first press and ignored the shutdown request (Hi Windows). Pressing the poewr button again.</key>
<key name="job_0429">Copying the Long-throw (drbd proxy) license file: [#!variable!file!#] into place.</key> <key name="job_0429">Copying the Long-throw (drbd proxy) license file: [#!variable!file!#] into place.</key>
<key name="job_0430">The fence device: [#!variable!device!#] no longer has a port associated with it, will remove it.</key> <key name="job_0430">The fence device: [#!variable!device!#] no longer has a port associated with it, will remove it.</key>
<key name="job_0431">Calling drbdadm adjust to load the new resource, then waiting for the DRBD device to appear.</key>
<key name="job_0432">The new DRBD resource: [#!variable!resource!#] now exists!</key>
<key name="job_0433">Still waiting for the new DRBD resource: [#!variable!resource!#] to appear...</key>
<key name="job_0434">Waiting for up to a minute to see if the peer connects before provisioning the server.</key>
<key name="job_0435">One or more peer disk state or roles are 'unknown', waiting: [#!variable!waiting!#] seconds longer.</key>
<key name="job_0436">Peer disk state or role still unknown after one minute, proceeding without it.</key>
<key name="job_0437"><![CDATA[Missing '--anvil <name_or_uuid>'.]]></key>
<key name="job_0438"><![CDATA[Missing '--name <server_name>'.]]></key>
<key name="job_0439"><![CDATA[Missing '--os <os_variant>'. Valid options match 'virt-install --os-variant' (run: 'osinfo-query os' and reference the 'Short ID' column).]]></key>
<key name="job_0440"><![CDATA[The OS: [#!variable!os!#] was not found. If you're sure the OS is valid, please run 'striker-parse-os-list --xml --new' and add the output to 'words.xml'.]]></key>
<key name="job_0441"><![CDATA[Missing '--cpu <1 ~ #!variable!threads!#>'.]]></key>
<key name="job_0442"><![CDATA[The number of CPU cores: [#!variable!cores!#] is invalid. Must be between 1 and #!variable!threads!#.]]></key>
<key name="job_0443"><![CDATA[Missing '--ram <bytes or human readable, min is 64KiB, max is #!variable!max_ram!#>'.]]></key>
<key name="job_0444"><![CDATA[The requested RAM: [#!variable!ram!#] is not valid. Must be between: [64KiB] and: [#!variable!max_ram!#].]]></key>
<key name="job_0445"><![CDATA[Missing '--storage-group <name or uuid>'. Valid options are:]]></key>
<key name="job_0446"><![CDATA[- Name: [#!variable!name!#], UUID: [#!variable!uuid!#].]]></key>
<key name="job_0447"><![CDATA[- The requested storage group: [#!variable!storage_group!#] does not appear to be valid. Valid options are:]]></key>
<key name="job_0448"><![CDATA[Missing '--storage-size <bytes or human readable>'. Max is: [#!variable!storage_group_size!#].]]></key>
<key name="job_0449"><![CDATA[Missing '--storage-size <bytes or human readable>'. Max will depend on selected --storage-group.]]></key>
<key name="job_0450"><![CDATA[The requested disk size: [#!variable!storage_size!#] is not valid. Must be between: [10MiB] and: [#!variable!max_size!#].]]></key>
<key name="job_0451"><![CDATA[Missing '--install-media <file_name or file_uuid>'. Valid options are:]]></key>
<key name="job_0452"><![CDATA[- File name: [#!variable!name!#], file UUID: [#!variable!uuid!#].]]></key>
<key name="job_0453"><![CDATA[The install file: [#!variable!file!#] is not an ISO, so it can't be used to install.]]></key>
<key name="job_0454"><![CDATA[The install file: [#!variable!file!#] was not found on this Anvil!. Valid options are:]]></key>
<key name="job_0455"><![CDATA[Is it in '/mnt/shared/files/' and are the daemons running?]]></key>
<key name="job_0456"><![CDATA[The driver file: [#!variable!file!#] is not an ISO, so it can't be used as an optical disc.]]></key>
<key name="job_0457"><![CDATA[The driver file: [#!variable!file!#] was not found on this Anvil!. Valid options are:]]></key>
<key name="job_0458"><![CDATA[Available options
--name - Alphanumeric, 1~16 characters long.
--os - Run: 'osinfo-query os' and reference the 'Short ID' column for valid options.
--cpu - 1 ~ #!varaible!threads!#
--ram - Bytes or human readable, min is 64KiB, max is #!variable!max_ram.
--storage-group - Name or UUID. Valid options are:]]></key>
<key name="job_0459"><![CDATA[ - Name: [#!variable!name!#], UUID: [#!variable!uuid!#], free space: [#!variable!free_space!#]]]></key>
<key name="job_0460"><![CDATA[ --storage-size - Disk size, see above for space available
--install-media - File name or file UUID. Available discs are:]]></key>
<key name="job_0461"><![CDATA[ - File name: [#!variable!name!#], file UUID: [#!variable!uuid!#], size: [#!variable!size!#]]]></key>
<key name="job_0462"><![CDATA[ --driver-disc - (optional) A driver disc to be added as a second optical drive. Valid options are above.]]></key>
<!-- Log entries --> <!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key> <key name="log_0001">Starting: [#!variable!program!#].</key>
<key name="log_0002"> <key name="log_0002">
@ -2303,7 +2349,9 @@ The file: [#!variable!file!#] needs to be updated. The difference is:
<key name="log_0729">The DRBD Proxy license file has expired.</key> <key name="log_0729">The DRBD Proxy license file has expired.</key>
<key name="log_0730">None of the MAC sddresses in the The DRBD Proxy license file match any of the MAC addresses on this system.</key> <key name="log_0730">None of the MAC sddresses in the The DRBD Proxy license file match any of the MAC addresses on this system.</key>
<key name="log_0731">The DRBD Proxy license file: [#!data!path::configs::drbd-proxy.license!#] is missing expected data or is malformed.</key> <key name="log_0731">The DRBD Proxy license file: [#!data!path::configs::drbd-proxy.license!#] is missing expected data or is malformed.</key>
<key name="log_0732">Updating logind to ignore ACPI power button events so that IPMI-based fence requests don't trigger an attempt to gracefully shut down. For more information, see: https://access.redhat.com/solutions/1578823</key>
<key name="log_0733">Restarting the daemon: [#!variable!daemon!#].</key>
<!-- Messages for users (less technical than log entries), though sometimes used for logs, too. --> <!-- 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> <key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key>
<key name="message_0002">The connection to: [#!variable!connection!#] was refused. If you recently booted the target, the network might have started, the ssh daemon might not be running yet.</key> <key name="message_0002">The connection to: [#!variable!connection!#] was refused. If you recently booted the target, the network might have started, the ssh daemon might not be running yet.</key>
@ -2634,7 +2682,7 @@ Are you sure that you want to delete the server: [#!variable!server_name!#]? [Ty
<key name="message_0233">It appears that another instance of 'anvil-safe-start' is already runing. Please wait for it to complete (or kill it manually if needed).</key> <key name="message_0233">It appears that another instance of 'anvil-safe-start' is already runing. Please wait for it to complete (or kill it manually if needed).</key>
<key name="message_0234">Preparing to rename a server.</key> <key name="message_0234">Preparing to rename a server.</key>
<key name="message_0235">Preparing to rename stop this node.</key> <key name="message_0235">Preparing to rename stop this node.</key>
<key name="message_0236">This records how long it took to migate a given server. The average of the last five migations is used to guess how long future migrations will take.</key> <key name="message_0236">This records how long it took to migrate a given server. The average of the last five migations is used to guess how long future migrations will take.</key>
<key name="message_0237">One or more servers are migrating. While this is the case, ScanCore post-scan checks are not performed.</key> <key name="message_0237">One or more servers are migrating. While this is the case, ScanCore post-scan checks are not performed.</key>
<key name="message_0238">Preventative live migration has completed.</key> <key name="message_0238">Preventative live migration has completed.</key>
<key name="message_0239">Preventative live migration has been disabled. We're healthier than our peer, but we will take no action.</key> <key name="message_0239">Preventative live migration has been disabled. We're healthier than our peer, but we will take no action.</key>
@ -3137,7 +3185,9 @@ If you are comfortable that the target has changed for a known reason, you can s
<key name="striker_0296">This indicates that this node or DR host has had base DRBD configured.</key> <key name="striker_0296">This indicates that this node or DR host has had base DRBD configured.</key>
<key name="striker_0297">This indicates that this node or DR host has completed all tasks needed to be a full member of the Anvil!.</key> <key name="striker_0297">This indicates that this node or DR host has completed all tasks needed to be a full member of the Anvil!.</key>
<key name="striker_0298">TCP Port</key> <key name="striker_0298">TCP Port</key>
<key name="striker_0299">Migration Network link #!variable!number!#</key>
<key name="striker_0300">This is where you configure the optional network dedicated to RAM-copy during live migrations.</key>
<!-- These are generally units and appended to numbers --> <!-- These are generally units and appended to numbers -->
<key name="suffix_0001">#!variable!number!#/sec</key> <key name="suffix_0001">#!variable!number!#/sec</key>
<key name="suffix_0002">s</key> <!-- Short suffix for 'seconds'. --> <key name="suffix_0002">s</key> <!-- Short suffix for 'seconds'. -->
@ -3298,7 +3348,7 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st
<key name="warning_0012">[ Warning ] - Failed to log into the host. Is the IP or root user's password right?</key> <key name="warning_0012">[ Warning ] - Failed to log into the host. Is the IP or root user's password right?</key>
<key name="warning_0013"><![CDATA[[ Warning ] - The target's host key has changed. If the target has been rebuilt, or the target IP reused, the old key will need to be removed. <a href="?striker=true&task=keys" target="_new">Click here</a> to resolve.]]></key> <key name="warning_0013"><![CDATA[[ Warning ] - The target's host key has changed. If the target has been rebuilt, or the target IP reused, the old key will need to be removed. <a href="?striker=true&task=keys" target="_new">Click here</a> to resolve.]]></key>
<key name="warning_0014">[ Warning ] - The host UUID: [#!variable!host_uuid!#] was not found in the #!data!path::json::all_status!# file on the local dashboard.</key> <key name="warning_0014">[ Warning ] - The host UUID: [#!variable!host_uuid!#] was not found in the #!data!path::json::all_status!# file on the local dashboard.</key>
<key name="warning_0015">[ Warning ] - To configure a host as either an #!string!brand_0002!# node or a disaster recovery host, there must be at least 6 network interfaces. This machine only has: [#!variable!interface_count!#] interfaces.</key> <key name="warning_0015">[ Warning ] - To configure a #!string!brand_0002!# sub-node, there must be at least 6 network interfaces. This machine only has: [#!variable!interface_count!#] interfaces.</key>
<key name="warning_0016">[ Warning ] - No databases are available. Changes to the network interfaces will be cached.</key> <key name="warning_0016">[ Warning ] - No databases are available. Changes to the network interfaces will be cached.</key>
<key name="warning_0017">[ Warning ] - The subnet mask is not valid</key> <key name="warning_0017">[ Warning ] - The subnet mask is not valid</key>
<key name="warning_0018">[ Warning ] - The IP address was specified, but the subnet mask was not</key> <key name="warning_0018">[ Warning ] - The IP address was specified, but the subnet mask was not</key>
@ -3497,766 +3547,8 @@ The error was:
#!variable!error!# #!variable!error!#
======== ========
</key> </key>
<key name="warning_0147">[ Warning ] - The interface: [#!variable!interface!#] is in a bond, but it is down. The system uptime is: [#!variable!uptime!#], so it might be a problem where the interface didn't start on boot as it should have. So we're going to bring the interface up.</key> <key name="warning_0147">[ Warning ] - The interface: [#!variable!interface!#] appears to be down (state: [#!variable!state!#]). The system uptime is: [#!variable!uptime!#], so it might be a problem where the interface didn't start on boot as it should have. So we're going to bring the interface up.</key>
<key name="warning_0148">[ Warning ] - The IPMI stonith resource: [#!variable!resource!#] is in the role: [#!variable!role!#] (should be 'Started'). Will check the IPMI config now.</key> <key name="warning_0148">[ Warning ] - The IPMI stonith resource: [#!variable!resource!#] is in the role: [#!variable!role!#] (should be 'Started'). Will check the IPMI config now.</key>
<!-- The entries below here are not sequential, but use a key to find the entry. -->
<!-- Run 'striker-parse-os-list to find new entries. -->
<key name="os_list_almalinux8"><![CDATA[AlmaLinux 8]]></key>
<key name="os_list_alpinelinux3.10"><![CDATA[Alpine Linux 3.10]]></key>
<key name="os_list_alpinelinux3.11"><![CDATA[Alpine Linux 3.11]]></key>
<key name="os_list_alpinelinux3.12"><![CDATA[Alpine Linux 3.12]]></key>
<key name="os_list_alpinelinux3.13"><![CDATA[Alpine Linux 3.13]]></key>
<key name="os_list_alpinelinux3.14"><![CDATA[Alpine Linux 3.14]]></key>
<key name="os_list_alpinelinux3.15"><![CDATA[Alpine Linux 3.15]]></key>
<key name="os_list_alpinelinux3.5"><![CDATA[Alpine Linux 3.5]]></key>
<key name="os_list_alpinelinux3.6"><![CDATA[Alpine Linux 3.6]]></key>
<key name="os_list_alpinelinux3.7"><![CDATA[Alpine Linux 3.7]]></key>
<key name="os_list_alpinelinux3.8"><![CDATA[Alpine Linux 3.8]]></key>
<key name="os_list_alpinelinux3.9"><![CDATA[Alpine Linux 3.9]]></key>
<key name="os_list_alt.p10"><![CDATA[ALT p10 StarterKits]]></key>
<key name="os_list_alt.p8"><![CDATA[ALT p8 StarterKits]]></key>
<key name="os_list_alt.p9"><![CDATA[ALT p9 StarterKits]]></key>
<key name="os_list_alt.sisyphus"><![CDATA[ALT regular]]></key>
<key name="os_list_alt8.0"><![CDATA[ALT 8 Education]]></key>
<key name="os_list_alt8.1"><![CDATA[ALT 8.1]]></key>
<key name="os_list_alt8.2"><![CDATA[ALT 8.2]]></key>
<key name="os_list_alt9.0"><![CDATA[ALT 9.0]]></key>
<key name="os_list_alt9.1"><![CDATA[ALT 9.1]]></key>
<key name="os_list_alt9.2"><![CDATA[ALT 9.2]]></key>
<key name="os_list_altlinux1.0"><![CDATA[Mandrake RE Spring 2001]]></key>
<key name="os_list_altlinux2.0"><![CDATA[ALT Linux 2.0]]></key>
<key name="os_list_altlinux2.2"><![CDATA[ALT Linux 2.2]]></key>
<key name="os_list_altlinux2.4"><![CDATA[ALT Linux 2.4]]></key>
<key name="os_list_altlinux3.0"><![CDATA[ALT Linux 3.0]]></key>
<key name="os_list_altlinux4.0"><![CDATA[ALT Linux 4.0]]></key>
<key name="os_list_altlinux4.1"><![CDATA[ALT Linux 4.1]]></key>
<key name="os_list_altlinux5.0"><![CDATA[ALT Linux 5.0]]></key>
<key name="os_list_altlinux6.0"><![CDATA[ALT Linux 6.0]]></key>
<key name="os_list_altlinux7.0"><![CDATA[ALT Linux 7.0]]></key>
<key name="os_list_android-x86-8.1"><![CDATA[Android-x86 8.1]]></key>
<key name="os_list_android-x86-9.0"><![CDATA[Android-x86 9.0]]></key>
<key name="os_list_archlinux"><![CDATA[Arch Linux]]></key>
<key name="os_list_asianux-unknown"><![CDATA[Asianux unknown]]></key>
<key name="os_list_asianux4.6"><![CDATA[Asianux Server 4 SP6]]></key>
<key name="os_list_asianux4.7"><![CDATA[Asianux Server 4 SP7]]></key>
<key name="os_list_asianux7.0"><![CDATA[Asianux Server 7]]></key>
<key name="os_list_asianux7.1"><![CDATA[Asianux Server 7 SP1]]></key>
<key name="os_list_asianux7.2"><![CDATA[Asianux Server 7 SP2]]></key>
<key name="os_list_asianux7.3"><![CDATA[Asianux Server 7 SP3]]></key>
<key name="os_list_asianux8.0"><![CDATA[Asianux Server 8]]></key>
<key name="os_list_caasp-unknown"><![CDATA[SUSE CaaS Platform Unknown]]></key>
<key name="os_list_caasp1.0"><![CDATA[SUSE CaaS Platform 1.0]]></key>
<key name="os_list_caasp2.0"><![CDATA[SUSE CaaS Platform 2.0]]></key>
<key name="os_list_caasp3.0"><![CDATA[SUSE CaaS Platform 3.0]]></key>
<key name="os_list_centos-stream8"><![CDATA[CentOS Stream 8]]></key>
<key name="os_list_centos-stream9"><![CDATA[CentOS Stream 9]]></key>
<key name="os_list_centos5.0"><![CDATA[CentOS 5.0]]></key>
<key name="os_list_centos5.1"><![CDATA[CentOS 5.1]]></key>
<key name="os_list_centos5.10"><![CDATA[CentOS 5.10]]></key>
<key name="os_list_centos5.11"><![CDATA[CentOS 5.11]]></key>
<key name="os_list_centos5.2"><![CDATA[CentOS 5.2]]></key>
<key name="os_list_centos5.3"><![CDATA[CentOS 5.3]]></key>
<key name="os_list_centos5.4"><![CDATA[CentOS 5.4]]></key>
<key name="os_list_centos5.5"><![CDATA[CentOS 5.5]]></key>
<key name="os_list_centos5.6"><![CDATA[CentOS 5.6]]></key>
<key name="os_list_centos5.7"><![CDATA[CentOS 5.7]]></key>
<key name="os_list_centos5.8"><![CDATA[CentOS 5.8]]></key>
<key name="os_list_centos5.9"><![CDATA[CentOS 5.9]]></key>
<key name="os_list_centos6.0"><![CDATA[CentOS 6.0]]></key>
<key name="os_list_centos6.1"><![CDATA[CentOS 6.1]]></key>
<key name="os_list_centos6.10"><![CDATA[CentOS 6.10]]></key>
<key name="os_list_centos6.2"><![CDATA[CentOS 6.2]]></key>
<key name="os_list_centos6.3"><![CDATA[CentOS 6.3]]></key>
<key name="os_list_centos6.4"><![CDATA[CentOS 6.4]]></key>
<key name="os_list_centos6.5"><![CDATA[CentOS 6.5]]></key>
<key name="os_list_centos6.6"><![CDATA[CentOS 6.6]]></key>
<key name="os_list_centos6.7"><![CDATA[CentOS 6.7]]></key>
<key name="os_list_centos6.8"><![CDATA[CentOS 6.8]]></key>
<key name="os_list_centos6.9"><![CDATA[CentOS 6.9]]></key>
<key name="os_list_centos7.0"><![CDATA[CentOS 7]]></key>
<key name="os_list_centos8"><![CDATA[CentOS 8]]></key>
<key name="os_list_circle-unknown"><![CDATA[Circle Linux Unknown]]></key>
<key name="os_list_circle8-unknown"><![CDATA[Circle Linux 8 Unknown]]></key>
<key name="os_list_circle8.4"><![CDATA[Circle Linux 8.4]]></key>
<key name="os_list_circle8.5"><![CDATA[Circle Linux 8.5]]></key>
<key name="os_list_circle9-unknown"><![CDATA[Circle Linux 9 Unknown]]></key>
<key name="os_list_circle9.0"><![CDATA[Circle Linux 9.0]]></key>
<key name="os_list_cirros0.3.0"><![CDATA[CirrOS 0.3.0]]></key>
<key name="os_list_cirros0.3.1"><![CDATA[CirrOS 0.3.1]]></key>
<key name="os_list_cirros0.3.2"><![CDATA[CirrOS 0.3.2]]></key>
<key name="os_list_cirros0.3.3"><![CDATA[CirrOS 0.3.3]]></key>
<key name="os_list_cirros0.3.4"><![CDATA[CirrOS 0.3.4]]></key>
<key name="os_list_cirros0.3.5"><![CDATA[CirrOS 0.3.5]]></key>
<key name="os_list_cirros0.4.0"><![CDATA[CirrOS 0.4.0]]></key>
<key name="os_list_cirros0.5.0"><![CDATA[CirrOS 0.5.0]]></key>
<key name="os_list_cirros0.5.1"><![CDATA[CirrOS 0.5.1]]></key>
<key name="os_list_cirros0.5.2"><![CDATA[CirrOS 0.5.2]]></key>
<key name="os_list_clearlinux"><![CDATA[Clear Linux OS]]></key>
<key name="os_list_debian1.1"><![CDATA[Debian GNU/Linux 1.1]]></key>
<key name="os_list_debian1.2"><![CDATA[Debian GNU/Linux 1.2]]></key>
<key name="os_list_debian1.3"><![CDATA[Debian GNU/Linux 1.3]]></key>
<key name="os_list_debian10"><![CDATA[Debian 10]]></key>
<key name="os_list_debian11"><![CDATA[Debian 11]]></key>
<key name="os_list_debian2.0"><![CDATA[Debian GNU/Linux 2.0]]></key>
<key name="os_list_debian2.1"><![CDATA[Debian GNU/Linux 2.1]]></key>
<key name="os_list_debian2.2"><![CDATA[Debian GNU/Linux 2.2]]></key>
<key name="os_list_debian3"><![CDATA[Debian GNU/Linux 3.0]]></key>
<key name="os_list_debian3.1"><![CDATA[Debian GNU/Linux 3.1]]></key>
<key name="os_list_debian4"><![CDATA[Debian GNU/Linux 4.0]]></key>
<key name="os_list_debian5"><![CDATA[Debian GNU/Linux 5.0]]></key>
<key name="os_list_debian6"><![CDATA[Debian 6.0]]></key>
<key name="os_list_debian7"><![CDATA[Debian 7]]></key>
<key name="os_list_debian8"><![CDATA[Debian 8]]></key>
<key name="os_list_debian9"><![CDATA[Debian 9]]></key>
<key name="os_list_debiantesting"><![CDATA[Debian testing]]></key>
<key name="os_list_dragonflybsd1.0"><![CDATA[DragonFlyBSD 1.0]]></key>
<key name="os_list_dragonflybsd1.0A"><![CDATA[DragonFlyBSD 1.0A]]></key>
<key name="os_list_dragonflybsd1.10.0"><![CDATA[DragonFlyBSD 1.10.0]]></key>
<key name="os_list_dragonflybsd1.10.1"><![CDATA[DragonFlyBSD 1.10.1]]></key>
<key name="os_list_dragonflybsd1.12.0"><![CDATA[DragonFlyBSD 1.12.0]]></key>
<key name="os_list_dragonflybsd1.12.1"><![CDATA[DragonFlyBSD 1.12.1]]></key>
<key name="os_list_dragonflybsd1.12.2"><![CDATA[DragonFlyBSD 1.12.2]]></key>
<key name="os_list_dragonflybsd1.2.0"><![CDATA[DragonFlyBSD 1.2.0]]></key>
<key name="os_list_dragonflybsd1.2.1"><![CDATA[DragonFlyBSD 1.2.1]]></key>
<key name="os_list_dragonflybsd1.2.2"><![CDATA[DragonFlyBSD 1.2.2]]></key>
<key name="os_list_dragonflybsd1.2.3"><![CDATA[DragonFlyBSD 1.2.3]]></key>
<key name="os_list_dragonflybsd1.2.4"><![CDATA[DragonFlyBSD 1.2.4]]></key>
<key name="os_list_dragonflybsd1.2.5"><![CDATA[DragonFlyBSD 1.2.5]]></key>
<key name="os_list_dragonflybsd1.2.6"><![CDATA[DragonFlyBSD 1.2.6]]></key>
<key name="os_list_dragonflybsd1.4.0"><![CDATA[DragonFlyBSD 1.4.0]]></key>
<key name="os_list_dragonflybsd1.4.4"><![CDATA[DragonFlyBSD 1.4.4]]></key>
<key name="os_list_dragonflybsd1.6.0"><![CDATA[DragonFlyBSD 1.6.0]]></key>
<key name="os_list_dragonflybsd1.8.0"><![CDATA[DragonFlyBSD 1.8.0]]></key>
<key name="os_list_dragonflybsd1.8.1"><![CDATA[DragonFlyBSD 1.8.1]]></key>
<key name="os_list_dragonflybsd2.0.0"><![CDATA[DragonFlyBSD 2.0.0]]></key>
<key name="os_list_dragonflybsd2.0.1"><![CDATA[DragonFlyBSD 2.0.1]]></key>
<key name="os_list_dragonflybsd2.10.1"><![CDATA[DragonFlyBSD 2.10.1]]></key>
<key name="os_list_dragonflybsd2.2.0"><![CDATA[DragonFlyBSD 2.2.0]]></key>
<key name="os_list_dragonflybsd2.2.1"><![CDATA[DragonFlyBSD 2.2.1]]></key>
<key name="os_list_dragonflybsd2.4.0"><![CDATA[DragonFlyBSD 2.4.0]]></key>
<key name="os_list_dragonflybsd2.4.1"><![CDATA[DragonFlyBSD 2.4.1]]></key>
<key name="os_list_dragonflybsd2.6.1"><![CDATA[DragonFlyBSD 2.6.1]]></key>
<key name="os_list_dragonflybsd2.6.2"><![CDATA[DragonFlyBSD 2.6.2]]></key>
<key name="os_list_dragonflybsd2.6.3"><![CDATA[DragonFlyBSD 2.6.3]]></key>
<key name="os_list_dragonflybsd2.8.2"><![CDATA[DragonFlyBSD 2.8.2]]></key>
<key name="os_list_dragonflybsd3.0.1"><![CDATA[DragonFlyBSD 3.0.1]]></key>
<key name="os_list_dragonflybsd3.2.1"><![CDATA[DragonFlyBSD 3.2.1]]></key>
<key name="os_list_dragonflybsd3.4.1"><![CDATA[DragonFlyBSD 3.4.1]]></key>
<key name="os_list_dragonflybsd3.4.2"><![CDATA[DragonFlyBSD 3.4.2]]></key>
<key name="os_list_dragonflybsd3.4.3"><![CDATA[DragonFlyBSD 3.4.3]]></key>
<key name="os_list_dragonflybsd3.6.0"><![CDATA[DragonFlyBSD 3.6.0]]></key>
<key name="os_list_dragonflybsd3.6.1"><![CDATA[DragonFlyBSD 3.6.1]]></key>
<key name="os_list_dragonflybsd3.6.2"><![CDATA[DragonFlyBSD 3.6.2]]></key>
<key name="os_list_dragonflybsd3.8.0"><![CDATA[DragonFlyBSD 3.8.0]]></key>
<key name="os_list_dragonflybsd3.8.1"><![CDATA[DragonFlyBSD 3.8.1]]></key>
<key name="os_list_dragonflybsd3.8.2"><![CDATA[DragonFlyBSD 3.8.2]]></key>
<key name="os_list_dragonflybsd4.0.0"><![CDATA[DragonFlyBSD 4.0.0]]></key>
<key name="os_list_dragonflybsd4.0.1"><![CDATA[DragonFlyBSD 4.0.1]]></key>
<key name="os_list_dragonflybsd4.2.0"><![CDATA[DragonFlyBSD 4.2.0]]></key>
<key name="os_list_dragonflybsd4.2.1"><![CDATA[DragonFlyBSD 4.2.1]]></key>
<key name="os_list_dragonflybsd4.2.3"><![CDATA[DragonFlyBSD 4.2.3]]></key>
<key name="os_list_dragonflybsd4.2.4"><![CDATA[DragonFlyBSD 4.2.4]]></key>
<key name="os_list_dragonflybsd4.4.1"><![CDATA[DragonFlyBSD 4.4.1]]></key>
<key name="os_list_dragonflybsd4.4.2"><![CDATA[DragonFlyBSD 4.4.2]]></key>
<key name="os_list_dragonflybsd4.4.3"><![CDATA[DragonFlyBSD 4.4.3]]></key>
<key name="os_list_dragonflybsd4.6.0"><![CDATA[DragonFlyBSD 4.6.0]]></key>
<key name="os_list_dragonflybsd4.6.1"><![CDATA[DragonFlyBSD 4.6.1]]></key>
<key name="os_list_dragonflybsd4.6.2"><![CDATA[DragonFlyBSD 4.6.2]]></key>
<key name="os_list_dragonflybsd4.8.0"><![CDATA[DragonFlyBSD 4.8.0]]></key>
<key name="os_list_dragonflybsd4.8.1"><![CDATA[DragonFlyBSD 4.8.1]]></key>
<key name="os_list_dragonflybsd5.0.0"><![CDATA[DragonFlyBSD 5.0.0]]></key>
<key name="os_list_dragonflybsd5.0.1"><![CDATA[DragonFlyBSD 5.0.1]]></key>
<key name="os_list_dragonflybsd5.0.2"><![CDATA[DragonFlyBSD 5.0.2]]></key>
<key name="os_list_dragonflybsd5.2.0"><![CDATA[DragonFlyBSD 5.2.0]]></key>
<key name="os_list_dragonflybsd5.2.1"><![CDATA[DragonFlyBSD 5.2.1]]></key>
<key name="os_list_dragonflybsd5.2.2"><![CDATA[DragonFlyBSD 5.2.2]]></key>
<key name="os_list_dragonflybsd5.4.0"><![CDATA[DragonFlyBSD 5.4.0]]></key>
<key name="os_list_dragonflybsd5.4.1"><![CDATA[DragonFlyBSD 5.4.1]]></key>
<key name="os_list_dragonflybsd5.4.2"><![CDATA[DragonFlyBSD 5.4.2]]></key>
<key name="os_list_dragonflybsd5.4.3"><![CDATA[DragonFlyBSD 5.4.3]]></key>
<key name="os_list_dragonflybsd5.6"><![CDATA[DragonFlyBSD 5.6]]></key>
<key name="os_list_elementary5.0"><![CDATA[Elementary OS 5.0 Juno]]></key>
<key name="os_list_eos3.1"><![CDATA[Endless OS 3.1]]></key>
<key name="os_list_eos3.10"><![CDATA[Endless OS 3.10]]></key>
<key name="os_list_eos3.2"><![CDATA[Endless OS 3.2]]></key>
<key name="os_list_eos3.3"><![CDATA[Endless OS 3.3]]></key>
<key name="os_list_eos3.4"><![CDATA[Endless OS 3.4]]></key>
<key name="os_list_eos3.5"><![CDATA[Endless OS 3.5]]></key>
<key name="os_list_eos3.6"><![CDATA[Endless OS 3.6]]></key>
<key name="os_list_eos3.7"><![CDATA[Endless OS 3.7]]></key>
<key name="os_list_eos3.8"><![CDATA[Endless OS 3.8]]></key>
<key name="os_list_eos3.9"><![CDATA[Endless OS 3.9]]></key>
<key name="os_list_eos4.0"><![CDATA[Endless OS 4.0]]></key>
<key name="os_list_fedora-coreos-next"><![CDATA[Fedora CoreOS]]></key>
<key name="os_list_fedora-coreos-stable"><![CDATA[Fedora CoreOS]]></key>
<key name="os_list_fedora-coreos-testing"><![CDATA[Fedora CoreOS]]></key>
<key name="os_list_fedora-rawhide"><![CDATA[Fedora Rawhide]]></key>
<key name="os_list_fedora-unknown"><![CDATA[Fedora]]></key>
<key name="os_list_fedora1"><![CDATA[Fedora Core 1]]></key>
<key name="os_list_fedora10"><![CDATA[Fedora 10]]></key>
<key name="os_list_fedora11"><![CDATA[Fedora 11]]></key>
<key name="os_list_fedora12"><![CDATA[Fedora 12]]></key>
<key name="os_list_fedora13"><![CDATA[Fedora 13]]></key>
<key name="os_list_fedora14"><![CDATA[Fedora 14]]></key>
<key name="os_list_fedora15"><![CDATA[Fedora 15]]></key>
<key name="os_list_fedora16"><![CDATA[Fedora 16]]></key>
<key name="os_list_fedora17"><![CDATA[Fedora 17]]></key>
<key name="os_list_fedora18"><![CDATA[Fedora 18]]></key>
<key name="os_list_fedora19"><![CDATA[Fedora 19]]></key>
<key name="os_list_fedora2"><![CDATA[Fedora Core 2]]></key>
<key name="os_list_fedora20"><![CDATA[Fedora 20]]></key>
<key name="os_list_fedora21"><![CDATA[Fedora 21]]></key>
<key name="os_list_fedora22"><![CDATA[Fedora 22]]></key>
<key name="os_list_fedora23"><![CDATA[Fedora 23]]></key>
<key name="os_list_fedora24"><![CDATA[Fedora 24]]></key>
<key name="os_list_fedora25"><![CDATA[Fedora 25]]></key>
<key name="os_list_fedora26"><![CDATA[Fedora 26]]></key>
<key name="os_list_fedora27"><![CDATA[Fedora 27]]></key>
<key name="os_list_fedora28"><![CDATA[Fedora 28]]></key>
<key name="os_list_fedora29"><![CDATA[Fedora 29]]></key>
<key name="os_list_fedora3"><![CDATA[Fedora Core 3]]></key>
<key name="os_list_fedora30"><![CDATA[Fedora 30]]></key>
<key name="os_list_fedora31"><![CDATA[Fedora 31]]></key>
<key name="os_list_fedora32"><![CDATA[Fedora 32]]></key>
<key name="os_list_fedora33"><![CDATA[Fedora 33]]></key>
<key name="os_list_fedora34"><![CDATA[Fedora 34]]></key>
<key name="os_list_fedora35"><![CDATA[Fedora 35]]></key>
<key name="os_list_fedora4"><![CDATA[Fedora Core 4]]></key>
<key name="os_list_fedora5"><![CDATA[Fedora Core 5]]></key>
<key name="os_list_fedora6"><![CDATA[Fedora Core 6]]></key>
<key name="os_list_fedora7"><![CDATA[Fedora 7]]></key>
<key name="os_list_fedora8"><![CDATA[Fedora 8]]></key>
<key name="os_list_fedora9"><![CDATA[Fedora 9]]></key>
<key name="os_list_freebsd1.0"><![CDATA[FreeBSD 1.0]]></key>
<key name="os_list_freebsd10.0"><![CDATA[FreeBSD 10.0]]></key>
<key name="os_list_freebsd10.1"><![CDATA[FreeBSD 10.1]]></key>
<key name="os_list_freebsd10.2"><![CDATA[FreeBSD 10.2]]></key>
<key name="os_list_freebsd10.3"><![CDATA[FreeBSD 10.3]]></key>
<key name="os_list_freebsd10.4"><![CDATA[FreeBSD 10.4]]></key>
<key name="os_list_freebsd11.0"><![CDATA[FreeBSD 11.0]]></key>
<key name="os_list_freebsd11.1"><![CDATA[FreeBSD 11.1]]></key>
<key name="os_list_freebsd11.2"><![CDATA[FreeBSD 11.2]]></key>
<key name="os_list_freebsd11.3"><![CDATA[FreeBSD 11.3]]></key>
<key name="os_list_freebsd11.4"><![CDATA[FreeBSD 11.4]]></key>
<key name="os_list_freebsd12.0"><![CDATA[FreeBSD 12.0]]></key>
<key name="os_list_freebsd12.1"><![CDATA[FreeBSD 12.1]]></key>
<key name="os_list_freebsd12.2"><![CDATA[FreeBSD 12.2]]></key>
<key name="os_list_freebsd12.3"><![CDATA[FreeBSD 12.3]]></key>
<key name="os_list_freebsd13.0"><![CDATA[FreeBSD 13.0]]></key>
<key name="os_list_freebsd2.0"><![CDATA[FreeBSD 2.0]]></key>
<key name="os_list_freebsd2.0.5"><![CDATA[FreeBSD 2.0.5]]></key>
<key name="os_list_freebsd2.2.8"><![CDATA[FreeBSD 2.2.8]]></key>
<key name="os_list_freebsd2.2.9"><![CDATA[FreeBSD 2.2.9]]></key>
<key name="os_list_freebsd3.0"><![CDATA[FreeBSD 3.0]]></key>
<key name="os_list_freebsd3.2"><![CDATA[FreeBSD 3.2]]></key>
<key name="os_list_freebsd4.0"><![CDATA[FreeBSD 4.0]]></key>
<key name="os_list_freebsd4.1"><![CDATA[FreeBSD 4.1]]></key>
<key name="os_list_freebsd4.10"><![CDATA[FreeBSD 4.10]]></key>
<key name="os_list_freebsd4.11"><![CDATA[FreeBSD 4.11]]></key>
<key name="os_list_freebsd4.2"><![CDATA[FreeBSD 4.2]]></key>
<key name="os_list_freebsd4.3"><![CDATA[FreeBSD 4.3]]></key>
<key name="os_list_freebsd4.4"><![CDATA[FreeBSD 4.4]]></key>
<key name="os_list_freebsd4.5"><![CDATA[FreeBSD 4.5]]></key>
<key name="os_list_freebsd4.6"><![CDATA[FreeBSD 4.6]]></key>
<key name="os_list_freebsd4.7"><![CDATA[FreeBSD 4.7]]></key>
<key name="os_list_freebsd4.8"><![CDATA[FreeBSD 4.8]]></key>
<key name="os_list_freebsd4.9"><![CDATA[FreeBSD 4.9]]></key>
<key name="os_list_freebsd5.0"><![CDATA[FreeBSD 5.0]]></key>
<key name="os_list_freebsd5.1"><![CDATA[FreeBSD 5.1]]></key>
<key name="os_list_freebsd5.2"><![CDATA[FreeBSD 5.2]]></key>
<key name="os_list_freebsd5.2.1"><![CDATA[FreeBSD 5.2.1]]></key>
<key name="os_list_freebsd5.3"><![CDATA[FreeBSD 5.3]]></key>
<key name="os_list_freebsd5.4"><![CDATA[FreeBSD 5.4]]></key>
<key name="os_list_freebsd5.5"><![CDATA[FreeBSD 5.5]]></key>
<key name="os_list_freebsd6.0"><![CDATA[FreeBSD 6.0]]></key>
<key name="os_list_freebsd6.1"><![CDATA[FreeBSD 6.1]]></key>
<key name="os_list_freebsd6.2"><![CDATA[FreeBSD 6.2]]></key>
<key name="os_list_freebsd6.3"><![CDATA[FreeBSD 6.3]]></key>
<key name="os_list_freebsd6.4"><![CDATA[FreeBSD 6.4]]></key>
<key name="os_list_freebsd7.0"><![CDATA[FreeBSD 7.0]]></key>
<key name="os_list_freebsd7.1"><![CDATA[FreeBSD 7.1]]></key>
<key name="os_list_freebsd7.2"><![CDATA[FreeBSD 7.2]]></key>
<key name="os_list_freebsd7.3"><![CDATA[FreeBSD 7.3]]></key>
<key name="os_list_freebsd7.4"><![CDATA[FreeBSD 7.4]]></key>
<key name="os_list_freebsd8.0"><![CDATA[FreeBSD 8.0]]></key>
<key name="os_list_freebsd8.1"><![CDATA[FreeBSD 8.1]]></key>
<key name="os_list_freebsd8.2"><![CDATA[FreeBSD 8.2]]></key>
<key name="os_list_freebsd8.3"><![CDATA[FreeBSD 8.3]]></key>
<key name="os_list_freebsd8.4"><![CDATA[FreeBSD 8.4]]></key>
<key name="os_list_freebsd9.0"><![CDATA[FreeBSD 9.0]]></key>
<key name="os_list_freebsd9.1"><![CDATA[FreeBSD 9.1]]></key>
<key name="os_list_freebsd9.2"><![CDATA[FreeBSD 9.2]]></key>
<key name="os_list_freebsd9.3"><![CDATA[FreeBSD 9.3]]></key>
<key name="os_list_freedos1.2"><![CDATA[FreeDOS 1.2]]></key>
<key name="os_list_freenix14.2"><![CDATA[Freenix 14.2]]></key>
<key name="os_list_gentoo"><![CDATA[Gentoo Linux]]></key>
<key name="os_list_gnome-continuous-3.10"><![CDATA[GNOME 3.10]]></key>
<key name="os_list_gnome-continuous-3.12"><![CDATA[GNOME 3.12]]></key>
<key name="os_list_gnome-continuous-3.14"><![CDATA[GNOME 3.14]]></key>
<key name="os_list_gnome3.6"><![CDATA[GNOME 3.6]]></key>
<key name="os_list_gnome3.8"><![CDATA[GNOME 3.8]]></key>
<key name="os_list_guix-1.1"><![CDATA[Guix 1.1]]></key>
<key name="os_list_guix-1.3"><![CDATA[Guix 1.3]]></key>
<key name="os_list_guix-hurd-latest"><![CDATA[Guix Hurd Latest]]></key>
<key name="os_list_guix-latest"><![CDATA[Guix latest]]></key>
<key name="os_list_haikunightly"><![CDATA[Haiku Nightly]]></key>
<key name="os_list_haikur1alpha1"><![CDATA[Haiku R1/Alpha1]]></key>
<key name="os_list_haikur1alpha2"><![CDATA[Haiku R1/Alpha2]]></key>
<key name="os_list_haikur1alpha3"><![CDATA[Haiku R1/Alpha3]]></key>
<key name="os_list_haikur1alpha4.1"><![CDATA[Haiku R1/Alpha4.1]]></key>
<key name="os_list_haikur1beta1"><![CDATA[Haiku R1/Beta1]]></key>
<key name="os_list_haikur1beta2"><![CDATA[Haiku R1/Beta2]]></key>
<key name="os_list_haikur1beta3"><![CDATA[Haiku R1/Beta3]]></key>
<key name="os_list_hyperbola03"><![CDATA[Hyperbola]]></key>
<key name="os_list_linux2016"><![CDATA[Generic Linux 2016]]></key>
<key name="os_list_linux2018"><![CDATA[Generic Linux 2018]]></key>
<key name="os_list_linux2020"><![CDATA[Generic Linux 2020]]></key>
<key name="os_list_macosx10.0"><![CDATA[MacOS X Cheetah]]></key>
<key name="os_list_macosx10.1"><![CDATA[MacOS X Puma]]></key>
<key name="os_list_macosx10.2"><![CDATA[MacOS X Jaguar]]></key>
<key name="os_list_macosx10.3"><![CDATA[MacOS X Panther]]></key>
<key name="os_list_macosx10.4"><![CDATA[MacOS X Tiger]]></key>
<key name="os_list_macosx10.5"><![CDATA[MacOS X Leopard]]></key>
<key name="os_list_macosx10.6"><![CDATA[MacOS X Snow Leopard]]></key>
<key name="os_list_macosx10.7"><![CDATA[MacOS X Lion]]></key>
<key name="os_list_mageia1"><![CDATA[Mageia 1]]></key>
<key name="os_list_mageia2"><![CDATA[Mageia 2]]></key>
<key name="os_list_mageia3"><![CDATA[Mageia 3]]></key>
<key name="os_list_mageia4"><![CDATA[Mageia 4]]></key>
<key name="os_list_mageia5"><![CDATA[Mageia 5]]></key>
<key name="os_list_mageia6"><![CDATA[Mageia 6]]></key>
<key name="os_list_mageia7"><![CDATA[Mageia 7]]></key>
<key name="os_list_mageia8"><![CDATA[Mageia 8]]></key>
<key name="os_list_mandrake10.0"><![CDATA[Mandrake Linux 10.0]]></key>
<key name="os_list_mandrake10.1"><![CDATA[Mandrake Linux 10.1]]></key>
<key name="os_list_mandrake10.2"><![CDATA[Mandrake Linux 10.2]]></key>
<key name="os_list_mandrake5.1"><![CDATA[Mandrake Linux 5.1]]></key>
<key name="os_list_mandrake5.2"><![CDATA[Mandrake Linux 5.2]]></key>
<key name="os_list_mandrake5.3"><![CDATA[Mandrake Linux 5.3]]></key>
<key name="os_list_mandrake6.0"><![CDATA[Mandrake Linux 6.0]]></key>
<key name="os_list_mandrake6.1"><![CDATA[Mandrake Linux 6.1]]></key>
<key name="os_list_mandrake7.0"><![CDATA[Mandrake Linux 7.0]]></key>
<key name="os_list_mandrake7.1"><![CDATA[Mandrake Linux 7.1]]></key>
<key name="os_list_mandrake7.2"><![CDATA[Mandrake Linux 7.2]]></key>
<key name="os_list_mandrake8.0"><![CDATA[Mandrake Linux 8.0]]></key>
<key name="os_list_mandrake8.1"><![CDATA[Mandrake Linux 8.1]]></key>
<key name="os_list_mandrake8.2"><![CDATA[Mandrake Linux 8.2]]></key>
<key name="os_list_mandrake9.0"><![CDATA[Mandrake Linux 9.0]]></key>
<key name="os_list_mandrake9.1"><![CDATA[Mandrake Linux 9.1]]></key>
<key name="os_list_mandrake9.2"><![CDATA[Mandrake Linux 9.2]]></key>
<key name="os_list_mandriva2006.0"><![CDATA[Mandriva Linux 2006.0]]></key>
<key name="os_list_mandriva2007"><![CDATA[Mandriva Linux 2007]]></key>
<key name="os_list_mandriva2007.1"><![CDATA[Mandriva Linux 2007 Spring]]></key>
<key name="os_list_mandriva2008.0"><![CDATA[Mandriva Linux 2008]]></key>
<key name="os_list_mandriva2008.1"><![CDATA[Mandriva Linux 2008 Spring]]></key>
<key name="os_list_mandriva2009.0"><![CDATA[Mandriva Linux 2009]]></key>
<key name="os_list_mandriva2009.1"><![CDATA[Mandriva Linux 2009 Spring]]></key>
<key name="os_list_mandriva2010.0"><![CDATA[Mandriva Linux 2010]]></key>
<key name="os_list_mandriva2010.1"><![CDATA[Mandriva Linux 2010 Spring]]></key>
<key name="os_list_mandriva2010.2"><![CDATA[Mandriva Linux 2010.2]]></key>
<key name="os_list_mandriva2011"><![CDATA[Mandriva Linux 2011]]></key>
<key name="os_list_manjaro"><![CDATA[Manjaro]]></key>
<key name="os_list_mbs1.0"><![CDATA[Mandriva Business Server 1.0]]></key>
<key name="os_list_mes5"><![CDATA[Mandriva Enterprise Server 5.0]]></key>
<key name="os_list_mes5.1"><![CDATA[Mandriva Enterprise Server 5.1]]></key>
<key name="os_list_miraclelinux8.4"><![CDATA[MIRACLE LINUX 8.4]]></key>
<key name="os_list_msdos6.22"><![CDATA[Microsoft MS-DOS 6.22]]></key>
<key name="os_list_netbsd0.8"><![CDATA[NetBSD 0.8]]></key>
<key name="os_list_netbsd0.9"><![CDATA[NetBSD 0.9]]></key>
<key name="os_list_netbsd1.0"><![CDATA[NetBSD 1.0]]></key>
<key name="os_list_netbsd1.1"><![CDATA[NetBSD 1.1]]></key>
<key name="os_list_netbsd1.2"><![CDATA[NetBSD 1.2]]></key>
<key name="os_list_netbsd1.3"><![CDATA[NetBSD 1.3]]></key>
<key name="os_list_netbsd1.4"><![CDATA[NetBSD 1.4]]></key>
<key name="os_list_netbsd1.5"><![CDATA[NetBSD 1.5]]></key>
<key name="os_list_netbsd1.6"><![CDATA[NetBSD 1.6]]></key>
<key name="os_list_netbsd2.0"><![CDATA[NetBSD 2.0]]></key>
<key name="os_list_netbsd3.0"><![CDATA[NetBSD 3.0]]></key>
<key name="os_list_netbsd4.0"><![CDATA[NetBSD 4.0]]></key>
<key name="os_list_netbsd5.0"><![CDATA[NetBSD 5.0]]></key>
<key name="os_list_netbsd5.1"><![CDATA[NetBSD 5.1]]></key>
<key name="os_list_netbsd6.0"><![CDATA[NetBSD 6.0]]></key>
<key name="os_list_netbsd6.1"><![CDATA[NetBSD 6.1]]></key>
<key name="os_list_netbsd7.0"><![CDATA[NetBSD 7.0]]></key>
<key name="os_list_netbsd7.1"><![CDATA[NetBSD 7.1]]></key>
<key name="os_list_netbsd7.1.1"><![CDATA[NetBSD 7.1.1]]></key>
<key name="os_list_netbsd7.1.2"><![CDATA[NetBSD 7.1.2]]></key>
<key name="os_list_netbsd7.2"><![CDATA[NetBSD 7.2]]></key>
<key name="os_list_netbsd8.0"><![CDATA[NetBSD 8.0]]></key>
<key name="os_list_netbsd8.1"><![CDATA[NetBSD 8.1]]></key>
<key name="os_list_netbsd8.2"><![CDATA[NetBSD 8.2]]></key>
<key name="os_list_netbsd9.0"><![CDATA[NetBSD 9.0]]></key>
<key name="os_list_netware4"><![CDATA[Novell Netware 4]]></key>
<key name="os_list_netware5"><![CDATA[Novell Netware 5]]></key>
<key name="os_list_netware6"><![CDATA[Novell Netware 6]]></key>
<key name="os_list_nixos-20.03"><![CDATA[NixOS 20.03]]></key>
<key name="os_list_nixos-20.09"><![CDATA[NixOS 20.09]]></key>
<key name="os_list_nixos-21.05"><![CDATA[NixOS 21.05]]></key>
<key name="os_list_nixos-unknown"><![CDATA[NixOS]]></key>
<key name="os_list_nixos-unstable"><![CDATA[NixOS Unstable]]></key>
<key name="os_list_oel4.4"><![CDATA[Oracle Enterprise Linux 4.4]]></key>
<key name="os_list_oel4.5"><![CDATA[Oracle Enterprise Linux 4.5]]></key>
<key name="os_list_oel4.6"><![CDATA[Oracle Enterprise Linux 4.6]]></key>
<key name="os_list_oel4.7"><![CDATA[Oracle Enterprise Linux 4.7]]></key>
<key name="os_list_oel4.8"><![CDATA[Oracle Enterprise Linux 4.8]]></key>
<key name="os_list_oel4.9"><![CDATA[Oracle Enterprise Linux 4.9]]></key>
<key name="os_list_oel5.0"><![CDATA[Oracle Enterprise Linux 5.0]]></key>
<key name="os_list_oel5.1"><![CDATA[Oracle Enterprise Linux 5.1]]></key>
<key name="os_list_oel5.2"><![CDATA[Oracle Enterprise Linux 5.2]]></key>
<key name="os_list_oel5.3"><![CDATA[Oracle Enterprise Linux 5.3]]></key>
<key name="os_list_oel5.4"><![CDATA[Oracle Enterprise Linux 5.4]]></key>
<key name="os_list_ol5.10"><![CDATA[Oracle Linux 5.10]]></key>
<key name="os_list_ol5.11"><![CDATA[Oracle Linux 5.11]]></key>
<key name="os_list_ol5.5"><![CDATA[Oracle Linux 5.5]]></key>
<key name="os_list_ol5.6"><![CDATA[Oracle Linux 5.6]]></key>
<key name="os_list_ol5.7"><![CDATA[Oracle Linux 5.7]]></key>
<key name="os_list_ol5.8"><![CDATA[Oracle Linux 5.8]]></key>
<key name="os_list_ol5.9"><![CDATA[Oracle Linux 5.9]]></key>
<key name="os_list_ol6.0"><![CDATA[Oracle Linux 6.0]]></key>
<key name="os_list_ol6.1"><![CDATA[Oracle Linux 6.1]]></key>
<key name="os_list_ol6.10"><![CDATA[Oracle Linux 6.10]]></key>
<key name="os_list_ol6.2"><![CDATA[Oracle Linux 6.2]]></key>
<key name="os_list_ol6.3"><![CDATA[Oracle Linux 6.3]]></key>
<key name="os_list_ol6.4"><![CDATA[Oracle Linux 6.4]]></key>
<key name="os_list_ol6.5"><![CDATA[Oracle Linux 6.5]]></key>
<key name="os_list_ol6.6"><![CDATA[Oracle Linux 6.6]]></key>
<key name="os_list_ol6.7"><![CDATA[Oracle Linux 6.7]]></key>
<key name="os_list_ol6.8"><![CDATA[Oracle Linux 6.8]]></key>
<key name="os_list_ol6.9"><![CDATA[Oracle Linux 6.9]]></key>
<key name="os_list_ol7.0"><![CDATA[Oracle Linux 7.0]]></key>
<key name="os_list_ol7.1"><![CDATA[Oracle Linux 7.1]]></key>
<key name="os_list_ol7.2"><![CDATA[Oracle Linux 7.2]]></key>
<key name="os_list_ol7.3"><![CDATA[Oracle Linux 7.3]]></key>
<key name="os_list_ol7.4"><![CDATA[Oracle Linux 7.4]]></key>
<key name="os_list_ol7.5"><![CDATA[Oracle Linux 7.5]]></key>
<key name="os_list_ol7.6"><![CDATA[Oracle Linux 7.6]]></key>
<key name="os_list_ol7.7"><![CDATA[Oracle Linux 7.7]]></key>
<key name="os_list_ol7.8"><![CDATA[Oracle Linux 7.8]]></key>
<key name="os_list_ol7.9"><![CDATA[Oracle Linux 7.9]]></key>
<key name="os_list_ol8.0"><![CDATA[Oracle Linux 8.0]]></key>
<key name="os_list_ol8.1"><![CDATA[Oracle Linux 8.1]]></key>
<key name="os_list_ol8.2"><![CDATA[Oracle Linux 8.2]]></key>
<key name="os_list_ol8.3"><![CDATA[Oracle Linux 8.3]]></key>
<key name="os_list_ol8.4"><![CDATA[Oracle Linux 8.4]]></key>
<key name="os_list_ol8.5"><![CDATA[Oracle Linux 8.5]]></key>
<key name="os_list_openbsd4.2"><![CDATA[OpenBSD 4.2]]></key>
<key name="os_list_openbsd4.3"><![CDATA[OpenBSD 4.3]]></key>
<key name="os_list_openbsd4.4"><![CDATA[OpenBSD 4.4]]></key>
<key name="os_list_openbsd4.5"><![CDATA[OpenBSD 4.5]]></key>
<key name="os_list_openbsd4.8"><![CDATA[OpenBSD 4.8]]></key>
<key name="os_list_openbsd4.9"><![CDATA[OpenBSD 4.9]]></key>
<key name="os_list_openbsd5.0"><![CDATA[OpenBSD 5.0]]></key>
<key name="os_list_openbsd5.1"><![CDATA[OpenBSD 5.1]]></key>
<key name="os_list_openbsd5.2"><![CDATA[OpenBSD 5.2]]></key>
<key name="os_list_openbsd5.3"><![CDATA[OpenBSD 5.3]]></key>
<key name="os_list_openbsd5.4"><![CDATA[OpenBSD 5.4]]></key>
<key name="os_list_openbsd5.5"><![CDATA[OpenBSD 5.5]]></key>
<key name="os_list_openbsd5.6"><![CDATA[OpenBSD 5.6]]></key>
<key name="os_list_openbsd5.7"><![CDATA[OpenBSD 5.7]]></key>
<key name="os_list_openbsd5.8"><![CDATA[OpenBSD 5.8]]></key>
<key name="os_list_openbsd5.9"><![CDATA[OpenBSD 5.9]]></key>
<key name="os_list_openbsd6.0"><![CDATA[OpenBSD 6.0]]></key>
<key name="os_list_openbsd6.1"><![CDATA[OpenBSD 6.1]]></key>
<key name="os_list_openbsd6.2"><![CDATA[OpenBSD 6.2]]></key>
<key name="os_list_openbsd6.3"><![CDATA[OpenBSD 6.3]]></key>
<key name="os_list_openbsd6.4"><![CDATA[OpenBSD 6.4]]></key>
<key name="os_list_openbsd6.5"><![CDATA[OpenBSD 6.5]]></key>
<key name="os_list_openbsd6.6"><![CDATA[OpenBSD 6.6]]></key>
<key name="os_list_openbsd6.7"><![CDATA[OpenBSD 6.7]]></key>
<key name="os_list_openbsd6.8"><![CDATA[OpenBSD 6.8]]></key>
<key name="os_list_openbsd6.9"><![CDATA[OpenBSD 6.9]]></key>
<key name="os_list_opensolaris2009.06"><![CDATA[OpenSolaris 2009.06]]></key>
<key name="os_list_opensuse-factory"><![CDATA[openSUSE]]></key>
<key name="os_list_opensuse-unknown"><![CDATA[openSUSE]]></key>
<key name="os_list_opensuse10.2"><![CDATA[openSUSE 10.2]]></key>
<key name="os_list_opensuse10.3"><![CDATA[openSUSE 10.3]]></key>
<key name="os_list_opensuse11.0"><![CDATA[openSUSE 11.0]]></key>
<key name="os_list_opensuse11.1"><![CDATA[openSUSE 11.1]]></key>
<key name="os_list_opensuse11.2"><![CDATA[openSUSE 11.2]]></key>
<key name="os_list_opensuse11.3"><![CDATA[openSUSE 11.3]]></key>
<key name="os_list_opensuse11.4"><![CDATA[openSUSE 11.4]]></key>
<key name="os_list_opensuse12.1"><![CDATA[openSUSE 12.1]]></key>
<key name="os_list_opensuse12.2"><![CDATA[openSUSE 12.2]]></key>
<key name="os_list_opensuse12.3"><![CDATA[openSUSE 12.3]]></key>
<key name="os_list_opensuse13.1"><![CDATA[openSUSE 13.1]]></key>
<key name="os_list_opensuse13.2"><![CDATA[openSUSE 13.2]]></key>
<key name="os_list_opensuse15.0"><![CDATA[openSUSE Leap 15.0]]></key>
<key name="os_list_opensuse15.1"><![CDATA[openSUSE Leap 15.1]]></key>
<key name="os_list_opensuse15.2"><![CDATA[openSUSE Leap 15.2]]></key>
<key name="os_list_opensuse15.3"><![CDATA[openSUSE Leap 15.3]]></key>
<key name="os_list_opensuse42.1"><![CDATA[openSUSE Leap 42.1]]></key>
<key name="os_list_opensuse42.2"><![CDATA[openSUSE Leap 42.2]]></key>
<key name="os_list_opensuse42.3"><![CDATA[openSUSE Leap 42.3]]></key>
<key name="os_list_opensusetumbleweed"><![CDATA[openSUSE Tumbleweed]]></key>
<key name="os_list_popos17.10"><![CDATA[Pop!_OS 17.10]]></key>
<key name="os_list_popos18.04"><![CDATA[Pop!_OS 18.04]]></key>
<key name="os_list_popos18.10"><![CDATA[Pop!_OS 18.10]]></key>
<key name="os_list_popos19.04"><![CDATA[Pop!_OS 19.04]]></key>
<key name="os_list_popos19.10"><![CDATA[Pop!_OS 19.10]]></key>
<key name="os_list_popos20.04"><![CDATA[Pop!_OS 20.04]]></key>
<key name="os_list_popos20.10"><![CDATA[Pop!_OS 20.10]]></key>
<key name="os_list_pureos8"><![CDATA[PureOS]]></key>
<key name="os_list_rhel-atomic-7.0"><![CDATA[Red Hat Enterprise Linux Atomic Host 7.0]]></key>
<key name="os_list_rhel-atomic-7.1"><![CDATA[Red Hat Enterprise Linux Atomic Host 7.1]]></key>
<key name="os_list_rhel-atomic-7.2"><![CDATA[Red Hat Enterprise Linux Atomic Host 7.2]]></key>
<key name="os_list_rhel-atomic-7.3"><![CDATA[Red Hat Enterprise Linux Atomic Host 7.3]]></key>
<key name="os_list_rhel-atomic-7.4"><![CDATA[Red Hat Enterprise Linux Atomic Host 7.4]]></key>
<key name="os_list_rhel-unknown"><![CDATA[Red Hat Enterprise Linux Unknown]]></key>
<key name="os_list_rhel2.1"><![CDATA[Red Hat Enterprise Linux 2.1]]></key>
<key name="os_list_rhel2.1.1"><![CDATA[Red Hat Enterprise Linux 2.1 Update 1]]></key>
<key name="os_list_rhel2.1.2"><![CDATA[Red Hat Enterprise Linux 2.1 Update 2]]></key>
<key name="os_list_rhel2.1.3"><![CDATA[Red Hat Enterprise Linux 2.1 Update 3]]></key>
<key name="os_list_rhel2.1.4"><![CDATA[Red Hat Enterprise Linux 2.1 Update 4]]></key>
<key name="os_list_rhel2.1.5"><![CDATA[Red Hat Enterprise Linux 2.1 Update 5]]></key>
<key name="os_list_rhel2.1.6"><![CDATA[Red Hat Enterprise Linux 2.1 Update 6]]></key>
<key name="os_list_rhel2.1.7"><![CDATA[Red Hat Enterprise Linux 2.1 Update 7]]></key>
<key name="os_list_rhel3"><![CDATA[Red Hat Enterprise Linux 3]]></key>
<key name="os_list_rhel3.1"><![CDATA[Red Hat Enterprise Linux 3 Update 1]]></key>
<key name="os_list_rhel3.2"><![CDATA[Red Hat Enterprise Linux 3 Update 2]]></key>
<key name="os_list_rhel3.3"><![CDATA[Red Hat Enterprise Linux 3 Update 3]]></key>
<key name="os_list_rhel3.4"><![CDATA[Red Hat Enterprise Linux 3 Update 4]]></key>
<key name="os_list_rhel3.5"><![CDATA[Red Hat Enterprise Linux 3 Update 5]]></key>
<key name="os_list_rhel3.6"><![CDATA[Red Hat Enterprise Linux 3 Update 6]]></key>
<key name="os_list_rhel3.7"><![CDATA[Red Hat Enterprise Linux 3 Update 7]]></key>
<key name="os_list_rhel3.8"><![CDATA[Red Hat Enterprise Linux 3 Update 8]]></key>
<key name="os_list_rhel3.9"><![CDATA[Red Hat Enterprise Linux 3 Update 9]]></key>
<key name="os_list_rhel4.0"><![CDATA[Red Hat Enterprise Linux 4.0]]></key>
<key name="os_list_rhel4.1"><![CDATA[Red Hat Enterprise Linux 4.1]]></key>
<key name="os_list_rhel4.2"><![CDATA[Red Hat Enterprise Linux 4.2]]></key>
<key name="os_list_rhel4.3"><![CDATA[Red Hat Enterprise Linux 4.3]]></key>
<key name="os_list_rhel4.4"><![CDATA[Red Hat Enterprise Linux 4.4]]></key>
<key name="os_list_rhel4.5"><![CDATA[Red Hat Enterprise Linux 4.5]]></key>
<key name="os_list_rhel4.6"><![CDATA[Red Hat Enterprise Linux 4.6]]></key>
<key name="os_list_rhel4.7"><![CDATA[Red Hat Enterprise Linux 4.7]]></key>
<key name="os_list_rhel4.8"><![CDATA[Red Hat Enterprise Linux 4.8]]></key>
<key name="os_list_rhel4.9"><![CDATA[Red Hat Enterprise Linux 4.9]]></key>
<key name="os_list_rhel5.0"><![CDATA[Red Hat Enterprise Linux 5.0]]></key>
<key name="os_list_rhel5.1"><![CDATA[Red Hat Enterprise Linux 5.1]]></key>
<key name="os_list_rhel5.10"><![CDATA[Red Hat Enterprise Linux 5.10]]></key>
<key name="os_list_rhel5.11"><![CDATA[Red Hat Enterprise Linux 5.11]]></key>
<key name="os_list_rhel5.2"><![CDATA[Red Hat Enterprise Linux 5.2]]></key>
<key name="os_list_rhel5.3"><![CDATA[Red Hat Enterprise Linux 5.3]]></key>
<key name="os_list_rhel5.4"><![CDATA[Red Hat Enterprise Linux 5.4]]></key>
<key name="os_list_rhel5.5"><![CDATA[Red Hat Enterprise Linux 5.5]]></key>
<key name="os_list_rhel5.6"><![CDATA[Red Hat Enterprise Linux 5.6]]></key>
<key name="os_list_rhel5.7"><![CDATA[Red Hat Enterprise Linux 5.7]]></key>
<key name="os_list_rhel5.8"><![CDATA[Red Hat Enterprise Linux 5.8]]></key>
<key name="os_list_rhel5.9"><![CDATA[Red Hat Enterprise Linux 5.9]]></key>
<key name="os_list_rhel6-unknown"><![CDATA[Red Hat Enterprise Linux 6 Unknown]]></key>
<key name="os_list_rhel6.0"><![CDATA[Red Hat Enterprise Linux 6.0]]></key>
<key name="os_list_rhel6.1"><![CDATA[Red Hat Enterprise Linux 6.1]]></key>
<key name="os_list_rhel6.10"><![CDATA[Red Hat Enterprise Linux 6.10]]></key>
<key name="os_list_rhel6.2"><![CDATA[Red Hat Enterprise Linux 6.2]]></key>
<key name="os_list_rhel6.3"><![CDATA[Red Hat Enterprise Linux 6.3]]></key>
<key name="os_list_rhel6.4"><![CDATA[Red Hat Enterprise Linux 6.4]]></key>
<key name="os_list_rhel6.5"><![CDATA[Red Hat Enterprise Linux 6.5]]></key>
<key name="os_list_rhel6.6"><![CDATA[Red Hat Enterprise Linux 6.6]]></key>
<key name="os_list_rhel6.7"><![CDATA[Red Hat Enterprise Linux 6.7]]></key>
<key name="os_list_rhel6.8"><![CDATA[Red Hat Enterprise Linux 6.8]]></key>
<key name="os_list_rhel6.9"><![CDATA[Red Hat Enterprise Linux 6.9]]></key>
<key name="os_list_rhel7-unknown"><![CDATA[Red Hat Enterprise Linux 7 Unknown]]></key>
<key name="os_list_rhel7.0"><![CDATA[Red Hat Enterprise Linux 7.0]]></key>
<key name="os_list_rhel7.1"><![CDATA[Red Hat Enterprise Linux 7.1]]></key>
<key name="os_list_rhel7.2"><![CDATA[Red Hat Enterprise Linux 7.2]]></key>
<key name="os_list_rhel7.3"><![CDATA[Red Hat Enterprise Linux 7.3]]></key>
<key name="os_list_rhel7.4"><![CDATA[Red Hat Enterprise Linux 7.4]]></key>
<key name="os_list_rhel7.5"><![CDATA[Red Hat Enterprise Linux 7.5]]></key>
<key name="os_list_rhel7.6"><![CDATA[Red Hat Enterprise Linux 7.6]]></key>
<key name="os_list_rhel7.7"><![CDATA[Red Hat Enterprise Linux 7.7]]></key>
<key name="os_list_rhel7.8"><![CDATA[Red Hat Enterprise Linux 7.8]]></key>
<key name="os_list_rhel7.9"><![CDATA[Red Hat Enterprise Linux 7.9]]></key>
<key name="os_list_rhel8-unknown"><![CDATA[Red Hat Enterprise Linux 8 Unknown]]></key>
<key name="os_list_rhel8.0"><![CDATA[Red Hat Enterprise Linux 8.0]]></key>
<key name="os_list_rhel8.1"><![CDATA[Red Hat Enterprise Linux 8.1]]></key>
<key name="os_list_rhel8.2"><![CDATA[Red Hat Enterprise Linux 8.2]]></key>
<key name="os_list_rhel8.3"><![CDATA[Red Hat Enterprise Linux 8.3]]></key>
<key name="os_list_rhel8.4"><![CDATA[Red Hat Enterprise Linux 8.4]]></key>
<key name="os_list_rhel8.5"><![CDATA[Red Hat Enterprise Linux 8.5]]></key>
<key name="os_list_rhel9-unknown"><![CDATA[Red Hat Enterprise Linux 9 Unknown]]></key>
<key name="os_list_rhel9.0"><![CDATA[Red Hat Enterprise Linux 9.0]]></key>
<key name="os_list_rhl1.0"><![CDATA[Red Hat Linux 1.0]]></key>
<key name="os_list_rhl1.1"><![CDATA[Red Hat Linux 1.1]]></key>
<key name="os_list_rhl2.0"><![CDATA[Red Hat Linux 2.0]]></key>
<key name="os_list_rhl2.1"><![CDATA[Red Hat Linux 2.1]]></key>
<key name="os_list_rhl3.0.3"><![CDATA[Red Hat Linux 3.0.3]]></key>
<key name="os_list_rhl4.0"><![CDATA[Red Hat Linux 4.0]]></key>
<key name="os_list_rhl4.1"><![CDATA[Red Hat Linux 4.1]]></key>
<key name="os_list_rhl4.2"><![CDATA[Red Hat Linux 4.2]]></key>
<key name="os_list_rhl5.0"><![CDATA[Red Hat Linux 5.0]]></key>
<key name="os_list_rhl5.1"><![CDATA[Red Hat Linux 5.1]]></key>
<key name="os_list_rhl5.2"><![CDATA[Red Hat Linux 5.2]]></key>
<key name="os_list_rhl6.0"><![CDATA[Red Hat Linux 6.0]]></key>
<key name="os_list_rhl6.1"><![CDATA[Red Hat Linux 6.1]]></key>
<key name="os_list_rhl6.2"><![CDATA[Red Hat Linux 6.2]]></key>
<key name="os_list_rhl7"><![CDATA[Red Hat Linux 7]]></key>
<key name="os_list_rhl7.1"><![CDATA[Red Hat Linux 7.1]]></key>
<key name="os_list_rhl7.2"><![CDATA[Red Hat Linux 7.2]]></key>
<key name="os_list_rhl7.3"><![CDATA[Red Hat Linux 7.3]]></key>
<key name="os_list_rhl8.0"><![CDATA[Red Hat Linux 8.0]]></key>
<key name="os_list_rhl9"><![CDATA[Red Hat Linux 9]]></key>
<key name="os_list_rocky-unknown"><![CDATA[Rocky Linux Unknown]]></key>
<key name="os_list_rocky8-unknown"><![CDATA[Rocky Linux 8 Unknown]]></key>
<key name="os_list_rocky8.4"><![CDATA[Rocky Linux 8.4]]></key>
<key name="os_list_rocky8.5"><![CDATA[Rocky Linux 8.5]]></key>
<key name="os_list_rocky8.6"><![CDATA[Rocky Linux 8.6]]></key>
<key name="os_list_rocky9-unknown"><![CDATA[Rocky Linux 9 Unknown]]></key>
<key name="os_list_rocky9.0"><![CDATA[Rocky Linux 9.0]]></key>
<key name="os_list_scientificlinux5.0"><![CDATA[Scientific Linux 5.0]]></key>
<key name="os_list_scientificlinux5.1"><![CDATA[Scientific Linux 5.1]]></key>
<key name="os_list_scientificlinux5.10"><![CDATA[Scientific Linux 5.10]]></key>
<key name="os_list_scientificlinux5.11"><![CDATA[Scientific Linux 5.11]]></key>
<key name="os_list_scientificlinux5.2"><![CDATA[Scientific Linux 5.2]]></key>
<key name="os_list_scientificlinux5.3"><![CDATA[Scientific Linux 5.3]]></key>
<key name="os_list_scientificlinux5.4"><![CDATA[Scientific Linux 5.4]]></key>
<key name="os_list_scientificlinux5.5"><![CDATA[Scientific Linux 5.5]]></key>
<key name="os_list_scientificlinux5.6"><![CDATA[Scientific Linux 5.6]]></key>
<key name="os_list_scientificlinux5.7"><![CDATA[Scientific Linux 5.7]]></key>
<key name="os_list_scientificlinux5.8"><![CDATA[Scientific Linux 5.8]]></key>
<key name="os_list_scientificlinux5.9"><![CDATA[Scientific Linux 5.9]]></key>
<key name="os_list_scientificlinux6.0"><![CDATA[Scientific Linux 6.0]]></key>
<key name="os_list_scientificlinux6.1"><![CDATA[Scientific Linux 6.1]]></key>
<key name="os_list_scientificlinux6.10"><![CDATA[Scientific Linux 6.10]]></key>
<key name="os_list_scientificlinux6.2"><![CDATA[Scientific Linux 6.2]]></key>
<key name="os_list_scientificlinux6.3"><![CDATA[Scientific Linux 6.3]]></key>
<key name="os_list_scientificlinux6.4"><![CDATA[Scientific Linux 6.4]]></key>
<key name="os_list_scientificlinux6.5"><![CDATA[Scientific Linux 6.5]]></key>
<key name="os_list_scientificlinux6.6"><![CDATA[Scientific Linux 6.6]]></key>
<key name="os_list_scientificlinux6.7"><![CDATA[Scientific Linux 6.7]]></key>
<key name="os_list_scientificlinux6.8"><![CDATA[Scientific Linux 6.8]]></key>
<key name="os_list_scientificlinux6.9"><![CDATA[Scientific Linux 6.9]]></key>
<key name="os_list_scientificlinux7-unknown"><![CDATA[Scientific Linux 7 Unknown]]></key>
<key name="os_list_scientificlinux7.0"><![CDATA[Scientific Linux 7.0]]></key>
<key name="os_list_scientificlinux7.1"><![CDATA[Scientific Linux 7.1]]></key>
<key name="os_list_scientificlinux7.2"><![CDATA[Scientific Linux 7.2]]></key>
<key name="os_list_scientificlinux7.3"><![CDATA[Scientific Linux 7.3]]></key>
<key name="os_list_scientificlinux7.4"><![CDATA[Scientific Linux 7.4]]></key>
<key name="os_list_scientificlinux7.5"><![CDATA[Scientific Linux 7.5]]></key>
<key name="os_list_scientificlinux7.6"><![CDATA[Scientific Linux 7.6]]></key>
<key name="os_list_silverblue-rawhide"><![CDATA[Fedora Silverblue Rawhide]]></key>
<key name="os_list_silverblue-unknown"><![CDATA[Fedora Silverblue]]></key>
<key name="os_list_silverblue28"><![CDATA[Fedora Silverblue 28]]></key>
<key name="os_list_silverblue29"><![CDATA[Fedora Silverblue 29]]></key>
<key name="os_list_silverblue30"><![CDATA[Fedora Silverblue 30]]></key>
<key name="os_list_silverblue31"><![CDATA[Fedora Silverblue 31]]></key>
<key name="os_list_silverblue32"><![CDATA[Fedora Silverblue 32]]></key>
<key name="os_list_silverblue33"><![CDATA[Fedora Silverblue 33]]></key>
<key name="os_list_silverblue34"><![CDATA[Fedora Silverblue 34]]></key>
<key name="os_list_silverblue35"><![CDATA[Fedora Silverblue 35]]></key>
<key name="os_list_slackware-current"><![CDATA[Slackware -current]]></key>
<key name="os_list_slackware14.2"><![CDATA[Slackware 14.2]]></key>
<key name="os_list_sle-unknown"><![CDATA[SUSE Linux Enterprise Unknown]]></key>
<key name="os_list_sle15"><![CDATA[SUSE Linux Enterprise 15]]></key>
<key name="os_list_sle15-unknown"><![CDATA[SUSE Linux Enterprise 15 Unknown]]></key>
<key name="os_list_sle15sp1"><![CDATA[SUSE Linux Enterprise 15 SP1]]></key>
<key name="os_list_sle15sp2"><![CDATA[SUSE Linux Enterprise 15 SP2]]></key>
<key name="os_list_sle15sp3"><![CDATA[SUSE Linux Enterprise 15 SP3]]></key>
<key name="os_list_sled10"><![CDATA[SUSE Linux Enterprise Desktop 10]]></key>
<key name="os_list_sled10sp1"><![CDATA[SUSE Linux Enterprise Desktop 10 SP1]]></key>
<key name="os_list_sled10sp2"><![CDATA[SUSE Linux Enterprise Desktop 10 SP2]]></key>
<key name="os_list_sled10sp3"><![CDATA[SUSE Linux Enterprise Desktop 10 SP3]]></key>
<key name="os_list_sled10sp4"><![CDATA[SUSE Linux Enterprise Desktop 10 SP4]]></key>
<key name="os_list_sled11"><![CDATA[SUSE Linux Enterprise Desktop 11]]></key>
<key name="os_list_sled11sp1"><![CDATA[SUSE Linux Enterprise Desktop 11 SP1]]></key>
<key name="os_list_sled11sp2"><![CDATA[SUSE Linux Enterprise Desktop 11 SP2]]></key>
<key name="os_list_sled11sp3"><![CDATA[SUSE Linux Enterprise Desktop 11 SP3]]></key>
<key name="os_list_sled11sp4"><![CDATA[SUSE Linux Enterprise Desktop 11 SP4]]></key>
<key name="os_list_sled12"><![CDATA[SUSE Linux Enterprise Desktop 12]]></key>
<key name="os_list_sled12-unknown"><![CDATA[SUSE Linux Enterprise Desktop 12 Unknown]]></key>
<key name="os_list_sled12sp1"><![CDATA[SUSE Linux Enterprise Desktop 12 SP1]]></key>
<key name="os_list_sled12sp2"><![CDATA[SUSE Linux Enterprise Desktop 12 SP2]]></key>
<key name="os_list_sled12sp3"><![CDATA[SUSE Linux Enterprise Desktop 12 SP3]]></key>
<key name="os_list_sled12sp4"><![CDATA[SUSE Linux Enterprise Desktop 12 SP4]]></key>
<key name="os_list_sled12sp5"><![CDATA[SUSE Linux Enterprise Desktop 12 SP5]]></key>
<key name="os_list_sled9"><![CDATA[SUSE Linux Enterprise Desktop 9]]></key>
<key name="os_list_slem5.0"><![CDATA[SUSE Linux Enterprise Micro]]></key>
<key name="os_list_sles10"><![CDATA[SUSE Linux Enterprise Server 10]]></key>
<key name="os_list_sles10sp1"><![CDATA[SUSE Linux Enterprise Server 10 SP1]]></key>
<key name="os_list_sles10sp2"><![CDATA[SUSE Linux Enterprise Server 10 SP2]]></key>
<key name="os_list_sles10sp3"><![CDATA[SUSE Linux Enterprise Server 10 SP3]]></key>
<key name="os_list_sles10sp4"><![CDATA[SUSE Linux Enterprise Server 10 SP4]]></key>
<key name="os_list_sles11"><![CDATA[SUSE Linux Enterprise Server 11]]></key>
<key name="os_list_sles11sp1"><![CDATA[SUSE Linux Enterprise Server 11 SP1]]></key>
<key name="os_list_sles11sp2"><![CDATA[SUSE Linux Enterprise Server 11 SP2]]></key>
<key name="os_list_sles11sp3"><![CDATA[SUSE Linux Enterprise Server 11 SP3]]></key>
<key name="os_list_sles11sp4"><![CDATA[SUSE Linux Enterprise Server 11 SP4]]></key>
<key name="os_list_sles12"><![CDATA[SUSE Linux Enterprise Server 12]]></key>
<key name="os_list_sles12-unknown"><![CDATA[SUSE Linux Enterprise Server 12 Unknown]]></key>
<key name="os_list_sles12sp1"><![CDATA[SUSE Linux Enterprise Server 12 SP1]]></key>
<key name="os_list_sles12sp2"><![CDATA[SUSE Linux Enterprise Server 12 SP2]]></key>
<key name="os_list_sles12sp3"><![CDATA[SUSE Linux Enterprise Server 12 SP3]]></key>
<key name="os_list_sles12sp4"><![CDATA[SUSE Linux Enterprise Server 12 SP4]]></key>
<key name="os_list_sles12sp5"><![CDATA[SUSE Linux Enterprise Server 12 SP5]]></key>
<key name="os_list_sles9"><![CDATA[SUSE Linux Enterprise Server 9]]></key>
<key name="os_list_solaris10"><![CDATA[Solaris 10]]></key>
<key name="os_list_solaris11"><![CDATA[Oracle Solaris 11]]></key>
<key name="os_list_solaris9"><![CDATA[Solaris 9]]></key>
<key name="os_list_trisquel9"><![CDATA[Trisquel]]></key>
<key name="os_list_ubuntu10.04"><![CDATA[Ubuntu 10.04 LTS]]></key>
<key name="os_list_ubuntu10.10"><![CDATA[Ubuntu 10.10]]></key>
<key name="os_list_ubuntu11.04"><![CDATA[Ubuntu 11.04]]></key>
<key name="os_list_ubuntu11.10"><![CDATA[Ubuntu 11.10]]></key>
<key name="os_list_ubuntu12.04"><![CDATA[Ubuntu 12.04 LTS]]></key>
<key name="os_list_ubuntu12.10"><![CDATA[Ubuntu 12.10]]></key>
<key name="os_list_ubuntu13.04"><![CDATA[Ubuntu 13.04]]></key>
<key name="os_list_ubuntu13.10"><![CDATA[Ubuntu 13.10]]></key>
<key name="os_list_ubuntu14.04"><![CDATA[Ubuntu 14.04 LTS]]></key>
<key name="os_list_ubuntu14.10"><![CDATA[Ubuntu 14.10]]></key>
<key name="os_list_ubuntu15.04"><![CDATA[Ubuntu 15.04]]></key>
<key name="os_list_ubuntu15.10"><![CDATA[Ubuntu 15.10]]></key>
<key name="os_list_ubuntu16.04"><![CDATA[Ubuntu 16.04]]></key>
<key name="os_list_ubuntu16.10"><![CDATA[Ubuntu 16.10]]></key>
<key name="os_list_ubuntu17.04"><![CDATA[Ubuntu 17.04]]></key>
<key name="os_list_ubuntu17.10"><![CDATA[Ubuntu 17.10]]></key>
<key name="os_list_ubuntu18.04"><![CDATA[Ubuntu 18.04 LTS]]></key>
<key name="os_list_ubuntu18.10"><![CDATA[Ubuntu 18.10]]></key>
<key name="os_list_ubuntu19.04"><![CDATA[Ubuntu 19.04]]></key>
<key name="os_list_ubuntu19.10"><![CDATA[Ubuntu 19.10]]></key>
<key name="os_list_ubuntu20.04"><![CDATA[Ubuntu 20.04 LTS]]></key>
<key name="os_list_ubuntu20.10"><![CDATA[Ubuntu 20.10]]></key>
<key name="os_list_ubuntu21.04"><![CDATA[Ubuntu 21.04]]></key>
<key name="os_list_ubuntu21.10"><![CDATA[Ubuntu 21.10]]></key>
<key name="os_list_ubuntu4.10"><![CDATA[Ubuntu 4.10]]></key>
<key name="os_list_ubuntu5.04"><![CDATA[Ubuntu 5.04]]></key>
<key name="os_list_ubuntu5.10"><![CDATA[Ubuntu 5.10]]></key>
<key name="os_list_ubuntu6.06"><![CDATA[Ubuntu 6.06 LTS]]></key>
<key name="os_list_ubuntu6.10"><![CDATA[Ubuntu 6.10]]></key>
<key name="os_list_ubuntu7.04"><![CDATA[Ubuntu 7.04]]></key>
<key name="os_list_ubuntu7.10"><![CDATA[Ubuntu 7.10]]></key>
<key name="os_list_ubuntu8.04"><![CDATA[Ubuntu 8.04 LTS]]></key>
<key name="os_list_ubuntu8.10"><![CDATA[Ubuntu 8.10]]></key>
<key name="os_list_ubuntu9.04"><![CDATA[Ubuntu 9.04]]></key>
<key name="os_list_ubuntu9.10"><![CDATA[Ubuntu 9.10]]></key>
<key name="os_list_unknown"><![CDATA[Unknown]]></key>
<key name="os_list_voidlinux"><![CDATA[Void Linux]]></key>
<key name="os_list_win1.0"><![CDATA[Microsoft Windows 1.0]]></key>
<key name="os_list_win10"><![CDATA[Microsoft Windows 10]]></key>
<key name="os_list_win2.0"><![CDATA[Microsoft Windows 2.0]]></key>
<key name="os_list_win2.1"><![CDATA[Microsoft Windows 2.1]]></key>
<key name="os_list_win2k"><![CDATA[Microsoft Windows 2000]]></key>
<key name="os_list_win2k12"><![CDATA[Microsoft Windows Server 2012]]></key>
<key name="os_list_win2k12r2"><![CDATA[Microsoft Windows Server 2012 R2]]></key>
<key name="os_list_win2k16"><![CDATA[Microsoft Windows Server 2016]]></key>
<key name="os_list_win2k19"><![CDATA[Microsoft Windows Server 2019]]></key>
<key name="os_list_win2k22"><![CDATA[Microsoft Windows Server 2022]]></key>
<key name="os_list_win2k3"><![CDATA[Microsoft Windows Server 2003]]></key>
<key name="os_list_win2k3r2"><![CDATA[Microsoft Windows Server 2003 R2]]></key>
<key name="os_list_win2k8"><![CDATA[Microsoft Windows Server 2008]]></key>
<key name="os_list_win2k8r2"><![CDATA[Microsoft Windows Server 2008 R2]]></key>
<key name="os_list_win3.1"><![CDATA[Microsoft Windows 3.1]]></key>
<key name="os_list_win7"><![CDATA[Microsoft Windows 7]]></key>
<key name="os_list_win8"><![CDATA[Microsoft Windows 8]]></key>
<key name="os_list_win8.1"><![CDATA[Microsoft Windows 8.1]]></key>
<key name="os_list_win95"><![CDATA[Microsoft Windows 95]]></key>
<key name="os_list_win98"><![CDATA[Microsoft Windows 98]]></key>
<key name="os_list_winme"><![CDATA[Microsoft Windows Millennium Edition]]></key>
<key name="os_list_winnt3.1"><![CDATA[Microsoft Windows NT Server 3.1]]></key>
<key name="os_list_winnt3.5"><![CDATA[Microsoft Windows NT Server 3.5]]></key>
<key name="os_list_winnt3.51"><![CDATA[Microsoft Windows NT Server 3.51]]></key>
<key name="os_list_winnt4.0"><![CDATA[Microsoft Windows NT Server 4.0]]></key>
<key name="os_list_winvista"><![CDATA[Microsoft Windows Vista]]></key>
<key name="os_list_winxp"><![CDATA[Microsoft Windows XP]]></key>
</language> </language>
<!-- 日本語 --> <!-- 日本語 -->
<language name="jp" long_name="日本語" description="Anvil! language file."> <language name="jp" long_name="日本語" description="Anvil! language file.">
@ -4283,7 +3575,7 @@ The error was:
<key name="brand_0002">Anvil!</key> <key name="brand_0002">Anvil!</key>
<key name="brand_0003">ストライカ</key> <key name="brand_0003">ストライカ</key>
<key name="brand_0004">スカンコア</key> <key name="brand_0004">スカンコア</key>
<key name="brand_0005"><![CDATA[&copy; 1997 - 2022 <a href="https://alteeve.com/" target="_new">Alteeve's Niche! Inc.</a>, トロント、オンタリオ、カナダ]]></key> <key name="brand_0005"><![CDATA[&copy; 1997 - 2023 <a href="https://alteeve.com/" target="_new">Alteeve's Niche! Inc.</a>, トロント、オンタリオ、カナダ]]></key>
</language> </language>
</words> </words>

@ -43,6 +43,7 @@ dist_sbin_SCRIPTS = \
anvil-update-issue \ anvil-update-issue \
anvil-update-states \ anvil-update-states \
anvil-update-system \ anvil-update-system \
anvil-version-changes \
anvil-watch-bonds \ anvil-watch-bonds \
scancore \ scancore \
striker-auto-initialize-all \ striker-auto-initialize-all \
@ -67,7 +68,8 @@ fencedir = ${FASEXECPREFIX}/sbin
dist_fence_SCRIPTS = \ dist_fence_SCRIPTS = \
fence_delay \ fence_delay \
fence_pacemaker fence_pacemaker \
unfence_pacemaker
sharedir = ${datarootdir}/anvil sharedir = ${datarootdir}/anvil

@ -9,6 +9,7 @@
# #
# TODO: # TODO:
# - Add support for boot ordering. # - Add support for boot ordering.
# - Check which node we want to put on and set a location constraint to prefer that node before calling pcs.
# #
use strict; use strict;

@ -213,6 +213,7 @@ sub reconfigure_network
my $organization = exists $anvil->data->{variables}{form}{config_step1}{organization}{value} ? $anvil->data->{variables}{form}{config_step1}{organization}{value} : ""; my $organization = exists $anvil->data->{variables}{form}{config_step1}{organization}{value} ? $anvil->data->{variables}{form}{config_step1}{organization}{value} : "";
my $bcn_count = exists $anvil->data->{variables}{form}{config_step1}{bcn_count}{value} ? $anvil->data->{variables}{form}{config_step1}{bcn_count}{value} : 1; my $bcn_count = exists $anvil->data->{variables}{form}{config_step1}{bcn_count}{value} ? $anvil->data->{variables}{form}{config_step1}{bcn_count}{value} : 1;
my $sn_count = exists $anvil->data->{variables}{form}{config_step1}{sn_count}{value} ? $anvil->data->{variables}{form}{config_step1}{sn_count}{value} : 0; my $sn_count = exists $anvil->data->{variables}{form}{config_step1}{sn_count}{value} ? $anvil->data->{variables}{form}{config_step1}{sn_count}{value} : 0;
my $mn_count = exists $anvil->data->{variables}{form}{config_step1}{mn_count}{value} ? $anvil->data->{variables}{form}{config_step1}{mn_count}{value} : 0;
my $ifn_count = exists $anvil->data->{variables}{form}{config_step1}{ifn_count}{value} ? $anvil->data->{variables}{form}{config_step1}{ifn_count}{value} : 1; my $ifn_count = exists $anvil->data->{variables}{form}{config_step1}{ifn_count}{value} ? $anvil->data->{variables}{form}{config_step1}{ifn_count}{value} : 1;
my $new_host_name = exists $anvil->data->{variables}{form}{config_step2}{host_name}{value} ? $anvil->data->{variables}{form}{config_step2}{host_name}{value} : ""; my $new_host_name = exists $anvil->data->{variables}{form}{config_step2}{host_name}{value} ? $anvil->data->{variables}{form}{config_step2}{host_name}{value} : "";
my $type = $anvil->Get->host_type(); my $type = $anvil->Get->host_type();
@ -222,8 +223,9 @@ sub reconfigure_network
domain => $domain, domain => $domain,
organization => $organization, organization => $organization,
bcn_count => $bcn_count, bcn_count => $bcn_count,
sn_count => $sn_count, sn_count => $sn_count,
ifn_count => $ifn_count, mn_count => $mn_count,
ifn_count => $ifn_count,
new_host_name => $new_host_name, new_host_name => $new_host_name,
type => $type, type => $type,
}}); }});
@ -324,7 +326,7 @@ sub reconfigure_network
if (not $gateway_interface) if (not $gateway_interface)
{ {
# IFN first, BCN second, SN last # IFN first, BCN second, SN last
foreach my $network_type ("ifn", "bcn", "sn") foreach my $network_type ("ifn", "bcn", "sn", "mn")
{ {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }});
@ -332,6 +334,7 @@ sub reconfigure_network
if ($network_type eq "bcn") { $count = $bcn_count; } if ($network_type eq "bcn") { $count = $bcn_count; }
elsif ($network_type eq "sn") { $count = $sn_count; } elsif ($network_type eq "sn") { $count = $sn_count; }
elsif ($network_type eq "ifn") { $count = $ifn_count; } elsif ($network_type eq "ifn") { $count = $ifn_count; }
elsif ($network_type eq "mn") { $count = $mn_count; }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
next if not $count; next if not $count;
@ -403,6 +406,9 @@ ORDER BY
's2:variable_name' => $variable_name, 's2:variable_name' => $variable_name,
's3:variable_value' => $variable_value, 's3:variable_value' => $variable_value,
}}); }});
# An undefined interface will have the MAC address value set to '1', ignore those.
next if $variable_value = 1;
if ($variable_name =~ /form::config_step2::(.*?)_mac_to_set::value/) if ($variable_name =~ /form::config_step2::(.*?)_mac_to_set::value/)
{ {
@ -442,7 +448,7 @@ ORDER BY
# This will be set to '1' if we make a change. # This will be set to '1' if we make a change.
my $changes = 0; my $changes = 0;
my $new_interfaces = []; my $new_interfaces = [];
foreach my $network_type ("bcn", "sn", "ifn") foreach my $network_type ("bcn", "sn", "mn", "ifn")
{ {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_type => $network_type }});
@ -450,6 +456,7 @@ ORDER BY
if ($network_type eq "bcn") { $count = $bcn_count; } if ($network_type eq "bcn") { $count = $bcn_count; }
elsif ($network_type eq "sn") { $count = $sn_count; } elsif ($network_type eq "sn") { $count = $sn_count; }
elsif ($network_type eq "ifn") { $count = $ifn_count; } elsif ($network_type eq "ifn") { $count = $ifn_count; }
elsif ($network_type eq "mn") { $count = $mn_count; }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
next if not $count; next if not $count;
@ -571,6 +578,12 @@ ORDER BY
$say_interface = "sn".$network_count; $say_interface = "sn".$network_count;
$interface_prefix = "SN"; $interface_prefix = "SN";
} }
elsif ($network_type eq "mn")
{
$say_network = "Migration Network ".$network_count;
$say_interface = "mn".$network_count;
$interface_prefix = "MN";
}
elsif ($network_type eq "ifn") elsif ($network_type eq "ifn")
{ {
$say_network = "Internet-Facing Network ".$network_count; $say_network = "Internet-Facing Network ".$network_count;
@ -1140,6 +1153,12 @@ ORDER BY
$say_interface = "ifn".$network_count; $say_interface = "ifn".$network_count;
$interface_prefix = "IFN"; $interface_prefix = "IFN";
} }
elsif ($network_type eq "mn")
{
$say_network = "Migration Network ".$network_count;
$say_interface = "mn".$network_count;
$interface_prefix = "MN";
}
my $say_defroute = $is_gateway ? "yes" : "no"; my $say_defroute = $is_gateway ? "yes" : "no";
my $cidr = $anvil->Convert->cidr({subnet_mask => $subnet_mask}); my $cidr = $anvil->Convert->cidr({subnet_mask => $subnet_mask});
my $new_link1_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Link_1"; my $new_link1_file = $anvil->data->{path}{directories}{ifcfg}."/ifcfg-".$interface_prefix."_".$network_count."_-_Link_1";

@ -1294,192 +1294,15 @@ sub handle_special_cases
{ {
my ($anvil) = @_; my ($anvil) = @_;
my $host_type = $anvil->Get->host_type(); # Thsi is now handled by 'anvil-version-changes'
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }}); my $shell_call = $anvil->data->{path}{exe}{'anvil-version-changes'}.$anvil->Log->switches;
if ($host_type ne "striker") $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
{
### TODO: Test that this is fixed. The bug is now ERRATA
# RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16
# We're a node or DR host. We need to touch this file.
my $work_around_file = "/etc/qemu/firmware/50-edk2-ovmf-cc.json";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { work_around_file => $work_around_file }});
if (not -e $work_around_file)
{
$anvil->Storage->write_file({
debug => 2,
file => $work_around_file,
body => "",
overwrite => 0,
backup => 0,
mode => "0644",
user => "root",
group => "root",
});
}
# Make sure DRBD compiled after a kernel upgrade.
$anvil->DRBD->_initialize_kmod({debug => 2});
}
if ($host_type eq "striker")
{
# This converts the old/broken 'notifications' tables with the more appropriately named 'alert-override'
if (1)
{
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}})
{
my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'anvil' AND table_name = 'notifications';";
$anvil->Log->variables({source => $THIS_FILE, uuid => $uuid, line => __LINE__, level => 2, list => { query => $query }});
my $count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
if ($count)
{
my $queries = [];
push @{$queries}, "DROP FUNCTION history_notifications() CASCADE;";
push @{$queries}, "DROP TABLE history.notifications;";
push @{$queries}, "DROP TABLE public.notifications;";
push @{$queries}, q|CREATE TABLE alert_overrides (
alert_override_uuid uuid not null primary key,
alert_override_recipient_uuid uuid not null, -- The recipient we're linking.
alert_override_host_uuid uuid not null, -- This host_uuid of the referenced machine
alert_override_alert_level integer not null, -- This is the alert level (at or above) that this user wants alerts from. If set to '-1', the record is deleted.
modified_date timestamp with time zone not null,
FOREIGN KEY(alert_override_host_uuid) REFERENCES hosts(host_uuid),
FOREIGN KEY(alert_override_recipient_uuid) REFERENCES recipients(recipient_uuid)
);
ALTER TABLE alert_overrides OWNER TO admin;
CREATE TABLE history.alert_overrides (
history_id bigserial,
alert_override_uuid uuid,
alert_override_recipient_uuid uuid,
alert_override_host_uuid uuid,
alert_override_alert_level integer,
modified_date timestamp with time zone not null
);
ALTER TABLE history.alert_overrides OWNER TO admin;
CREATE FUNCTION history_alert_overrides() RETURNS trigger
AS $$
DECLARE
history_alert_overrides RECORD;
BEGIN
SELECT INTO history_alert_overrides * FROM alert_overrides WHERE alert_override_uuid = new.alert_override_uuid;
INSERT INTO history.alert_overrides
(alert_override_uuid,
alert_override_recipient_uuid,
alert_override_host_uuid,
alert_override_alert_level,
modified_date)
VALUES
(history_alert_overrides.alert_override_uuid,
history_alert_overrides.alert_override_recipient_uuid,
history_alert_overrides.alert_override_host_uuid,
history_alert_overrides.alert_override_alert_level,
history_alert_overrides.modified_date);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
ALTER FUNCTION history_alert_overrides() OWNER TO admin;
CREATE TRIGGER trigger_alert_overrides
AFTER INSERT OR UPDATE ON alert_overrides
FOR EACH ROW EXECUTE PROCEDURE history_alert_overrides();
|;
foreach my $query (@{$queries})
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
}
$anvil->Database->write({debug => 2, uuid => $uuid, query => $queries, source => $THIS_FILE, line => __LINE__});
}
}
}
# This checks to make sure that the 'audits' table exists (added late into M3.0 pre-release)
if (0)
{
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}})
{
my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'anvil' AND table_name = 'audits';";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
my $count = $anvil->Database->query({query => $query, uuid => $uuid, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
if (not $count)
{
# Add the table.
my $query = q|
CREATE TABLE audits (
audit_uuid uuid primary key,
audit_user_uuid uuid not null, -- This is the users -> user_uuid the audit is tracking
audit_details text not null, -- This is the information explaining the action being audited.
modified_date timestamp with time zone not null,
FOREIGN KEY(audit_user_uuid) REFERENCES users(user_uuid)
);
ALTER TABLE audits OWNER TO admin;
CREATE TABLE history.audits (
history_id bigserial,
audit_uuid uuid,
audit_user_uuid uuid,
audit_details text,
modified_date timestamp with time zone not null
);
ALTER TABLE history.audits OWNER TO admin;
CREATE FUNCTION history_audits() RETURNS trigger
AS $$
DECLARE
history_audits RECORD;
BEGIN
SELECT INTO history_audits * FROM audits WHERE audit_uuid = new.audit_uuid;
INSERT INTO history.audits
(audit_uuid,
audit_user_uuid,
audit_details,
modified_date)
VALUES
(history_audit.audit_uuid,
history_audit.audit_user_uuid,
history_audit.audit_details,
history_audit.modified_date);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
ALTER FUNCTION history_audits() OWNER TO admin;
CREATE TRIGGER trigger_audits
AFTER INSERT OR UPDATE ON audits
FOR EACH ROW EXECUTE PROCEDURE history_audits();
|;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
$anvil->Database->write({debug => 2, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
}
}
}
}
### TODO: Remove these later. This is here to clean up how we used to handle db_in_use and lock_request flags. my ($states_output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call, source => $THIS_FILE, line => __LINE__});
if (1) $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
{ states_output => $states_output,
# Broadly clear all states that are '0' now. return_code => $return_code,
my $queries = []; }});
push @{$queries}, "DELETE FROM states WHERE state_name LIKE 'db_in_use::%' AND state_note != '1';";
push @{$queries}, "DELETE FROM history.variables WHERE variable_name = 'lock_request';";
push @{$queries}, "DELETE FROM variables WHERE variable_name = 'lock_request';";
foreach my $query (@{$queries})
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
}
$anvil->Database->write({debug => 2, query => $queries, source => $THIS_FILE, line => __LINE__});
}
return(0); return(0);
} }

@ -1068,7 +1068,11 @@ sub configure_pacemaker
} }
} }
} }
# Make sure logind is update to handle fencing properly
# see - https://access.redhat.com/solutions/1578823
$anvil->Cluster->configure_logind({debug => 2});
# Enable fencing and set the retry to INFINITY, if needed. # Enable fencing and set the retry to INFINITY, if needed.
$anvil->data->{cib}{parsed}{data}{stonith}{'max-attempts'} = "" if not defined $anvil->data->{cib}{parsed}{data}{stonith}{'max-attempts'}; $anvil->data->{cib}{parsed}{data}{stonith}{'max-attempts'} = "" if not defined $anvil->data->{cib}{parsed}{data}{stonith}{'max-attempts'};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {

@ -30,6 +30,8 @@
# TODO: # TODO:
# - If two Strikers have the same file name, but different sizes, we get into a yo-yo of updating the two # - If two Strikers have the same file name, but different sizes, we get into a yo-yo of updating the two
# sides. If this happens, we need to rsync the larger one over the smaller one. # sides. If this happens, we need to rsync the larger one over the smaller one.
#
# - Create a ".done.<file_name>" when the upload completes so that we know it's time to add it to the database.
# #
# NOTE: # NOTE:
# - remove unsyncs, add syncs. # - remove unsyncs, add syncs.

@ -123,6 +123,17 @@ sub process_interactive
$anvil->data->{old_config}{ram}{'bytes'} = ""; $anvil->data->{old_config}{ram}{'bytes'} = "";
# Did they specify an Anvil! system? # Did they specify an Anvil! system?
if (not $anvil->data->{switches}{anvil})
{
# Is this machine in an Anvil!?
$anvil->Database->get_hosts();
my $host_uuid = $anvil->Get->host_uuid();
my $anvil->data->{switches}{anvil} = $anvil->data->{hosts}{host_uuid}{$host_uuid}{anvil_uuid}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_uuid => $host_uuid,
"switches::anvil" => $anvil->data->{switches}{anvil},
}});
}
if ($anvil->data->{switches}{anvil}) if ($anvil->data->{switches}{anvil})
{ {
$anvil->Get->anvil_from_switch({anvil => $anvil->data->{switches}{anvil}}); $anvil->Get->anvil_from_switch({anvil => $anvil->data->{switches}{anvil}});
@ -142,8 +153,14 @@ sub process_interactive
}}); }});
} }
if (not $anvil->data->{target_server}{anvil_uuid}) # If we don't habe a server, show the list of servers.
if (not $anvil->data->{switches}{server})
{ {
# If we've got an Anvil!, show the VMs on it. Otherwise, show all VMs.
if ($anvil->data->{switches}{anvil_name})
{
}
} }
return(0); return(0);

@ -34,7 +34,25 @@ my $anvil = Anvil::Tools->new();
# Read switches (target ([user@]host[:port]) and the file with the target's password. If the password is # Read switches (target ([user@]host[:port]) and the file with the target's password. If the password is
# passed directly, it will be used. Otherwise, the password will be read from the database. # passed directly, it will be used. Otherwise, the password will be read from the database.
$anvil->Get->switches({list => ["anvil", "anvil-name", "anvil-uuid", "ci-test", "driver-disc", "cpu", "install-media", "machine", "name", "options", "os", "pre-test", "uuid", "ram", "storage-group", "storage-size", "use-image"], man => $THIS_FILE}); $anvil->Get->switches({list => [
"anvil",
"anvil-name",
"anvil-uuid",
"ci-test",
"driver-disc",
"cpu",
"install-media",
"machine",
"name",
"network",
"options",
"os",
"pre-test",
"uuid",
"ram",
"storage-group",
"storage-size",
"use-image"], man => $THIS_FILE});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0115", variables => { program => $THIS_FILE }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0115", variables => { program => $THIS_FILE }});
@ -122,8 +140,8 @@ sub run_jobs
if (not $waiting_reported) if (not $waiting_reported)
{ {
$anvil->Job->update_progress({ $anvil->Job->update_progress({
progress => 5, progress => 5,
message => "job_0275", message => "job_0275",
}); });
$waiting_reported = 1; $waiting_reported = 1;
} }
@ -138,8 +156,8 @@ sub run_jobs
# We're ready! # We're ready!
$waiting = 0; $waiting = 0;
$anvil->Job->update_progress({ $anvil->Job->update_progress({
progress => 8, progress => 8,
message => "job_0276", message => "job_0276",
}); });
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0276"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0276"});
} }
@ -147,8 +165,8 @@ sub run_jobs
{ {
# Cluster is coming up, but it's not up yet. # Cluster is coming up, but it's not up yet.
$anvil->Job->update_progress({ $anvil->Job->update_progress({
progress => 6, progress => 6,
message => "job_0278", message => "job_0278",
}); });
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0278"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0278"});
} }
@ -457,9 +475,7 @@ sub provision_server
say_memory => $say_memory, say_memory => $say_memory,
}}); }});
### TODO: Support user-selected IFN. For now, we hard-code it to 'ifn_bridge1' Also allow a MAC to be ### TODO: Support disk images (ie: sysprep) via '--import'. The device used for booting is the first
### set with '--network ifn1_bridge1[:mac],ifn2_bridge1[:mac]...'.
### Support disk images (ie: sysprep) via '--import'. The device used for booting is the first
### device specified via "--disk" ### device specified via "--disk"
### Consider support for TPM, RNG and watchdog devices ### Consider support for TPM, RNG and watchdog devices
my $server_uuid = $anvil->data->{job}{server_uuid} ? $anvil->data->{job}{server_uuid} : $anvil->Get->uuid(); my $server_uuid = $anvil->data->{job}{server_uuid} ? $anvil->data->{job}{server_uuid} : $anvil->Get->uuid();
@ -467,17 +483,36 @@ sub provision_server
"job::os" => $anvil->data->{job}{os}, "job::os" => $anvil->data->{job}{os},
server_uuid => $server_uuid, server_uuid => $server_uuid,
}}); }});
my $disk_bus = ",target.bus=virtio"; my $disk_bus = ",target.bus=virtio";
my $nic_model = ",model.type=virtio";
if ($anvil->data->{job}{os} eq "win7") if ($anvil->data->{job}{os} eq "win7")
{ {
$disk_bus = ""; $disk_bus = "";
$nic_model = ",model.type=e1000e";
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disk_bus => $disk_bus }});
disk_bus => $disk_bus,
nic_model => $nic_model, # Setup the network line
}}); my $nic = "bridge=";
if ($anvil->data->{job}{network}{bridge})
{
$nic .= $anvil->data->{job}{network}{bridge};
}
else
{
$nic .= "ifn1_bridge1";
}
if ($anvil->data->{job}{network}{model})
{
$nic .= ",model.type=".$anvil->data->{job}{network}{model};
}
elsif ($anvil->data->{job}{os} eq "win7")
{
$nic .= ",model.type=e1000e";
}
if ($anvil->data->{job}{network}{mac})
{
$nic .= ",mac.address=".$anvil->data->{job}{network}{mac};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { nic => $nic }});
my $shell_call = $anvil->data->{path}{exe}{'virt-install'}." --connect qemu:///system \\\n"; my $shell_call = $anvil->data->{path}{exe}{'virt-install'}." --connect qemu:///system \\\n";
$shell_call .= "--name ".$server." \\\n"; $shell_call .= "--name ".$server." \\\n";
@ -486,7 +521,7 @@ sub provision_server
$shell_call .= " --events on_poweroff=destroy,on_reboot=restart \\\n"; $shell_call .= " --events on_poweroff=destroy,on_reboot=restart \\\n";
$shell_call .= " --vcpus ".$anvil->data->{job}{cpu_cores}.",sockets=1,cores=".$anvil->data->{job}{cpu_cores}." \\\n"; $shell_call .= " --vcpus ".$anvil->data->{job}{cpu_cores}.",sockets=1,cores=".$anvil->data->{job}{cpu_cores}." \\\n";
$shell_call .= " --cpu host \\\n"; $shell_call .= " --cpu host \\\n";
$shell_call .= " --network bridge=ifn1_bridge1".$nic_model." \\\n"; $shell_call .= " --network ".$nic." \\\n";
$shell_call .= " --graphics vnc \\\n"; $shell_call .= " --graphics vnc \\\n";
$shell_call .= " --sound ich9 \\\n"; $shell_call .= " --sound ich9 \\\n";
$shell_call .= " --clock offset=".$clock_offset.",rtc_tickpolicy=catchup \\\n"; $shell_call .= " --clock offset=".$clock_offset.",rtc_tickpolicy=catchup \\\n";
@ -670,7 +705,17 @@ sub provision_server
output => $output, output => $output,
return_code => $return_code, return_code => $return_code,
}}); }});
# Call an adjust in case we forced the resource to disable fencing.
$shell_call = $anvil->data->{path}{exe}{drbdadm}." adjust ".$anvil->data->{job}{server_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
($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,
}});
return(0); return(0);
} }
@ -723,8 +768,8 @@ sub startup_resource
task => "up", task => "up",
}); });
# If both sides are Inconsistent, for node 1 to primary # If both sides are Inconsistent, for node 1 to primary. If we're primary and the peer is
# Unknown, force to primary.
my $waiting = 1; my $waiting = 1;
while($waiting) while($waiting)
{ {
@ -889,8 +934,93 @@ sub startup_resource
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { all_ready => $all_ready }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { all_ready => $all_ready }});
if ($all_ready) if ($all_ready)
{ {
# If we're here, we're mostly ready, however we're in a bit of a spot if our
# peer hasn't connected. We're going to wait up to one minute for the peer to
# connect. If it doesn't, we're going to force back to primary.
$anvil->Job->update_progress({
progress => 59,
message => "job_0434",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0434"});
my $wait_until = time + 60;
my $resource = $anvil->data->{job}{server_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:time' => time,
's2:wait_until' => $wait_until,
's3:resource' => $resource,
}});
while ($waiting)
{
$anvil->DRBD->gather_data({debug => 3});
my $any_unknown = 0;
foreach my $volume (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}{$resource}{volume}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { volume => $volume }});
foreach my $peer (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer => $peer }});
if (not defined $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{local_disk_state})
{
$any_unknown = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { volume => $volume }});
next;
}
my $local_disk_state = $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{local_disk_state};
my $peer_disk_state = $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{peer_disk_state};
my $local_role = $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{local_role};
my $peer_role = $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{peer_role};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:local_disk_state' => $local_disk_state,
's2:peer_disk_state' => $peer_disk_state,
's3:local_role' => $local_role,
's4:peer_role' => $peer_role,
}});
if (($peer_disk_state =~ /unknown/i) or ($peer_role =~ /unknown/i))
{
$any_unknown = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { volume => $volume }});
}
last if $any_unknown;
}
last if $any_unknown;
}
if ($any_unknown)
{
if (time > $wait_until)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0436"});
my $shell_call = $anvil->data->{path}{exe}{drbdsetup}." net-options ".$anvil->data->{job}{server_name}." ".$anvil->data->{job}{drbd_peer_node_id}." --set-defaults --_name=".$anvil->data->{job}{peer_short_name}." --protocol=C --fencing=dont-care";
$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,
}});
$waiting = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
}
else
{
my $waiting = $wait_until - time;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0435", variables => { waiting => $waiting }});
sleep 5;
}
}
else
{
$waiting = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
}
}
$waiting = 0; $waiting = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
} }
if ($waiting) if ($waiting)
@ -918,13 +1048,13 @@ sub create_md
# Create the DRBD metadata # Create the DRBD metadata
my $shell_call = $anvil->data->{path}{exe}{drbdadm}." --force create-md --max-peers=3 ".$anvil->data->{job}{server_name}; my $shell_call = $anvil->data->{path}{exe}{drbdadm}." --force create-md --max-peers=3 ".$anvil->data->{job}{server_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); $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}); my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output, output => $output,
return_code => $return_code, return_code => $return_code,
}}); }});
### Return codes ### Return codes
# 0 == Success # 0 == Success
# 1 == ? # 1 == ?
@ -934,17 +1064,17 @@ sub create_md
# Metadata creation failed. # Metadata creation failed.
$anvil->Job->update_progress({ $anvil->Job->update_progress({
progress => 100, progress => 100,
message => "error_0204,!!return_code!".$return_code."!!,!!output!".$output."!!", message => "error_0204,!!return_code!".$return_code."!!,!!output!".$output."!!",
job_status => "failed", job_status => "failed",
}); });
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => 'err', key => "error_0204", variables => { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => 'err', key => "error_0204", variables => {
return_code => $return_code, return_code => $return_code,
output => $output, output => $output,
}}); }});
$anvil->nice_exit({exit_code => 1}); $anvil->nice_exit({exit_code => 1});
} }
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0579", variables => { resource => $anvil->data->{job}{server_name} }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0579", variables => { resource => $anvil->data->{job}{server_name} }});
$anvil->Job->update_progress({ $anvil->Job->update_progress({
progress => 50, progress => 50,
message => "job_0191,!!resource!".$anvil->data->{job}{server_name}."!!", message => "job_0191,!!resource!".$anvil->data->{job}{server_name}."!!",
@ -1339,6 +1469,27 @@ sub parse_job_data
$anvil->data->{job}{os} =~ s/\s+?//; $anvil->data->{job}{os} =~ s/\s+?//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::os' => $anvil->data->{job}{os} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::os' => $anvil->data->{job}{os} }});
} }
if ($line =~ /network=(.*)$/)
{
$anvil->data->{job}{network}{raw} = $1;
$anvil->data->{job}{network}{raw} =~ s/^\s+//;
$anvil->data->{job}{network}{raw} =~ s/\s+?//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::network::raw' => $anvil->data->{job}{network}{raw} }});
# Now break up the data, if needed.
my $problem = process_network($anvil, $anvil->data->{job}{network}{raw});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
if ($problem)
{
$anvil->Job->update_progress({
progress => 100,
message => "error_0398,!!network!".$anvil->data->{job}{network}{raw}."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => 'err', key => "error_0398", variables => { job_uuid => $anvil->data->{job}{network}{raw} }});
$anvil->nice_exit({exit_code => 1});
}
}
} }
# We need a server name and storage group UUID regardless of which mode we're in. # We need a server name and storage group UUID regardless of which mode we're in.
@ -1414,7 +1565,7 @@ sub parse_job_data
$anvil->nice_exit({exit_code => 1}); $anvil->nice_exit({exit_code => 1});
} }
} }
if (not $anvil->data->{job}{server_name}) if (not $anvil->data->{job}{server_name})
{ {
# No server name given # No server name given
@ -1515,6 +1666,7 @@ sub parse_job_data
}}); }});
$anvil->nice_exit({exit_code => 1}); $anvil->nice_exit({exit_code => 1});
} }
# Driver disc is optional. # Driver disc is optional.
$anvil->data->{new_server}{driver_iso_path} = ""; $anvil->data->{new_server}{driver_iso_path} = "";
if (($anvil->data->{job}{driver_iso_uuid}) && ($anvil->data->{job}{driver_iso_uuid} ne "none")) if (($anvil->data->{job}{driver_iso_uuid}) && ($anvil->data->{job}{driver_iso_uuid} ne "none"))
@ -1561,6 +1713,90 @@ sub parse_job_data
return(0); return(0);
} }
sub process_network
{
my ($anvil, $string) = @_;
my $problem = 0;
my $anvil_uuid = $anvil->data->{new_server}{anvil_uuid};
my $anvil_name = $anvil->data->{new_server}{anvil_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
anvil_uuid => $anvil_uuid,
anvil_name => $anvil_name,
}});
$anvil->data->{job}{network}{bridge} = "";
$anvil->data->{job}{network}{mac} = "";
$anvil->data->{job}{network}{model} = "";
if (($string =~ /bridge=(.*?),/) or ($string =~ /bridge=(.*?)$/))
{
my $bridge_name = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bridge_name => $bridge_name }});
# Make sure the bridge is valid.
if ((exists $anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}) && ($anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on_nodes}))
{
$anvil->data->{job}{network}{bridge} = $bridge_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::network::bridge' => $anvil->data->{job}{network}{bridge} }});
}
else
{
print "The requested bridge: [".$bridge_name."] is not valid.\n";
print "Valid bridges on the Anvil! node: [".$anvil_name."] are:'\n";
foreach my $bridge_name (sort {$a cmp $b} keys %{$anvil->data->{anvil_resources}{$anvil_uuid}{bridges}})
{
next if $anvil->data->{anvil_resources}{$anvil_uuid}{bridges}{$bridge_name}{on_nodes};
print "- ".$bridge_name."\n";
}
$problem = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
}
}
if (($string =~ /mac=(.*?),/) or ($string =~ /mac=(.*?)$/))
{
my $mac_address = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }});
# Validate it
if (not $anvil->Validate->mac({mac => $mac_address}))
{
print "The requested MAC address: [".$mac_address."] does not appear to be valid.\n";
$problem = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
}
else
{
$anvil->data->{job}{network}{mac} = $mac_address;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::network::mac' => $anvil->data->{job}{network}{mac} }});
}
}
if (($string =~ /model=(.*?),/) or ($string =~ /model=(.*?)$/))
{
my $model = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { model => $model }});
$anvil->Get->virsh_list_net({debug => 2});
if (exists $anvil->data->{osinfo}{net}{$model})
{
$anvil->data->{job}{network}{model} = $model;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::network::model' => $anvil->data->{job}{network}{model} }});
}
else
{
print "The requested network model: [".$model."] does not appear to be valid.\n";
print "Valid network models are:\n";
foreach my $model (sort {$a cmp $b} keys %{$anvil->data->{osinfo}{net}})
{
print "- ".$model." (".$anvil->data->{osinfo}{net}{$model}{vendor}." - ".$anvil->data->{osinfo}{net}{$model}{product}.")\n";
}
$problem = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
}
}
return($problem);
}
sub check_anvil sub check_anvil
{ {
my ($anvil) = @_; my ($anvil) = @_;
@ -1920,20 +2156,10 @@ sub interactive_ask_server_cpu
}})."\n"; }})."\n";
my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}; my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid};
my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}; my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid};
my $dr1_host_uuid = $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid} : "";
print $anvil->Words->string({key => "job_0163", variables => { core => 1, model => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node1_host_uuid}{cpu}{model} }})."\n"; print $anvil->Words->string({key => "job_0163", variables => { core => 1, model => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node1_host_uuid}{cpu}{model} }})."\n";
print $anvil->Words->string({key => "job_0163", variables => { core => 2, model => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node2_host_uuid}{cpu}{model} }})."\n"; print $anvil->Words->string({key => "job_0163", variables => { core => 2, model => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node2_host_uuid}{cpu}{model} }})."\n";
if ($anvil->data->{anvil_resources}{$anvil_uuid}{has_dr})
{
print $anvil->Words->string({key => "job_0164", variables => {
model => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node1_host_uuid}{cpu}{model},
cores => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$dr1_host_uuid}{cpu}{cores},
threads => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$dr1_host_uuid}{cpu}{threads},
}})."\n";
}
print $terminal->Tgoto('cm', 0, 4)."? "; print $terminal->Tgoto('cm', 0, 4)."? ";
my $answer = <STDIN>; my $answer = <STDIN>;
chomp $answer; chomp $answer;
@ -2015,12 +2241,6 @@ sub interactive_ask_server_ram
ram_node1 => $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node1_host_uuid}{ram}{hardware}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node1_host_uuid}{ram}{hardware}, unit => "M"}).")", ram_node1 => $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node1_host_uuid}{ram}{hardware}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node1_host_uuid}{ram}{hardware}, unit => "M"}).")",
ram_node2 => $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node2_host_uuid}{ram}{hardware}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node2_host_uuid}{ram}{hardware}, unit => "M"}).")", ram_node2 => $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node2_host_uuid}{ram}{hardware}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$node2_host_uuid}{ram}{hardware}, unit => "M"}).")",
}})."\n"; }})."\n";
if ($anvil->data->{anvil_resources}{$anvil_uuid}{has_dr})
{
print $anvil->Words->string({key => "job_0168", variables => {
ram_available => $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$dr1_host_uuid}{ram}{hardware}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{host_uuid}{$dr1_host_uuid}{ram}{hardware}, unit => "M"}).")",
}})."\n";
}
print $terminal->Tgoto('cm', 0, 5)."? "; print $terminal->Tgoto('cm', 0, 5)."? ";
my $answer = <STDIN>; my $answer = <STDIN>;
@ -2089,15 +2309,11 @@ sub interactive_ask_server_storage_group
my $storage_group_uuid = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid}; my $storage_group_uuid = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid};
my $vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}; my $vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size};
my $vg_free = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size}; my $vg_free = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size};
my $dr_size = $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size_on_dr} : 0;
my $dr_free = $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{available_on_dr} : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:storage_group_name' => $storage_group_name, 's1:storage_group_name' => $storage_group_name,
's2:storage_group_uuid' => $storage_group_uuid, 's2:storage_group_uuid' => $storage_group_uuid,
's3:vg_size' => $vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size}).")", 's3:vg_size' => $vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size}).")",
's4:vg_free' => $vg_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_free}).")", 's4:vg_free' => $vg_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_free}).")",
's5:dr_size' => $dr_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $dr_size}).")",
's6:dr_free' => $dr_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $dr_free}).")",
}}); }});
if ($anvil->data->{switches}{'storage-group'}) if ($anvil->data->{switches}{'storage-group'})
@ -2113,8 +2329,6 @@ sub interactive_ask_server_storage_group
$show_list .= $anvil->Words->string({key => "job_0169", variables => { $show_list .= $anvil->Words->string({key => "job_0169", variables => {
vg_free => $anvil->Convert->bytes_to_human_readable({'bytes' => $vg_free}), vg_free => $anvil->Convert->bytes_to_human_readable({'bytes' => $vg_free}),
vg_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size}), vg_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size}),
dr_free => $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->Convert->bytes_to_human_readable({'bytes' => $dr_free}) : "--",
dr_size => $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->Convert->bytes_to_human_readable({'bytes' => $dr_size}) : "--",
}})."\n"; }})."\n";
} }
@ -2138,7 +2352,6 @@ sub interactive_ask_server_storage_group
} }
my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}; my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid};
my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}; my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid};
my $dr1_host_uuid = $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid} : "";
print $anvil->Words->string({key => "job_0171"})."\n"; print $anvil->Words->string({key => "job_0171"})."\n";
print $show_list."\n"; print $show_list."\n";
@ -2182,16 +2395,12 @@ sub interactive_ask_server_storage_size
my $storage_group_name = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{group_name}; my $storage_group_name = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{group_name};
my $vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}; my $vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size};
my $vg_free = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size}; my $vg_free = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size};
my $dr_size = $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size_on_dr} : 0;
my $dr_free = $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{available_on_dr} : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:anvil_uuid' => $anvil_uuid, 's1:anvil_uuid' => $anvil_uuid,
's2:storage_group_name' => $storage_group_name, 's2:storage_group_name' => $storage_group_name,
's3:storage_group_uuid' => $storage_group_uuid, 's3:storage_group_uuid' => $storage_group_uuid,
's4:vg_size' => $vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size}).")", 's4:vg_size' => $vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size}).")",
's5:vg_free' => $vg_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_free}).")", 's5:vg_free' => $vg_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_free}).")",
's6:dr_size' => $dr_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $dr_size}).")",
's7:dr_free' => $dr_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $dr_free}).")",
}}); }});
$anvil->Database->get_anvils(); $anvil->Database->get_anvils();
@ -2200,13 +2409,9 @@ sub interactive_ask_server_storage_size
$vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}; $vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size};
$vg_free = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size}; $vg_free = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size};
$dr_size = $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size_on_dr} : 0;
$dr_free = $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{available_on_dr} : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:vg_size' => $vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size}).")", 's1:vg_size' => $vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size}).")",
's2:vg_free' => $vg_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_free}).")", 's2:vg_free' => $vg_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_free}).")",
's3:dr_size' => $dr_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $dr_size}).")",
's4:dr_free' => $dr_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $dr_free}).")",
}}); }});
# I need a list of Storage groups, # I need a list of Storage groups,
@ -2249,7 +2454,6 @@ sub interactive_ask_server_storage_size
} }
my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}; my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid};
my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}; my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid};
my $dr1_host_uuid = $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid} : "";
print $anvil->Words->string({key => "job_0175", variables => { print $anvil->Words->string({key => "job_0175", variables => {
storage_group => $say_storage_group, storage_group => $say_storage_group,
@ -2259,10 +2463,12 @@ sub interactive_ask_server_storage_size
print $terminal->Tgoto('cm', 0, 7)."? "; print $terminal->Tgoto('cm', 0, 7)."? ";
my $answer = <STDIN>; my $answer = <STDIN>;
chomp $answer; chomp $answer;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }});
if ($answer eq "") if (($answer eq "") or ($answer eq "100%"))
{ {
$answer = $default_storage_size; $answer = $default_storage_size;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }});
} }
if ($answer) if ($answer)
{ {
@ -2273,6 +2479,28 @@ sub interactive_ask_server_storage_size
}); });
# Make sure they've asked for at least 10 MiB # Make sure they've asked for at least 10 MiB
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer_bytes => $answer_bytes }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer_bytes => $answer_bytes }});
# If the answer is within 1GiB set it to the available space.
if ($answer_bytes =~ /^\d+$/)
{
my $difference = $answer_bytes - $vg_free;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $difference }});
$difference =~ s/^-//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
difference => $difference." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $difference}).")",
}});
if ($difference < (2**30))
{
# Close enough.
$answer_bytes = $vg_free;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
answer_bytes => $answer_bytes." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $answer_bytes}).")",
}});
}
}
if (($answer_bytes eq "!!error!!") or if (($answer_bytes eq "!!error!!") or
(not $answer_bytes) or (not $answer_bytes) or
($answer_bytes < (10*(2**20))) or ($answer_bytes < (10*(2**20))) or
@ -2366,7 +2594,6 @@ sub interactive_ask_server_install_media
} }
my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}; my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid};
my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}; my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid};
my $dr1_host_uuid = $anvil->data->{anvil_resources}{$anvil_uuid}{has_dr} ? $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid} : "";
print $anvil->Words->string({key => "job_0178"})."\n"; print $anvil->Words->string({key => "job_0178"})."\n";
print $iso_list."\n"; print $iso_list."\n";
@ -2502,23 +2729,33 @@ sub interactive_ask_server_os
my $language = $anvil->Words->language; my $language = $anvil->Words->language;
my $os_list = ""; my $os_list = "";
my $default_os = $anvil->data->{switches}{os} ? $anvil->data->{switches}{os} : ""; my $default_os = $anvil->data->{switches}{os} ? $anvil->data->{switches}{os} : "";
my $longest_os = 0;
foreach my $os_code (split/,/, $anvil->data->{sys}{servers}{os_short_list}) foreach my $os_code (split/,/, $anvil->data->{sys}{servers}{os_short_list})
{ {
$os_code =~ s/ //g; $os_code =~ s/ //g;
my $os_key = "os_list_".$os_code;
my $os_name = $anvil->Words->string({key => $os_key}); next if not exists $anvil->data->{osinfo}{'os-list'}{$os_code};
if ($os_name =~ /#!not_found/) if (length($os_code) > $longest_os)
{ {
# Skip it. $longest_os = length($os_code);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { longest_os => $longest_os }});
} }
}
$anvil->Get->virsh_list_os({debug => 2});
foreach my $os_code (split/,/, $anvil->data->{sys}{servers}{os_short_list})
{
$os_code =~ s/ //g;
next if not exists $anvil->data->{osinfo}{'os-list'}{$os_code};
my $os_name = $anvil->data->{osinfo}{'os-list'}{$os_code}{name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:os_code' => $os_code, 's1:os_code' => $os_code,
's2:os_key' => $os_key,
's2:os_name' => $os_name, 's2:os_name' => $os_name,
}}); }});
# Still here? # Still here?
$os_list .= " - ".sprintf("%-10s", $os_code)." - ".$os_name."\n"; $os_list .= " - ".sprintf("%-${longest_os}s", $os_code)." - ".$os_name."\n";
} }
my $retry = 0; my $retry = 0;
@ -2567,11 +2804,14 @@ sub interactive_ask_server_os
if ($answer) if ($answer)
{ {
# Is this valid? # Is this valid?
my $os_key = "os_list_".$answer; if (exists $anvil->data->{osinfo}{'os-list'}{$answer})
$os_name = $anvil->Words->string({key => $os_key}); {
$os_name = $anvil->data->{osinfo}{'os-list'}{$answer}{name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { os_name => $os_name }});
}
} }
if ((not $answer) or ($os_name =~ /#!not_found/)) if ((not $answer) or ($os_name eq ""))
{ {
# invalid. # invalid.
$retry = 1; $retry = 1;
@ -2615,7 +2855,7 @@ sub interactive_ask_server_confirm
if (not $anvil->data->{new_server}{anvil_uuid}) if (not $anvil->data->{new_server}{anvil_uuid})
{ {
# Instantly fatal # Instantly fatal
print "Missing '--anvil <name_or_uuid>'\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "job_0437"});
$anvil->nice_exit({exit_code => 1}); $anvil->nice_exit({exit_code => 1});
} }
@ -2636,12 +2876,14 @@ sub interactive_ask_server_confirm
my $problem = 0; my $problem = 0;
if (not $anvil->data->{switches}{name}) if (not $anvil->data->{switches}{name})
{ {
print "Missing '--name <server_name>'\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0438"});
$problem = 1; $problem = 1;
} }
if (not $anvil->data->{switches}{os}) if (not $anvil->data->{switches}{os})
{ {
print "Missing '--os <os_variant>', valid options match 'virt-install --os-variant' (run: 'osinfo-query os' and reference the 'Short ID' column).\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0439"});
$problem = 1; $problem = 1;
} }
else else
@ -2655,18 +2897,28 @@ sub interactive_ask_server_confirm
}}); }});
if ($os_name =~ /#!not_found/) if ($os_name =~ /#!not_found/)
{ {
print "The OS: [".$anvil->data->{switches}{os}."] was not found. If you're sure the OS is valid, please run 'striker-parse-os-list --xml --new' and add the output to 'words.xml'.\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0440", variables => {
os => $anvil->data->{switches}{os},
}});
$problem = 1; $problem = 1;
} }
} }
if (not $anvil->data->{switches}{cpu}) if (not $anvil->data->{switches}{cpu})
{ {
print "Missing '--cpu <1 ~ ".$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}.">'\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0441", variables => {
threads => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads},
}});
$problem = 1; $problem = 1;
} }
elsif (($anvil->data->{switches}{cpu} =~ /\D/) or ($anvil->data->{switches}{cpu} > $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}) or ($anvil->data->{switches}{cpu} < 1)) elsif (($anvil->data->{switches}{cpu} =~ /\D/) or ($anvil->data->{switches}{cpu} > $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}) or ($anvil->data->{switches}{cpu} < 1))
{ {
print "The number of CPU cores: [".$anvil->data->{switches}{cpu}."] is invalid. Must be between 1 and ".$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}.".\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0442", variables => {
cores => $anvil->data->{switches}{cpu},
threads => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads},
}});
$problem = 1; $problem = 1;
} }
@ -2683,7 +2935,10 @@ sub interactive_ask_server_confirm
}}); }});
if (not $anvil->data->{switches}{ram}) if (not $anvil->data->{switches}{ram})
{ {
print "Missing '--ram <bytes or human readable, min is 64KiB, max is ".$say_max_ram.">'\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0443", variables => {
max_ram => $say_max_ram,
}});
$problem = 1; $problem = 1;
} }
elsif (($requested_ram eq "!!error!!") or elsif (($requested_ram eq "!!error!!") or
@ -2692,7 +2947,11 @@ sub interactive_ask_server_confirm
($requested_ram > $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available})) ($requested_ram > $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available}))
{ {
# Invalid # Invalid
print "The requested RAM: [".$anvil->data->{switches}{ram}."] is not valid. Must be between: [64KiB] and: [".$say_max_ram."]\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0444", variables => {
ram => $anvil->data->{switches}{ram},
max_ram => $say_max_ram,
}});
$problem = 1; $problem = 1;
} }
@ -2700,10 +2959,14 @@ sub interactive_ask_server_confirm
my $storage_group_uuid = ""; my $storage_group_uuid = "";
if (not $anvil->data->{switches}{'storage-group'}) if (not $anvil->data->{switches}{'storage-group'})
{ {
print "Missing '--storage-group <name or uuid>. Valid options are:'\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0445"});
foreach my $storage_group_name (sort {$a cmp $b} keys %{$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}}) foreach my $storage_group_name (sort {$a cmp $b} keys %{$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}})
{ {
print "- Name: [".$storage_group_name."], UUID: [".$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid}."]\n" if not $anvil->data->{switches}{options}; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0446", variables => {
name => $storage_group_name,
uuid => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid},
}});
} }
$problem = 1; $problem = 1;
} }
@ -2732,10 +2995,16 @@ sub interactive_ask_server_confirm
else else
{ {
# Invalid # Invalid
print "- The requested storage group: [".$storage_group."] does not appear to be valid. Valid options are;\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0447", variables => {
storage_group => $storage_group,
}});
foreach my $storage_group_name (sort {$a cmp $b} keys %{$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}}) foreach my $storage_group_name (sort {$a cmp $b} keys %{$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}})
{ {
print "- Name: [".$storage_group_name."], UUID: [".$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid}."]\n" if not $anvil->data->{switches}{options}; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0446", variables => {
name => $storage_group_name,
uuid => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid},
}});
} }
$problem = 1; $problem = 1;
} }
@ -2743,13 +3012,16 @@ sub interactive_ask_server_confirm
my $say_max_storage_group_size = $max_storage_group_size ? $anvil->Convert->bytes_to_human_readable({"bytes" => $max_storage_group_size}) : ""; my $say_max_storage_group_size = $max_storage_group_size ? $anvil->Convert->bytes_to_human_readable({"bytes" => $max_storage_group_size}) : "";
if (not $anvil->data->{switches}{'storage-size'}) if (not $anvil->data->{switches}{'storage-size'})
{ {
my $print = $anvil->data->{switches}{options} ? 0 : 1;
if ($max_storage_group_size) if ($max_storage_group_size)
{ {
print "Missing '--storage-size <bytes or human readable. max is ".$say_max_storage_group_size.">'\n" if not $anvil->data->{switches}{options}; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0448", variables => {
storage_group_size => $say_max_storage_group_size,
}});
} }
else else
{ {
print "Missing '--storage-size <bytes or human readable>. Max will depend on selected --storage-group.'\n" if not $anvil->data->{switches}{options}; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0449"});
} }
$problem = 1; $problem = 1;
} }
@ -2765,7 +3037,11 @@ sub interactive_ask_server_confirm
($requested_disk > $max_storage_group_size)) ($requested_disk > $max_storage_group_size))
{ {
# Invalid # Invalid
print "The requested disk size: [".$anvil->data->{switches}{'storage-size'}."] is not valid. Must be between: [10MiB] and: [".$say_max_storage_group_size."]\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0450", variables => {
storage_size => $anvil->data->{switches}{'storage-size'},
max_size => $say_max_storage_group_size,
}});
$problem = 1; $problem = 1;
} }
} }
@ -2778,7 +3054,18 @@ sub interactive_ask_server_confirm
} }
if (not $anvil->data->{switches}{'install-media'}) if (not $anvil->data->{switches}{'install-media'})
{ {
print "Missing '--install-media <file name or file_uuid>'\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0451"});
foreach my $file_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}})
{
my $file_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid};
my $file_type = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type};
next if $file_type ne "iso";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0452", variables => {
name => $file_name,
uuid => $file_uuid,
}});
}
$problem = 1; $problem = 1;
} }
else else
@ -2790,7 +3077,8 @@ sub interactive_ask_server_confirm
my $file_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid}; my $file_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid};
my $file_type = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type}; my $file_type = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type};
my $file_directory = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory}; my $file_directory = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory};
if ($file_name eq $anvil->data->{switches}{'install-media'}) if (($file_name eq $anvil->data->{switches}{'install-media'}) or
($file_uuid eq $anvil->data->{switches}{'install-media'}))
{ {
# Found it. # Found it.
$found = 1; $found = 1;
@ -2805,7 +3093,10 @@ sub interactive_ask_server_confirm
else else
{ {
# Not an ISO. # Not an ISO.
print "The install file: [".$anvil->data->{switches}{'install-media'}."] is not an ISO, so it can't be used to install.\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0453", variables => {
file => $anvil->data->{switches}{'install-media'},
}});
$problem = 1; $problem = 1;
} }
} }
@ -2813,8 +3104,24 @@ sub interactive_ask_server_confirm
} }
if (not $found) if (not $found)
{ {
print "The install file: [".$anvil->data->{switches}{'install-media'}."] was not found on this Anvil!.\n" if not $anvil->data->{switches}{options}; if (not $anvil->data->{switches}{options})
print "Is it in '/mnt/shared/files/' and are the daemons running?\n" if not $anvil->data->{switches}{options}; {
my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0454", variables => {
file => $anvil->data->{switches}{'install-media'},
}});
foreach my $file_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}})
{
my $file_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid};
my $file_type = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type};
next if $file_type ne "iso";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0452", variables => {
name => $file_name,
uuid => $file_uuid,
}});
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0455"});
}
$problem = 1; $problem = 1;
} }
} }
@ -2827,7 +3134,8 @@ sub interactive_ask_server_confirm
my $file_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid}; my $file_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid};
my $file_type = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type}; my $file_type = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type};
my $file_directory = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory}; my $file_directory = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory};
if ($file_name eq $anvil->data->{switches}{'driver-disc'}) if (($file_name eq $anvil->data->{switches}{'driver-disc'}) or
($file_uuid eq $anvil->data->{switches}{'driver-disc'}))
{ {
# Found it. # Found it.
$found = 1; $found = 1;
@ -2842,7 +3150,10 @@ sub interactive_ask_server_confirm
else else
{ {
# Not an ISO. # Not an ISO.
print "The driver file: [".$anvil->data->{switches}{'driver-disc'}."] is not an ISO, so it can't be used as an optical disc.\n" if not $anvil->data->{switches}{options}; my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0456", variables => {
file => $anvil->data->{switches}{'driver-disc'},
}});
$problem = 1; $problem = 1;
} }
} }
@ -2850,8 +3161,24 @@ sub interactive_ask_server_confirm
} }
if (not $found) if (not $found)
{ {
print "The driver file: [".$anvil->data->{switches}{'driver-disc'}."] was not found on this Anvil!.\n" if not $anvil->data->{switches}{options}; if (not $anvil->data->{switches}{options})
print "Is it in '/mnt/shared/files/' and are the daemons running?\n" if not $anvil->data->{switches}{options}; {
my $print = $anvil->data->{switches}{options} ? 0 : 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0457", variables => {
file => $anvil->data->{switches}{'driver-disc'},
}});
foreach my $file_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}})
{
my $file_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid};
my $file_type = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type};
next if $file_type ne "iso";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0452", variables => {
name => $file_name,
uuid => $file_uuid,
}});
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => $print, level => 0, key => "job_0455"});
}
$problem = 1; $problem = 1;
} }
} }
@ -2887,21 +3214,22 @@ sub interactive_ask_server_confirm
else else
{ {
# Show valid options to build a VM in a machine-parsable way. # Show valid options to build a VM in a machine-parsable way.
print "Available options print $anvil->Words->string({key => "job_0458", variables => {
--name - alphanumeric, 1~16 characters long. threads => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads},
--os - Valid options; run: 'osinfo-query os' and reference the 'Short ID' column. max_ram => $say_max_ram,
--cpu - 1 ~ ".$anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}." }})."\n";
--ram - bytes or human readable, min is 64KiB, max is ".$say_max_ram."
--storage-group - name or uuid. Valid options are:\n";
foreach my $storage_group_name (sort {$a cmp $b} keys %{$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}}) foreach my $storage_group_name (sort {$a cmp $b} keys %{$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}})
{ {
my $storage_group_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid}; my $storage_group_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid};
my $max_storage_group_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size}; my $max_storage_group_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size};
my $say_max_storage_group_size = $anvil->Convert->bytes_to_human_readable({"bytes" => $max_storage_group_size}); my $say_max_storage_group_size = $anvil->Convert->bytes_to_human_readable({"bytes" => $max_storage_group_size});
print " - Name: [".$storage_group_name."], UUID: [".$anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid}."], free space: [".$say_max_storage_group_size."]\n"; print $anvil->Words->string({key => "job_0459", variables => {
name => $storage_group_name,
uuid => $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid},
free_space => $say_max_storage_group_size,
}})."\n";
} }
print " --storage-size - Disk size, see above for space available print $anvil->Words->string({key => "job_0460"})."\n";
--install-media - file name or file UUID. Available discs are:\n";
foreach my $file_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}}) foreach my $file_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}})
{ {
my $file_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid}; my $file_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid};
@ -2909,12 +3237,25 @@ sub interactive_ask_server_confirm
my $file_size = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size}; my $file_size = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size};
next if $file_type ne "iso"; next if $file_type ne "iso";
my $say_size = $anvil->Convert->bytes_to_human_readable({"bytes" => $file_size}); my $say_size = $anvil->Convert->bytes_to_human_readable({"bytes" => $file_size});
print " - File name: [".$file_name."], file UUID: [".$file_uuid."], size: [".$say_size."]\n"; print $anvil->Words->string({key => "job_0461", variables => {
name => $file_name,
uuid => $file_uuid,
size => $say_max_storage_group_size,
}})."\n";
} }
print " --driver-disc - (optional) A driver disc to be added as a second optical drive. Valid options are above.\n"; print $anvil->Words->string({key => "job_0462"})."\n";
} }
$anvil->nice_exit({exit_code => 0}); $anvil->nice_exit({exit_code => 0});
} }
### TODO: Better sanity check this
# If we were passed network info, validate it.
if ($anvil->data->{switches}{network})
{
$problem = process_network($anvil, $anvil->data->{switches}{network});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
}
if ($problem) if ($problem)
{ {
$anvil->nice_exit({exit_code => 1}); $anvil->nice_exit({exit_code => 1});

@ -73,12 +73,10 @@ sub collect_anvil_data
$anvil->data->{anvil_data}{$anvil_name}{description} = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_description}; $anvil->data->{anvil_data}{$anvil_name}{description} = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_description};
$anvil->data->{anvil_data}{$anvil_name}{node1_host_uuid} = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node1_host_uuid}; $anvil->data->{anvil_data}{$anvil_name}{node1_host_uuid} = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node1_host_uuid};
$anvil->data->{anvil_data}{$anvil_name}{node2_host_uuid} = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node2_host_uuid}; $anvil->data->{anvil_data}{$anvil_name}{node2_host_uuid} = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node2_host_uuid};
$anvil->data->{anvil_data}{$anvil_name}{dr1_host_uuid} = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_dr1_host_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"s1:anvil_data::${anvil_name}::anvil_description" => $anvil->data->{anvil_data}{$anvil_name}{description}, "s1:anvil_data::${anvil_name}::anvil_description" => $anvil->data->{anvil_data}{$anvil_name}{description},
"s2:anvil_data::${anvil_name}::node1_host_uuid" => $anvil->data->{anvil_data}{$anvil_name}{node1_host_uuid}, "s2:anvil_data::${anvil_name}::node1_host_uuid" => $anvil->data->{anvil_data}{$anvil_name}{node1_host_uuid},
"s3:anvil_data::${anvil_name}::node2_host_uuid" => $anvil->data->{anvil_data}{$anvil_name}{node2_host_uuid}, "s3:anvil_data::${anvil_name}::node2_host_uuid" => $anvil->data->{anvil_data}{$anvil_name}{node2_host_uuid},
"s4:anvil_data::${anvil_name}::dr1_host_uuid" => $anvil->data->{anvil_data}{$anvil_name}{dr1_host_uuid},
}}); }});
if (length($anvil_name) > $anvil->data->{longest}{anvil_name}) if (length($anvil_name) > $anvil->data->{longest}{anvil_name})
@ -99,34 +97,22 @@ sub collect_anvil_data
my $node1_host_uuid = $anvil->data->{anvil_data}{$anvil_name}{node1_host_uuid}; my $node1_host_uuid = $anvil->data->{anvil_data}{$anvil_name}{node1_host_uuid};
my $node2_host_uuid = $anvil->data->{anvil_data}{$anvil_name}{node2_host_uuid}; my $node2_host_uuid = $anvil->data->{anvil_data}{$anvil_name}{node2_host_uuid};
my $dr1_host_uuid = $anvil->data->{anvil_data}{$anvil_name}{dr1_host_uuid};
if ($anvil->data->{switches}{detailed}) if ($anvil->data->{switches}{detailed})
{ {
$anvil->data->{anvil_data}{$anvil_name}{node1_host_name} = $anvil->data->{hosts}{host_uuid}{$node1_host_uuid}{host_name}; $anvil->data->{anvil_data}{$anvil_name}{node1_host_name} = $anvil->data->{hosts}{host_uuid}{$node1_host_uuid}{host_name};
$anvil->data->{anvil_data}{$anvil_name}{node2_host_name} = $anvil->data->{hosts}{host_uuid}{$node2_host_uuid}{host_name}; $anvil->data->{anvil_data}{$anvil_name}{node2_host_name} = $anvil->data->{hosts}{host_uuid}{$node2_host_uuid}{host_name};
$anvil->data->{anvil_data}{$anvil_name}{dr1_host_name} = "";
if ($dr1_host_uuid)
{
$anvil->data->{anvil_data}{$anvil_name}{dr1_host_name} = $anvil->data->{hosts}{host_uuid}{$dr1_host_uuid}{host_name};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"s1:anvil_data::${anvil_name}::node1_host_name" => $anvil->data->{anvil_data}{$anvil_name}{node1_host_name}, "s1:anvil_data::${anvil_name}::node1_host_name" => $anvil->data->{anvil_data}{$anvil_name}{node1_host_name},
"s2:anvil_data::${anvil_name}::node2_host_name" => $anvil->data->{anvil_data}{$anvil_name}{node2_host_name}, "s2:anvil_data::${anvil_name}::node2_host_name" => $anvil->data->{anvil_data}{$anvil_name}{node2_host_name},
"s3:anvil_data::${anvil_name}::dr1_host_name" => $anvil->data->{anvil_data}{$anvil_name}{dr1_host_name},
}}); }});
} }
else else
{ {
$anvil->data->{anvil_data}{$anvil_name}{node1_host_name} = $anvil->data->{hosts}{host_uuid}{$node1_host_uuid}{short_host_name}; $anvil->data->{anvil_data}{$anvil_name}{node1_host_name} = $anvil->data->{hosts}{host_uuid}{$node1_host_uuid}{short_host_name};
$anvil->data->{anvil_data}{$anvil_name}{node2_host_name} = $anvil->data->{hosts}{host_uuid}{$node2_host_uuid}{short_host_name}; $anvil->data->{anvil_data}{$anvil_name}{node2_host_name} = $anvil->data->{hosts}{host_uuid}{$node2_host_uuid}{short_host_name};
if ($dr1_host_uuid)
{
$anvil->data->{anvil_data}{$anvil_name}{dr1_host_name} = $anvil->data->{hosts}{host_uuid}{$dr1_host_uuid}{short_host_name};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"s1:anvil_data::${anvil_name}::node1_host_name" => $anvil->data->{anvil_data}{$anvil_name}{node1_host_name}, "s1:anvil_data::${anvil_name}::node1_host_name" => $anvil->data->{anvil_data}{$anvil_name}{node1_host_name},
"s2:anvil_data::${anvil_name}::node2_host_name" => $anvil->data->{anvil_data}{$anvil_name}{node2_host_name}, "s2:anvil_data::${anvil_name}::node2_host_name" => $anvil->data->{anvil_data}{$anvil_name}{node2_host_name},
"s3:anvil_data::${anvil_name}::dr1_host_name" => $anvil->data->{anvil_data}{$anvil_name}{dr1_host_name},
}}); }});
} }
@ -202,18 +188,12 @@ sub collect_anvil_data
my $vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}; my $vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size};
my $free_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size}; my $free_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size};
my $sg_used = $vg_size - $free_size; my $sg_used = $vg_size - $free_size;
my $vg_size_on_dr = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size_on_dr};
my $free_size_on_dr = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{available_on_dr};
my $sg_used_on_dr = $vg_size_on_dr - $free_size_on_dr;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:storage_group_name' => $storage_group_name, 's1:storage_group_name' => $storage_group_name,
's2:storage_group_uuid' => $storage_group_uuid, 's2:storage_group_uuid' => $storage_group_uuid,
's3:vg_size' => $anvil->Convert->add_commas({number => $vg_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size}).")", 's3:vg_size' => $anvil->Convert->add_commas({number => $vg_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size}).")",
's4:free_size' => $anvil->Convert->add_commas({number => $free_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $free_size}).")", 's4:free_size' => $anvil->Convert->add_commas({number => $free_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $free_size}).")",
's5:sg_used' => $anvil->Convert->add_commas({number => $sg_used})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $sg_used}).")", 's5:sg_used' => $anvil->Convert->add_commas({number => $sg_used})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $sg_used}).")",
's6:vg_size_on_dr' => $anvil->Convert->add_commas({number => $vg_size_on_dr})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size_on_dr}).")",
's7:free_size_on_dr' => $anvil->Convert->add_commas({number => $free_size_on_dr})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $free_size_on_dr}).")",
's8:sg_used_on_dr' => $anvil->Convert->add_commas({number => $sg_used_on_dr})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $sg_used_on_dr}).")",
}}); }});
$anvil->data->{anvil_data}{$anvil_name}{storage_group}{$storage_group_name}{say_used_size} = $anvil->Convert->bytes_to_human_readable({'bytes' => $sg_used}); $anvil->data->{anvil_data}{$anvil_name}{storage_group}{$storage_group_name}{say_used_size} = $anvil->Convert->bytes_to_human_readable({'bytes' => $sg_used});
@ -266,7 +246,7 @@ sub show_anvils
my $longest_ram_free = length($ram_free_header) > $anvil->data->{longest}{ram_free} ? length($ram_free_header) : $anvil->data->{longest}{ram_free}; my $longest_ram_free = length($ram_free_header) > $anvil->data->{longest}{ram_free} ? length($ram_free_header) : $anvil->data->{longest}{ram_free};
my $bridge_header = $anvil->Words->string({key => "header_0077"}); my $bridge_header = $anvil->Words->string({key => "header_0077"});
my $longest_bridge_string = length($bridge_header) > $anvil->data->{longest}{bridge_string} ? length($bridge_header) : $anvil->data->{longest}{bridge_string}; my $longest_bridge_string = length($bridge_header) > $anvil->data->{longest}{bridge_string} ? length($bridge_header) : $anvil->data->{longest}{bridge_string};
my $storage_group_header = $anvil->Words->string({key => "header_0078"}); my $storage_group_header = $anvil->Words->string({key => "header_0070"});
my $longest_storage_group = length($storage_group_header) > $anvil->data->{longest}{storage_group} ? length($storage_group_header) : $anvil->data->{longest}{storage_group}; my $longest_storage_group = length($storage_group_header) > $anvil->data->{longest}{storage_group} ? length($storage_group_header) : $anvil->data->{longest}{storage_group};
my $sg_used_header = $anvil->Words->string({key => "header_0079"}); my $sg_used_header = $anvil->Words->string({key => "header_0079"});
my $longest_sg_used = length($sg_used_header) > $anvil->data->{longest}{sg_used} ? length($sg_used_header) : $anvil->data->{longest}{sg_used}; my $longest_sg_used = length($sg_used_header) > $anvil->data->{longest}{sg_used} ? length($sg_used_header) : $anvil->data->{longest}{sg_used};
@ -287,37 +267,37 @@ sub show_anvils
# Anvil! # Anvil!
my $break_line = "+-".sprintf("%0${longest_anvil_name}d", 0); my $break_line = "+-".sprintf("%0${longest_anvil_name}d", 0);
my $header_line = "| ".sprintf("%-${longest_anvil_name}s", $anvil_header)." "; my $header_line = "| ".sprintf("%-${longest_anvil_name}s", $anvil_header)." ";
my $blank_lead = "| ".sprintf("%-${longest_anvil_name}s", $anvil_header)." "; my $blank_lead = "| ".sprintf("%-${longest_anvil_name}s", "")." ";
if ($anvil->data->{switches}{detailed}) if ($anvil->data->{switches}{detailed})
{ {
# Description # Description
$break_line .= "-+-".sprintf("%0${longest_description}d", 0); $break_line .= "-+-".sprintf("%0${longest_description}d", 0);
$header_line .= "| ".sprintf("%-${longest_description}s", $description_header)." "; $header_line .= "| ".sprintf("%-${longest_description}s", $description_header)." ";
$blank_lead .= " ".sprintf("%-${longest_description}s", $description_header)." "; $blank_lead .= "| ".sprintf("%-${longest_description}s", "")." ";
} }
# CPU String # CPU String
$break_line .= "-+-".sprintf("%0${longest_cpu_string}d", 0); $break_line .= "-+-".sprintf("%0${longest_cpu_string}d", 0);
$header_line .= "| ".sprintf("%-${longest_cpu_string}s", $cpu_header)." "; $header_line .= "| ".sprintf("%-${longest_cpu_string}s", $cpu_header)." ";
$blank_lead .= " ".sprintf("%-${longest_cpu_string}s", $cpu_header)." "; $blank_lead .= "| ".sprintf("%-${longest_cpu_string}s", "")." ";
if ($anvil->data->{switches}{detailed}) if ($anvil->data->{switches}{detailed})
{ {
# RAM used # RAM used
$break_line .= "-+-".sprintf("%0${longest_ram_used}d", 0); $break_line .= "-+-".sprintf("%0${longest_ram_used}d", 0);
$header_line .= "| ".sprintf("%-${longest_ram_used}s", $ram_used_header)." "; $header_line .= "| ".sprintf("%-${longest_ram_used}s", $ram_used_header)." ";
$blank_lead .= " ".sprintf("%-${longest_ram_used}s", $ram_used_header)." "; $blank_lead .= "| ".sprintf("%-${longest_ram_used}s", "")." ";
} }
# RAM Free # RAM Free
$break_line .= "-+-".sprintf("%0${longest_ram_free}d", 0); $break_line .= "-+-".sprintf("%0${longest_ram_free}d", 0);
$header_line .= "| ".sprintf("%-${longest_ram_free}s", $ram_free_header)." "; $header_line .= "| ".sprintf("%-${longest_ram_free}s", $ram_free_header)." ";
$blank_lead .= " ".sprintf("%-${longest_ram_free}s", $ram_free_header)." "; $blank_lead .= "| ".sprintf("%-${longest_ram_free}s", "")." ";
# Bridges # Bridges
$break_line .= "-+-".sprintf("%0${longest_bridge_string}d", 0); $break_line .= "-+-".sprintf("%0${longest_bridge_string}d", 0);
$header_line .= "| ".sprintf("%-${longest_bridge_string}s", $bridge_header)." "; $header_line .= "| ".sprintf("%-${longest_bridge_string}s", $bridge_header)." ";
$blank_lead .= " ".sprintf("%-${longest_bridge_string}s", $bridge_header)." "; $blank_lead .= "| ".sprintf("%-${longest_bridge_string}s", "");
# Storage Group # Storage Group
$break_line .= "-+-".sprintf("%0${longest_storage_group}d", 0); $break_line .= "-+-".sprintf("%0${longest_storage_group}d", 0);
@ -379,7 +359,8 @@ sub show_anvils
$storage_line .= " | ".sprintf("%-${longest_sg_used}s", $say_used_size); $storage_line .= " | ".sprintf("%-${longest_sg_used}s", $say_used_size);
} }
$storage_line .= " | ".sprintf("%-${longest_sg_free}s", $say_free_size)." |"; $storage_line .= " | ".sprintf("%-${longest_sg_free}s", $say_free_size)." |";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { storage_line => $storage_line }});
push @{$storage_groups}, $storage_line; push @{$storage_groups}, $storage_line;
} }
@ -741,27 +722,17 @@ sub collect_server_data
# have a matching node name. # have a matching node name.
my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}; my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid};
my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}; my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid};
my $dr1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid};
# Get names. # Get names.
my $node1_host_name = $anvil->data->{hosts}{host_uuid}{$node1_host_uuid}{host_name}; my $node1_host_name = $anvil->data->{hosts}{host_uuid}{$node1_host_uuid}{host_name};
my $node1_short_host_name = $anvil->data->{hosts}{host_uuid}{$node1_host_uuid}{short_host_name}; my $node1_short_host_name = $anvil->data->{hosts}{host_uuid}{$node1_host_uuid}{short_host_name};
my $node2_host_name = $anvil->data->{hosts}{host_uuid}{$node2_host_uuid}{host_name}; my $node2_host_name = $anvil->data->{hosts}{host_uuid}{$node2_host_uuid}{host_name};
my $node2_short_host_name = $anvil->data->{hosts}{host_uuid}{$node2_host_uuid}{short_host_name}; my $node2_short_host_name = $anvil->data->{hosts}{host_uuid}{$node2_host_uuid}{short_host_name};
my $dr1_host_name = "";
my $dr1_short_host_name = "";
if (($dr1_host_uuid) && (exists $anvil->data->{hosts}{host_uuid}{$dr1_host_uuid}))
{
$dr1_host_name = $anvil->data->{hosts}{host_uuid}{$dr1_host_uuid}{host_name};
$dr1_short_host_name = $anvil->data->{hosts}{host_uuid}{$dr1_host_uuid}{short_host_name};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:node1_host_name' => $node1_host_name, 's1:node1_host_name' => $node1_host_name,
's2:node1_short_host_name' => $node1_short_host_name, 's2:node1_short_host_name' => $node1_short_host_name,
's3:node2_host_name' => $node2_host_name, 's3:node2_host_name' => $node2_host_name,
's4:node2_short_host_name' => $node2_short_host_name, 's4:node2_short_host_name' => $node2_short_host_name,
's5:dr1_host_name' => $dr1_host_name,
's6:dr1_short_host_name' => $dr1_short_host_name,
}}); }});
# Storage info. # Storage info.
@ -806,9 +777,7 @@ sub collect_server_data
if (($drbd_node eq $node1_host_name) or if (($drbd_node eq $node1_host_name) or
($drbd_node eq $node1_short_host_name) or ($drbd_node eq $node1_short_host_name) or
($drbd_node eq $node2_host_name) or ($drbd_node eq $node2_host_name) or
($drbd_node eq $node2_short_host_name) or ($drbd_node eq $node2_short_host_name))
(($dr1_host_name) && ($drbd_node eq $dr1_host_name)) or
(($dr1_short_host_name) && ($drbd_node eq $dr1_short_host_name)))
{ {
$anvil->data->{server_data}{$server_name}{server_uuid}{$server_uuid}{disk}{$resource}{$volume}{node}{$drbd_node}{drbd_path} = $anvil->data->{drbd}{drbd_node}{$drbd_node}{config}{resource}{$resource}{volume}{$volume}{drbd_path}; $anvil->data->{server_data}{$server_name}{server_uuid}{$server_uuid}{disk}{$resource}{$volume}{node}{$drbd_node}{drbd_path} = $anvil->data->{drbd}{drbd_node}{$drbd_node}{config}{resource}{$resource}{volume}{$volume}{drbd_path};
$anvil->data->{server_data}{$server_name}{server_uuid}{$server_uuid}{disk}{$resource}{$volume}{node}{$drbd_node}{drbd_path_by_res} = $anvil->data->{drbd}{drbd_node}{$drbd_node}{config}{resource}{$resource}{volume}{$volume}{drbd_path_by_res}; $anvil->data->{server_data}{$server_name}{server_uuid}{$server_uuid}{disk}{$resource}{$volume}{node}{$drbd_node}{drbd_path_by_res} = $anvil->data->{drbd}{drbd_node}{$drbd_node}{config}{resource}{$resource}{volume}{$volume}{drbd_path_by_res};
@ -835,11 +804,6 @@ sub collect_server_data
$node_host_uuid = $node2_host_uuid; $node_host_uuid = $node2_host_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { node_host_uuid => $node_host_uuid }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { node_host_uuid => $node_host_uuid }});
} }
elsif (($drbd_node eq $dr1_host_name) or ($drbd_node eq $dr1_short_host_name))
{
$node_host_uuid = $dr1_host_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { node_host_uuid => $node_host_uuid }});
}
# How big is this LV? # How big is this LV?
my $backing_lv = $anvil->data->{server_data}{$server_name}{server_uuid}{$server_uuid}{disk}{$resource}{$volume}{node}{$drbd_node}{backing_lv}; my $backing_lv = $anvil->data->{server_data}{$server_name}{server_uuid}{$server_uuid}{disk}{$resource}{$volume}{node}{$drbd_node}{backing_lv};

@ -0,0 +1,411 @@
#!/usr/bin/perl
#
# This does checks for changes that are needed because of version changes. Over time, checks here can be
# removed.
use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
use Text::Diff;
$| = 1;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
my $anvil = Anvil::Tools->new();
# Get a list of all interfaces with IP addresses.
$anvil->Get->switches({list => [
]});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}});
$anvil->Database->connect({sensitive => 1});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132" });
if (not $anvil->data->{sys}{database}{connections})
{
# No databases, exit.
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0003" });
$anvil->nice_exit({ exit_code => 1 });
}
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 eq "striker")
{
striker_checks($anvil);
}
elsif ($host_type eq "node")
{
node_checks($anvil);
}
elsif ($host_type eq "dr")
{
dr_checks($anvil);
}
$anvil->nice_exit({exit_code => 0});
#############################################################################################################
# Functions #
#############################################################################################################
# Check for things that need to happen on Striker dashboards.
sub striker_checks
{
my ($anvil) = @_;
# This converts the old/broken 'notifications' tables with the more appropriately named 'alert-override'
update_notifications($anvil);
### NOTE: Disabled until review complete
# This checks to make sure that the 'audits' table exists (added late into M3.0 pre-release)
#update_audits($anvil);
### NOTE: Disabled until review complete
# This checks to make sure that the new dr_links table exists, and that existing anvil_dr1_host_uuid
# entries are copied.
update_dr_links($anvil);
### TODO: Remove these later. This is here to clean up how we used to handle db_in_use and lock_request flags.
if (1)
{
# Broadly clear all states that are '0' now.
my $queries = [];
push @{$queries}, "DELETE FROM states WHERE state_name LIKE 'db_in_use::%' AND state_note != '1';";
push @{$queries}, "DELETE FROM history.variables WHERE variable_name = 'lock_request';";
push @{$queries}, "DELETE FROM variables WHERE variable_name = 'lock_request';";
foreach my $query (@{$queries})
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
}
$anvil->Database->write({debug => 2, query => $queries, source => $THIS_FILE, line => __LINE__});
}
return(0);
}
# Check for things that need to happen on Anvil! Subnodes.
sub node_checks
{
my ($anvil) = @_;
# RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16
handle_bz1961562($anvil);
# Make sure DRBD compiled after a kernel upgrade.
$anvil->DRBD->_initialize_kmod({debug => 2});
# Make sure logind is update to handle fencing properly
# see - https://access.redhat.com/solutions/1578823
$anvil->Cluster->configure_logind({debug => 2});
return(0);
}
# Check for things that need to happen on DR hosts.
sub dr_checks
{
my ($anvil) = @_;
# RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16
handle_bz1961562($anvil);
# Make sure DRBD compiled after a kernel upgrade.
$anvil->DRBD->_initialize_kmod({debug => 2});
return(0);
}
#
sub update_dr_links
{
my ($anvil) = @_;
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}})
{
my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'anvil' AND table_name = 'dr_links';";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
my $count = $anvil->Database->query({query => $query, uuid => $uuid, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
if (not $count)
{
# Add the table.
my $query = q|
CREATE TABLE dr_links (
dr_link_uuid uuid not null primary key,
dr_link_host_uuid uuid not null,
dr_link_anvil_uuid uuid not null,
dr_link_note text, -- Set to 'DELETE' when no longer used.
modified_date timestamp with time zone not null,
FOREIGN KEY(dr_link_host_uuid) REFERENCES hosts(host_uuid),
FOREIGN KEY(dr_link_anvil_uuid) REFERENCES anvils(anvil_uuid)
);
ALTER TABLE dr_links OWNER TO admin;
CREATE TABLE history.dr_links (
history_id bigserial,
dr_link_uuid uuid,
dr_link_host_uuid uuid,
dr_link_anvil_uuid uuid,
dr_link_note text,
modified_date timestamp with time zone not null
);
ALTER TABLE history.dr_links OWNER TO admin;
CREATE FUNCTION history_dr_links() RETURNS trigger
AS $$
DECLARE
history_dr_links RECORD;
BEGIN
SELECT INTO history_dr_links * FROM dr_links WHERE dr_link_uuid = new.dr_link_uuid;
INSERT INTO history.dr_links
(dr_link_uuid,
dr_link_host_uuid,
dr_link_anvil_uuid,
dr_link_note,
modified_date)
VALUES
(history_dr_links.dr_link_uuid,
history_dr_links.dr_link_host_uuid,
history_dr_links.dr_link_anvil_uuid,
history_dr_links.dr_link_note,
history_dr_links.modified_date);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
ALTER FUNCTION history_dr_links() OWNER TO admin;
CREATE TRIGGER trigger_dr_links
AFTER INSERT OR UPDATE ON dr_links
FOR EACH ROW EXECUTE PROCEDURE history_dr_links();
|;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
$anvil->Database->write({debug => 2, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
}
}
# Now make sure that existing DR entries are copied here.
$anvil->Database->get_hosts({deubg => 2});
$anvil->Database->get_dr_links({debug => 2});
foreach my $anvil_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_name}})
{
my $anvil_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid};
my $anvil_dr1_host_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_dr1_host_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"s1:anvil_name" => $anvil_name,
"s2:anvil_uuid" => $anvil_uuid,
"s3:anvil_dr1_host_uuid" => $anvil_dr1_host_uuid,
}});
if ($anvil_dr1_host_uuid)
{
my $dr1_host_name = $anvil->data->{hosts}{host_uuid}{$anvil_dr1_host_uuid}{short_host_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { dr1_host_name => $dr1_host_name }});
if ((not exists $anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}) or
(not exists $anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}{dr_link_host_uuid}{$anvil_dr1_host_uuid}) or
(not exists $anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}{dr_link_host_uuid}{$anvil_dr1_host_uuid}{dr_link_uuid}))
{
# Add it.
my $dr_link_uuid = $anvil->Database->insert_or_update_dr_links({
debug => 2,
dr_link_anvil_uuid => $anvil_uuid,
dr_link_host_uuid => $anvil_dr1_host_uuid,
dr_link_note => "auto_generated",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { dr1_host_name => $dr1_host_name }});
}
}
}
return(0);
}
# This checks to make sure that the 'audits' table exists (added late into M3.0 pre-release)
sub update_audits
{
my ($anvil) = @_;
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}})
{
my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'anvil' AND table_name = 'audits';";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
my $count = $anvil->Database->query({query => $query, uuid => $uuid, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
if (not $count)
{
# Add the table.
my $query = q|
CREATE TABLE audits (
audit_uuid uuid primary key,
audit_user_uuid uuid not null, -- This is the users -> user_uuid the audit is tracking
audit_details text not null, -- This is the information explaining the action being audited.
modified_date timestamp with time zone not null,
FOREIGN KEY(audit_user_uuid) REFERENCES users(user_uuid)
);
ALTER TABLE audits OWNER TO admin;
CREATE TABLE history.audits (
history_id bigserial,
audit_uuid uuid,
audit_user_uuid uuid,
audit_details text,
modified_date timestamp with time zone not null
);
ALTER TABLE history.audits OWNER TO admin;
CREATE FUNCTION history_audits() RETURNS trigger
AS $$
DECLARE
history_audits RECORD;
BEGIN
SELECT INTO history_audits * FROM audits WHERE audit_uuid = new.audit_uuid;
INSERT INTO history.audits
(audit_uuid,
audit_user_uuid,
audit_details,
modified_date)
VALUES
(history_audit.audit_uuid,
history_audit.audit_user_uuid,
history_audit.audit_details,
history_audit.modified_date);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
ALTER FUNCTION history_audits() OWNER TO admin;
CREATE TRIGGER trigger_audits
AFTER INSERT OR UPDATE ON audits
FOR EACH ROW EXECUTE PROCEDURE history_audits();
|;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
$anvil->Database->write({debug => 2, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
}
}
return(0);
}
# This converts the old/broken 'notifications' tables with the more appropriately named 'alert-override'
sub update_notifications
{
my ($anvil) = @_;
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}})
{
my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'anvil' AND table_name = 'notifications';";
$anvil->Log->variables({source => $THIS_FILE, uuid => $uuid, line => __LINE__, level => 2, list => { query => $query }});
my $count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
if ($count)
{
my $queries = [];
push @{$queries}, "DROP FUNCTION history_notifications() CASCADE;";
push @{$queries}, "DROP TABLE history.notifications;";
push @{$queries}, "DROP TABLE public.notifications;";
push @{$queries}, q|CREATE TABLE alert_overrides (
alert_override_uuid uuid not null primary key,
alert_override_recipient_uuid uuid not null, -- The recipient we're linking.
alert_override_host_uuid uuid not null, -- This host_uuid of the referenced machine
alert_override_alert_level integer not null, -- This is the alert level (at or above) that this user wants alerts from. If set to '-1', the record is deleted.
modified_date timestamp with time zone not null,
FOREIGN KEY(alert_override_host_uuid) REFERENCES hosts(host_uuid),
FOREIGN KEY(alert_override_recipient_uuid) REFERENCES recipients(recipient_uuid)
);
ALTER TABLE alert_overrides OWNER TO admin;
CREATE TABLE history.alert_overrides (
history_id bigserial,
alert_override_uuid uuid,
alert_override_recipient_uuid uuid,
alert_override_host_uuid uuid,
alert_override_alert_level integer,
modified_date timestamp with time zone not null
);
ALTER TABLE history.alert_overrides OWNER TO admin;
CREATE FUNCTION history_alert_overrides() RETURNS trigger
AS $$
DECLARE
history_alert_overrides RECORD;
BEGIN
SELECT INTO history_alert_overrides * FROM alert_overrides WHERE alert_override_uuid = new.alert_override_uuid;
INSERT INTO history.alert_overrides
(alert_override_uuid,
alert_override_recipient_uuid,
alert_override_host_uuid,
alert_override_alert_level,
modified_date)
VALUES
(history_alert_overrides.alert_override_uuid,
history_alert_overrides.alert_override_recipient_uuid,
history_alert_overrides.alert_override_host_uuid,
history_alert_overrides.alert_override_alert_level,
history_alert_overrides.modified_date);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
ALTER FUNCTION history_alert_overrides() OWNER TO admin;
CREATE TRIGGER trigger_alert_overrides
AFTER INSERT OR UPDATE ON alert_overrides
FOR EACH ROW EXECUTE PROCEDURE history_alert_overrides();
|;
foreach my $query (@{$queries})
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
}
$anvil->Database->write({debug => 2, uuid => $uuid, query => $queries, source => $THIS_FILE, line => __LINE__});
}
}
return(0);
}
sub handle_bz1961562
{
my ($anvil) = @_;
### TODO: Test that this is fixed. The bug is now ERRATA
# RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16
# We're a node or DR host. We need to touch this file.
my $work_around_file = "/etc/qemu/firmware/50-edk2-ovmf-cc.json";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { work_around_file => $work_around_file }});
if (not -e $work_around_file)
{
$anvil->Storage->write_file({
debug => 2,
file => $work_around_file,
body => "",
overwrite => 0,
backup => 0,
mode => "0644",
user => "root",
group => "root",
});
}
return(0);
}

@ -75,17 +75,20 @@ my $conf = {
path => { path => {
exe => { exe => {
cibadmin => "/usr/sbin/cibadmin", cibadmin => "/usr/sbin/cibadmin",
crm_attribute => "/usr/sbin/crm_attribute",
crm_error => "/usr/sbin/crm_error", crm_error => "/usr/sbin/crm_error",
drbdadm => "/usr/sbin/drbdadm", drbdadm => "/usr/sbin/drbdadm",
echo => "/usr/bin/echo", echo => "/usr/bin/echo",
getent => "/usr/bin/getent", getent => "/usr/bin/getent",
logger => "/usr/bin/logger", logger => "/usr/bin/logger",
pcs => "/usr/sbin/pcs",
stonith_admin => "/usr/sbin/stonith_admin", stonith_admin => "/usr/sbin/stonith_admin",
}, },
}, },
# The script will set this. # The script will set this.
cluster => { cluster => {
target_node => "", target_node => "",
fence_method => "stonith", # Will change to 'constraint' if we're using a DRBD resource is passed.
}, },
# These are the environment variables set by DRBD. See 'man drbd.conf' # These are the environment variables set by DRBD. See 'man drbd.conf'
# -> 'handlers'. # -> 'handlers'.
@ -112,7 +115,7 @@ my $conf = {
find_executables($conf); find_executables($conf);
# Something for the logs # Something for the logs
to_log($conf, {message => "DRBD pacemaker's stonith handler invoked.", 'line' => __LINE__}); to_log($conf, {message => "The Anvil! DRBD fence handler invoked.", 'line' => __LINE__});
# These are the full host names of the nodes given their IDs. # These are the full host names of the nodes given their IDs.
foreach my $i (0..31) foreach my $i (0..31)
@ -122,21 +125,22 @@ foreach my $i (0..31)
{ {
$conf->{environment}{$key} = $ENV{$key}; $conf->{environment}{$key} = $ENV{$key};
my $level = $conf->{environment}{$key} eq "" ? 3 : 2; my $level = $conf->{environment}{$key} eq "" ? 3 : 2;
to_log($conf, {message => "DRBD Environment variable: [$key] -> [".$conf->{environment}{$key}."]", 'line' => __LINE__, level => $level}); to_log($conf, {message => "DRBD Environment variable: [".$key."] -> [".$conf->{environment}{$key}."]", 'line' => __LINE__, level => $level});
} }
} }
# Record the environment variables # Record the environment variables
foreach my $key (sort {$a cmp $b} keys %{$conf->{environment}}) foreach my $key (sort {$a cmp $b} keys %{$conf->{environment}})
{ {
# $conf->{environment}{DRBD_RESOURCE} -> [srv51-Workstation3]
my $level = $conf->{environment}{$key} eq "" ? 3 : 2; my $level = $conf->{environment}{$key} eq "" ? 3 : 2;
to_log($conf, {message => "DRBD Environment variable: [$key] -> [".$conf->{environment}{$key}."]", 'line' => __LINE__, level => $level}); to_log($conf, {message => "DRBD Environment variable: [".$key."] -> [".$conf->{environment}{$key}."]", 'line' => __LINE__, level => $level});
} }
foreach my $key (sort {$a cmp $b} keys %ENV) foreach my $key (sort {$a cmp $b} keys %ENV)
{ {
next if exists $conf->{environment}{$key}; next if exists $conf->{environment}{$key};
my $level = $ENV{$key} eq "" ? 3 : 2; my $level = $ENV{$key} eq "" ? 3 : 2;
to_log($conf, {message => "System Environment variable: [$key] -> [".$ENV{$key}."]", 'line' => __LINE__, level => 3}); to_log($conf, {message => "System Environment variable: [".$key."] -> [".$ENV{$key}."]", 'line' => __LINE__, level => 3});
} }
# Make sure we at least have the target's IP. # Make sure we at least have the target's IP.
@ -152,10 +156,21 @@ identify_peer($conf);
# If we're still alive, we now need to check the DRBD resource disk state locally. # If we're still alive, we now need to check the DRBD resource disk state locally.
get_drbd_status($conf); get_drbd_status($conf);
to_log($conf, {message => "Ready to fence: [".$conf->{cluster}{target_node}."]", 'line' => __LINE__, level => 1}); # Is there a specific resource?
if ($conf->{environment}{DRBD_RESOURCE})
{
# Prevent the resource from running on the peer.
to_log($conf, {message => "We're being asked to fence the specific resource: [".$conf->{environment}{DRBD_RESOURCE}."]. Switching to location constraint fencing to prevent the resource from running on: [".$conf->{cluster}{target_node}."].", 'line' => __LINE__});
$conf->{cluster}{fence_method} = "constraint";
to_log($conf, {message => "cluster::fence_method: [".$conf->{cluster}{fence_method}."].", 'line' => __LINE__, level => 2});
}
else
{
to_log($conf, {message => "We were not given a specific resource to fence. Requesting pacemaker to stonith. Node: [".$conf->{cluster}{target_node}."] should power power off..", 'line' => __LINE__});
}
# Do the deed # No, do the deed
kill_target($conf); perform_fence($conf);
# If we hit here, something very wrong happened. # If we hit here, something very wrong happened.
exit(1); exit(1);
@ -165,6 +180,159 @@ exit(1);
# Functions # # Functions #
############################################################################################################# #############################################################################################################
# This creates a location constraint that prevents the resource / server from running on the peer node.
sub create_constraint
{
my ($conf) = @_;
my $target_server = $conf->{environment}{DRBD_RESOURCE};
my $target_node = $conf->{cluster}{target_node};
to_log($conf, {message => "Will now create a location constraint against: [".$target_server."] preventing it from running on: [".$target_node."].", 'line' => __LINE__, level => 1});
# Make sure there's a rule to apply the node attribute against.
my $rule_found = 0;
my $rule_name = "drbd-fenced_".$target_server;
my $shell_call = $conf->{path}{exe}{pcs}." constraint location config show ".$target_server;
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>)
{
# This should not generate output.
chomp;
my $line = $_;
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2});
if ($line =~ /Expression: $rule_name /)
{
$rule_found = 1;
to_log($conf, {message => "rule_found: [".$rule_found."]", 'line' => __LINE__, level => 2});
last;
}
}
close $file_handle;
if (not $rule_found)
{
# We can't fence.
to_log($conf, {message => "The fence rule: [".$rule_name."] was now found in the cluster, unable to fence by constraint.", 'line' => __LINE__, level => 0, priority => "err"});
exit(1);
}
# Set the node attribute
my $rule_set = 0;
$shell_call = $conf->{path}{exe}{crm_attribute}." --type nodes --node ".$target_node." --name ".$rule_name." --update 1";
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open ($file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>)
{
# This should not generate output.
chomp;
my $line = $_;
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2});
}
close $file_handle;
# Check that the rule was set.
$rule_set = 0;
my $rule_output = "";
$shell_call = $conf->{path}{exe}{crm_attribute}." --type nodes --node ".$target_node." --name ".$rule_name." --query";
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open ($file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>)
{
# This should not generate output.
chomp;
my $line = $_;
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2});
if (($line =~ /name=$rule_name/) && ($line =~ /value=1/))
{
$rule_set = 1;
to_log($conf, {message => "rule_set: [".$rule_set."]", 'line' => __LINE__, level => 2});
last;
}
else
{
$rule_output .= $line."\n";
}
}
close $file_handle;
if (not $rule_set)
{
# We can't fence.
$rule_output =~ s/\n$//gs;
to_log($conf, {message => "The node attribute triggering the fence rule: [".$rule_name."] against the node: [".$target_node."] appears to have not been set. Expected a string with 'name=".$rule_name." value=1' but got: [".$rule_output."].", 'line' => __LINE__, level => 0, priority => "err"});
exit(1);
}
# Place the constraint. and wait up to a minute for the target's DRBD resource to not be primary (in
# case the resource is running on the peer and needs to migrate here, which should not happen but
# best to check and be safe).
my $stop_waiting = time + 60;
my $peer_rolee = "";
my $waiting = 1;
while ($waiting)
{
# Check the peer's disk state
my $shell_call = $conf->{path}{exe}{drbdadm}." status ".$target_server;
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>)
{
# This should not generate output.
chomp;
my $line = $_;
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 3});
if ($line =~ /$target_node role:(.*)$/)
{
$peer_rolee = $1;
to_log($conf, {message => "peer_rolee: [".$peer_rolee."]", 'line' => __LINE__, level => 2});
last;
}
}
close $file_handle;
if (lc($peer_rolee) ne "primary")
{
# We're good, fence is complete.
to_log($conf, {message => "Resource: [".$target_server."] has been fenced via location constraint successfully!", 'line' => __LINE__, level => 1});
exit(7);
}
if (time > $stop_waiting)
{
# Done waiting, failed.
to_log($conf, {message => "The resource: [".$target_server."] on the fence target: [".$target_node."] is still in the 'Primary' role after a minute. Is the server running on the target? Is something else holding it open? Giving up on the location constraint fence attempt.", 'line' => __LINE__, level => 0, priority => "err"});
exit(1);
}
# Check again in a couple seconds.
sleep 2;
}
return(0);
}
sub perform_fence
{
my ($conf) = @_;
if ($conf->{cluster}{fence_method} eq "constraint")
{
create_constraint($conf);
}
else
{
kill_target($conf);
}
return(0);
}
# This reads the status of all resources. If we're not all UpToDate, check if the peer is. If the peer is, # This reads the status of all resources. If we're not all UpToDate, check if the peer is. If the peer is,
# abort. If not, proceed (someone is gouig to have a bad day, but maybe some servers will live) # abort. If not, proceed (someone is gouig to have a bad day, but maybe some servers will live)
sub get_drbd_status sub get_drbd_status
@ -176,32 +344,32 @@ sub get_drbd_status
my $local_all_uptodate = 1; my $local_all_uptodate = 1;
my $peer_all_uptodate = 1; my $peer_all_uptodate = 1;
my $shell_call = $conf->{path}{exe}{drbdadm}." status all"; my $shell_call = $conf->{path}{exe}{drbdadm}." status all";
to_log($conf, {message => "Calling: [$shell_call]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>) while(<$file_handle>)
{ {
# This should not generate output. # This should not generate output.
chomp; chomp;
my $line = $_; my $line = $_;
to_log($conf, {message => "Output: [$line]", 'line' => __LINE__, level => 3}); to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 3});
if (not $line) if (not $line)
{ {
$resource = ""; $resource = "";
$peer = ""; $peer = "";
to_log($conf, {message => "resource: [$resource], peer: [$peer]", 'line' => __LINE__, level => 3}); to_log($conf, {message => "resource: [".$resource."], peer: [".$peer."]", 'line' => __LINE__, level => 3});
next; next;
} }
if ($line =~ /^(\S+)\s+role/) if ($line =~ /^(\S+)\s+role/)
{ {
$resource = $1; $resource = $1;
to_log($conf, {message => "resource: [$resource]", 'line' => __LINE__, level => 3}); to_log($conf, {message => "resource: [".$resource."]", 'line' => __LINE__, level => 3});
next; next;
} }
if ($line =~ /^\s+(.*?) role:/) if ($line =~ /^\s+(.*?) role:/)
{ {
$peer = $1; $peer = $1;
to_log($conf, {message => "peer: [$peer]", 'line' => __LINE__, level => 3}); to_log($conf, {message => "peer: [".$peer."]", 'line' => __LINE__, level => 3});
next; next;
} }
if ($resource) if ($resource)
@ -210,11 +378,11 @@ sub get_drbd_status
{ {
my $local_dstate = $1; my $local_dstate = $1;
$local_dstate =~ s/\s.*$//; $local_dstate =~ s/\s.*$//;
to_log($conf, {message => "local_dstate: [$local_dstate]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "local_dstate: [".$local_dstate."]", 'line' => __LINE__, level => 2});
if (lc($local_dstate) ne "uptodate") if (lc($local_dstate) ne "uptodate")
{ {
$local_all_uptodate = 0; $local_all_uptodate = 0;
to_log($conf, {message => "local_all_uptodate: [$local_all_uptodate]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "local_all_uptodate: [".$local_all_uptodate."]", 'line' => __LINE__, level => 2});
} }
next; next;
} }
@ -222,11 +390,11 @@ sub get_drbd_status
{ {
my $peer_dstate = $1; my $peer_dstate = $1;
$peer_dstate =~ s/\s.*$//; $peer_dstate =~ s/\s.*$//;
to_log($conf, {message => "peer: [$peer], peer_dstate: [$peer_dstate]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "peer: [".$peer."], peer_dstate: [".$peer_dstate."]", 'line' => __LINE__, level => 2});
if (lc($peer_dstate) ne "uptodate") if (lc($peer_dstate) ne "uptodate")
{ {
$peer_all_uptodate = 0; $peer_all_uptodate = 0;
to_log($conf, {message => "peer_all_uptodate: [$peer_all_uptodate]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "peer_all_uptodate: [".$peer_all_uptodate."]", 'line' => __LINE__, level => 2});
} }
next; next;
} }
@ -234,11 +402,9 @@ sub get_drbd_status
} }
close $file_handle; close $file_handle;
my $return_code = $?;
to_log($conf, {message => "Return code: [$return_code]", 'line' => __LINE__, level => 2});
# If we're not all UpToDate, but the peer is, abort # If we're not all UpToDate, but the peer is, abort
to_log($conf, {message => "local_all_uptodate: [$local_all_uptodate], peer_all_uptodate: [$peer_all_uptodate]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "local_all_uptodate: [".$local_all_uptodate."], peer_all_uptodate: [".$peer_all_uptodate."]", 'line' => __LINE__, level => 2});
if ((not $local_all_uptodate) && ($peer_all_uptodate)) if ((not $local_all_uptodate) && ($peer_all_uptodate))
{ {
# We're not good # We're not good
@ -260,30 +426,28 @@ sub identify_peer
# First, can we translate the IP to a host name? # First, can we translate the IP to a host name?
my $shell_call = $conf->{path}{exe}{getent}." hosts ".$target_ip; my $shell_call = $conf->{path}{exe}{getent}." hosts ".$target_ip;
to_log($conf, {message => "Calling: [$shell_call]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>) while(<$file_handle>)
{ {
# This should not generate output. # This should not generate output.
chomp; chomp;
my $line = $_; my $line = $_;
to_log($conf, {message => "Output: [$line]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2});
if ($line =~ /^$target_ip\s+(.*)$/) if ($line =~ /^$target_ip\s+(.*)$/)
{ {
# This could be multiple names. # This could be multiple names.
$target_host = $1; $target_host = $1;
to_log($conf, {message => "target_host: [$target_host]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "target_host: [".$target_host."]", 'line' => __LINE__, level => 2});
#to_log($conf, {message => ">> target_host: [$target_host]", 'line' => __LINE__, level => 2}); #to_log($conf, {message => ">> target_host: [".$target_host."]", 'line' => __LINE__, level => 2});
# Strip off any suffix, we only want the short name. # Strip off any suffix, we only want the short name.
#$target_host =~ s/\..*//; #$target_host =~ s/\..*//;
#to_log($conf, {message => "<< target_host: [$target_host]", 'line' => __LINE__, level => 2}); #to_log($conf, {message => "<< target_host: [".$target_host."]", 'line' => __LINE__, level => 2});
#last; #last;
} }
} }
close $file_handle; close $file_handle;
my $return_code = $?;
to_log($conf, {message => "Return code: [$return_code]", 'line' => __LINE__, level => 2});
# If I got the host name, try to match it to a pacemaker node name. # If I got the host name, try to match it to a pacemaker node name.
if ($target_host) if ($target_host)
@ -296,18 +460,18 @@ sub identify_peer
my $host_name = $ENV{HOSTNAME}; my $host_name = $ENV{HOSTNAME};
my $short_host_name = $ENV{HOSTNAME}; my $short_host_name = $ENV{HOSTNAME};
$short_host_name =~ s/\..*$//; $short_host_name =~ s/\..*$//;
to_log($conf, {message => "host_name: [$host_name], short_host_name: [".$short_host_name."]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "host_name: [".$host_name."], short_host_name: [".$short_host_name."]", 'line' => __LINE__, level => 2});
foreach my $hash_ref (sort {$a cmp $b} @{$body->{configuration}{nodes}{node}}) foreach my $hash_ref (sort {$a cmp $b} @{$body->{configuration}{nodes}{node}})
{ {
my $node = $hash_ref->{uname}; my $node = $hash_ref->{uname};
my $id = $hash_ref->{id}; my $id = $hash_ref->{id};
to_log($conf, {message => "node: [$node], id: [$id]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "node: [".$node."], id: [".$id."]", 'line' => __LINE__, level => 2});
foreach my $target_name (split/ /, $target_host) foreach my $target_name (split/ /, $target_host)
{ {
to_log($conf, {message => ">> target_name: [$target_name]", 'line' => __LINE__, level => 2}); to_log($conf, {message => ">> target_name: [".$target_name."]", 'line' => __LINE__, level => 2});
$target_name =~ s/\..*//; $target_name =~ s/\..*//;
to_log($conf, {message => "<< target_name: [$target_name]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "<< target_name: [".$target_name."]", 'line' => __LINE__, level => 2});
if ($node =~ /^$target_name/) if ($node =~ /^$target_name/)
{ {
$conf->{cluster}{target_node} = $node; $conf->{cluster}{target_node} = $node;
@ -321,7 +485,7 @@ sub identify_peer
# We've got some data... # We've got some data...
my $name = defined $hash_ref->{instance_attributes}{nvpair}{name} ? $hash_ref->{instance_attributes}{nvpair}{name} : ""; my $name = defined $hash_ref->{instance_attributes}{nvpair}{name} ? $hash_ref->{instance_attributes}{nvpair}{name} : "";
my $value = defined $hash_ref->{instance_attributes}{nvpair}{value} ? $hash_ref->{instance_attributes}{nvpair}{value} : ""; my $value = defined $hash_ref->{instance_attributes}{nvpair}{value} ? $hash_ref->{instance_attributes}{nvpair}{value} : "";
to_log($conf, {message => "node: [$node] instance attribyte name: [$name], value: [$value]", 'line' => __LINE__, level => 1}); to_log($conf, {message => "node: [".$node."] instance attribyte name: [".$name."], value: [".$value."]", 'line' => __LINE__, level => 1});
if (($name eq "maintenance") and ($value eq "on")) if (($name eq "maintenance") and ($value eq "on"))
{ {
# We're in maintenance mode, abort. # We're in maintenance mode, abort.
@ -334,7 +498,7 @@ sub identify_peer
} }
my $quorate = $body->{'have-quorum'}; my $quorate = $body->{'have-quorum'};
to_log($conf, {message => "quorate: [$quorate]", 'line' => __LINE__, level => 1}); to_log($conf, {message => "quorate: [".$quorate."]", 'line' => __LINE__, level => 1});
if (not $quorate) if (not $quorate)
{ {
to_log($conf, {message => "This not is not quorate. Refusing to fence the peer!", 'line' => __LINE__, level => 0, priority => "err"}); to_log($conf, {message => "This not is not quorate. Refusing to fence the peer!", 'line' => __LINE__, level => 0, priority => "err"});
@ -347,19 +511,19 @@ sub identify_peer
my $node = $hash_ref->{uname}; my $node = $hash_ref->{uname};
my $join = $hash_ref->{'join'}; my $join = $hash_ref->{'join'};
my $expected = $hash_ref->{expected}; my $expected = $hash_ref->{expected};
to_log($conf, {message => "node: [$node] join: [$join], expected: [$expected]", 'line' => __LINE__, level => 3}); to_log($conf, {message => "node: [".$node."] join: [".$join."], expected: [".$expected."]", 'line' => __LINE__, level => 3});
if ($node eq $conf->{cluster}{target_node}) if ($node eq $conf->{cluster}{target_node})
{ {
to_log($conf, {message => "Checking the status of target node: [$node].", 'line' => __LINE__, level => 1}); to_log($conf, {message => "Checking the status of target node: [".$node."].", 'line' => __LINE__, level => 1});
if (($join eq "down") && ($expected eq "down")) if (($join eq "down") && ($expected eq "down"))
{ {
# The node is out. # The node is out.
to_log($conf, {message => "The node: [$node] is already down. No actual fence needed.", 'line' => __LINE__, level => 1}); to_log($conf, {message => "The node: [".$node."] is already down. No actual fence needed.", 'line' => __LINE__, level => 1});
exit(7); exit(7);
} }
else else
{ {
to_log($conf, {message => "The node: [$node] is: [$join/$expected] (join/expected). Proceeding with the fence action.", 'line' => __LINE__, level => 1}); to_log($conf, {message => "The node: [".$node."] is: [".$join."/".$expected."] (join/expected). Proceeding with the fence action.", 'line' => __LINE__, level => 1});
} }
} }
} }
@ -385,14 +549,14 @@ sub read_cib
my $body = ""; my $body = "";
my $cib = '<?xml version="1.0" encoding="UTF-8"?>'; my $cib = '<?xml version="1.0" encoding="UTF-8"?>';
my $shell_call = $conf->{path}{exe}{cibadmin}." --local --query"; my $shell_call = $conf->{path}{exe}{cibadmin}." --local --query";
to_log($conf, {message => "Calling: [$shell_call]", 'line' => __LINE__, level => 3}); to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 3});
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>) while(<$file_handle>)
{ {
# This should not generate output. # This should not generate output.
chomp; chomp;
my $line = $_; my $line = $_;
to_log($conf, {message => "Output: [$line]", 'line' => __LINE__, level => 3}); to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 3});
$cib .= "\n".$line; $cib .= "\n".$line;
if ($line =~ /Signon to CIB failed/i) if ($line =~ /Signon to CIB failed/i)
@ -404,21 +568,19 @@ sub read_cib
if ($line =~ /^<cib .*?>$/) if ($line =~ /^<cib .*?>$/)
{ {
$xml_opened = 1; $xml_opened = 1;
to_log($conf, {message => "xml_opened: [$xml_opened].", 'line' => __LINE__, level => 3}); to_log($conf, {message => "xml_opened: [".$xml_opened."].", 'line' => __LINE__, level => 3});
} }
if ($line =~ /^<\/cib>$/) if ($line =~ /^<\/cib>$/)
{ {
$xml_closed = 1; $xml_closed = 1;
to_log($conf, {message => "xml_closed: [$xml_closed].", 'line' => __LINE__, level => 3}); to_log($conf, {message => "xml_closed: [".$xml_closed."].", 'line' => __LINE__, level => 3});
} }
} }
close $file_handle; close $file_handle;
my $return_code = $?; to_log($conf, {message => "cib: ==========\n".$cib."\n==========", 'line' => __LINE__, level => 3});
to_log($conf, {message => "Return code: [$return_code]", 'line' => __LINE__, level => 3});
to_log($conf, {message => "cib: ==========\n$cib\n==========", 'line' => __LINE__, level => 3});
# Now parse the CIB XML if I read it OK. # Now parse the CIB XML if I read it OK.
to_log($conf, {message => "xml_opened: [$xml_opened], xml_closed: [$xml_closed].", 'line' => __LINE__, level => 3}); to_log($conf, {message => "xml_opened: [".$xml_opened."], xml_closed: [".$xml_closed."].", 'line' => __LINE__, level => 3});
if (($xml_opened) && ($xml_closed)) if (($xml_opened) && ($xml_closed))
{ {
# We're good # We're good
@ -428,7 +590,7 @@ sub read_cib
if (not $test) if (not $test)
{ {
chomp $@; chomp $@;
my $error = "[ Error ] - The was a problem parsing: [$cib]. The error was:\n"; my $error = "[ Error ] - The was a problem parsing: [".$cib."]. The error was:\n";
$error .= "===========================================================\n"; $error .= "===========================================================\n";
$error .= $@."\n"; $error .= $@."\n";
$error .= "===========================================================\n"; $error .= "===========================================================\n";
@ -455,28 +617,34 @@ sub find_executables
my $check = ""; my $check = "";
my $bad = 0; my $bad = 0;
# Log entries can only happen if I've found 'logger', so an extra check will be made on 'to_log' # If PATH isn't set, set it (could have been scrubbed by a caller).
if (not $ENV{PATH})
{
$ENV{PATH} = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin";
}
# Log entries can only happen if I've found 'logger', so an extra check will be made on 'to_log'
# calls. # calls.
my @dirs = split/:/, $ENV{PATH}; my @dirs = split/:/, $ENV{PATH};
foreach my $exe (sort {$b cmp $a} keys %{$conf->{path}{exe}}) foreach my $exe (sort {$b cmp $a} keys %{$conf->{path}{exe}})
{ {
if ( not -e $conf->{path}{exe}{$exe} ) if ( not -e $conf->{path}{exe}{$exe} )
{ {
to_log($conf, {message => "The program: [$exe] is not at: [".$conf->{path}{exe}{$exe}."]. Looking for it now...", 'line' => __LINE__, level => 1}); to_log($conf, {message => "The program: [".$exe."] is not at: [".$conf->{path}{exe}{$exe}."]. Looking for it now...", 'line' => __LINE__, level => 1});
foreach my $path (@dirs) foreach my $path (@dirs)
{ {
$check = "$path/$exe"; $check = "$path/$exe";
$check =~ s/\/\//\//g; $check =~ s/\/\//\//g;
to_log($conf, {message => "Checking: [$check]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "Checking: [".$check."]", 'line' => __LINE__, level => 2});
if ( -e $check ) if ( -e $check )
{ {
if (-e $conf->{path}{exe}{logger}) if (-e $conf->{path}{exe}{logger})
{ {
to_log($conf, {message => "Found it! Changed path for: [$exe] from: [".$conf->{path}{exe}{$exe}."] to: [$check]", 'line' => __LINE__, level => 1}); to_log($conf, {message => "Found it! Changed path for: [".$exe."] from: [".$conf->{path}{exe}{$exe}."] to: [".$check."]", 'line' => __LINE__, level => 1});
} }
else else
{ {
warn "DEBUG: Found it! Changed path for: [$exe] from: [".$conf->{path}{exe}{$exe}."] to: [$check]\n"; warn "DEBUG: Found it! Changed path for: [".$exe."] from: [".$conf->{path}{exe}{$exe}."] to: [".$check."]\n";
} }
$conf->{path}{exe}{$exe} = $check; $conf->{path}{exe}{$exe} = $check;
} }
@ -493,17 +661,17 @@ sub find_executables
} }
# Make sure it exists now. # Make sure it exists now.
to_log($conf, {message => "Checking again if: [$exe] is at: [".$conf->{path}{exe}{$exe}."].", 'line' => __LINE__, level => 3}); to_log($conf, {message => "Checking again if: [".$exe."] is at: [".$conf->{path}{exe}{$exe}."].", 'line' => __LINE__, level => 3});
if (not -e $conf->{path}{exe}{$exe}) if (not -e $conf->{path}{exe}{$exe})
{ {
$bad = 1; $bad = 1;
if (-e $conf->{path}{exe}{logger}) if (-e $conf->{path}{exe}{logger})
{ {
to_log($conf, {message => "Failed to find executable: [$exe]. Unable to proceed.", 'line' => __LINE__, level => 0}); to_log($conf, {message => "Failed to find executable: [".$exe."]. Unable to proceed.", 'line' => __LINE__, level => 0});
} }
else else
{ {
warn "Failed to find executable: [$exe]. Unable to proceed.\n"; warn "Failed to find executable: [".$exe."]. Unable to proceed.\n";
} }
} }
} }
@ -527,27 +695,27 @@ sub check_peer_is_fenced
my $node = $hash_ref->{uname}; my $node = $hash_ref->{uname};
my $join = $hash_ref->{'join'}; my $join = $hash_ref->{'join'};
my $expected = $hash_ref->{expected}; my $expected = $hash_ref->{expected};
to_log($conf, {message => "node: [$node] join: [$join], expected: [$expected]", 'line' => __LINE__, level => 3}); to_log($conf, {message => "node: [".$node."] join: [".$join."], expected: [".$expected."]", 'line' => __LINE__, level => 3});
if ($node eq $conf->{cluster}{target_node}) if ($node eq $conf->{cluster}{target_node})
{ {
to_log($conf, {message => "Checking the status of target node: [$node].", 'line' => __LINE__, level => 1}); to_log($conf, {message => "Checking the status of target node: [".$node."].", 'line' => __LINE__, level => 1});
if (($join eq "down") && ($expected eq "down")) if (($join eq "down") && ($expected eq "down"))
{ {
# The node is out. # The node is out.
to_log($conf, {message => "The node: [$node] has been fenced successfully.", 'line' => __LINE__, level => 1}); to_log($conf, {message => "The node: [".$node."] has been fenced successfully.", 'line' => __LINE__, level => 1});
# Call 'drbdadm adjust all' as it seems like drbd's in-memory can change # Call 'drbdadm adjust all' as it seems like drbd's in-memory can change
# causing 'incompatible <fence option>' on return of the peer. # causing 'incompatible <fence option>' on return of the peer.
to_log($conf, {message => "Reloading DRBD config from disk to ensure in-memory and on-disk configs match.", 'line' => __LINE__, level => 1}); to_log($conf, {message => "Reloading DRBD config from disk to ensure in-memory and on-disk configs match.", 'line' => __LINE__, level => 1});
my $shell_call = $conf->{path}{exe}{drbdadm}." adjust all"; my $shell_call = $conf->{path}{exe}{drbdadm}." adjust all";
to_log($conf, {message => "Calling: [$shell_call]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>) while(<$file_handle>)
{ {
# This should not generate output. # This should not generate output.
chomp; chomp;
my $line = $_; my $line = $_;
to_log($conf, {message => "Output: [$line]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2});
} }
close $file_handle; close $file_handle;
to_log($conf, {message => "Fence completed successfully!", 'line' => __LINE__, level => 1}); to_log($conf, {message => "Fence completed successfully!", 'line' => __LINE__, level => 1});
@ -556,7 +724,7 @@ sub check_peer_is_fenced
} }
else else
{ {
to_log($conf, {message => "The node: [$node] is: [$join/$expected] (join/expected). It has not yet been fenced.", 'line' => __LINE__, level => 1}); to_log($conf, {message => "The node: [".$node."] is: [".$join."/".$expected."] (join/expected). It has not yet been fenced.", 'line' => __LINE__, level => 1});
} }
} }
} }
@ -570,15 +738,15 @@ sub kill_target
my ($conf) = @_; my ($conf) = @_;
# Variables # Variables
my $shell_call = $conf->{path}{exe}{stonith_admin}." --fence ".$conf->{cluster}{target_node}." --verbose; RC=\$?; ".$conf->{path}{exe}{crm_error}." \$RC; ".$conf->{path}{exe}{echo}." rc:\$RC"; my $shell_call = $conf->{path}{exe}{stonith_admin}." --fence ".$conf->{cluster}{target_node}." --reboot --verbose; RC=\$?; ".$conf->{path}{exe}{crm_error}." \$RC; ".$conf->{path}{exe}{echo}." rc:\$RC";
to_log($conf, {message => "Calling: [$shell_call]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>) while(<$file_handle>)
{ {
# This should not generate output. # This should not generate output.
chomp; chomp;
my $line = $_; my $line = $_;
to_log($conf, {message => "Output: [$line]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2});
} }
close $file_handle; close $file_handle;
@ -587,7 +755,7 @@ sub kill_target
my $start_time = time; my $start_time = time;
my $end_time = $start_time + 300; my $end_time = $start_time + 300;
my $fenced = 0; my $fenced = 0;
to_log($conf, {message => "start_time: [$start_time], end_time: [$end_time]", 'line' => __LINE__, level => 2}); to_log($conf, {message => "start_time: [".$start_time."], end_time: [".$end_time."]", 'line' => __LINE__, level => 2});
until ($fenced) until ($fenced)
{ {
# This will exit # This will exit
@ -595,7 +763,7 @@ sub kill_target
if (time > $end_time) if (time > $end_time)
{ {
# Done waiting, failed. # Done waiting, failed.
to_log($conf, {message => "The node has not been fenced after five minutes. Giving up..", 'line' => __LINE__, level => 0, priority => "err"}); to_log($conf, {message => "The node has not been fenced after five minutes. Giving up.", 'line' => __LINE__, level => 0, priority => "err"});
exit(1); exit(1);
} }
else else

@ -355,7 +355,7 @@ sub get_password
else else
{ {
# We have the password. Delete the entry now. # We have the password. Delete the entry now.
my $query = "DELETE FROM states WHERE state_uuid=".$anvil->Database->quote($anvil->data->{switches}{'state-uuid'}).";"; my $query = "DELETE FROM states WHERE state_uuid = ".$anvil->Database->quote($anvil->data->{switches}{'state-uuid'}).";";
$anvil->Database->write({uuid => $anvil->data->{sys}{host_uuid}, debug => 3, query => $query, source => $THIS_FILE, line => __LINE__}); $anvil->Database->write({uuid => $anvil->data->{sys}{host_uuid}, debug => 3, query => $query, source => $THIS_FILE, line => __LINE__});
} }

@ -0,0 +1,652 @@
#!/usr/bin/perl
#
# Author: Madison Kelly (mkelly@alteeve.ca)
# Alteeve's Niche! Inc. - https://alteeve.com/w/
# Version: 0.0.1
# License: GPL v2+
#
# This program ties LINBIT's DRBD fencing into pacemaker's stonith. It provides a power-fence alternative to
# the default 'crm-{un,}fence-peer.sh' {un,}fence-handler.
#
# WARNING: This unfence handler is probably not safe to use outside of an Anvil! IA platform. It makes a lot
# of operational assumptions about the system and desired goals.
#
# TODO:
# -
### NOTE: This doesn't use Anvil::Tools on purpose. We want to be quick and depend on as few things as
### possible.
use strict;
use warnings;
use XML::Simple;
use Data::Dumper;
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
my $conf = {
'log' => {
facility => "local0",
level => 2,
line_numbers => 1,
tag => $THIS_FILE,
},
# If a program isn't at the defined path, $ENV{PATH} will be searched.
path => {
exe => {
cibadmin => "/usr/sbin/cibadmin",
crm_attribute => "/usr/sbin/crm_attribute",
crm_error => "/usr/sbin/crm_error",
drbdadm => "/usr/sbin/drbdadm",
echo => "/usr/bin/echo",
getent => "/usr/bin/getent",
logger => "/usr/bin/logger",
pcs => "/usr/sbin/pcs",
stonith_admin => "/usr/sbin/stonith_admin",
},
},
# The script will set this.
cluster => {
target_node => "",
},
# These are the environment variables set by DRBD. See 'man drbd.conf'
# -> 'handlers'.
environment => {
# The resource triggering the fence.
'DRBD_RESOURCE' => defined $ENV{DRBD_RESOURCE} ? $ENV{DRBD_RESOURCE} : "",
# The resource minor number, or, in the case of volumes, numbers.
'DRBD_MINOR' => defined $ENV{DRBD_MINOR} ? $ENV{DRBD_MINOR} : "",
# This is the address format (ipv4, ipv6, etc)
'DRBD_PEER_AF' => defined $ENV{DRBD_PEER_AF} ? $ENV{DRBD_PEER_AF} : "",
# This is the IP address of the target node.
'DRBD_PEER_ADDRESS' => defined $ENV{DRBD_PEER_ADDRESS} ? $ENV{DRBD_PEER_ADDRESS} : "",
# This isn't set
'DRBD_PEERS' => defined $ENV{DRBD_PEERS} ? $ENV{DRBD_PEERS} : "",
### NOTE: Below here are undocumented variables. Don't expect them to always be useful.
# My node ID
'DRBD_MY_NODE_ID' => defined $ENV{DRBD_MY_NODE_ID} ? $ENV{DRBD_MY_NODE_ID} : "",
# The target's ID
'DRBD_PEER_NODE_ID' => defined $ENV{DRBD_PEER_NODE_ID} ? $ENV{DRBD_PEER_NODE_ID} : "",
},
};
# Find executables.
find_executables($conf);
# Something for the logs
to_log($conf, {message => "The Anvil! DRBD unfence handler has been invoked.", 'line' => __LINE__});
# These are the full host names of the nodes given their IDs.
foreach my $i (0..31)
{
my $key = "DRBD_NODE_ID_".$i;
if ((exists $ENV{$key}) && (defined $ENV{$key}))
{
$conf->{environment}{$key} = $ENV{$key};
my $level = $conf->{environment}{$key} eq "" ? 3 : 2;
to_log($conf, {message => "DRBD Environment variable: [$key] -> [".$conf->{environment}{$key}."]", 'line' => __LINE__, level => $level});
}
}
# Record the environment variables
foreach my $key (sort {$a cmp $b} keys %{$conf->{environment}})
{
# $conf->{environment}{DRBD_RESOURCE} -> [srv51-Workstation3]
my $level = $conf->{environment}{$key} eq "" ? 3 : 2;
to_log($conf, {message => "DRBD Environment variable: [$key] -> [".$conf->{environment}{$key}."]", 'line' => __LINE__, level => $level});
}
foreach my $key (sort {$a cmp $b} keys %ENV)
{
next if exists $conf->{environment}{$key};
my $level = $ENV{$key} eq "" ? 3 : 2;
to_log($conf, {message => "System Environment variable: [$key] -> [".$ENV{$key}."]", 'line' => __LINE__, level => 3});
}
# Make sure we at least have the target's IP.
if (not $conf->{environment}{DRBD_PEER_ADDRESS})
{
to_log($conf, {message => "Called without target's IP. Nothing to do, exiting. Were we called by 'pcs stonith list'?", 'line' => __LINE__, level => 1, priority => "alert"});
exit(1);
}
# This also checks that we're quorate and not in maintenance mode.
identify_peer($conf);
# If we're still alive, we now need to check the DRBD resource disk state locally.
get_drbd_status($conf);
# Is there a specific resource?
if ($conf->{environment}{DRBD_RESOURCE})
{
# Prevent the resource from running on the peer.
to_log($conf, {message => "We're being asked to unfence the specific resource: [".$conf->{environment}{DRBD_RESOURCE}."]. Will remove the node attribute blocking this server from running on: [".$conf->{cluster}{target_node}."].", 'line' => __LINE__});
remove_constraint($conf);
}
else
{
to_log($conf, {message => "We were not given a specific resource to unfence. Nothing to do.", 'line' => __LINE__});
exit(0);
}
# If we hit here, something very wrong happened.
exit(1);
#############################################################################################################
# Functions #
#############################################################################################################
# This removes a location constraint that prevents the resource / server from running on the peer node.
sub remove_constraint
{
my ($conf) = @_;
my $target_server = $conf->{environment}{DRBD_RESOURCE};
my $target_node = $conf->{cluster}{target_node};
to_log($conf, {message => "Will now create a location constraint against: [".$target_server."] preventing it from running on: [".$target_node."].", 'line' => __LINE__, level => 1});
# Check that the rule was set.
my $rule_name = "drbd-fenced_".$target_server;
my $rule_set = 1;
my $rule_found = 0;
my $rule_output = "";
my $shell_call = $conf->{path}{exe}{crm_attribute}." --type nodes --node ".$target_node." --name ".$rule_name." --query";
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>)
{
# This should not generate output.
chomp;
my $line = $_;
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2});
if (($line =~ /name=$rule_name/) && ($line =~ /value=0/))
{
$rule_set = 0;
to_log($conf, {message => "rule_set: [".$rule_set."]", 'line' => __LINE__, level => 2});
last;
}
else
{
$rule_output .= $line."\n";
}
}
close $file_handle;
if (not $rule_set)
{
# No need to unfence.
to_log($conf, {message => "The node attribute rule: [".$rule_name."] against the node: [".$target_node."] was not found. No need to unfence.", 'line' => __LINE__, level => 0, priority => "err"});
exit(0);
}
# Clear the node attribute
$shell_call = $conf->{path}{exe}{crm_attribute}." --type nodes --node ".$target_node." --name ".$rule_name." --update 0";
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open ($file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>)
{
# This should not generate output.
chomp;
my $line = $_;
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 3});
}
close $file_handle;
# Check that the rule was set.
$rule_output = "";
$shell_call = $conf->{path}{exe}{crm_attribute}." --type nodes --node ".$target_node." --name ".$rule_name." --query";
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open ($file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>)
{
# This should not generate output.
chomp;
my $line = $_;
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2});
if (($line =~ /name=$rule_name/) && ($line =~ /value=0/))
{
$rule_set = 0;
to_log($conf, {message => "rule_set: [".$rule_set."]", 'line' => __LINE__, level => 2});
last;
}
else
{
$rule_output .= $line."\n";
}
}
close $file_handle;
if (not $rule_set)
{
# Success!
to_log($conf, {message => "The node attribute rule: [".$rule_name."] against the node: [".$target_node."] has been cleared successfully.", 'line' => __LINE__, level => 0, priority => "err"});
exit(0);
}
else
{
# Failed.
$rule_output =~ s/\n$//gs;
to_log($conf, {message => "The node attribute triggering the fence rule: [".$rule_name."] against the node: [".$target_node."] appears to have not been cleared. Expected a string with 'name=".$rule_name." value=0' but got: [".$rule_output."].", 'line' => __LINE__, level => 0, priority => "err"});
exit(1);
}
return(0);
}
# This reads the status of all resources. If we're not all UpToDate, check if the peer is. If the peer is,
# abort. If not, proceed (someone is gouig to have a bad day, but maybe some servers will live)
sub get_drbd_status
{
my ($conf) = @_;
my $resource = "";
my $peer = "";
my $local_all_uptodate = 1;
my $peer_all_uptodate = 1;
my $shell_call = $conf->{path}{exe}{drbdadm}." status all";
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>)
{
# This should not generate output.
chomp;
my $line = $_;
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 3});
if (not $line)
{
$resource = "";
$peer = "";
to_log($conf, {message => "resource: [".$resource."], peer: [".$peer."]", 'line' => __LINE__, level => 3});
next;
}
if ($line =~ /^(\S+)\s+role/)
{
$resource = $1;
to_log($conf, {message => "resource: [".$resource."]", 'line' => __LINE__, level => 3});
next;
}
if ($line =~ /^\s+(.*?) role:/)
{
$peer = $1;
to_log($conf, {message => "peer: [".$peer."]", 'line' => __LINE__, level => 3});
next;
}
if ($resource)
{
if ($line =~ /disk:(.*)$/)
{
my $local_dstate = $1;
$local_dstate =~ s/\s.*$//;
to_log($conf, {message => "local_dstate: [".$local_dstate."]", 'line' => __LINE__, level => 2});
if (lc($local_dstate) ne "uptodate")
{
$local_all_uptodate = 0;
to_log($conf, {message => "local_all_uptodate: [".$local_all_uptodate."]", 'line' => __LINE__, level => 2});
}
next;
}
if ($line =~ /peer-disk:(.*)$/)
{
my $peer_dstate = $1;
$peer_dstate =~ s/\s.*$//;
to_log($conf, {message => "peer: [".$peer."], peer_dstate: [".$peer_dstate."]", 'line' => __LINE__, level => 2});
if (lc($peer_dstate) ne "uptodate")
{
$peer_all_uptodate = 0;
to_log($conf, {message => "peer_all_uptodate: [".$peer_all_uptodate."]", 'line' => __LINE__, level => 2});
}
next;
}
}
}
close $file_handle;
# If we're not all UpToDate, but the peer is, abort
to_log($conf, {message => "local_all_uptodate: [".$local_all_uptodate."], peer_all_uptodate: [".$peer_all_uptodate."]", 'line' => __LINE__, level => 2});
if ((not $local_all_uptodate) && ($peer_all_uptodate))
{
# We're not good
to_log($conf, {message => "This node has DRBD resources that are not UpToDate, but the peer is fully UpToDate. Aborting.", 'line' => __LINE__, level => 0, priority => "err"});
exit(1);
}
return(0);
}
# This identifies the pacemaker name of the target node. If it can't find the peer, it exits with '1'.
sub identify_peer
{
my ($conf) = @_;
# I know the target's (SN) IP, map it to a node.
my $target_host = "";
my $target_ip = $conf->{environment}{DRBD_PEER_ADDRESS};
# First, can we translate the IP to a host name?
my $shell_call = $conf->{path}{exe}{getent}." hosts ".$target_ip;
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2});
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>)
{
# This should not generate output.
chomp;
my $line = $_;
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2});
if ($line =~ /^$target_ip\s+(.*)$/)
{
# This could be multiple names.
$target_host = $1;
to_log($conf, {message => "target_host: [".$target_host."]", 'line' => __LINE__, level => 2});
#to_log($conf, {message => ">> target_host: [".$target_host."]", 'line' => __LINE__, level => 2});
# Strip off any suffix, we only want the short name.
#$target_host =~ s/\..*//;
#to_log($conf, {message => "<< target_host: [".$target_host."]", 'line' => __LINE__, level => 2});
#last;
}
}
close $file_handle;
# If I got the host name, try to match it to a pacemaker node name.
if ($target_host)
{
# Get the current CIB (in an XML::Simple hash). This will exit if it fails to read the XML
# and convert it to an XML::Simple hash.
my $body = read_cib($conf);
# Parse the XML.
my $host_name = $ENV{HOSTNAME};
my $short_host_name = $ENV{HOSTNAME};
$short_host_name =~ s/\..*$//;
to_log($conf, {message => "host_name: [".$host_name."], short_host_name: [".$short_host_name."]", 'line' => __LINE__, level => 2});
foreach my $hash_ref (sort {$a cmp $b} @{$body->{configuration}{nodes}{node}})
{
my $node = $hash_ref->{uname};
my $id = $hash_ref->{id};
to_log($conf, {message => "node: [".$node."], id: [".$id."]", 'line' => __LINE__, level => 2});
foreach my $target_name (split/ /, $target_host)
{
to_log($conf, {message => ">> target_name: [".$target_name."]", 'line' => __LINE__, level => 2});
$target_name =~ s/\..*//;
to_log($conf, {message => "<< target_name: [".$target_name."]", 'line' => __LINE__, level => 2});
if ($node =~ /^$target_name/)
{
$conf->{cluster}{target_node} = $node;
to_log($conf, {message => "Found the pacemaker name of the target node: [".$conf->{cluster}{target_node}."]", 'line' => __LINE__, level => 1});
}
elsif ($node =~ /^$short_host_name/)
{
# This is me. Am I in maintenance mode?
if (exists $hash_ref->{instance_attributes})
{
# We've got some data..
my $name = defined $hash_ref->{instance_attributes}{nvpair}{name} ? $hash_ref->{instance_attributes}{nvpair}{name} : "";
my $value = defined $hash_ref->{instance_attributes}{nvpair}{value} ? $hash_ref->{instance_attributes}{nvpair}{value} : "";
to_log($conf, {message => "node: [".$node."] instance attribyte name: [".$name."], value: [".$value."]", 'line' => __LINE__, level => 1});
if (($name eq "maintenance") and ($value eq "on"))
{
# We're in maintenance mode, abort.
to_log($conf, {message => "This node is in maintenance mode. Not able to fence!", 'line' => __LINE__, level => 0, priority => "err"});
exit(1);
}
}
}
}
}
my $quorate = $body->{'have-quorum'};
to_log($conf, {message => "quorate: [".$quorate."]", 'line' => __LINE__, level => 1});
if (not $quorate)
{
to_log($conf, {message => "This not is not quorate. Refusing to fence the peer!", 'line' => __LINE__, level => 0, priority => "err"});
exit(1);
}
# See if the target node is already out of the cluster.
foreach my $hash_ref (@{$body->{status}{node_state}})
{
my $node = $hash_ref->{uname};
my $join = $hash_ref->{'join'};
my $expected = $hash_ref->{expected};
to_log($conf, {message => "node: [".$node."] join: [".$join."], expected: [".$expected."]", 'line' => __LINE__, level => 3});
if ($node eq $conf->{cluster}{target_node})
{
to_log($conf, {message => "Checking the status of target node: [".$node."].", 'line' => __LINE__, level => 1});
if (($join eq "down") && ($expected eq "down"))
{
# The node is out.
to_log($conf, {message => "The node: [".$node."] is already down. No actual fence needed.", 'line' => __LINE__, level => 1});
exit(7);
}
else
{
to_log($conf, {message => "The node: [".$node."] is: [".$join."/".$expected."] (join/expected). Proceeding with the fence action.", 'line' => __LINE__, level => 1});
}
}
}
}
# Did I find the target?
if (not $conf->{cluster}{target_node})
{
to_log($conf, {message => "Failed to find the pacemaker name of the target node. Unable to proceed!", 'line' => __LINE__, level => 0, priority => "err"});
exit(1);
}
return(0);
}
# This reads in the CIB XML and returns it as a single multi-line string.
sub read_cib
{
my ($conf) = @_;
my $xml_opened = 0;
my $xml_closed = 0;
my $body = "";
my $cib = '<?xml version="1.0" encoding="UTF-8"?>';
my $shell_call = $conf->{path}{exe}{cibadmin}." --local --query";
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 3});
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>)
{
# This should not generate output.
chomp;
my $line = $_;
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 3});
$cib .= "\n".$line;
if ($line =~ /Signon to CIB failed/i)
{
# Failed to connect, we're probably not in the cluster.
to_log($conf, {message => "This node does not appear to be in the cluster. Unable to get the CIB status.", 'line' => __LINE__, level => 0, priority => "err"});
exit(1);
}
if ($line =~ /^<cib .*?>$/)
{
$xml_opened = 1;
to_log($conf, {message => "xml_opened: [".$xml_opened."].", 'line' => __LINE__, level => 3});
}
if ($line =~ /^<\/cib>$/)
{
$xml_closed = 1;
to_log($conf, {message => "xml_closed: [".$xml_closed."].", 'line' => __LINE__, level => 3});
}
}
close $file_handle;
to_log($conf, {message => "cib: ==========\n".$cib."\n==========", 'line' => __LINE__, level => 3});
# Now parse the CIB XML if I read it OK.
to_log($conf, {message => "xml_opened: [".$xml_opened."], xml_closed: [".$xml_closed."].", 'line' => __LINE__, level => 3});
if (($xml_opened) && ($xml_closed))
{
# We're good
local $@;
my $xml = XML::Simple->new();
my $test = eval { $body = $xml->XMLin($cib, KeyAttr => { language => 'name', key => 'name' }, ForceArray => [ 'id' ]) };
if (not $test)
{
chomp $@;
my $error = "[ Error ] - The was a problem parsing: [".$cib."]. The error was:\n";
$error .= "===========================================================\n";
$error .= $@."\n";
$error .= "===========================================================\n";
to_log($conf, {message => $error, 'line' => __LINE__, level => 0, priority => "err"});
exit(1);
}
}
else
{
# Failed to read the CIB XML.
to_log($conf, {message => "This node does not appear to be in the cluster. Unable to read the CIB XML properly.", 'line' => __LINE__, level => 2, priority => "err"});
exit(1);
}
return($body);
}
# This checks the given paths and, if something isn't found, it searches PATH trying to find it.
sub find_executables
{
my ($conf) = @_;
# Variables.
my $check = "";
my $bad = 0;
# If PATH isn't set, set it (could have been scrubbed by a caller).
if (not $ENV{PATH})
{
$ENV{PATH} = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin";
}
# Log entries can only happen if I've found 'logger', so an extra check will be made on 'to_log'
# calls.
my @dirs = split/:/, $ENV{PATH};
foreach my $exe (sort {$b cmp $a} keys %{$conf->{path}{exe}})
{
if ( not -e $conf->{path}{exe}{$exe} )
{
to_log($conf, {message => "The program: [".$exe."] is not at: [".$conf->{path}{exe}{$exe}."]. Looking for it now...", 'line' => __LINE__, level => 1});
foreach my $path (@dirs)
{
$check = "$path/$exe";
$check =~ s/\/\//\//g;
to_log($conf, {message => "Checking: [".$check."]", 'line' => __LINE__, level => 2});
if ( -e $check )
{
if (-e $conf->{path}{exe}{logger})
{
to_log($conf, {message => "Found it! Changed path for: [".$exe."] from: [".$conf->{path}{exe}{$exe}."] to: [".$check."]", 'line' => __LINE__, level => 1});
}
else
{
warn "DEBUG: Found it! Changed path for: [".$exe."] from: [".$conf->{path}{exe}{$exe}."] to: [".$check."]\n";
}
$conf->{path}{exe}{$exe} = $check;
}
else
{
to_log($conf, {message => "Not found.", 'line' => __LINE__, level => 2});
}
}
}
else
{
to_log($conf, {message => "Found!", 'line' => __LINE__, level => 3});
next;
}
# Make sure it exists now.
to_log($conf, {message => "Checking again if: [".$exe."] is at: [".$conf->{path}{exe}{$exe}."].", 'line' => __LINE__, level => 3});
if (not -e $conf->{path}{exe}{$exe})
{
$bad = 1;
if (-e $conf->{path}{exe}{logger})
{
to_log($conf, {message => "Failed to find executable: [".$exe."]. Unable to proceed.", 'line' => __LINE__, level => 0});
}
else
{
warn "Failed to find executable: [".$exe."]. Unable to proceed.\n";
}
}
}
if ($bad)
{
exit(1);
}
return(0);
}
# Log file entries
sub to_log
{
my ($conf, $parameters) = @_;
my $facility = defined $parameters->{facility} ? $parameters->{facility} : $conf->{'log'}{facility};
my $level = defined $parameters->{level} ? $parameters->{level} : 1;
my $line = defined $parameters->{'line'} ? $parameters->{'line'} : 0;
my $message = defined $parameters->{message} ? $parameters->{message} : "";
my $priority = defined $parameters->{priority} ? $parameters->{priority} : "";
# Leave if we don't care about this message
return if $level > $conf->{'log'}{level};
return if not $message;
# Build the message. We log the line
if (($conf->{'log'}{line_numbers}) && ($line))
{
$message = $line."; ".$message;
}
my $priority_string = $facility;
if ($priority)
{
$priority_string .= ".".$priority;
}
elsif ($level eq "0")
{
$priority_string .= ".notice";
}
elsif (($level eq "1") or ($level eq "2"))
{
$priority_string .= ".info";
}
else
{
$priority_string .= ".debug";
}
# Clean up the string for bash
$message =~ s/"/\\\"/gs;
#$message =~ s/\(/\\\(/gs;
my $shell_call = $conf->{path}{exe}{logger}." --priority ".$priority_string." --tag ".$conf->{'log'}{tag}." -- \"".$message."\"";
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n";
while(<$file_handle>)
{
# This should not generate output.
chomp;
my $line = $_;
print "Unexpected logging output: [".$line."]\n";
}
close $file_handle;
return(0);
}
Loading…
Cancel
Save