* Updated Tools->_anvil_version() and Get->anvil_version() to now pick up a SchemaVersion from anvil.sql. This will change only when the schema changes and is used when Database->connect() is checking compatibility with other anvil database hosts. This will make it only break connection when there is a reason to do so. The anvil_version still remains as an informational version that will help when supporting users later.

* Updated Cluster->add_server() to now set failure timeouts to actual numbers instead of INFINITY after discovering that INFINITY doesn't work in those cases.
* Updated Databsae->get_hosts to now check if other entries have the same host name, and if so, to set their host_key to 'DELETED'. This should make it easier to handle when a hardware machine is replaced by new hardware but uses the same host_name.
* Updated Email->check_queue() to start and enable postfix.service if it's found to not be running.
* Updated Get->available_resources() to return '!!no_data!!' when a given host hasn't got any data in scan_lvm_vgs. Now use this in anvil-provision-server to exit if a node or dr host hasn't run scancore yet.
* Fixed a bug in scan-lvm where the pvs_uuid wasn't being loaded properly, preventing lost PVs, VGs and LVs from being flagged as deleted.
* Started work on anvil-migate-server, though it's far from complete.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 4 years ago
parent 89dec8e1f9
commit 413a4f73c2
  1. 7
      Anvil/Tools.pm
  2. 10
      Anvil/Tools/Cluster.pm
  3. 63
      Anvil/Tools/Database.pm
  4. 27
      Anvil/Tools/Email.pm
  5. 222
      Anvil/Tools/Get.pm
  6. 4
      Anvil/Tools/Remote.pm
  7. 4
      Anvil/Tools/Storage.pm
  8. 3
      Anvil/Tools/System.pm
  9. 4
      ocf/alteeve/server
  10. 1
      scancore-agents/scan-drbd/scan-drbd
  11. 5
      scancore-agents/scan-drbd/scan-drbd.xml
  12. 7
      scancore-agents/scan-lvm/scan-lvm
  13. 5
      share/anvil.sql
  14. 16
      share/words.xml
  15. 4
      tools/anvil-delete-server
  16. 240
      tools/anvil-migrate-server
  17. 9
      tools/anvil-provision-server
  18. 3
      tools/test.pl

@ -734,14 +734,15 @@ sub _anvil_version
my $self = shift;
my $anvil = $self;
$anvil->{HOST}{ANVIL_VERSION} = "" if not defined $anvil->{HOST}{ANVIL_VERSION};
$anvil->{HOST}{ANVIL_VERSION} = "" if not defined $anvil->{HOST}{ANVIL_VERSION};
$anvil->{HOST}{SCHEMA_VERSION} = "" if not defined $anvil->{HOST}{SCHEMA_VERSION};
if ($anvil->{HOST}{ANVIL_VERSION} eq "")
{
# Try to read the local Anvil! version.
$anvil->{HOST}{ANVIL_VERSION} = $anvil->Get->anvil_version();
($anvil->{HOST}{ANVIL_VERSION}, $anvil->{HOST}{SCHEMA_VERSION}) = $anvil->Get->anvil_version();
}
return($anvil->{HOST}{ANVIL_VERSION});
return($anvil->{HOST}{ANVIL_VERSION}, $anvil->{HOST}{SCHEMA_VERSION});
}
=head2 _get_hash_reference

