# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html #TODO: delete entries not being refreshed if no noderange package xCAT_plugin::conserver; use strict; use xCAT::Table; use xCAT::Utils; use Getopt::Long; use Sys::Hostname; use strict; use Data::Dumper; my @cservers = qw(mrv cyclades); my %termservers; #list of noted termservers my $usage_string= " makeconservercf noderange makeconservercf [-l|--local] makeconservercf -h|--help makeconservercf -v|--version -l|--local The conserver gets set up only on the local host. The default goes down to all the conservers on the server nodes and set them up. -h|--help Display this usage statement. -v|--version Display the version number."; my $version_string=xCAT::Utils->Version(); sub handled_commands { return { makeconservercf => "conserver" } } sub preprocess_request { my $request = shift; if ($request->{_xcatdest}) { return [$request]; } #exit if preprocessed 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::Utils->determinehostname(); my %iphash=(); foreach(@hostinfo) { $iphash{$_}=1;} $Getopt::Long::ignorecase=0; #$Getopt::Long::pass_through=1; if(!GetOptions( 'l|local' => \$::LOCAL, 'h|help' => \$::HELP, 'v|version' => \$::VERSION)) { $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=>"Invalide option -l or --local when there are nodes specofied."}); $request = {}; return; } } # get site master my $master=xCAT::Utils->get_site_Master(); if (!$master) { $master=hostname(); } # get conserver for each node 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) { # my $reqcopy = {%$request}; $reqcopy->{'_xcatdest'} = $master; $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 SN 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})) { $doit=1; } } if ($doit) { my $reqcopy = {%$request}; $reqcopy->{'_xcatdest'} = $cons; $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; } } return \@requests; } sub process_request { my $req = shift; my $cb = shift; if ($req->{command}->[0] eq "makeconservercf") { makeconservercf($req,$cb); } } sub docfheaders { # Put in standard headers common to all conserver.cf files my $content = shift; my $numlines = @$content; my @meat = grep(!/^#/,@$content); unless (grep(/^config \* {/,@meat)) { push @$content,"config * {\n"; push @$content," sslrequired yes;\n"; push @$content," sslauthority /etc/xcat/cert/ca.pem;\n"; push @$content," sslcredentials /etc/xcat/cert/server-cred.pem;\n"; push @$content,"}\n"; } unless (grep(/^default full/,@meat)) { push @$content,"default full { rw *; }\n"; } unless (grep(/^default cyclades/,@meat)) { push @$content,"default cyclades { type host; portbase 7000; portinc 1; }\n" } unless (grep(/^default mrv/,@meat)) { push @$content,"default mrv { type host; portbase 2000; portinc 100; }\n" } unless (grep(/^access \*/,@meat)) { #push @$content,"#xCAT BEGIN ACCESS\n"; push @$content,"access * {\n"; push @$content," trusted 127.0.0.1;\n"; if (xCAT::Utils->isServiceNode()) { my $master=xCAT::Utils->get_site_Master(); push @$content, " trusted $master;\n"; } push @$content,"}\n"; #push @$content,"#xCAT END ACCESS\n"; } unless (grep(/^default \*/,@meat)) { push @$content,"default * {\n"; push @$content," logfile /var/log/consoles/&;\n"; push @$content," timestamp 1hab;\n"; push @$content," include full;\n"; push @$content," master localhost;\n"; #-- if option "conserverondemand" in site table is set to yes #-- then start all consoles on demand #-- this helps eliminate many ssh connections to blade AMM #-- which seems to kill AMMs occasionally my $sitetab = xCAT::Table->new('site'); my $vcon = $sitetab->getAttribs({key => "consoleondemand"}, 'value'); if ($vcon and $vcon->{"value"} and $vcon->{"value"} eq "yes" ) { push @$content," options ondemand;\n"; } push @$content,"}\n"; } } sub makeconservercf { my $req = shift; %termservers = (); #clear hash of existing entries my $cb = shift; my $nodes = $req->{node}; my $svboot=0; if (exists($req->{svboot})) { $svboot=1;} my $cfile; my @filecontent; open $cfile,'/etc/conserver.cf'; while (<$cfile>) { push @filecontent,$_; } close $cfile; docfheaders(\@filecontent); my $isSN=xCAT::Utils->isServiceNode(); my @hostinfo=xCAT::Utils->determinehostname(); my %iphash=(); foreach(@hostinfo) {$iphash{$_}=1;} #print "process_request nodes=@$nodes\n"; 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,['cons','serialport','mgt','conserver','termserver','termport']); #to make the result consistent to getAllNodeAttribs 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']); } #cfgents should now have all the nodes, so we can fill in our hashes one at a time. # skip the one that does not have 'cons' defined, unless a serialport setting suggests otherwise my @cfgents=(); foreach (@cfgents1) { if ($_->{cons} or defined($_->{'serialport'})) { push @cfgents, $_; } } # get the teminal servers and terminal port when cons is mrv or cyclades foreach (@cfgents) { unless ($_->{cons}) {$_->{cons} = $_->{mgt};} #populate with fallback #my $cmeth=$_->{cons}; #if (grep(/^$cmeth$/,@cservers)) { #terminal server, more attribs needed # my $node = $_->{node}; # my $tent = $hmtab->getNodeAttribs($node,["termserver","termport"]); # $_->{termserver} = $tent->{termserver}; # $termservers{$tent->{termserver}} = 1; # $_->{termport}= $tent->{termport}; #} } # nodes defined, it is either on the service node or mkconserver is call with noderange on mn if (($nodes and @$nodes > 0) or $req->{noderange}->[0]) { # strip all xCAT configured stuff from config if the original command was for all nodes if (($req->{_allnodes}) && ($req->{_allnodes}->[0]==1)) {zapcfg(\@filecontent);} foreach (@$nodes) { my $node = $_; foreach (@cfgents) { if ($_->{node} eq $node) { if ($_->{termserver} and not $termservers{$_->{termserver}}) { dotsent($_,\@filecontent); $termservers{$_->{termserver}}=1; #prevent needless cycles being burned } donodeent($_,\@filecontent); } } } } else { #no nodes specified, do em all up zapcfg(\@filecontent); # strip all xCAT configured stuff from config # 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}; } } 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}}) { dotsent($_,\@filecontent); $termservers{$_->{termserver}}=1; #prevent needless cycles being burned } if ( $type{$_->{node}} !~ /fsp|bpa|hmc|ivm/ ) { donodeent($_,\@filecontent); } } } } open $cfile,'>','/etc/conserver.cf'; foreach (@filecontent) { print $cfile $_; } close $cfile; if (!$svboot) { #restart conserver daemon my $cmd; if (-f "/var/run/conserver.pid") { $cmd = "/etc/init.d/conserver restart"; } else { $cmd = "/etc/init.d/conserver start"; } xCAT::Utils->runcmd($cmd, -1); } } sub dotsent { my $cfgent = shift; my $tserv = $cfgent->{termserver}; my $content = shift; my $idx = 0; my $toidx = -1; my $skip = 0; my $skipnext = 0; while ($idx < $#$content) { # Go through and delete that which would match my entry if ($content->[$idx] =~ /^#xCAT BEGIN $tserv TS/) { $toidx=$idx; #TODO put it back right where I found it $skip = 1; $skipnext=1; } elsif ($content->[$idx] =~ /^#xCAT END $tserv TS/) { $skipnext = 0; } if ($skip) { splice (@$content,$idx,1); } else { $idx++; } $skip = $skipnext; } push @$content,"#xCAT BEGIN $tserv TS\n"; push @$content,"default $tserv {\n"; push @$content," include ".$cfgent->{cons}.";\n"; push @$content," host $tserv;\n"; push @$content,"}\n"; push @$content,"#xCAT END $tserv TS\n"; } sub donodeent { my $cfgent = shift; my $node = $cfgent->{node}; my $content = shift; my $idx=0; my $toidx=-1; my $skip = 0; my $skipnext = 0; my $isSN=xCAT::Utils->isServiceNode(); while ($idx < $#$content) { # Go through and delete that which would match my entry if ($content->[$idx] =~ /^#xCAT BEGIN $node CONS/) { $toidx=$idx; #TODO put it back right where I found it $skip = 1; $skipnext=1; } elsif ($content->[$idx] =~ /^#xCAT END $node CONS/) { $skipnext = 0; } if ($skip) { splice (@$content,$idx,1); } else { $idx++; } $skip = $skipnext; } push @$content,"#xCAT BEGIN $node CONS\n"; push @$content,"console $node {\n"; #if ($cfgent->{cons} my $cmeth=$cfgent->{cons}; #print $cmeth."\n"; if (grep(/^$cmeth$/,@cservers)) { push @$content," include ".$cfgent->{termserver}.";\n"; push @$content," port ".$cfgent->{termport}.";\n"; if ((!$isSN) && ($cfgent->{conserver}) && xCAT::Utils->thishostisnot($cfgent->{conserver})) { # let the master handle it push @$content," master ".$cfgent->{conserver}.";\n"; } } else { #a script method... push @$content," type exec;\n"; if ((!$isSN) && ($cfgent->{conserver}) && xCAT::Utils->thishostisnot($cfgent->{conserver})) { # let the master handle it push @$content," master ".$cfgent->{conserver}.";\n"; } else { # handle it here my $locerror = $isSN ? "PERL_BADLANG=0 " : ''; # on service nodes, often LC_ALL is not set and perl complains push @$content," exec $locerror".$::XCATROOT."/share/xcat/cons/".$cmeth." ".$node.";\n" } } push @$content,"}\n"; push @$content,"#xCAT END $node CONS\n"; } sub zapcfg { my $content = shift; my $idx=0; my $toidx=-1; my $skip = 0; my $skipnext = 0; while ($idx <= $#$content) { # Go through and delete that which would match my entry if ($content->[$idx] =~ /^#xCAT BEGIN/) { $toidx=$idx; #TODO put it back right where I found it $skip = 1; $skipnext=1; } elsif ($content->[$idx] =~ /^#xCAT END/) { $skipnext = 0; } if ($skip) { splice (@$content,$idx,1); } else { $idx++; } $skip = $skipnext; } } 1;