xcat-core/xCAT-server/lib/xcat/plugins/partimage.pm

328 lines
8.8 KiB
Perl

# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::partimage;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use Storable qw(dclone);
use Sys::Syslog;
use File::Temp qw/tempdir/;
use xCAT::Table;
use xCAT::Utils;
use xCAT::MsgUtils;
use xCAT::Template;
use xCAT::Postage;
use Data::Dumper;
use Getopt::Long;
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("pass_through");
use File::Path;
use File::Copy;
my $pImageDir = "$::XCATROOT/share/xcat/install/partimage";
my $tftpDir = "/tftpboot";
my @cpiopid;
sub handled_commands
{
return {
mkinstall => "nodetype:os=imagecapture|imagerestore",
};
}
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 'mkinstall'){
return mkinstall($request, $callback, $doreq);
}else{
errorSig($callback, "Error in partimage plugin, shouldn't call anything other than mkinstall function");
}
}
sub errorSig {
$callback = shift;
$msg = shift;
$callback->({
error => ["$msg"],
errorcode => [1]
});
}
# function used for creating boot attributes for partimage functionality.
sub mkinstall {
# we should be passed in the request which has the list of nodes
my $request = shift;
# The callback function for what to print out and do with results.
my $callback = shift;
# doreq isn't used here, but we shift for good measure.
my $doreq = shift;
my @nodes = @{$request->{node}};
my $node;
# read the nodetype table for our nodes.
my $ostab = xCAT::Table->new('nodetype');
my %doneimgs;
my $mode;
# go through each node:
# 1. Verify that nodetype table is filled out and accurate
# 2. Create auto file by subbing through variables.
foreach $node (@nodes){
my $osinst;
my $ent = $ostab->getNodeAttribs($node, ['profile', 'os', 'arch']);
# Verify that the user configured the tables like we expect them to.
# if not, then give them a stern warning.
unless ($ent->{os}){
errorSig($callback, "No OS defined in nodetype for $node. OS types for partimage should be imagecapture or imagerestore");
next;
}
unless($ent->{arch}){
errorSig($callback, "No arch defined in nodetype for $node. only x86 and x86_64 supported");
next;
}
unless($ent->{profile}){
errorSig($callback, "No profile defined in nodetype for $node. Default profile types are found in $pImageDir");
next;
}
my $os = $ent->{os};
my $arch = $ent->{arch};
my $profile = $ent->{profile};
if($os eq "imagecapture"){
$mode = "capture";
}
elsif($os eq "imagerestore"){
$mode = "restore";
}else{
errorSig($callback, "Unrecognized mode: $os. Should be imagecapture or imagerestore for partimage");
next;
}
# This is step 2. Take the template file and sub in the variables
# so that there is a file in /install/autoinst/$node for each node
my $tmplfile=get_tmpl_file_name("/install/custom/install/partimage", $profile, $os, $arch);
if (! $tmplfile) { $tmplfile=get_tmpl_file_name($pImageDir, $profile, $os, $arch); }
unless ( -r "$tmplfile"){
errorSig($callback, "No profile template exists for $profile");
next;
}
my $instDir = xCAT::Utils->get_site_attribute("installdir");
if(! defined($instDir)){
$instDir = "/install";
}
#Call the Template class to do substitution to produce the file
my $tmperr = xCAT::Template->subvars(
$tmplfile,
"$instDir/autoinst/$node",
$node
);
# if there were problems during substitution then let the user know
# about it.
if ($tmperr){
$callback->({
node => [{
name => [$node],
error => [$tmperr],
errorcode => [1]
}]
});
next;
}
# part 3. Create the image capture/restore image
# this is just a repackaging of the existing xCAT nbfs
# partimage uses the 32 bit one. No problems if its 64 bit
# the 32 bit will still work. This way we can capture 64 bit
# and 64 bit os's. What a clever tool!
unless ($doneimgs{"$os|$arch"}){
mkpath("$tftpDir/xcat/partimage/x86");
mkpath("$instDir/partimage");
# verify ramdisk is there
if(-r "$tftpDir/xcat/nbfs.x86.gz"){
copy("$tftpDir/xcat/nbfs.x86.gz", "$tftpDir/xcat/partimage/x86/");
}else{
errorSig($callback, "$tftpDir/xcat/nbfs.x86.gz was not found, please run 'mknb x86' first.");
}
# verify that kernel is there
if(-r "$tftpDir/xcat/nbk.x86"){
copy("$tftpDir/xcat/nbk.x86", "$tftpDir/xcat/partimage/x86/");
}else{
errorSig($callback, "$tftpDir/xcat/nbk.x86 was not found, please run 'mknb x86' first.");
}
# now get the image supped up
mkPartImage($node,$tftpDir,$callback);
$doneimgs{"$os|$arch"} = 1;
}
# part 4. Create the boot parameters for the machine.
#
# if there was an error then setBootParams will return 1;
# if there is no error it will return 0.
if(setBootParams($node,$callback, $instDir, $mode)){
next;
}
}
}
sub mkPartImage{
# modify nbfs.tgz
my $node = shift;
my $tftpDir = shift;
my $callback = shift;
my $pDataDir = "$pImageDir/data";
my $pNbDir = "$tftpDir/xcat/partimage/x86/tmp";
# $callback->({data=>["Creating nbfs.$arch.gz in $tftpdir/xcat"]});
print "$pNbDir";
# remove this directory if it exists.
if(-d $pNbDir){
rmtree("$pNbDir");
# errorSig($callback, "could not remove $pNbDir");
}
if(-r "$pNbDir/../pnbfs.x86.gz"){
unless(unlink("$pNbDir/../pnbfs.x86.gz") == 0 ){
errorSig($callback, "could not remove $pNbDir/../pnbfs.x86.gz\n");
}
}
mkpath($pNbDir);
$callback=>({data=>["cd $pNbDir; gunzip -c ../nbfs.x86.gz | cpio -id"]});
system("cd $pNbDir; gunzip -c ../nbfs.x86.gz | cpio -id");
# Tput files
# /usr/bin/tput
copy("$pDataDir/tput", "$pNbDir/usr/bin/");
system("chmod 755 $pNbDir/usr/bin/");
# /lib/libdl.so.2
copy("$pDataDir/lib/libdl.so.2", "$pNbDir/lib/");
# /usr/lib/libncursesw.so.5
copy("$pDataDir/lib/libncursesw.so.5", "$pNbDir/usr/lib/");
copy("$pDataDir/lib/ld-linux.so.2", "$pNbDir/lib/");
system("chmod 755 $pNbDir/lib/ld-linux.so.2");
copy("$pDataDir/lib/libc.so.6", "$pNbDir/lib/");
system("chmod 755 $pNbDir/lib/libc.so.6");
copy("$pDataDir/sfdisk", "$pNbDir/sbin");
system("chmod 755 $pNbDir/sbin/sfdisk");
copy("$pDataDir/e2fsck", "$pNbDir/sbin");
system("chmod 755 $pNbDir/sbin/e2fsck");
copy("$pDataDir/partimage", "$pNbDir/bin/");
system("chmod 755 $pNbDir/bin/partimage");
copy("$pDataDir/partimage.sh", "$pNbDir/etc/init.d/S80partimage.sh");
unlink("$pNbDir/etc/init.d/S99xcat.sh");
# we make a new nbfs.x86.gz and call it pnbfs. Its really just
# nbfs with some other things added and taken away.
system("cd $pNbDir; find . | cpio -o -H newc | gzip -9 > $pNbDir/../pnbfs.x86.gz");
# remove all the junk after compressing the file.
rmtree("$pNbDir");
}
# set the boot parameters of the node. If we return 1 there is an error
# if we return 0 everything went according to plan.
sub setBootParams{
my $node = shift;
my $callback = shift;
my $instDir = shift;
my $mode = shift;
# get all the info we need for the node:
# ms, NIC, etc.
my $restab = xCAT::Table->new('noderes');
my $bptab = xCAT::Table->new('bootparams',-create=>1);
my $hmtab = xCAT::Table->new('nodehm');
my $ent =
$restab->getNodeAttribs(
$node,['nfsserver', 'primarynic', 'installnic']
);
my $sent =
$hmtab->getNodeAttribs(
$node, ['serialport', 'serialspeed', 'serialflow']
);
unless ($ent and $ent->{nfsserver}){
errorSig($callback, "No noderes.nfsserver for $node defined");
$callback->("No noderes.nfsserver for $node defined");
return 1;
}
my $kernel = "xcat/partimage/x86/nbk.x86";
my $initrd = "xcat/partimage/x86/pnbfs.x86.gz";
my $kcmdline = "quiet";
# console stuff
if (defined $sent->{serialport}){
unless ($sent->{serialspeed}){
errorSig($callback, "serialport defined, but no serialspeed for $node in nodehm table");
return 1;
}
$kcmdline .=
" console=ttyS"
. $sent->{serialport} . ","
. $sent->{serialspeed};
if($sent and ($sent->{serialflow} =~ /(ctsrts|cts|hard)/)){
$kcmdline .= "n8r";
}
}
# xcatd stuff
# get xCATd port:
my $xPort = xCAT::Utils->get_site_attribute("xcatdport");
$kcmdline .= " xcatd=" . $ent->{nfsserver} . ":" . $xPort;
$kcmdline .= " pcfg=http://" . $ent->{nfsserver} . "/$instDir/autoinst/$node";
$kcmdline .= " mode=$mode";
$bptab->setNodeAttribs(
$node, {
kernel => $kernel,
initrd => $initrd,
kcmdline => $kcmdline
}
);
return 0;
}
sub get_tmpl_file_name {
my $base=shift;
my $profile=shift;
my $os=shift;
my $arch=shift;
if (-r "$base/$profile.$os.$arch.tmpl") {
return "$base/$profile.$os.$arch.tmpl";
}
elsif (-r "$base/$profile.$arch.tmpl") {
return "$base/$profile.$arch.tmpl";
}
elsif (-r "$base/$profile.$os.tmpl") {
return "$base/$profile.$os.tmpl";
}
elsif (-r "$base/$profile.tmpl") {
return "$base/$profile.tmpl";
}
return "";
}
1;