@ -197,9 +197,11 @@ sub add_server
target_role => $target_role,
}});
### NOTE: 'INFINITY' doesn't work in some cases, so we set 1 day timeouts. If windows can't install
### an OS update in 24 hours, there's probably deeper issues.
### TODO: If the target_role is 'started' because the server was running, we may need to later do an
### update to set it to 'stopped' after we've verified it's in the cluster below.
my $resource_command = $anvil->data->{path}{exe}{pcs}." resource create ".$server_name." ocf:alteeve:server name=\"".$server_name."\" meta allow-migrate=\"true\" target-role=\"".$target_role."\" op monitor interval=\"60\" start timeout=\"INFINITY\" on-fail=\"block\" stop timeout=\"INFINITY\" on-fail=\"block\" migrate_to timeout=\"INFINITY\"";
my $resource_command = $anvil->data->{path}{exe}{pcs}." resource create ".$server_name." ocf:alteeve:server name=\"".$server_name."\" meta allow-migrate=\"true\" target-role=\"".$target_role."\" op monitor interval=\"60\" start timeout=\"300\" on-fail=\"block\" stop timeout=\"86400\" on-fail=\"block\" migrate_to timeout=\"86400\"";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { resource_command => $resource_command }});
my ($output, $return_code) = $anvil->System->call({shell_call => $resource_command});
@ -341,7 +343,7 @@ FROM
WHERE
scan_lvm_vg_host_uuid = ".$anvil->Database->quote($host_uuid)."
ORDER BY
scan_lvm_vg_size ASC;
scan_lvm_vg_size ASC
;";
$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__});
@ -388,7 +390,6 @@ ORDER BY
$anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid} = $row->[5];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"ungrouped_vg_count::${this_is}" => $anvil->data->{ungrouped_vg_count}{$this_is},
"ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::count" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{count},
"ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_uuid" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_uuid},
"ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_name" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_name},
"ungrouped_vgs::${scan_lvm_vg_size}::host_uuid::${host_uuid}::vg_extent_size" => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_extent_size}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_extent_size}}).")",
@ -399,12 +400,13 @@ ORDER BY
}
}
# Fing ungrouped VGs and see if we can group them. First by looking for identical sizes.
# Find ungrouped VGs and see if we can group them. First by looking for identical sizes.
my $reload_storage_groups = 0;
foreach my $scan_lvm_vg_size (sort {$a cmp $b} keys %{$anvil->data->{ungrouped_vgs}})
{
# If there are two or three VGs, we can create a group.
my $count = keys %{$anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { count => $count }});
if (($count == 2) or ($count == 3))
{
# Create the volume group ... group. First we need a group number

@ -1300,26 +1300,28 @@ sub connect
# sure it matches ours. If it doesn't, skip this database.
if (not $is_local)
{
my $local_version = $anvil->_anvil_version({debug => $debug});
my $remote_version = $anvil->Get->anvil_version({
my ($local_anvil_version, $local_schema_version) = $anvil->_anvil_version({debug => $debug});
my ($remote_anvil_version, $remote_schema_version) = $anvil->Get->anvil_version({
debug => $debug,
target => $host,
password => $password,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:host' => $host,
's2:remote_version' => $remote_version,
's3:local_version' => $local_version,
's1:host' => $host,
's2:local_anvil_version' => $local_anvil_version,
's3:remote_anvil_version' => $remote_anvil_version,
's4:local_schema_version' => $local_schema_version,
's5:remote_schema_version' => $remote_schema_version,
}});
# TODO: Periodically, we fail to get the remote version. For now, we proceed if
# everything else is OK. Might be better to pause a re-try... To be determined.
if (($remote_version) && ($remote_version ne $local_version))
if (($remote_schema_version) && ($remote_schema_version ne $local_schema_version))
{
# Version doesn't match,
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0145", variables => {
host => $host,
local_version => $anvil->_anvil_version,
target_version => $remote_version,
local_version => $local_schema_version,
target_version => $remote_schema_version,
}});
# Delete the information about this database. We'll try again on nexy
@ -2578,7 +2580,7 @@ It also sets the variables;
hosts::host_uuid::<host_uuid>::host_name = <host_name>;
hosts::host_uuid::<host_uuid>::host_type = <host_type; node, dr or dashboard>
hosts::host_uuid::<host_uuid>::host_key = <Machine's public key / fingerprint>
hosts::host_uuid::<host_uuid>::host_key = <Machine's public key / fingerprint, set to DELETED when the host is no longer used>
hosts::host_uuid::<host_uuid>::host_ipmi = <If equiped, this is how to log into the host's IPMI BMC, including the password!>
hosts::host_uuid::<host_uuid>::anvil_name = <anvil_name if associated with an anvil>
hosts::host_uuid::<host_uuid>::anvil_uuid = <anvil_uuid if associated with an anvil>
@ -6876,6 +6878,49 @@ WHERE
$anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
}
# If a node was replaced, there could a host_uuid with the same host name. If that's found for this
# host, delete the other's host_key and register an alert.
$query = "
SELECT
host_uuid
FROM
hosts
WHERE
host_name = ".$anvil->Database->quote($host_name)."
AND
host_uuid != ".$anvil->Database->quote($host_uuid)."
AND
host_key != 'DELETED'
;";
$results = $anvil->Database->query({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
$count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
my $other_host_uuid = $row->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { other_host_uuid => $other_host_uuid }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert", key => "warning_0075", variables => {
host_name => $host_name,
host_uuid => $other_host_uuid,
}});
my $query = "
UPDATE
hosts
SET
host_key = 'DELETED',
modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
WHERE
host_uuid = ".$anvil->Database->quote($other_host_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__});
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0126", variables => { method => "Database->insert_or_update_hosts()" }});
return($host_uuid);
}

