Local modifications to ClusterLabs/Anvil by Alteeve
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

371 lines
14 KiB

#!/usr/bin/perl
use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
use Text::Diff;
use Term::Cap;
use Time::Local;
$| = 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({debug => 2, list => ["watch"]});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}});
$anvil->Database->connect();
$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 });
}
our $t = Term::Cap->Tgetent;
# One shot or continuous?
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'switches::watch' => $anvil->data->{switches}{watch},
}});
if ($anvil->data->{switches}{watch})
{
# Disconnect before we go into the loop
$anvil->Database->disconnect();
# Do we have an interval?
my $interval = 2;
if ($anvil->data->{switches}{watch} =~ /^\d+$/)
{
$interval = $anvil->data->{switches}{watch};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { interval => $interval }});
# Loop until terminated.
while(1)
{
$anvil->refresh();
$anvil->Database->connect();
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0132"});
if ($anvil->data->{sys}{database}{connections})
{
show_status($anvil);
$anvil->Database->disconnect();
}
else
{
# No databases available.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "log_0738"});
}
sleep $interval;
}
}
else
{
# Once and exit.
$anvil->Database->connect();
show_status($anvil);
}
$anvil->nice_exit({exit_code => 0});
#############################################################################################################
# Functions #
#############################################################################################################
sub show_status
{
my ($anvil) = @_;
if ($anvil->data->{switches}{watch})
{
system('clear');
print $t->Tgoto("cm", 0, 0);
}
if ($anvil->data->{switches}{watch})
{
my $date = $anvil->Get->date_and_time();
print "-=] Updated: ".$date." - Press '<ctrl> + <c>' to exit\n";
}
### TODO: Add support for checking/monitoring DR hosts
# Get the node states
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")
{
print "This must be run on a subnode. Exiting.\n";
$anvil->nice_exit({exit_code => 1});
}
### TODO: Make this work outside the cluster, for cases when servers are running outside the
### pacemaker cluster stack.
# Are we a cluster member?
my $problem = $anvil->Cluster->parse_cib();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
if ($problem)
{
print "This subnode is not in the cluster (failed to parse the CIB). Exiting.\n";
$anvil->nice_exit({exit_code => 1});
}
# Load host information so that we can check for IPMI configs, if needed.
$anvil->Database->get_hosts();
$anvil->Database->get_anvils();
show_servers($anvil);
print "\n";
show_nodes($anvil);
return(0);
}
sub show_servers
{
my ($anvil) = @_;
# Show the server states
return(0);
}
sub show_nodes
{
my ($anvil) = @_;
# Headers
$anvil->data->{'say'}{subnode} = $anvil->Words->string({key => "header_0117"});
$anvil->data->{'say'}{host_status} = $anvil->Words->string({key => "header_0118"});
$anvil->data->{'say'}{pacemaker_status} = $anvil->Words->string({key => "header_0119"});
$anvil->data->{'say'}{maintenance_mode} = $anvil->Words->string({key => "header_0120"});
my $longest_node_name = length($anvil->data->{'say'}{subnode});
my $longest_host_status = length($anvil->data->{'say'}{host_status});
my $longest_pacemaker_status = length($anvil->data->{'say'}{pacemaker_status});
my $longest_maintenance_mode = length($anvil->data->{'say'}{maintenance_mode});
### Strings
# host states
$anvil->data->{'say'}{unknown} = $anvil->Words->string({key => "unit_0004"});
$anvil->data->{'say'}{online} = $anvil->Words->string({key => "striker_0308"});
$anvil->data->{'say'}{powered_off} = $anvil->Words->string({key => "striker_0307"});
$anvil->data->{'say'}{stopping} = $anvil->Words->string({key => "striker_0309"});
$anvil->data->{'say'}{booting} = $anvil->Words->string({key => "striker_0310"});
# Cluster states (online from above)
$anvil->data->{'say'}{offline} = $anvil->Words->string({key => "striker_0311"});
$anvil->data->{'say'}{transitioning} = $anvil->Words->string({key => "striker_0312"});
# Maintenance mode.
$anvil->data->{'say'}{maintenance_mode} = $anvil->Words->string({key => "striker_0313"});
$anvil->data->{'say'}{normal_operation} = $anvil->Words->string({key => "striker_0314"});
# Get the length of the node strings.
foreach my $node_name (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{data}{node}})
{
my $host_uuid = $anvil->Database->get_host_uuid_from_string({string => $node_name});
my $host_status = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_status};
my $maintenance_mode = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{'maintenance-mode'};
my $in_ccm = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{in_ccm};
my $crmd = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{crmd};
my $join = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{'join'};
my $ready = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{ready};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:node_name' => $node_name,
's2:host_uuid' => $host_uuid,
's3:host_status' => $host_status,
's4:maintenance_mode' => $maintenance_mode,
's5:in_ccm' => $in_ccm,
's6:crmd' => $crmd,
's7:join' => $join,
's8:ready' => $ready,
}});
# Convert the host state to a string.
my $say_host_status = $anvil->data->{'say'}{unknown};
if ($host_status eq "online")
{
$say_host_status = $anvil->data->{'say'}{online};
}
elsif ($host_status eq "powered off")
{
$say_host_status = $anvil->data->{'say'}{powered_off};
}
elsif ($host_status eq "stopping")
{
$say_host_status = $anvil->data->{'say'}{stopping};
}
elsif ($host_status eq "booting")
{
$say_host_status = $anvil->data->{'say'}{booting};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_host_status => $say_host_status }});
# Convert the Pacemaker state.
my $say_pacemaker_status = $anvil->data->{'say'}{unknown};
if ($ready)
{
$say_pacemaker_status = $anvil->data->{'say'}{online};
}
elsif (($in_ccm) or ($crmd) or ($join))
{
# Transitioning
$say_pacemaker_status = $anvil->data->{'say'}{transitioning};
}
else
{
$say_pacemaker_status = $anvil->data->{'say'}{offline};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_pacemaker_status => $say_pacemaker_status }});
# Maintenance mode
my $anvil_maintenance_mode = $anvil->System->maintenance_mode({host_uuid => $host_uuid});
my $say_maintenance_mode = (($maintenance_mode) or ($anvil_maintenance_mode)) ? $anvil->data->{'say'}{maintenance_mode} : $anvil->data->{'say'}{normal_operation};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
anvil_maintenance_mode => $anvil_maintenance_mode,
say_maintenance_mode => $say_maintenance_mode,
}});
# Update the lengths, if needed
if (length($node_name) > $longest_node_name)
{
$longest_node_name = length($node_name);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { longest_node_name => $longest_node_name }});
}
if (length($say_host_status) > $longest_host_status)
{
$longest_host_status = length($say_host_status);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { longest_host_status => $longest_host_status }});
}
if (length($say_pacemaker_status) > $longest_pacemaker_status)
{
$longest_pacemaker_status = length($say_pacemaker_status);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { longest_pacemaker_status => $longest_pacemaker_status }});
}
if (length($say_maintenance_mode) > $longest_maintenance_mode)
{
$longest_maintenance_mode = length($say_maintenance_mode);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { longest_maintenance_mode => $longest_maintenance_mode }});
}
}
=cut
Subnode Status:
+---------+---------------+--------------------+-------------------+
| Subnode | Host Status | Pacemaker Status | Maintenance Mode |
+---------+---------------+--------------------+-------------------+
| <node1> | <host status> | <Pacemaker status> | <mainteance mode> |
| <node2> | <Host status> | <Pacemaker status> | <mainteance mode> |
+---------+---------------+--------------------+-------------------+
Servers:
+-------------+----------------+------------------+-------------+-----------------+--------------+
| server name | <server state> | <resource state> | <host node> | <prefered host> | <dbrd fence> |
+-------------+----------------+------------------+-------------+-----------------+--------------+
=cut
# Now look again to show the subnode states
my $subnode_divider = ""; for (1..$longest_node_name) { $subnode_divider .= "-"; }
my $host_status_divider = ""; for (1..$longest_host_status) { $host_status_divider .= "-"; }
my $pacemaker_status_divider = ""; for (1..$longest_pacemaker_status) { $pacemaker_status_divider .= "-"; }
my $maintenance_mode_divider = ""; for (1..$longest_maintenance_mode) { $maintenance_mode_divider .= "-"; }
my $say_subnode_header = $anvil->Words->center_text({string => $anvil->data->{'say'}{subnode}, width => $longest_node_name});
my $say_host_status_header = $anvil->Words->center_text({string => $anvil->data->{'say'}{host_status}, width => $longest_host_status});
my $say_pacemaker_status_header = $anvil->Words->center_text({string => $anvil->data->{'say'}{pacemaker_status}, width => $longest_pacemaker_status});
my $say_maintenance_mode_header = $anvil->Words->center_text({string => $anvil->data->{'say'}{maintenance_mode}, width => $longest_maintenance_mode});
my $divider_line = "+-".$subnode_divider."-+-".$host_status_divider."-+-".$pacemaker_status_divider."-+-".$maintenance_mode_divider."-+\n";
print $divider_line;
print "| ".$say_subnode_header." | ".$say_host_status_header." | ".$say_pacemaker_status_header." | ".$say_maintenance_mode_header." |\n";
print $divider_line;
foreach my $node_name (sort {$a cmp $b} keys %{$anvil->data->{cib}{parsed}{data}{node}})
{
my $host_uuid = $anvil->Database->get_host_uuid_from_string({string => $node_name});
my $host_status = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_status};
my $maintenance_mode = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{'maintenance-mode'};
my $in_ccm = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{in_ccm};
my $crmd = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{crmd};
my $join = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{'join'};
my $ready = $anvil->data->{cib}{parsed}{data}{node}{$node_name}{node_state}{ready};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:node_name' => $node_name,
's2:host_uuid' => $host_uuid,
's3:host_status' => $host_status,
's4:maintenance_mode' => $maintenance_mode,
's5:in_ccm' => $in_ccm,
's6:crmd' => $crmd,
's7:join' => $join,
's8:ready' => $ready,
}});
# Convert the host state to a string.
my $say_host_status = $anvil->data->{'say'}{unknown};
if ($host_status eq "online")
{
$say_host_status = $anvil->data->{'say'}{online};
}
elsif ($host_status eq "powered off")
{
$say_host_status = $anvil->data->{'say'}{powered_off};
}
elsif ($host_status eq "stopping")
{
$say_host_status = $anvil->data->{'say'}{stopping};
}
elsif ($host_status eq "booting")
{
$say_host_status = $anvil->data->{'say'}{booting};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_host_status => $say_host_status }});
# Convert the Pacemaker state.
my $say_pacemaker_status = $anvil->data->{'say'}{unknown};
if ($ready)
{
$say_pacemaker_status = $anvil->data->{'say'}{online};
}
elsif (($in_ccm) or ($crmd) or ($join))
{
# Transitioning
$say_pacemaker_status = $anvil->data->{'say'}{transitioning};
}
else
{
$say_pacemaker_status = $anvil->data->{'say'}{offline};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_pacemaker_status => $say_pacemaker_status }});
# Maintenance mode
my $anvil_maintenance_mode = $anvil->System->maintenance_mode({host_uuid => $host_uuid});
my $say_maintenance_mode = (($maintenance_mode) or ($anvil_maintenance_mode)) ? $anvil->data->{'say'}{maintenance_mode} : $anvil->data->{'say'}{normal_operation};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
anvil_maintenance_mode => $anvil_maintenance_mode,
say_maintenance_mode => $say_maintenance_mode,
}});
print "| ";
print sprintf("%-${longest_node_name}s", $node_name)." | ";
print sprintf("%-${longest_host_status}s", $say_host_status)." | ";
print sprintf("%-${longest_pacemaker_status}s", $say_pacemaker_status)." | ";
print sprintf("%-${longest_maintenance_mode}s", $say_maintenance_mode)." |\n";
}
print $divider_line;
return(0);
}