# 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 -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;