diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm index d35625ac..b8f17509 100644 --- a/Anvil/Tools.pm +++ b/Anvil/Tools.pm @@ -47,6 +47,7 @@ use Anvil::Tools::Get; use Anvil::Tools::Job; use Anvil::Tools::Log; use Anvil::Tools::Remote; +use Anvil::Tools::Server; use Anvil::Tools::Storage; use Anvil::Tools::System; use Anvil::Tools::Template; @@ -125,6 +126,7 @@ sub new LOG => Anvil::Tools::Log->new(), JOB => Anvil::Tools::Job->new(), REMOTE => Anvil::Tools::Remote->new(), + SERVER => Anvil::Tools::Server->new(), STORAGE => Anvil::Tools::Storage->new(), SYSTEM => Anvil::Tools::System->new(), TEMPLATE => Anvil::Tools::Template->new(), @@ -163,6 +165,7 @@ sub new $anvil->Log->parent($anvil); $anvil->Job->parent($anvil); $anvil->Remote->parent($anvil); + $anvil->Server->parent($anvil); $anvil->Storage->parent($anvil); $anvil->System->parent($anvil); $anvil->Template->parent($anvil); @@ -520,6 +523,18 @@ sub Remote return ($self->{HANDLE}{REMOTE}); } +=head2 Server + +Access the C methods via 'C<< $anvil->Server->method >>'. + +=cut +sub Server +{ + my $self = shift; + + return ($self->{HANDLE}{SERVER}); +} + =head2 Storage Access the C methods via 'C<< $anvil->Storage->method >>'. diff --git a/Anvil/Tools/DRBD.pm b/Anvil/Tools/DRBD.pm index 272189e5..4452ebb0 100755 --- a/Anvil/Tools/DRBD.pm +++ b/Anvil/Tools/DRBD.pm @@ -83,7 +83,7 @@ This parses the DRBD status on the local or remote system. The data collected is - drbd::status::::resource::::connection::::volume::::{db-dt MiB-s,db0-dt0 MiB-s,db1-dt1 MiB-s,estimated-seconds-to-finish,percent-resync-done,rs-db0-sectors,rs-db1-sectors,rs-dt-start-ms,rs-dt0-ms,rs-dt1-ms,rs-failed,rs-paused-ms,rs-same-csum,rs-total,want} - drbd::status::::resource::::devices::volume::::{al-writes,bm-writes,client,disk-state,lower-pending,minor,quorum,read,size,upper-pending,written} -If any data was stored in a previous call, it will be deleted before the new data is collected and stored. +If any data for the host was stored in a previous call, it will be deleted before the new data is collected and stored. Parameters; @@ -126,11 +126,18 @@ sub get_status # Is this a local call or a remote call? my $shell_call = $anvil->data->{path}{exe}{drbdsetup}." status --json"; my $output = ""; - my $host = $anvil->_short_hostname; + my $host = $anvil->_short_hostname({debug => $debug}); if (($target) && ($target ne "local") && ($target ne $anvil->_hostname) && ($target ne $anvil->_short_hostname)) { + # Clear the hash where we'll store the data. + $host = $target; + if (exists $anvil->data->{drbd}{status}{$host}) + { + delete $anvil->data->{drbd}{status}{$host}; + } + # Remote call. - ($output, my $error, my $return_code) = $anvil->Remote->call({ + ($output, my $error, $anvil->data->{drbd}{status}{$host}{return_code}) = $anvil->Remote->call({ debug => $debug, shell_call => $shell_call, target => $target, @@ -139,25 +146,24 @@ sub get_status remote_user => $remote_user, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - error => $error, - output => $output, - return_code => $return_code, + error => $error, + output => $output, + "drbd::status::${host}::return_code" => $anvil->data->{drbd}{status}{return_code}, }}); - $host = $target; } else { + # Clear the hash where we'll store the data. + if (exists $anvil->data->{drbd}{status}{$host}) + { + delete $anvil->data->{drbd}{status}{$host}; + } + # Local. - ($output, my $return_code) = $anvil->System->call({shell_call => $shell_call}); + ($output, $anvil->data->{drbd}{status}{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 }}); } - # Clear the hash where we'll store the data. - if (exists $anvil->data->{drbd}{status}{$host}) - { - delete $anvil->data->{drbd}{status}{$host}; - } - # Parse the output. my $json = JSON->new->allow_nonref; my $drbd_status = $json->decode($output); diff --git a/Anvil/Tools/Server.pm b/Anvil/Tools/Server.pm new file mode 100755 index 00000000..b9a5a09c --- /dev/null +++ b/Anvil/Tools/Server.pm @@ -0,0 +1,198 @@ +package Anvil::Tools::Server; +# +# This module contains methods used to manager servers +# + +use strict; +use warnings; +use Scalar::Util qw(weaken isweak); +use Data::Dumper; + +our $VERSION = "3.0.0"; +my $THIS_FILE = "Server.pm"; + +### Methods; +# status + +=pod + +=encoding utf8 + +=head1 NAME + +Anvil::Tools::Server + +Provides all methods related to (virtual) servers. + +=head1 SYNOPSIS + + use Anvil::Tools; + + # Get a common object handle on all Anvil::Tools modules. + my $anvil = Anvil::Tools->new(); + + # Access to methods using '$anvil->Server->X'. + # + # + +=head1 METHODS + +Methods in this module; + +=cut +sub new +{ + my $class = shift; + my $self = { + }; + + bless $self, $class; + + return ($self); +} + +# Get a handle on the Anvil::Tools object. I know that technically that is a sibling module, but it makes more +# sense in this case to think of it as a parent. +sub parent +{ + my $self = shift; + my $parent = shift; + + $self->{HANDLE}{TOOLS} = $parent if $parent; + + # Defend against memory leads. See Scalar::Util'. + if (not isweak($self->{HANDLE}{TOOLS})) + { + weaken($self->{HANDLE}{TOOLS});; + } + + return ($self->{HANDLE}{TOOLS}); +} + +############################################################################################################# +# Public methods # +############################################################################################################# + +=head2 get_status + +This reads in a server's XML definition file from disk, if available, and from memory, if the server is running. The XML is analyzed and data is stored in the following locations; + + - + +Any pre-existing data on the server is flushed before the new information is processed. + +Parameters; + +=head3 password (optional) + +This is the password to use when connecting to a remote machine. If not set, but C<< target >> is, an attempt to connect without a password will be made. + +=head3 port (optional) + +This is the TCP port to use when connecting to a remote machine. If not set, but C<< target >> is, C<< 22 >> will be used. + +=head3 remote_user (optional, default 'root') + +If C<< target >> is set, this will be the user we connect to the remote machine as. + +=head3 server (required) + +This is the name of the server we're gathering data on. + +=head3 target (optional) + +This is the IP or host name of the machine to read the version of. If this is not set, the local system's version is checked. + +=cut +# NOTE: the version is set in anvil.spec by sed'ing the release and arch onto anvil.version in anvil-core's %post +sub get_status +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + + my $password = defined $parameter->{password} ? $parameter->{password} : ""; + my $port = defined $parameter->{port} ? $parameter->{port} : ""; + my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root"; + my $server = defined $parameter->{server} ? $parameter->{server} : ""; + my $target = defined $parameter->{target} ? $parameter->{target} : "local"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + password => $anvil->Log->secure ? $password : $anvil->Words->string({key => "log_0186"}), + port => $port, + remote_user => $remote_user, + target => $target, + }}); + + if (not $server) + { + return(1); + } + if (exists $anvil->data->{server}{$server}) + { + delete $anvil->data->{server}{$server}; + } + + # Is this a local call or a remote call? + my $shell_call = $anvil->data->{path}{exe}{virsh}." dumpxml ".$server; + if (($target) && ($target ne "local") && ($target ne $anvil->_hostname) && ($target ne $anvil->_short_hostname)) + { + # Remote call. + $host = $target; + + ($anvil->data->{server}{$server}{running}{xml}, my $error, $anvil->data->{server}{$server}{running}{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, + "server::${server}::running::xml" => $anvil->data->{server}{$server}{running}{xml}, + "server::${server}::running::return_code" => $anvil->data->{server}{$server}{running}{return_code}, + }}); + } + else + { + # Local. + ($anvil->data->{server}{$server}{running}{xml}, $anvil->data->{server}{$server}{running}{return_code}) = $anvil->System->call({shell_call => $shell_call}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "server::${server}::running::xml" => $anvil->data->{server}{$server}{running}{xml}, + "server::${server}::running::return_code" => $anvil->data->{server}{$server}{running}{return_code}, + }}); + } + + # If the return code was non-zero, we can't parse the XML. + if ($anvil->data->{server}{$server}{running}{return_code}) + { + $anvil->data->{server}{$server}{running}{xml} = ""; + } + + # Now get the on-disk XML. + ($anvil->data->{server}{$server}{disk}{xml}) = $anvil->Storage->read_file({ + debug => $debug, + password => $password, + port => $port, + remote_user => $remote_user, + target => $target, + force_read => 1, + file => $anvil->data->{path}{directories}{shared}{definitions}."/".$server.".xml", + }) + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "server::${server}::disk::xml" => $anvil->data->{server}{$server}{disk}{xml}, + }}); + + return(0); +} + +# =head3 +# +# Private Functions; +# +# =cut + +############################################################################################################# +# Private functions # +############################################################################################################# diff --git a/ocf/alteeve/server b/ocf/alteeve/server index a2bcfa24..97b5c5c0 100755 --- a/ocf/alteeve/server +++ b/ocf/alteeve/server @@ -98,9 +98,6 @@ my $anvil = Anvil::Tools->new(); $anvil->Log->level({set => 2}); $anvil->Log->secure({set => 1}); -$anvil->DRBD->get_status({debug => 2}); -die; - ### Read or Set the environment variables # This is the name of the server we're managing. # Example values: $anvil->data->{environment}{OCF_RESKEY_name} = defined $ENV{OCF_RESKEY_name} ? $ENV{OCF_RESKEY_name} : ""; # srv01-c7 @@ -142,7 +139,6 @@ if ((not $anvil->data->{switches}{metadaata}) and (not $anvil->data->{switches}{ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, key => "log_0298"}); } - ### TEST: to be removed later if ($anvil->data->{switches}{test1}) { @@ -317,110 +313,6 @@ sub start_server my $server = $anvil->data->{environment}{OCF_RESKEY_name}; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0303", variables => { server => $server }}); - # If the server is already here, we'll do nothing else. - my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." list"}); - if ($return_code) - { - # If this fails, we want to exit with OCF_ERR_CONFIGURED (6) so that pacemaker doesn't try to - # also start the server on another node, because we don't know the state of it here. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "log_0304", variables => { return_code => $return_code, output => $output }}); - } - foreach my $line (split/\n/, $output) - { - $line =~ s/^\s+//; - $line =~ s/\s+$//; - $line =~ s/\s+/ /g; - - if ($line =~ /^(\d+) $server (.*)$/) - { - my $state = $2; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - server => $server, - 'state' => $state, - }}); - - # Make sure the server is shut down, if it is listed at all. Any other state is - # unexpected and needs to be sorted by a human. - if ($state ne "shut down") - { - # Abort - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0306", variables => { server => $server, 'state' => $state }}); - $anvil->nice_exit({exit_code => 0}); - } - last; - } - } - - # We need to boot, validate everything. - validate_all($anvil); - - # If we're still alive, we're ready to boot. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0305", variables => { server => $server }}); - - my $definition_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server.".xml"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { definition_file => $definition_file }}); - - $return_code = undef; - $output = undef; - ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." create $definition_file"}); - if ($return_code) - { - # If this fails, we want to exit with OCF_ERR_CONFIGURED (6) so that pacemaker doesn't try to - # also start the server on another node, because we don't know the state of it here. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "log_0307", variables => { - server => $server, - return_code => $return_code, - output => $output, - }}); - $anvil->nice_exit({exit_code => 6}); - } - - # Verify that it started. - sleep 2; - $return_code = undef; - $output = undef; - ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." list"}); - if ($return_code) - { - # If this fails, we want to exit with OCF_ERR_CONFIGURED (6) so that pacemaker doesn't try to - # also start the server on another node, because we don't know the state of it here. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "log_0308", variables => { - server => $server, - return_code => $return_code, - output => $output, - }}); - $anvil->nice_exit({exit_code => 6}); - } - foreach my $line (split/\n/, $output) - { - $line =~ s/^\s+//; - $line =~ s/\s+$//; - $line =~ s/\s+/ /g; - - if ($line =~ /^(\d+) $server (.*)$/) - { - my $state = $2; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - server => $server, - 'state' => $state, - }}); - - if ($state eq "running") - { - # Success! - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0309", variables => { server => $server }}); - $anvil->nice_exit({exit_code => 0}); - } - else - { - # WTF? - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "log_0310", variables => { server => $server, 'state' => $state }}); - $anvil->nice_exit({exit_code => 6}); - } - - last; - } - } # If we're still alive, then we didn't see the server in the list of running servers, which is really weird. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "log_0311", variables => { server => $server }}); @@ -1451,22 +1343,18 @@ sub validate_storage_drbd } # Now read in the status of the drbd devices - $return_code = undef; - (my $status_json, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{drbdsetup}." status --json"}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { - status_json => $status_json, - return_code => $return_code, - json_length => length($status_json), - }}); - if ($return_code) + my $host = $anvil->_short_hostname; + $anvil->DRBD->get_status({debug => 2}); + if ($anvil->data->{drbd}{status}{$host}{return_code}) { # Something went wrong. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "log_0377", variables => { - return_code => $return_code, + return_code => $anvil->data->{drbd}{status}{$host}{return_code}, status_json => $status_json, }}); $anvil->nice_exit({exit_code => 1}); } + die; # If DRBD is not up, the returned JSON output will not actually exist. if (($status_json =~ /No currently configured DRBD found/si) or (not check_drbd_status($anvil, $status_json))) @@ -1535,88 +1423,6 @@ sub validate_storage_drbd $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { status_json => $status_json }}); check_drbd_status($anvil, $status_json); - ### NOTE: The checks below might no longer be needed. -=cut - # Make sure I saw all disks. - my $check_again = 0; - foreach my $device_path (sort {$a cmp $b} keys %{$anvil->data->{server}{disks}}) - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - device_path => $device_path, - "server::disks::${device_path}" => $anvil->data->{server}{disks}{$device_path}, - }}); - - if ($anvil->data->{server}{disks}{$device_path} eq "check") - { - # Failed to see it, see if we can bring it up. - $check_again = 1; - my $resource = $anvil->data->{device_path}{$device_path}{resource}; - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0384", variables => { - resource => $resource, - device_path => $device_path, - }}); - - #manage_drbd_resource($anvil, "up", $resource); - (my $drbdadm_output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{drbdadm}." up $resource"}); - if ($return_code) - { - # Something went wrong. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "log_0380", variables => { - resource => $resource, - return_code => $return_code, - drbdadm_output => $drbdadm_output, - }}); - $anvil->nice_exit({exit_code => 1}); - } - } - - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { check_again => $check_again }}); - if ($check_again) - { - # Give the resource a few seconds to start. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0381"}); - sleep 3; - - # Check again. - $return_code = undef; - $status_json = undef; - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0385"}); - ($status_json, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{drbdsetup}." status --json"}); - if ($return_code) - { - # Something went wrong. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "log_0382", variables => { - return_code => $return_code, - status_json => $status_json, - }}); - $anvil->nice_exit({exit_code => 1}); - } - - # Check again. - check_drbd_status($anvil, $status_json); - } - } - - # Do I need to check again? - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {check_again => $check_again }}); - if ($check_again) - { - foreach my $device_path (sort {$a cmp $b} keys %{$anvil->data->{server}{disks}}) - { - if ($anvil->data->{server}{disks}{$device_path} eq "check") - { - # Failed. - my $resource = $anvil->data->{device_path}{$device_path}{resource}; - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "log_0386", variables => { - resource => $resource, - device_path => $device_path, - }}); - $anvil->nice_exit({exit_code => 1}); - } - } - } -=cut - ### TODO: Finish this, whatever this was going to be... # If I am about to push a server off, we need to make sure the peer is UpToDate if ($anvil->data->{switches}{migrate_to})