xcat-core/xCAT-server/lib/xcat/plugins/confluent.pm
jjohnson2 4d3e209c3e Push xCAT nodegroup membership to confluent
With noderanges, it would be very handy for xCAT
group membership to be reflected in confluent.  Have
makeconfluentcfg push that data into confluent.
2015-03-30 15:25:33 -04:00

441 lines
15 KiB
Perl

# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html
#TODO: delete entries not being refreshed if no noderange
package xCAT_plugin::confluent;
use strict;
use warnings;
use xCAT::PasswordUtils;
use xCAT::Table;
use xCAT::Utils;
use xCAT::TableUtils;
use Getopt::Long;
use Sys::Hostname;
use xCAT::SvrUtils;
use Confluent::Client;
use strict;
my %termservers; #list of noted termservers
my $usage_string=
" makeconfluentcfg [-d|--delete] noderange
makeconfluentcf [-l|--local]
makeconfluentcf [-c|--confluent]
makeconfluentcf
makeconfluentcf -h|--help
makeconfluentcf -v|--version
-c|--confluent Configure confluent only on the host.
The default goes down to all the confluent instances on
the server nodes and set them up
-l|--local Configure confluent only on the local system.
The default goes down to all the confluent instances on
the server nodes and set them up
-d|--delete Conserver has the relevant entries for the given noderange removed immediately from configuration
-h|--help Display this usage statement.
-V|--verbose Verbose mode.
-v|--version Display the version number.";
my $version_string=xCAT::Utils->Version();
sub handled_commands {
return {
makeconfluentcfg => "confluent"
}
}
sub preprocess_request {
my $request = shift;
#if ($request->{_xcatdest}) { return [$request]; } #exit if preprocessed
if ($request->{_xcatpreprocessed}->[0] == 1) { return [$request]; }
my $callback=shift;
my @requests;
my $noderange = $request->{node}; #Should be arrayref
#display usage statement if -h
my $extrargs = $request->{arg};
my @exargs=($request->{arg});
if (ref($extrargs)) {
@exargs=@$extrargs;
}
@ARGV=@exargs;
my $isSN=xCAT::Utils->isServiceNode();
my @hostinfo=xCAT::NetworkUtils->determinehostname();
my %iphash=();
foreach(@hostinfo) { $iphash{$_}=1;}
$Getopt::Long::ignorecase=0;
#$Getopt::Long::pass_through=1;
if(!GetOptions(
'c|confluent' => \$::CONSERVER,
'l|local' => \$::LOCAL,
'h|help' => \$::HELP,
'D|debug' => \$::DEBUG,
'v|version' => \$::VERSION,
'V|verbose' => \$::VERBOSE)) {
$request = {};
return;
}
if ($::HELP) {
$callback->({data=>$usage_string});
$request = {};
return;
}
if ($::VERSION) {
$callback->({data=>$version_string});
$request = {};
return;
}
if ($::LOCAL) {
if ($noderange && @$noderange>0) {
$callback->({data=>"Invalid option -l or --local when there are nodes specified."});
$request = {};
return;
}
}
if ($::CONSERVER && $::LOCAL) {
$callback->({data=>"Can not specify -l or --local together with -c or --confluent."});
$request = {};
return;
}
# get site master
my $master=xCAT::TableUtils->get_site_Master();
if (!$master) { $master=hostname(); }
my %cons_hash=();
my $hmtab = xCAT::Table->new('nodehm');
my @items;
my $allnodes=1;
if ($noderange && @$noderange>0) {
$allnodes=0;
my $hmcache=$hmtab->getNodesAttribs($noderange,['node', 'serialport','cons', 'conserver']);
foreach my $node (@$noderange) {
my $ent=$hmcache->{$node}->[0]; #$hmtab->getNodeAttribs($node,['node', 'serialport','cons', 'conserver']);
push @items,$ent;
}
} else {
$allnodes=1;
@items = $hmtab->getAllNodeAttribs(['node', 'serialport','cons', 'conserver']);
}
my @nodes=();
foreach (@items) {
if (((!defined($_->{cons})) || ($_->{cons} eq "")) and !defined($_->{serialport})) { next;} #skip if 'cons' is not defined for this node, unless serialport suggests otherwise
if (defined($_->{conserver})) { push @{$cons_hash{$_->{conserver}}{nodes}}, $_->{node};}
else { push @{$cons_hash{$master}{nodes}}, $_->{node};}
push @nodes,$_->{node};
}
#send all nodes to the MN
if (!$isSN && !$::CONSERVER) { #If -c flag is set, do not add the all nodes to the management node
if ($::VERBOSE) {
my $rsp;
$rsp->{data}->[0] = "Configuring nodes in confluent on the management node";
xCAT::MsgUtils->message("I", $rsp, $callback);
}
my $reqcopy = {%$request};
$reqcopy->{'_xcatdest'} = $master;
$reqcopy->{_xcatpreprocessed}->[0] = 1;
$reqcopy->{'_allnodes'} = $allnodes; # the original command comes with nodes or not
if ($allnodes==1) { @nodes=(); }
$reqcopy->{node} = \@nodes;
push @requests, $reqcopy;
if ($::LOCAL) { return \@requests; }
}
# send to conserver hosts
foreach my $cons (keys %cons_hash) {
#print "cons=$cons\n";
my $doit=0;
if ($isSN) {
if (exists($iphash{$cons})) { $doit=1; }
} else {
if (!exists($iphash{$cons}) || $::CONSERVER) { $doit=1; }
}
if ($doit) {
my $reqcopy = {%$request};
$reqcopy->{'_xcatdest'} = $cons;
$reqcopy->{_xcatpreprocessed}->[0] = 1;
$reqcopy->{'_allnodes'} = [$allnodes]; # the original command comes with nodes or not
$reqcopy->{node} = $cons_hash{$cons}{nodes};
my $no=$reqcopy->{node};
#print "node=@$no\n";
push @requests, $reqcopy;
} #end if
} #end foreach
if ($::DEBUG) {
my $rsp;
$rsp->{data}->[0] = "In preprocess_request, request is " . Dumper(@requests);
xCAT::MsgUtils->message("I", $rsp, $callback);
}
return \@requests;
}
sub process_request {
my $req = shift;
my $cb = shift;
if ($req->{command}->[0] eq "makeconfluentcfg") {
makeconfluentcfg($req,$cb);
}
}
# Read the file, get db info, update the file contents, and then write the file
sub makeconfluentcfg {
my $req = shift;
%termservers = (); #clear hash of existing entries
my $cb = shift;
my $extrargs = $req->{arg};
my @exargs=($req->{arg});
if (ref($extrargs)) {
@exargs=@$extrargs;
}
@ARGV=@exargs;
$Getopt::Long::ignorecase=0;
#$Getopt::Long::pass_through=1;
my $delmode;
GetOptions('d|delete' => \$delmode,
);
my $nodes = $req->{node};
my $svboot=0;
if (exists($req->{svboot})) { $svboot=1;}
my $confluent = Confluent::Client->new(); # just the local form for now..
my $isSN=xCAT::Utils->isServiceNode();
my @hostinfo=xCAT::NetworkUtils->determinehostname();
my %iphash=();
foreach(@hostinfo) {$iphash{$_}=1;}
#print "process_request nodes=@$nodes\n";
# Get db info for the nodes related to console
my $hmtab = xCAT::Table->new('nodehm');
my @cfgents1;# = $hmtab->getAllNodeAttribs(['cons','serialport','mgt','conserver','termserver','termport']);
if (($nodes and @$nodes > 0) or $req->{noderange}->[0]) {
@cfgents1 = $hmtab->getNodesAttribs($nodes,['node','cons','serialport','mgt','conserver','termserver','termport','consoleondemand']);
# Adjust the data structure to make the result consistent with the getAllNodeAttribs() call we make if a noderange was not specified
my @tmpcfgents1;
foreach my $ent (@cfgents1)
{
foreach my $nodeent ( keys %$ent)
{
push @tmpcfgents1, $ent->{$nodeent}->[0] ;
}
}
@cfgents1 = @tmpcfgents1
} else {
@cfgents1 = $hmtab->getAllNodeAttribs(['cons','serialport','mgt','conserver','termserver','termport','consoleondemand']);
}
#cfgents1 should now have all the nodes, so we can fill in the cfgents array and cfgenthash one at a time.
# skip the nodes that do not have 'cons' defined, unless a serialport setting suggests otherwise
my @cfgents=();
my %cfgenthash;
foreach (@cfgents1) {
if ($_->{cons} or defined($_->{'serialport'})) {
unless ($_->{cons}) {$_->{cons} = $_->{mgt};} #populate with fallback
push @cfgents, $_;
$cfgenthash{$_->{node}} = $_; # also put the ref to the entry in a hash for quick look up
}
}
if ($::DEBUG) {
my $rsp;
$rsp->{data}->[0] = "In makeconservercf, cfgents is " . Dumper(@cfgents);
xCAT::MsgUtils->message("I", $rsp, $cb);
}
# if nodes defined, it is either on the service node or makeconserver was called with noderange on mn
if (($nodes and @$nodes > 0) or $req->{noderange}->[0]) {
# strip all xCAT configured nodes from config if the original command was for all nodes
#if (($req->{_allnodes}) && ($req->{_allnodes}->[0]==1)) {} #TODO: identify nodes that will be removed
# call donodeent to add all node entries into the file. It will return the 1st node in error.
my $node;
if ($node=donodeent(\%cfgenthash,$confluent,$delmode, $cb)) {
#$cb->({node=>[{name=>$node,error=>"Bad configuration, check attributes under the nodehm category",errorcode=>1}]});
xCAT::SvrUtils::sendmsg([1,"Bad configuration, check attributes under the nodehm category"],$cb,$node);
}
} else { #no nodes specified, do em all up
#zapcfg(\@filecontent); # strip all xCAT configured nodes from config
#TODO: identify nodes to be removed
# get nodetype so we can filter out node types without console support
my $typetab = xCAT::Table->new('nodetype');
my %type;
if ( defined($typetab)) {
my @ents = $typetab->getAllNodeAttribs([qw(node nodetype)]);
foreach (@ents) {
$type{$_->{node}}=$_->{nodetype};
}
}
# remove nodes that arent for this SN or type of node doesnt have console
foreach (@cfgents) {
my $keepdoing=0;
if ($isSN && $_->{conserver} && exists($iphash{$_->{conserver}})) {
$keepdoing=1; #only hanlde the nodes that use this SN as the conserver
}
if (!$isSN) { $keepdoing=1;} #handle all for MN
if ($keepdoing) {
if ($_->{termserver} and not $termservers{$_->{termserver}}) {
die "confluent does not currently support termserver";
$termservers{$_->{termserver}}=1; # dont add this one again
}
if ( $type{$_->{node}} =~ /fsp|bpa|hmc|ivm/ ) {
$keepdoing=0; # these types dont have consoles
}
}
if (!$keepdoing) { delete $cfgenthash{$_->{node}}; } # remove this node from the hash so we dont process it later
}
# Now add into the file all the node entries that we kept
my $node;
if ($node=donodeent(\%cfgenthash,$confluent, undef, $cb)) {
# donodeent will return the 1st node in error
#$cb->({node=>[{name=>$node,error=>"Bad configuration, check attributes under the nodehm category",errorcode=>1}]});
xCAT::SvrUtils::sendmsg([1,"Bad configuration, check attributes under the nodehm category"],$cb,$node);
}
}
}
# Add entries in the file for each node. This function used to do 1 node at a time, but was changed to do
# all nodes at once for performance reasons. If there is a problem with a nodes config, this
# function will return that node name as the one in error.
sub donodeent {
my $cfgenthash = shift;
my $confluent = shift;
my $delmode = shift;
my $cb = shift;
my $idx=0;
my $toidx=-1;
my $skip = 0;
my $skipnext = 0;
# Delete all the previous stanzas of the nodes specified
my $isSN=xCAT::Utils->isServiceNode();
my $curnode;
# Loop till find the start of a node stanza and remove lines till get to the end of the stanza
my %currnodes;
$confluent->read('/nodes/');
my $listitem = $confluent->next_result();
while ($listitem) {
if (exists $listitem->{item}) {
my $name = $listitem->{item}->{href};
$name =~ s/\/$//;
$currnodes{$name} = 1;
}
$listitem = $confluent->next_result();
}
if ($delmode) {
foreach my $confnode (keys %currnodes) {
if ($cfgenthash->{$confnode}) {
$confluent->delete('/nodes/' . $confnode);
}
return;
}
}
my @toconfignodes = keys %{$cfgenthash};
my $ipmitab = xCAT::Table->new('ipmi', -create=>0);
my $ipmientries = {};
if ($ipmitab) {
$ipmientries = $ipmitab->getNodesAttribs(\@toconfignodes,
[qw/bmc username password/]);
}
my $ipmiauthdata = xCAT::PasswordUtils::getIPMIAuth(
noderange=>\@toconfignodes, ipmihash=>$ipmientries);
my $nltab = xCAT::Table->new('nodelist', -create=>0);
my $groupdata = {};
if ($nltab) {
$groupdata = $nltab->getNodesAttribs(\@toconfignodes,
[qw/groups/]);
}
my @cfgroups;
foreach (keys %$groupdata) {
push @cfgroups, split /,/,$groupdata->{$_}->[0]->{groups};
}
$confluent->read('/nodegroups/');
my $currgroup = $confluent->next_result();
my %currgroups;
while ($currgroup) {
if (exists $currgroup->{item}) {
my $groupname = $currgroup->{item}->{href};
$groupname =~ s/\/$//;
$currgroups{$groupname} = 1;
}
$currgroup = $confluent->next_result();
}
foreach (@cfgroups) {
if (not exists $currgroups{$_}) {
$confluent->create('/nodegroups/', parameters=>{name=>$_});
my $rsp = $confluent->next_result();
while ($rsp) {
if (exists $rsp->{error}) {
xCAT::SvrUtils::sendmsg([1,"Confluent error: " . $rsp->{error}],$cb);
}
$rsp = $confluent->next_result();
}
}
}
# Go thru all nodes specified to add them to the file
foreach my $node (sort keys %$cfgenthash) {
my $cfgent = $cfgenthash->{$node};
my $cmeth=$cfgent->{cons};
if (not $cmeth) {
return $node;
}
if ($cmeth ne 'ipmi') {
$cmeth = 'xcat' . $cmeth;
}
my %parameters;
$parameters{'console.method'} = $cmeth;
if ($cmeth eq 'ipmi') {
$parameters{'secret.hardwaremanagementuser'} =
$ipmiauthdata->{$node}->{username};
$parameters{'secret.hardwaremanagementpassword'} =
$ipmiauthdata->{$node}->{password};
my $bmc = $ipmientries->{$node}->[0]->{bmc};
$bmc =~ s/,.*//;
$parameters{'hardwaremanagement.manager'} = $bmc;
}
if (defined($cfgent->{consoleondemand})) {
if ($cfgent->{consoleondemand}) {
$parameters{'console.logging'} = 'none';
}
else {
$parameters{'console.logging'} = 'full';
}
} elsif ($::XCATSITEVALS{'consoleondemand'} and $::XCATSITEVALS{'consoleondemand'} !~ m/^n/) {
$parameters{'console.logging'} = 'none';
}
$parameters{'groups'} = [split /,/,$groupdata->{$node}->[0]->{'groups'}];
if (exists $currnodes{$node}) {
$confluent->update('/nodes/'.$node.'/attributes/current', parameters=>\%parameters);
my $rsp = $confluent->next_result();
while ($rsp) {
if (exists $rsp->{error}) {
xCAT::SvrUtils::sendmsg([1,"Confluent error: " . $rsp->{error}],$cb,$node);
}
$rsp = $confluent->next_result();
}
} else {
$parameters{name} = $node;
$confluent->create('/nodes/', parameters=>\%parameters);
my $rsp = $confluent->next_result();
while ($rsp) {
if (exists $rsp->{error}) {
xCAT::SvrUtils::sendmsg([1,"Confluent error: " . $rsp->{error}],$cb,$node);
}
$rsp = $confluent->next_result();
}
}
}
return 0;
}
1;