@ -218,8 +218,31 @@ sub check_queue
if ($queue =~ /^postqueue: warning:/)
{
# Something is up, we can't proceed.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "warning_0069", variables => { output => $queue }});
return("!!error!!");
if (($queue =~ /Mail system is down/) && (($< == 0) or ($> == 0)))
{
# Enable and start the postfix daemon.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "warning_0076", variables => { output => $queue }});
$anvil->System->enable_daemon({debug => $debug, daemon => "postfix.service"});
$anvil->System->start_daemon({debug => $debug, daemon => "postfix.service"});
# Try to check the queue again.
($queue, $return_code) = $anvil->System->call({debug => $debug, shell_call => $anvil->data->{path}{exe}{postqueue}." -j"});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
queue => $queue,
return_code => $return_code,
}});
if ($queue =~ /^postqueue: warning:/)
{
# Still down
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "warning_0077", variables => { output => $queue }});
return("!!error!!");
}
}
else
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "warning_0069", variables => { output => $queue }});
return("!!error!!");
}
}
# This is empty if there is nothing in the queue.

@ -8,9 +8,10 @@ use warnings;
use Scalar::Util qw(weaken isweak);
use Data::Dumper;
use Encode;
use UUID::Tiny qw(:std);
use Net::Netmask;
use JSON;
use Net::Netmask;
use Text::Diff;
use UUID::Tiny qw(:std);
our $VERSION = "3.0.0";
my $THIS_FILE = "Get.pm";
@ -166,6 +167,8 @@ WHERE
This reads to C<< VERSION >> file of a local or remote machine. If the version file isn't found, C<< 0 >> is returned.
This also reads the main C<< anvil.sql >> schema file and parses
Parameters;
=head3 password (optional)
@ -198,7 +201,6 @@ sub anvil_version
my $port = defined $parameter->{port} ? $parameter->{port} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $version = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
password => $anvil->Log->is_secure($password),
port => $port,
@ -206,116 +208,198 @@ sub anvil_version
target => $target,
}});
# The variables that will store the versions.
my $anvil_version = 0;
my $schema_version = 0;
my $schema_body = "";
# Is this a local call or a remote call?
if ($anvil->Network->is_local({host => $target}))
{
# Local.
$version = $anvil->Storage->read_file({file => $anvil->data->{path}{configs}{'anvil.version'}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { version => $version }});
$anvil_version = $anvil->Storage->read_file({file => $anvil->data->{path}{configs}{'anvil.version'}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_version => $anvil_version }});
# Did we actually read a version?
if ($anvil_version eq "!!error!!")
{
$anvil_version = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { anvil_version => $anvil_version }});
}
# Now read in the SQL schema
$schema_body = $anvil->Storage->read_file({file => $anvil->data->{path}{sql}{'anvil.sql'}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_version => $anvil_version }});
# Did we actually read a version?
if ($version eq "!!error!!")
if ($schema_body eq "!!error!!")
{
$version = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { version => $version }});
$schema_version = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { schema_version => $schema_version }});
}
}
else
{
### TODO: Remote calls are fragile. Move the version of dashboards into a variable to read from the database.
# Remote call. If we're running as the apache user, we need to read the cached version for
# the peer. otherwise, after we read the version, will write the cached version.
my $user = getpwuid($<);
my $cache_file = $anvil->data->{path}{directories}{anvil}."/anvil.".$target.".version";
my $user = getpwuid($<);
my $anvil_cache_file = $anvil->data->{path}{directories}{anvil}."/anvil.".$target.".version";
my $schema_cache_file = $anvil->data->{path}{directories}{anvil}."/anvil.".$target.".schema";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
cache_file => $cache_file,
user => $user,
anvil_cache_file => $anvil_cache_file,
schema_cache_file => $schema_cache_file,
user => $user,
}});
if ($user eq "apache")
{
# Try to read the local cached version.
if (-e $cache_file)
if (-e $anvil_cache_file)
{
# Read it in.
$anvil_version = $anvil->Storage->read_file({file => $anvil_cache_file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_version => $anvil_version }});
}
if (-e $schema_cache_file)
{
# Read it in.
$version = $anvil->Storage->read_file({file => $cache_file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { version => $version }});
$schema_body = $anvil->Storage->read_file({file => $schema_cache_file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_version => $anvil_version }});
}
}
else
{
my $shell_call = "
if [ -e ".$anvil->data->{path}{configs}{'anvil.version'}." ];
foreach my $file ($anvil->data->{path}{configs}{'anvil.version'}, $anvil->data->{path}{sql}{'anvil.sql'})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { file => $file }});
my $shell_call = "
if [ -e ".$file." ];
then
cat ".$anvil->data->{path}{configs}{'anvil.version'}.";
cat ".$file.";
else
echo 0;
fi;
";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
my ($output, $error, $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
port => $port,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
error => $error,
output => $output,
}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
my ($file_body, $error, $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
port => $port,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
file_body => $file_body,
error => $error,
}});
$version = defined $output ? $output : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { version => $version }});
if ($file eq $anvil->data->{path}{configs}{'anvil.version'})
{
$anvil_version = $file_body;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_version => $anvil_version }});
}
else
{
$schema_body = $file_body;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { schema_body => $schema_body }});
}
# Create/Update the cache file.
if ($version)
{
my $update_cache = 1;
my $old_version = "";
if (-e $cache_file)
# Create/Update the cache file.
if ($file_body)
{
$old_version = $anvil->Storage->read_file({file => $cache_file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { old_version => $old_version }});
if ($old_version eq $version)
my $update_cache = 0;
my $old_file_body = "";
my $cache_file = $file eq $anvil->data->{path}{configs}{'anvil.version'} ? $anvil_cache_file : $schema_cache_file;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { cache_file => $cache_file }});
if (-e $cache_file)
{
# No need to update
$update_cache = 0;
$old_file_body = $anvil->Storage->read_file({file => $cache_file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
old_file_body => $old_file_body,
file_body => $file_body,
}});
my $difference = diff \$old_file_body, \$file_body, { STYLE => 'Unified' };
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { difference => $difference }});
if ($difference)
{
# update needed
$update_cache = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { update_cache => $update_cache }});
}
}
else
{
# Cache file needs to be created.
$update_cache = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { update_cache => $update_cache }});
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { update_cache => $update_cache }});
if ($update_cache)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0437", variables => {
target => $target,
file => $cache_file,
}});
$anvil->Storage->write_file({
debug => $debug,
file => $cache_file,
body => $version,
mode => "0666",
overwrite => 1,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { update_cache => $update_cache }});
if ($update_cache)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0437", variables => {
target => $target,
file => $cache_file,
}});
$anvil->Storage->write_file({
debug => $debug,
backup => 0,
file => $cache_file,
body => $file_body,
mode => "0666",
overwrite => 1,
});
}
}
}
}
}
# Clear off any newline.
$version =~ s/\n//gs;
$anvil_version =~ s/\n//gs;
# Pull the schema version out of the schema body.
foreach my $line (split/\n/, $schema_body)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
if ($line =~ /-- SchemaVersion: (\d+\.\d+\.\d+)/)
{
$schema_version = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { schema_version => $schema_version }});
last;
}
}
return($version);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
anvil_version => $anvil_version,
schema_version => $schema_version,
}});
return($anvil_version, $schema_version);
}
=head2 available_resources
This method looks at the resource used and available on the nodes in a given Anvil! system. The DR's resources are also ready, but don't contribute to the "least available" values.
If either node has no data in the C<< scan_hardware >> table, this method will return with C<< !!no_data!! >>. Callers should abort on this value and remind the user that ScanCore needs to run on both nodes.
Data is store in the following hashes;
anvil_resources::<anvil_uuid>::cpu::cores
anvil_resources::<anvil_uuid>::cpu::threads
anvil_resources::<anvil_uuid>::ram::available
anvil_resources::<anvil_uuid>::ram::allocated
anvil_resources::<anvil_uuid>::ram::hardware
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>::vg_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.
Parameters;
@ -460,8 +544,8 @@ WHERE
if (not $count)
{
# Looks like scan-hardware hasn't run. We can't use this host yet.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0169", variables => { host_name => $host_name }});
next;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0236", variables => { host_name => $host_name }});
return('!!no_data!!');
}
my $scan_hardware_cpu_cores = $results->[0]->[0];
@ -1970,13 +2054,19 @@ sub trusted_hosts
my $host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name};
my $host_type = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type};
my $host_key = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_key};
my $anvil_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{anvil_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:host_uuid' => $host_uuid,
's2:host_name' => $host_name,
's3:host_type' => $host_type,
's4:anvil_name' => $anvil_name,
's4:host_key' => $host_key,
's5:anvil_name' => $anvil_name,
}});
# Skip if the host_key is 'DELETED'.
next if $host_key eq "DELETED";
if ($anvil->Get->host_type eq "striker")
{
# Add all known machines

@ -1020,10 +1020,10 @@ sub _call_ssh_keyscan
}});
# Redirect STDERR to STDOUT and grep off the comments.
my $shell_call = $anvil->data->{path}{exe}{'ssh-keyscan'}." ".$target." 2>&1 | ".$anvil->data->{path}{exe}{'grep'}." -v ^# >> ".$known_hosts;
my $shell_call = $anvil->data->{path}{exe}{'ssh-keyscan'}." -4 -t ecdsa-sha2-nistp256 ".$target." 2>&1 | ".$anvil->data->{path}{exe}{'grep'}." -v ^# >> ".$known_hosts;
if (($port) && ($port ne "22"))
{
$shell_call = $anvil->data->{path}{exe}{'ssh-keyscan'}." -p ".$port." ".$target." 2>&1 | ".$anvil->data->{path}{exe}{'grep'}." -v ^# >> ".$known_hosts;
$shell_call = $anvil->data->{path}{exe}{'ssh-keyscan'}." -4 -t ecdsa-sha2-nistp256 -p ".$port." ".$target." 2>&1 | ".$anvil->data->{path}{exe}{'grep'}." -v ^# >> ".$known_hosts;
}
my $output = $anvil->System->call({debug => $debug, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output }});

