xcat-core/xCAT-server/lib/xcat/plugins/toolscenter.pm
2012-04-12 05:37:26 +00:00

415 lines
14 KiB
Perl

# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
#This plugin enables stateless boot of IBM Bootable Media Creator as
#a provisioning target.
#Instead of 'genimage', the first step here is to visit IBM support website
#and download the bootable media creator utility
#Download the version intended to run on your management node, regardless of
#the managed node platform. I.e. if your management node is RHEL5 and your
#managed nodes are SLES10, an example download would be:
#https://www-947.ibm.com/systems/support/supportsite.wss/docdisplay?lndocid=MIGR-5079820&brandind=5000008
#Then, execute the utility. Mostly choose preferred options, but you must:
#-Use '--tui' (this instructs ToolsCenter to evoke the text startup path that xCAT coopts
#-Use --pxe /instal/netboot/bomc/x86_64/compute (x86_64 may be x86 and compute may be whatever profile name is preferable).
#-m should be given a list of 'machine type' numbers. If the nodes underwent
#the xCAT discovery process, this can be extracted from the vpd.mtm property:
#$ nodels n3 vpd.mtm
#n3: 7321
#It should then be possible to run 'nodeset <noderange> netboot=bomc-x86_64-compute'
#Future ToolsCenter enhancements may dictate that we drop support for version 1.10 to cleanly take advantage of it
package xCAT_plugin::toolscenter;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use Storable qw(dclone);
use Sys::Syslog;
use Thread qw(yield);
use POSIX qw(WNOHANG nice);
use xCAT::Table;
use xCAT::Utils;
use xCAT::MsgUtils;
use xCAT::SvrUtils;
#use Data::Dumper;
use Getopt::Long;
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("pass_through");
use File::Path;
use File::Copy;
use File::stat;
use File::Temp qw/mkdtemp/;
use strict;
my @cpiopid;
sub handled_commands
{
return {
mknetboot => "nodetype:os=(bomc.*)|(toolscenter.*)",
};
}
sub preprocess_request
{
my $req = shift;
my $callback = shift;
return [$req]; #calls are only made from pre-farmed out scenarios
if ($req->{command}->[0] eq 'copycd')
{ #don't farm out copycd
return [$req];
}
my $stab = xCAT::Table->new('site');
my $sent;
($sent) = $stab->getAttribs({key => 'sharedtftp'}, 'value');
unless ( $sent
and defined($sent->{value})
and ($sent->{value} =~ /no/i or $sent->{value} =~ /0/))
{
#unless requesting no sharedtftp, don't make hierarchical call
return [$req];
}
my %localnodehash;
my %dispatchhash;
my $nrtab = xCAT::Table->new('noderes');
my $nrents = $nrtab->getNodesAttribs($req->{node},[qw(tftpserver servicenode)]);
foreach my $node (@{$req->{node}})
{
my $nodeserver;
my $tent = $nrents->{$node}->[0]; #$nrtab->getNodeAttribs($node, ['tftpserver']);
if ($tent) { $nodeserver = $tent->{tftpserver} }
unless ($tent and $tent->{tftpserver})
{
$tent = $nrents->{$node}->[0]; #$nrtab->getNodeAttribs($node, ['servicenode']);
if ($tent) { $nodeserver = $tent->{servicenode} }
}
if ($nodeserver)
{
$dispatchhash{$nodeserver}->{$node} = 1;
}
else
{
$localnodehash{$node} = 1;
}
}
my @requests;
my $reqc = {%$req};
$reqc->{node} = [keys %localnodehash];
if (scalar(@{$reqc->{node}})) { push @requests, $reqc }
foreach my $dtarg (keys %dispatchhash)
{ #iterate dispatch targets
my $reqcopy = {%$req}; #deep copy
$reqcopy->{'_xcatdest'} = $dtarg;
$reqcopy->{node} = [keys %{$dispatchhash{$dtarg}}];
push @requests, $reqcopy;
}
return \@requests;
}
sub process_request
{
my $request = shift;
my $callback = shift;
my $doreq = shift;
my $distname = undef;
my $arch = undef;
my $path = undef;
if ($request->{command}->[0] eq 'mknetboot')
{
return mknetboot($request, $callback, $doreq);
}
}
sub mknetboot
{
my $req = shift;
my $callback = shift;
my $doreq = shift;
unless ($req->{arg}) {
$req->{arg} = [];
}
my @args = @{$req->{arg}};
my @nodes = @{$req->{node}};
my $ostab = xCAT::Table->new('nodetype');
my $sitetab = xCAT::Table->new('site');
my $installroot = xCAT::Utils->getInstallDir();
my $tftpdir = xCAT::Utils->getTftpDir();
my $xcatiport;
if ($sitetab)
{
(my $ref) = $sitetab->getAttribs({key => 'xcatiport'}, 'value');
if ($ref and $ref->{value})
{
$xcatiport = $ref->{value};
}
}
my %oents = %{$ostab->getNodesAttribs(\@nodes,[qw(os arch profile)])};
my $restab = xCAT::Table->new('noderes');
my $bptab = xCAT::Table->new('bootparams',-create=>1);
my $hmtab = xCAT::Table->new('nodehm');
my $firmtab = xCAT::Table->new('firmware');
my $firmhash = $firmtab->getNodesAttribs(\@nodes, ['cfgfile']);
my $reshash = $restab->getNodesAttribs(\@nodes, ['tftpserver','xcatmaster']);
my $hmhash =
$hmtab->getNodesAttribs(\@nodes,
['serialport', 'serialspeed', 'serialflow']);
#my $addkcmdhash =
# $bptab->getNodesAttribs(\@nodes, ['addkcmdline']);
foreach my $node (@nodes)
{
my $ent = $oents{$node}->[0]; #ostab->getNodeAttribs($node, ['os', 'arch', 'profile']);
unless ($ent->{os} and $ent->{arch} and $ent->{profile})
{
$callback->(
{
error => ["Insufficient nodetype entry for $node"],
errorcode => [1]
}
);
next;
}
my $osver = $ent->{os};
my $platform;
my $arch = $ent->{arch};
my $profile = $ent->{profile};
my $suffix = 'gz';
my $path = "/$installroot/netboot/$osver/$arch/$profile";
my $tpath = "/$tftpdir/xcat/netboot/$osver/$arch/$profile";
my $firmfile = $firmhash->{$node}->[0]->{cfgfile};
if ($firmfile) {
copy($firmfile,"$path/repo/$node.cfgfile");
}
my $asu = "/toolscenter/asu";
if ($ent->{arch} eq "x86_64") {
$asu = "/toolscenter/asu64";
}
unless ( -r "$path/img2a" and -r "$path/img3a" and -r "$path/tc.zip") {
$callback->(
{
error => ["Unavailable or unrecognized IBM ToolsCenter image in $path"],
errorcode => [1]
}
);
next;
}
unless (-r "$path/img2b" and # but not if it's newer
stat("$path/img2b")->mtime > stat("$path/img2a")->mtime) {
system("dd if=$path/img2a of=$path/img2b bs=2048 skip=1");
}
unless (-r "$path/img3b" and # but not if it's newer
stat("$path/img3b")->mtime > stat("$path/img3a")->mtime) {
system("dd if=$path/img3a of=$path/img3b bs=2048 skip=1");
}
unless (-r "$path/tc.xcat.zip" and
# regen if tc.zip is newer - they updated the repo underneath us
stat("$path/tc.xcat.zip")->mtime > stat("$path/tc.zip")->mtime) {
my $dpath = mkdtemp("/tmp/xcat/toolscenter.$$.XXXXXXX");
unless (-d $dpath) {
$callback->({error => ["Failure creating temporary directory to extract ToolsCenter content for xCAT customization" ], errorcode => [1]});
return 1;
}
chdir $dpath;
system("unzip $path/tc.zip");
my $menush;
open($menush,">","menu/menu.sh");
print $menush "#!/bin/sh -x\n";
print $menush 'LOG_PATH=/bomc/${hostname}',"\n";
print $menush 'mkdir -p $LOG_PATH',"\n";
print $menush 'ERROR_FILE=/bomc/${hostname}/bomc.error',"\n";
print $menush 'LOG_FILE=/bomc/${hostname}/bomc.log',"\n";
print $menush '${UXSPI_BINARY_PATH} update --unattended --firmware -l ${UXSPI_BOOTABLE} --timeout=${UXSPI_TIMEOUT} >${LOG_FILE} 2>${ERROR_FILE}'."\n";
print $menush 'DIR=`dirname $0`'."\n";
print $menush 'ERROR_FILE=/bomc/${hostname}/asu.error',"\n";
print $menush 'LOG_FILE=/bomc/${hostname}/asu.log',"\n";
print $menush 'if [ "${cmos_file}" != "" ]; then',"\n";
print $menush " $asu",' batch ${cmos_file} >${LOG_FILE} 2>${ERROR_FILE}', "\n";
print $menush "fi\n";
print $menush '$DIR/calltoxcat.awk ${xcat_server} '."$xcatiport\n";
print $menush "reboot\n";
close($menush);
open($menush,">","menu/calltoxcat.awk");
print $menush <<'ENDOFAWK';
#!/bin/awk -f
BEGIN {
xcatdhost = ARGV[1]
xcatdport = ARGV[2]
flag = ARGV[3]
if (!flag) flag = "next"
ns = "/inet/tcp/0/" ARGV[1] "/" xcatdport
while(1) {
if((ns |& getline) > 0)
print $0 | "logger -p local4.info -t xcat"
if($0 == "ready")
print flag |& ns
if($0 == "done")
break
}
close(ns)
exit 0
}
ENDOFAWK
close($menush);
open($menush,"<","menu/unattended_menu.sh");
my @oldunattendmenu = <$menush>; #store old menu;
close($menush);
open($menush,">","menu/unattended_menu.sh");
foreach (@oldunattendmenu) {
if (/^exit 0/) { #the exit line, hijack this
print $menush 'DIR=`dirname $0`'."\n";
print $menush 'mkdir -p $LOG_PATH',"\n";
print $menush 'ERROR_FILE=/bomc/${hostname}/asu.error',"\n";
print $menush 'LOG_FILE=/bomc/${hostname}/asu.log',"\n";
print $menush 'if [ ${cmos_file} != "" ]; then',"\n";
print $menush " $asu",' batch ${cmos_file} >${LOG_FILE} 2>${ERROR_FILE}', "\n";
print $menush "fi\n";
print $menush '$DIR/calltoxcat.awk ${xcat_server} '."$xcatiport\n";
print $menush "reboot\n";
} else {
print $menush $_;
}
}
close($menush);
system("zip $path/tc.xcat.zip -r .");
chdir "..";
system("rm -rf $dpath");
}
mkpath($tpath);
unless ( -r "$tpath/img2b" and
stat("$path/img2b")->mtime < stat("$tpath/img2b")->mtime) {
copy("$path/img2b",$tpath);
}
unless ( -r "$tpath/img3b" and
stat("$path/img3b")->mtime < stat("$tpath/img3b")->mtime) {
copy("$path/img3b",$tpath);
}
unless ( -r "$tpath/tcrootfs" and
stat("$path/tcrootfs")->mtime < stat("$tpath/tcrootfs")->mtime) {
copy("$path/tcrootfs",$tpath);
}
unless ( -r "$tpath/tc.zip" and
stat("$path/tc.xcat.zip")->mtime < stat("$tpath/tc.zip")->mtime) {
copy("$path/tc.xcat.zip","$tpath/tc.zip");
}
unless ( -r "$tpath/img2b" and -r "$tpath/img3b" and
-r "$tpath/tcrootfs" and -r "$tpath/tc.zip")
{
$callback->(
{
error => [ "Copying to $tpath failed" ],
errorcode => [1]
}
);
next;
}
$ent = $reshash->{$node}->[0];#$restab->getNodeAttribs($node, ['primarynic']);
my $sent = $hmhash->{$node}->[0];
# $hmtab->getNodeAttribs($node,
# ['serialport', 'serialspeed', 'serialflow']);
# determine image server, if tftpserver use it, else use xcatmaster
# last resort use self
my $imgsrv;
my $ient;
my $xcatserver;
if ($reshash->{$node}->[0]->{xcatmaster}) {
$xcatserver = $reshash->{$node}->[0]->{xcatmaster};
} else {
$xcatserver = '!myipfn!';
}
$ient = $reshash->{$node}->[0]; #$restab->getNodeAttribs($node, ['tftpserver']);
if ($ient and $ient->{tftpserver})
{
$imgsrv = $ient->{tftpserver};
}
else
{
$imgsrv = $xcatserver;
}
unless ($imgsrv)
{
$callback->(
{
error => [
"Unable to determine or reasonably guess the image server for $node"
],
errorcode => [1]
}
);
next;
}
$tpath =~ s!/$tftpdir/!!;
my $kcmdline = "vga=0x317 root=/dev/ram0 rw ramdisk_size=100000 tftp_server=$imgsrv tftp_tcrootfs=$tpath/tcrootfs tftp_tczip=$tpath/tc.zip xcat_server=$xcatserver hostname=$node";
if ($firmfile) {
$kcmdline .= " cmos_file=/bomc/$node.cfgfile";
}
if (defined $sent->{serialport})
{
#my $sent = $hmtab->getNodeAttribs($node,['serialspeed','serialflow']);
unless ($sent->{serialspeed})
{
$callback->(
{
error => [
"serialport defined, but no serialspeed for $node in nodehm table"
],
errorcode => [1]
}
);
next;
}
$kcmdline .=
" console=tty0 console=ttyS" . $sent->{serialport} . "," . $sent->{serialspeed};
if ($sent->{serialflow} =~ /(hard|tcs|ctsrts)/)
{
$kcmdline .= "n8r";
}
}
# add the addkcmdline attribute to the end
# of the command, if it exists
#my $addkcmd = $addkcmdhash->{$node}->[0];
# add the extra addkcmd command info, if in the table
#if ($addkcmd->{'addkcmdline'}) {
# $kcmdline .= " ";
# $kcmdline .= $addkcmd->{'addkcmdline'};
#}
my $kernstr="$tpath/img2b";
$bptab->setNodeAttribs(
$node,
{
kernel => "$kernstr",
initrd => "$tpath/img3b",
kcmdline => $kcmdline
}
);
}
#my $rc = xCAT::Utils->create_postscripts_tar();
#if ( $rc != 0 ) {
# xCAT::MsgUtils->message( "S", "Error creating postscripts tar file." );
#}
}
1;