package Anvil::Tools::Cluster ;
#
# This module contains methods related to Pacemaker/pcs and clustering functions in general.
#
use strict ;
use warnings ;
use Data::Dumper ;
use XML::Simple qw( :strict ) ;
use XML::LibXML ;
use Scalar::Util qw( weaken isweak ) ;
our $ VERSION = "3.0.0" ;
my $ THIS_FILE = "Cluster.pm" ;
### Methods;
# check_node_status
# get_peers
# parse_cib
# start_cluster
= pod
= encoding utf8
= head1 NAME
Anvil::Tools:: Cluster
Provides all methods related to clustering specifically ( pacemaker , pcs , etc ) .
= 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->Cluster->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 check_node_status
This takes a node name ( generally the short host name ) and , using a C << parse_cib >> call data ( made before calling this method ) , the node ' s ready state will be checked . If the node is ready , C << 1 >> is returned . If not , C << 0 >> is returned . If there is a problem , C << ! ! error ! ! >> is returned .
Parameters ;
= head3 node_name ( required )
This is the node name as used when configured in the cluster . In most cases , this is the short host name .
= cut
sub check_node_status
{
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->check_node_status()" } } ) ;
my $ node_name = defined $ parameter - > { node_name } ? $ parameter - > { node_name } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
node_name = > $ node_name ,
} } ) ;
if ( not $ node_name )
{
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > 0 , priority = > "err" , key = > "log_0020" , variables = > { method = > "Database->get_host_from_uuid()" , parameter = > "host_uuid" } } ) ;
return ( "!!error!!" ) ;
}
if ( not exists $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } )
{
$ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { in_ccm } = 0 ;
$ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { crmd } = 0 ;
$ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { 'join' } = 0 ;
$ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { ready } = 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::data::node::${node_name}::node_state::in_ccm" = > $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { in_ccm } ,
"cib::parsed::data::node::${node_name}::node_state::crmd" = > $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { crmd } ,
"cib::parsed::data::node::${node_name}::node_state::join" = > $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { 'join' } ,
"cib::parsed::data::node::${node_name}::node_state::ready" = > $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { ready } ,
} } ) ;
}
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::data::node::${node_name}::node_state::ready" = > $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { ready } ,
} } ) ;
return ( $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { ready } ) ;
}
= head2 get_peers
This method uses the local machine 's host UUID and finds the host names of the cluster memebers. If this host is in a cluster and it is a node, the peer' s short host name is returned . Otherwise , an empty string is returned .
The data is stored as ;
sys::anvil::node1:: host_uuid
sys::anvil::node1:: host_name
sys::anvil::node2:: host_uuid
sys::anvil::node2:: host_name
sys::anvil::dr1:: host_uuid
sys::anvil::dr1:: host_name
To assist with lookup , the following are also set ;
sys::anvil:: i_am = { node1 , node2 , dr1 }
sys::anvil:: peer_is = { node1 , node2 } # Not set if this host is 'dr1'
This method takes no parameters .
= cut
sub get_peers
{
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->get_peers()" } } ) ;
$ anvil - > data - > { sys } { anvil } { node1 } { host_uuid } = "" ;
$ anvil - > data - > { sys } { anvil } { node1 } { host_name } = "" ;
$ anvil - > data - > { sys } { anvil } { node2 } { host_uuid } = "" ;
$ anvil - > data - > { sys } { anvil } { node2 } { host_name } = "" ;
$ anvil - > data - > { sys } { anvil } { dr1 } { host_uuid } = "" ;
$ anvil - > data - > { sys } { anvil } { dr1 } { host_name } = "" ;
$ anvil - > data - > { sys } { anvil } { i_am } = "" ;
$ anvil - > data - > { sys } { anvil } { peer_is } = "" ;
# Load hosts and anvils
$ anvil - > Database - > get_hosts ( { debug = > $ debug } ) ;
$ anvil - > Database - > get_anvils ( { debug = > $ debug } ) ;
# Is ths host in an anvil?
my $ host_uuid = $ anvil - > Get - > host_uuid ( { debug = > $ debug } ) ;
my $ in_anvil = "" ;
my $ found = 0 ;
my $ peer = "" ;
foreach my $ anvil_uuid ( keys % { $ anvil - > data - > { anvils } { anvil_uuid } } )
{
my $ anvil_node1_host_uuid = $ anvil - > data - > { anvils } { anvil_uuid } { $ anvil_uuid } { anvil_node1_host_uuid } ;
my $ anvil_node2_host_uuid = $ anvil - > data - > { anvils } { anvil_uuid } { $ anvil_uuid } { anvil_node2_host_uuid } ;
my $ anvil_dr1_host_uuid = $ anvil - > data - > { anvils } { anvil_uuid } { $ anvil_uuid } { anvil_dr1_host_uuid } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
anvil_node1_host_uuid = > $ anvil_node1_host_uuid ,
anvil_node2_host_uuid = > $ anvil_node2_host_uuid ,
anvil_dr1_host_uuid = > $ anvil_dr1_host_uuid ,
} } ) ;
if ( $ host_uuid eq $ anvil_node1_host_uuid )
{
# Found our Anvil!, and we're node 1.
$ found = 1 ;
$ anvil - > data - > { sys } { anvil } { i_am } = "node1" ;
$ anvil - > data - > { sys } { anvil } { peer_is } = "node2" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
found = > $ found ,
"sys::anvil::i_am" = > $ anvil - > data - > { sys } { anvil } { i_am } ,
"sys::anvil::peer_is" = > $ anvil - > data - > { sys } { anvil } { peer_is } ,
} } ) ;
}
elsif ( $ host_uuid eq $ anvil_node2_host_uuid )
{
# Found our Anvil!, and we're node 1.
$ found = 1 ;
$ anvil - > data - > { sys } { anvil } { i_am } = "node2" ;
$ anvil - > data - > { sys } { anvil } { peer_is } = "node1" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
found = > $ found ,
"sys::anvil::i_am" = > $ anvil - > data - > { sys } { anvil } { i_am } ,
"sys::anvil::peer_is" = > $ anvil - > data - > { sys } { anvil } { peer_is } ,
} } ) ;
}
elsif ( $ host_uuid eq $ anvil_dr1_host_uuid )
{
# Found our Anvil!, and we're node 1.
$ found = 1 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { found = > $ found } } ) ;
}
if ( $ found )
{
$ anvil - > data - > { sys } { anvil } { node1 } { host_uuid } = $ anvil_node1_host_uuid ;
$ anvil - > data - > { sys } { anvil } { node1 } { host_name } = $ anvil - > data - > { hosts } { host_uuid } { $ anvil_node1_host_uuid } { host_name } ;
$ anvil - > data - > { sys } { anvil } { node2 } { host_uuid } = $ anvil_node2_host_uuid ;
$ anvil - > data - > { sys } { anvil } { node2 } { host_name } = $ anvil - > data - > { hosts } { host_uuid } { $ anvil_node2_host_uuid } { host_name } ;
$ anvil - > data - > { sys } { anvil } { dr1 } { host_uuid } = $ anvil_dr1_host_uuid ? $ anvil_dr1_host_uuid : "" ;
$ anvil - > data - > { sys } { anvil } { dr1 } { host_name } = $ anvil_dr1_host_uuid ? $ anvil - > data - > { hosts } { host_uuid } { $ anvil_dr1_host_uuid } { host_name } : "" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"sys::anvil::node1::host_uuid" = > $ anvil - > data - > { sys } { anvil } { node1 } { host_uuid } ,
"sys::anvil::node1::host_name" = > $ anvil - > data - > { sys } { anvil } { node1 } { host_name } ,
"sys::anvil::node2::host_uuid" = > $ anvil - > data - > { sys } { anvil } { node2 } { host_uuid } ,
"sys::anvil::node2::host_name" = > $ anvil - > data - > { sys } { anvil } { node2 } { host_name } ,
"sys::anvil::dr1::host_uuid" = > $ anvil - > data - > { sys } { anvil } { dr1 } { host_uuid } ,
"sys::anvil::dr1::host_name" = > $ anvil - > data - > { sys } { anvil } { dr1 } { host_name } ,
} } ) ;
# If this is a node, return the peer's short host name.
if ( $ anvil - > data - > { sys } { anvil } { i_am } )
{
$ peer = $ anvil - > data - > { sys } { anvil } { i_am } eq "node1" ? $ anvil - > data - > { sys } { anvil } { node1 } { host_name } : $ anvil - > data - > { sys } { anvil } { node2 } { host_name } ;
$ peer =~ s/\..*// ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { peer = > $ peer } } ) ;
}
last ;
}
}
return ( $ peer ) ;
}
= head2 parse_cib
This reads in the CIB XML and parses it . On success , it returns C << 0 >> . On failure ( ie: pcsd isn ' t running ) , returns C << 1 >> .
= cut
sub parse_cib
{
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->parse_cib()" } } ) ;
# If we parsed before, delete it.
if ( exists $ anvil - > data - > { cib } { parsed } )
{
delete $ anvil - > data - > { cib } { parsed } ;
}
# This stores select data we've pulled out that's meant to be easier to find.
if ( exists $ anvil - > data - > { cib } { data } )
{
delete $ anvil - > data - > { cib } { data } ;
}
my $ problem = 1 ;
my $ shell_call = $ anvil - > data - > { path } { exe } { pcs } . " cluster cib" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { shell_call = > $ shell_call } } ) ;
my ( $ cib_data , $ return_code ) = $ anvil - > System - > call ( { debug = > 3 , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
cib_data = > $ cib_data ,
return_code = > $ return_code ,
} } ) ;
if ( $ return_code )
{
# Failed to read the CIB.
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , key = > "warning_0052" } ) ;
}
else
{
my $ dom = eval { XML::LibXML - > load_xml ( string = > $ cib_data ) ; } ;
if ( $@ )
{
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , key = > "warning_0053" , variables = > {
cib = > $ cib_data ,
error = > $@ ,
} } ) ;
}
else
{
### NOTE: Full CIB details;
### - https://clusterlabs.org/pacemaker/doc/en-US/Pacemaker/2.0/html-single/Pacemaker_Explained/index.html
# Successful parse!
$ problem = 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > { problem = > $ problem } } ) ;
foreach my $ nvpair ( $ dom - > findnodes ( '/cib/configuration/crm_config/cluster_property_set/nvpair' ) )
{
my $ nvpair_id = $ nvpair - > { id } ;
foreach my $ variable ( sort { $ a cmp $ b } keys % { $ nvpair } )
{
next if $ variable eq "id" ;
$ anvil - > data - > { cib } { parsed } { configuration } { crm_config } { cluster_property_set } { nvpair } { $ nvpair_id } { $ variable } = $ nvpair - > { $ variable } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::configuration::crm_config::cluster_property_set::nvpair::${nvpair_id}::${variable}" = > $ anvil - > data - > { cib } { parsed } { configuration } { crm_config } { cluster_property_set } { nvpair } { $ nvpair_id } { $ variable } ,
} } ) ;
}
}
foreach my $ node ( $ dom - > findnodes ( '/cib/configuration/nodes/node' ) )
{
my $ node_id = $ node - > { id } ;
foreach my $ variable ( sort { $ a cmp $ b } keys % { $ node } )
{
next if $ variable eq "id" ;
$ anvil - > data - > { cib } { parsed } { configuration } { nodes } { $ node_id } { $ variable } = $ node - > { $ variable } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::configuration::nodes::${node_id}::${variable}" = > $ anvil - > data - > { cib } { parsed } { configuration } { nodes } { $ node_id } { $ variable } ,
} } ) ;
if ( $ variable eq "uname" )
{
my $ node = $ node - > { $ variable } ;
$ anvil - > data - > { cib } { parsed } { data } { node } { $ node } { id } = $ node_id ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::data::node::${node}::id" = > $ anvil - > data - > { cib } { parsed } { data } { node } { $ node } { id } ,
} } ) ;
# Preload state values (in case they're not read in this CIB.
$ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ node_id } { in_ccm } = "false" ;
$ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ node_id } { crmd } = "offline" ;
$ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ node_id } { 'join' } = "down" ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::node_state::${node_id}::in_ccm" = > $ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ node_id } { in_ccm } ,
"cib::parsed::cib::node_state::${node_id}::crmd" = > $ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ node_id } { crmd } ,
"cib::parsed::cib::node_state::${node_id}::join" = > $ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ node_id } { 'join' } ,
} } ) ;
}
}
}
foreach my $ clone ( $ dom - > findnodes ( '/cib/configuration/resources/clone' ) )
{
my $ clone_id = $ clone - > { id } ;
foreach my $ primitive ( $ clone - > findnodes ( './primitive' ) )
{
my $ primitive_id = $ primitive - > { id } ;
$ anvil - > data - > { cib } { parsed } { cib } { resources } { clone } { $ clone_id } { primitive } { $ primitive_id } { class } = $ primitive - > { class } ;
$ anvil - > data - > { cib } { parsed } { cib } { resources } { clone } { $ clone_id } { primitive } { $ primitive_id } { type } = $ primitive - > { type } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::resources::clone::${clone_id}::primitive::${primitive_id}::class" = > $ anvil - > data - > { cib } { parsed } { cib } { resources } { clone } { $ clone_id } { primitive } { $ primitive_id } { class } ,
"cib::parsed::cib::resources::clone::${clone_id}::primitive::${primitive_id}::type" = > $ anvil - > data - > { cib } { parsed } { cib } { resources } { clone } { $ clone_id } { primitive } { $ primitive_id } { type } ,
} } ) ;
foreach my $ op ( $ primitive - > findnodes ( './operations/op' ) )
{
my $ op_id = $ op - > { id } ;
foreach my $ variable ( sort { $ a cmp $ b } keys % { $ op } )
{
next if $ variable eq "id" ;
$ anvil - > data - > { cib } { parsed } { cib } { resources } { clone } { $ clone_id } { operations } { $ op_id } { $ variable } = $ op - > { $ variable } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::resources::clone::${clone_id}::operations::${op_id}::${variable}" = > $ anvil - > data - > { cib } { parsed } { cib } { resources } { clone } { $ clone_id } { operations } { $ op_id } { $ variable } ,
} } ) ;
}
}
}
foreach my $ meta_attributes ( $ clone - > findnodes ( './meta_attributes' ) )
{
my $ meta_attributes_id = $ meta_attributes - > { id } ;
foreach my $ nvpair ( $ meta_attributes - > findnodes ( './nvpair' ) )
{
my $ id = $ nvpair - > { id } ;
foreach my $ variable ( sort { $ a cmp $ b } keys % { $ nvpair } )
{
next if $ variable eq "id" ;
$ anvil - > data - > { cib } { parsed } { cib } { resources } { clone } { $ clone_id } { meta_attributes } { $ id } { $ variable } = $ nvpair - > { $ variable } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::resources::clone::${clone_id}::meta_attributes::${id}::${variable}" = > $ anvil - > data - > { cib } { parsed } { cib } { resources } { clone } { $ clone_id } { meta_attributes } { $ id } { $ variable } ,
} } ) ;
}
}
}
}
### TODO: /cib/configuration/constraints
foreach my $ node_state ( $ dom - > findnodes ( '/cib/status/node_state' ) )
{
my $ id = $ node_state - > { id } ;
foreach my $ variable ( sort { $ a cmp $ b } keys % { $ node_state } )
{
next if $ variable eq "id" ;
$ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ id } { $ variable } = $ node_state - > { $ variable } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::node_state::${id}::${variable}" = > $ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ id } { $ variable } ,
} } ) ;
}
foreach my $ lrm ( $ node_state - > findnodes ( './lrm' ) )
{
my $ lrm_id = $ lrm - > { id } ;
foreach my $ lrm_resource ( $ lrm - > findnodes ( './lrm_resources/lrm_resource' ) )
{
my $ lrm_resource_id = $ lrm_resource - > { id } ;
$ anvil - > data - > { cib } { parsed } { cib } { status } { node_state } { $ id } { lrm_id } { $ lrm_id } { lrm_resource } { $ lrm_resource_id } { type } = $ lrm_resource - > { type } ;
$ anvil - > data - > { cib } { parsed } { cib } { status } { node_state } { $ id } { lrm_id } { $ lrm_id } { lrm_resource } { $ lrm_resource_id } { class } = $ lrm_resource - > { class } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::status::node_state::${id}::lrm_id::${lrm_id}::lrm_resource::${lrm_resource_id}::type" = > $ anvil - > data - > { cib } { parsed } { cib } { status } { node_state } { $ id } { lrm_id } { $ lrm_id } { lrm_resource } { $ lrm_resource_id } { type } ,
"cib::parsed::cib::status::node_state::${id}::lrm_id::${lrm_id}::lrm_resource::${lrm_resource_id}::class" = > $ anvil - > data - > { cib } { parsed } { cib } { status } { node_state } { $ id } { lrm_id } { $ lrm_id } { lrm_resource } { $ lrm_resource_id } { class } ,
} } ) ;
foreach my $ lrm_rsc_op ( $ lrm_resource - > findnodes ( './lrm_rsc_op' ) )
{
my $ lrm_rsc_op_id = $ lrm_rsc_op - > { id } ;
foreach my $ variable ( sort { $ a cmp $ b } keys % { $ lrm_rsc_op } )
{
next if $ variable eq "id" ;
$ 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 } { $ variable } = $ lrm_rsc_op - > { $ variable } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::status::node_state::${id}::lrm_id::${lrm_id}::lrm_resource::${lrm_resource_id}::lrm_rsc_op_id::${lrm_rsc_op_id}::${variable}" = > $ 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 } { $ variable } ,
} } ) ;
}
}
}
}
foreach my $ transient_attributes ( $ node_state - > findnodes ( './transient_attributes' ) )
{
# Currently, there seems to be no other data stored here.
my $ transient_attributes_id = $ transient_attributes - > { id } ;
foreach my $ instance_attributes ( $ transient_attributes - > findnodes ( './instance_attributes' ) )
{
$ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ id } { transient_attributes_id } { $ transient_attributes_id } { instance_attributes_id } = $ instance_attributes - > { id } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::status::node_state::${id}::transient_attributes_id::${transient_attributes_id}::instance_attributes_id" = > $ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ id } { transient_attributes_id } { $ transient_attributes_id } { instance_attributes_id } ,
} } ) ;
}
}
}
foreach my $ primitive ( $ dom - > findnodes ( '/cib/configuration/resources/primitive' ) )
{
my $ id = $ primitive - > { id } ;
$ anvil - > data - > { cib } { parsed } { cib } { resources } { primitive } { $ id } { type } = $ primitive - > { type } ;
$ anvil - > data - > { cib } { parsed } { cib } { resources } { primitive } { $ id } { class } = $ primitive - > { class } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::resources::primitive:${id}::type" = > $ anvil - > data - > { cib } { parsed } { cib } { resources } { primitive } { $ id } { type } ,
"cib::parsed::cib::resources::primitive:${id}::class" = > $ anvil - > data - > { cib } { parsed } { cib } { resources } { primitive } { $ id } { class } ,
} } ) ;
foreach my $ nvpair ( $ primitive - > findnodes ( './instance_attributes/nvpair' ) )
{
my $ nvpair_id = $ nvpair - > { id } ;
foreach my $ variable ( sort { $ a cmp $ b } keys % { $ nvpair } )
{
next if $ variable eq "id" ;
$ anvil - > data - > { cib } { parsed } { cib } { resources } { primitive } { $ id } { instance_attributes } { $ nvpair_id } { $ variable } = $ nvpair - > { $ variable } ; ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::resources::primitive::${id}::instance_attributes::${nvpair_id}::${variable}" = > $ anvil - > data - > { cib } { parsed } { cib } { resources } { primitive } { $ id } { instance_attributes } { $ nvpair_id } { $ variable } ,
} } ) ;
}
}
foreach my $ nvpair ( $ primitive - > findnodes ( './operations/op' ) )
{
my $ nvpair_id = $ nvpair - > { id } ;
foreach my $ variable ( sort { $ a cmp $ b } keys % { $ nvpair } )
{
next if $ variable eq "id" ;
$ anvil - > data - > { cib } { parsed } { cib } { resources } { primitive } { $ id } { operations } { op } { $ nvpair_id } { $ variable } = $ nvpair - > { $ variable } ; ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::resources::primitive::${id}::operations::op::${nvpair_id}::${variable}" = > $ anvil - > data - > { cib } { parsed } { cib } { resources } { primitive } { $ id } { operations } { op } { $ nvpair_id } { $ variable } ,
} } ) ;
}
}
}
foreach my $ attribute ( $ dom - > findnodes ( '/cib' ) )
{
foreach my $ variable ( sort { $ a cmp $ b } keys % { $ attribute } )
{
$ anvil - > data - > { cib } { parsed } { cib } { $ variable } = $ attribute - > { $ variable } ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::cib::${variable}" = > $ anvil - > data - > { cib } { parsed } { cib } { $ variable } ,
} } ) ;
}
}
}
}
# Pull some data out for easier access.
$ anvil - > data - > { cib } { parsed } { peer } { ready } = "" ;
$ anvil - > data - > { cib } { parsed } { peer } { name } = "" ;
foreach my $ node_name ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { cib } { parsed } { data } { node } } )
{
# The "coming up" order is 'in_ccm' then 'crmd' then 'join'.
my $ node_id = $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { id } ;
my $ in_ccm = $ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ node_id } { in_ccm } eq "true" ? 1 : 0 ; # 'true' or 'false' - Corosync member
my $ crmd = $ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ node_id } { crmd } eq "online" ? 1 : 0 ; # 'online' or 'offline' - In corosync process group
my $ join = $ anvil - > data - > { cib } { parsed } { cib } { node_state } { $ node_id } { 'join' } eq "member" ? 1 : 0 ; # 'member' or 'down' - Completed controller join process
my $ ready = ( ( $ in_ccm ) && ( $ crmd ) && ( $ join ) ) ? 1 : 0 ; # Our summary of if the node is "up"
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
's1:node_name' = > $ node_name ,
's2:node_id' = > $ node_id ,
's3:in_ccm' = > $ in_ccm ,
's4:crmd' = > $ crmd ,
's5:join' = > $ join ,
's6:ready' = > $ ready ,
} } ) ;
$ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { in_ccm } = $ in_ccm ;
$ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { crmd } = $ crmd ;
$ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { 'join' } = $ join ;
$ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { ready } = $ ready ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::data::node::${node_name}::node_state::in_ccm" = > $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { in_ccm } ,
"cib::parsed::data::node::${node_name}::node_state::crmd" = > $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { crmd } ,
"cib::parsed::data::node::${node_name}::node_state::join" = > $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { 'join' } ,
"cib::parsed::data::node::${node_name}::node_state::ready" = > $ anvil - > data - > { cib } { parsed } { data } { node } { $ node_name } { node_state } { ready } ,
} } ) ;
# Is this me or the peer?
if ( ( $ node_name ne $ anvil - > _host_name ) && ( $ node_name ne $ anvil - > _short_host_name ) )
{
# It's our peer.
$ anvil - > data - > { cib } { parsed } { peer } { ready } = $ ready ;
$ anvil - > data - > { cib } { parsed } { peer } { name } = $ node_name ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
"cib::parsed::peer::ready" = > $ anvil - > data - > { cib } { parsed } { peer } { ready } ,
"cib::parsed::peer::name" = > $ anvil - > data - > { cib } { parsed } { peer } { name } ,
} } ) ;
}
}
return ( $ problem ) ;
}
= head2 start_cluster
This will join the local node to the pacemaker cluster . Optionally , it can try to start the cluster on both nodes if C << all >> is set .
Parameters ;
= head3 all ( optional , default '0' )
If set , the cluster will be started on both ( all ) nodes .
= cut
sub start_cluster
{
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->parse_cib()" } } ) ;
my $ all = defined $ parameter - > { all } ? $ parameter - > { all } : 0 ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
all = > $ all ,
} } ) ;
my $ success = 1 ;
my $ shell_call = $ anvil - > data - > { path } { exe } { pcs } . " cluster start" ;
if ( $ all )
{
$ shell_call . = " --all" ;
}
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , secure = > 0 , list = > {
shell_call = > $ shell_call ,
} } ) ;
my ( $ output , $ return_code ) = $ anvil - > System - > call ( { debug = > 3 , shell_call = > $ shell_call } ) ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , secure = > 0 , list = > {
output = > $ output ,
return_code = > $ return_code ,
} } ) ;
return ( $ success ) ;
}
# =head3
#
# Private Functions;
#
# =cut
#############################################################################################################
# Private functions #
#############################################################################################################