@ -2680,7 +2680,7 @@ sub rsync
# Make sure we know the fingerprint of the remote machine
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, key => "log_0158", variables => { target => $target, user => $< }});
$anvil->Remote->add_target_to_known_hosts({
debug => $debug,
debug => 2,
target => $target,
user => $<,
});
@ -2724,7 +2724,7 @@ sub rsync
$failed = 1;
my $source = $path.".ssh\/known_hosts";
my $destination = $path."known_hosts.".$anvil->Get->date_and_time({file_name => 1});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
path => $path,
line_number => $line_number,
failed => $failed,

@ -813,6 +813,9 @@ sub check_ssh_keys
host_key => $host_key,
}});
# If the host_key is 'DELETED', skip if.
next if $host_key eq "DELETED";
# Is this in the file and, if so, has it changed?
my $found = 0;
my $test_line = $host_name." ".$host_key;

@ -3,7 +3,7 @@
# This is the resource agent used to manage servers on the Anvil! Intelligent Availability platform.
#
# License: GNU General Public License (GPL) v2+
# (c) 1997-2020 - Alteeve's Niche! Inc.
# (c) 1997-2021 - Alteeve's Niche! Inc.
#
# 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
@ -83,7 +83,7 @@ use Data::Dumper;
$| = 1;
# The name of this file is just 'server', which isn't helpful, so we manually set it to our RA name.
my $THIS_FILE = "ocf:alteeve:server";
my $THIS_FILE = "ocf:alteeve:server";
my $running_directory = ($0 =~ /^(.*?)\/server/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{

@ -252,7 +252,6 @@ WHERE
new_scan_drbd_flush_md => $new_scan_drbd_flush_md ? "#!string!scan_drbd_unit_0001!#" : "#!string!scan_drbd_unit_0002!#",
new_scan_drbd_timeout => $new_scan_drbd_timeout,
say_scan_drbd_total_sync_speed => $say_scan_drbd_total_sync_speed,
new_scan_drbd_common_xml => $new_scan_drbd_common_xml,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_drbd_message_0001", variables => $variables});
$anvil->Alert->register({alert_level => "warning", message => "scan_drbd_message_0001", variables => $variables, set_by => $THIS_FILE, sort_position => $anvil->data->{'scan-drbd'}{alert_sort}++});

@ -43,11 +43,6 @@ DRBD has been found to be configured on this host.
- Current Resync Speed: [#!variable!say_scan_drbd_total_sync_speed!#]
Note: Disk and metadata flushes should be enabled _unless_ you're using nodes with RAID controllers with flash-backed write cache.
Raw 'drbdadm dump-xml' output:
========
#!variable!new_scan_drbd_common_xml!#
========
</key>
<key name="scan_drbd_message_0002">
The disk flush configuration has changed from: [#!variable!old_value!#] to: [#!variable!new_value!#].

@ -872,8 +872,9 @@ INSERT INTO
my $scan_lvm_pv_uuid = $anvil->data->{sql}{scan_lvm_pvs}{scan_lvm_pv_internal_uuid}{$scan_lvm_pv_internal_uuid}{scan_lvm_pv_uuid};
my $old_scan_lvm_pv_name = $anvil->data->{sql}{scan_lvm_pvs}{scan_lvm_pv_internal_uuid}{$scan_lvm_pv_internal_uuid}{scan_lvm_pv_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
scan_lvm_pv_uuid => $scan_lvm_pv_uuid,
old_scan_lvm_pv_name => $old_scan_lvm_pv_name,
's1:scan_lvm_pv_internal_uuid' => $scan_lvm_pv_internal_uuid,
's2:scan_lvm_pv_uuid' => $scan_lvm_pv_uuid,
's3:old_scan_lvm_pv_name' => $old_scan_lvm_pv_name,
}});
next if $old_scan_lvm_pv_name eq "DELETED";
@ -936,7 +937,7 @@ WHERE
# services tables.
my $scan_lvm_pv_internal_uuid = $row->[1];
my $scan_lvm_pv_name = $row->[2];
$anvil->data->{sql}{scan_lvm_pvs}{scan_lvm_pv_internal_uuid}{$scan_lvm_pv_internal_uuid}{scan_lvm_pv_name} = $row->[0];;
$anvil->data->{sql}{scan_lvm_pvs}{scan_lvm_pv_internal_uuid}{$scan_lvm_pv_internal_uuid}{scan_lvm_pv_uuid} = $row->[0];;
$anvil->data->{sql}{scan_lvm_pvs}{scan_lvm_pv_internal_uuid}{$scan_lvm_pv_internal_uuid}{scan_lvm_pv_name} = $scan_lvm_pv_name;
$anvil->data->{sql}{scan_lvm_pvs}{scan_lvm_pv_internal_uuid}{$scan_lvm_pv_internal_uuid}{scan_lvm_pv_used_by_vg} = $row->[3];
$anvil->data->{sql}{scan_lvm_pvs}{scan_lvm_pv_internal_uuid}{$scan_lvm_pv_internal_uuid}{scan_lvm_pv_attributes} = $row->[4];

@ -1,5 +1,10 @@
-- This is the core database schema for the Anvil! Intelligent Availability platform.
--
-- The line below is used by machines in the Anvil! to know if their software version is compatible with the
-- database servers. As such, do NOT edit the line below unless you know why you're changing it.
-- - Version follows: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-- SchemaVersion: 0.0.0
--
-- It expects PostgreSQL v. 9.1+
--
-- Table construction rules;

@ -16,7 +16,7 @@ Author: Madison Kelly <mkelly@alteeve.ca>
<key name="brand_0002">Anvil!</key>
<key name="brand_0003">Striker</key>
<key name="brand_0004">ScanCore</key>
<key name="brand_0005"><![CDATA[&copy; 1997 - 2020 <a href="https://alteeve.com/" target="_new">Alteeve's Niche! Inc.</a>, Toronto, Ontario, Canada]]></key>
<key name="brand_0005"><![CDATA[&copy; 1997 - 2021 <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_0007">Node</key>
<key name="brand_0008">DR Host</key>
@ -303,7 +303,7 @@ Output (if any):
<key name="error_0218">Unable to connect to any databases, unable to continue.</key>
<key name="error_0219">Unable to find the server uuid to delete from the job UUID: [#!variable!job_uuid!#].</key>
<key name="error_0220">Unable to find a server name to match the server UUID: [#!variable!server_uuid!#].</key>
<key name="error_0221">#!free!#</key>
<key name="error_0221">This tool is only designed to migrate servers between nodes, and this is a DR host.</key>
<key name="error_0222">The cluster does not appear to be running, unable to delete a server at this time. We'll sleep for a bit and then exit, and the try again.</key>
<key name="error_0223">The server: [#!variable!server_name!#] appears to have failed to stop.</key>
<key name="error_0224">Unable to delete the server resource: [#!variable!server_name!#] as the cluster isn't running or there was a problem parsing the cluster CIB.</key>
@ -313,6 +313,12 @@ Output (if any):
<key name="error_0228">Unable to delete the resource: [#!variable!resource!#] because it wasn't found in DRBD's config.</key>
<key name="error_0229">One or more peers need us, and we're not allowed to wait. Deletion aborted.</key>
<key name="error_0230">The shell call: [#!variable!shell_call!#] was expected to return '0', but instead the return code: [#!variable!return_code!#] was received. The output, if any, was: [#!variable!output!#].</key>
<key name="error_0231">This host is not an Anvil! node or DR host, unable to migrate servers.</key>
<key name="error_0232">Unable to find the server to migrate in the job UUID: [#!variable!job_uuid!#].</key>
<key name="error_0233">The cluster does not appear to be running, unable to migrate servers at this time. We'll sleep for a bit and then exit, and the try again.</key>
<key name="error_0234">Unable to find the target host to migrate to the job UUID: [#!variable!job_uuid!#].</key>
<key name="error_0235">The migration target host: [#!variable!target_host_uuid!#] is either invalid, or doesn't match one of the nodes in this Anvil! system.</key>
<key name="error_0236">There appears to be no resource data in the database for the host: [#!variable!host_name!#]. Has ScanCore run and, specifically, has 'scan-hardware' run yet? Unable to provide available resources for this Anvil! system.</key>
<!-- Files templates -->
<!-- NOTE: Translating these files requires an understanding of which likes are translatable -->
@ -647,6 +653,7 @@ It should be provisioned in the next minute or two.</key>
<key name="job_0214">Storage has been released. Checking that the server has flagged as deleted in the database.</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_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>
<!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key>
@ -1644,6 +1651,8 @@ Are you sure that you want to delete the server: [#!variable!server_name!#]? [Ty
<key name="message_0214">The server is running on the host: [#!variable!host_name!#], assigning the job to it.</key>
<key name="message_0215">The server is not running anywhere, assigning the job to this host.</key>
<key name="message_0216">The server is running here, assigning the job to this host.</key>
<key name="message_0217">Preparing to delete a server.</key>
<key name="message_0218">Preparing to migrate a server (or all servers).</key>
<!-- Success messages shown to the user -->
<key name="ok_0001">Saved the mail server information successfully!</key>
@ -2223,6 +2232,9 @@ Read UUID: .... [#!variable!read_uuid!#]
<key name="warning_0072">[ Warning ] - The file: [#!variable!file_path!#] was not found on any accessible Striker dashboard (or it isn't the same size as recorded in the database). Will sleep for a minute and exit, then we'll try again.</key>
<key name="warning_0073">[ Warning ] - No databases are available. Some functions of this resource agent will not be available.</key>
<key name="warning_0074">[ Warning ] - Our disk state for the peer: [#!variable!peer_name!#] on resource: [#!variable!resource!#], volume: [#!variable!volume!#] is: [#!variable!disk_state!#].</key>
<key name="warning_0075">[ Warning ] - We were asked to insert or update a host with the name: [#!variable!host_name!#]. Another host: [#!variable!host_uuid!#] has the same name, which could be a failed node that is being replaced. We're going to set it's 'host_key' to 'DELETED'. If this warning is logged only once, and after a machine is replaced, it's safe to ignore. If this warning is repeatedly being logged, then there are two active machines with the same host name, and that needs to be fixed.</key>
<key name="warning_0076">[ Warning ] - It looks like the postfix daemon is not running. Enabling and starting it now.</key>
<key name="warning_0077">[ Warning ] - Checking the mail queue after attempting to start postgres appears to have still failed. Output received was: [#!variable!output!#].</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. -->

@ -75,7 +75,7 @@ if ($anvil->data->{switches}{'job-uuid'})
progress => 1,
job_picked_up_by => $$,
job_picked_up_at => time,
message => "message_0207",
message => "message_0217",
});
# Are we in an Anvil! system?
@ -264,7 +264,7 @@ sub remove_from_pacemaker
}
# Register the job with the peers.
my $anvil_uuid = $anvil->Cluster->get_anvil_uuid();
my $anvil_uuid = $anvil->data->{sys}{anvil_uuid};
my $peers = [];
if ($anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid} eq $anvil->Get->host_uuid)
{

@ -0,0 +1,240 @@
#!/usr/bin/perl
#
# This migrates servers from one node to another. It can operate on a single server, or it can be used to
# migrate all servers to a given host.
#
# Exit codes;
# 0 = Normal exit.
# 1 = Any problem that causes an early exit.
#
use strict;
use warnings;
use Anvil::Tools;
require POSIX;
use Term::Cap;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
my $anvil = Anvil::Tools->new();
# 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.
$anvil->data->{switches}{'job-uuid'} = "";
$anvil->Get->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'switches::job-uuid' => $anvil->data->{switches}{'job-uuid'} }});
$anvil->Database->connect();
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"});
if (not $anvil->data->{sys}{database}{connections})
{
# No databases, sleep for a bit and then exit. The daemon will retry after we exit.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0218"});
sleep 10;
$anvil->nice_exit({exit_code => 1});
}
$anvil->data->{sys}{anvil_uuid} = $anvil->Cluster->get_anvil_uuid({debug => 2});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'sys::anvil_uuid' => $anvil->data->{sys}{anvil_uuid} }});
# If we don't have a job UUID, try to find one.
if (not $anvil->data->{switches}{'job-uuid'})
{
# Load the job data.
$anvil->data->{switches}{'job-uuid'} = $anvil->Job->get_job_uuid({program => $THIS_FILE});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::job-uuid" => $anvil->data->{switches}{'job-uuid'} }});
}
# If we still don't have a job-uuit, go into interactive mode.
if ($anvil->data->{switches}{'job-uuid'})
{
# Load the job data.
$anvil->Job->clear();
$anvil->Job->get_job_details();
$anvil->Job->update_progress({
progress => 1,
job_picked_up_by => $$,
job_picked_up_at => time,
message => "message_0218",
});
# Are we in an Anvil! system?
if (not $anvil->data->{sys}{anvil_uuid})
{
# We're not in an Anvil!.
if ($anvil->data->{switches}{'job-uuid'})
{
# Mark the job as failed.
$anvil->Job->update_progress({
progress => 100,
message => "error_0231",
job_status => "failed",
});
}
# Log an exit.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => 'err', key => "error_0231"});
$anvil->nice_exit({exit_code => 1});
}
# Job data will be in $anvil->data->{jobs}{job_data}
run_jobs($anvil);
}
else
{
if (not $anvil->data->{sys}{anvil_uuid})
{
# We can't do anything, exit.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => 'err', key => "error_0231"});
$anvil->nice_exit({exit_code => 1});
}
# Interactive!
ask_for_server($anvil);
}
$anvil->nice_exit({exit_code => 0});
#############################################################################################################
# Functions #
#############################################################################################################
# This actually provisions a VM.
sub run_jobs
{
my ($anvil) = @_;
# This parses the jobs::job_data intp variables.
parse_job_data($anvil);
return(0);
}
# This parses and verifies the job data
sub parse_job_data
{
my ($anvil) = @_;
$anvil->data->{job}{server_uuid} = "";
$anvil->data->{job}{peer_mode} = 0;
foreach my $line (split/\n/, $anvil->data->{jobs}{job_data})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
if ($line =~ /server_uuid=(.*)$/)
{
$anvil->data->{job}{server_uuid} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::server_uuid' => $anvil->data->{job}{server_uuid} }});
}
if ($line =~ /target_host_uuid=(.*)$/)
{
$anvil->data->{job}{target_host_uuid} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::target_host_uuid' => $anvil->data->{job}{target_host_uuid} }});
}
}
# Did we get a server UUID?
if (not $anvil->data->{job}{server_uuid})
{
$anvil->Job->update_progress({
progress => 100,
message => "error_0232,!!job_uuid!".$anvil->data->{switches}{'job-uuid'}."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0232", variables => { job_uuid => $anvil->data->{switches}{'job-uuid'} }});
$anvil->nice_exit({exit_code => 1});
}
# Does the server UUID match to a server?
$anvil->Database->get_servers();
my $server_uuid = $anvil->data->{job}{server_uuid};
if (($server_uuid ne "all") && (not exists $anvil->data->{servers}{server_uuid}{$server_uuid}))
{
# Server UUID is invalid
$anvil->Job->update_progress({
progress => 100,
message => "error_0220,!!server_uuid!".$server_uuid."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0220", variables => { server_uuid => $server_uuid }});
$anvil->nice_exit({exit_code => 1});
}
# Is the target_host_uuid a host in our Anvil! system?
if (not $anvil->data->{job}{target_host_uuid})
{
$anvil->Job->update_progress({
progress => 100,
message => "error_0234,!!job_uuid!".$anvil->data->{switches}{'job-uuid'}."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0234", variables => { job_uuid => $anvil->data->{switches}{'job-uuid'} }});
$anvil->nice_exit({exit_code => 1});
}
else
{
my $anvil_uuid = $anvil->data->{sys}{anvil_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};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
anvil_uuid => $anvil_uuid,
node1_host_uuid => $node1_host_uuid,
node2_host_uuid => $node2_host_uuid,
}});
if (($anvil->data->{job}{target_host_uuid} ne $node1_host_uuid) &&
($anvil->data->{job}{target_host_uuid} ne $node2_host_uuid))
{
$anvil->Job->update_progress({
progress => 100,
message => "error_0235,!!target_host_uuid!".$anvil->data->{job}{target_host_uuid}."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0235", variables => { target_host_uuid => $anvil->data->{job}{target_host_uuid} }});
$anvil->nice_exit({exit_code => 1});
}
}
my $host_type = $anvil->Get->host_type();
if ($host_type eq "node")
{
my $problem = $anvil->Cluster->parse_cib({debug => 2});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
if ($problem)
{
# The cluster isn't running, sleep and exit.
$anvil->Job->update_progress({
progress => 0,
message => "error_0233",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0233"});
sleep 10;
$anvil->nice_exit({exit_code => 1});
}
}
else
{
# This is not the tool to move to DR.
$anvil->Job->update_progress({
progress => 100,
message => "error_0221",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0221"});
$anvil->nice_exit({exit_code => 1});
}
return(0);
}

@ -1491,11 +1491,16 @@ sub interactive_ask_anvil_name
anvil_name => $anvil->data->{new_server}{anvil_name},
anvil_uuid => $anvil->data->{new_server}{anvil_uuid},
}})."\n";
$anvil->Get->available_resources({
my $problem = $anvil->Get->available_resources({
debug => 2,
anvil_uuid => $anvil->data->{new_server}{anvil_uuid},
});
if ($problem eq "!!no_data!!")
{
# We can't proceed.
print $anvil->Words->string({key => "job_0217"})."\n";
$anvil->nice_exit({exit_code => 1});
}
last;
}
else

@ -26,7 +26,6 @@ $anvil->Get->switches;
$anvil->Database->connect({debug => 3});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0132"});
#$anvil->DRBD->delete_resource({debug => 2, resource => "srv01-test"});
$anvil->Cluster->assemble_storage_groups({debug => 2, anvil_uuid => '1aded871-fcb1-4473-9b97-6e9c246fc568'});
$anvil->nice_exit({exit_code => 0});

Loading…
Cancel
Save