407 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			407 lines
		
	
	
		
			14 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);
 | 
						|
 | 
						|
# 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';
 | 
						|
  }
 | 
						|
  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;
 |