From 60c15d4c94bc18fa956a6a4193b9d6b43d459b2f Mon Sep 17 00:00:00 2001 From: mxi1 Date: Thu, 17 Mar 2011 15:55:44 +0000 Subject: [PATCH] Added the new command "imgcapture" to svn trunk. please run "man imgcapture" for more information. git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@9076 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd --- xCAT-client/pods/man1/imgcapture.1.pod | 101 +++++++ xCAT-client/xCAT-client.spec | 1 + xCAT-server/lib/perl/xCAT/SvrUtils.pm | 10 + xCAT-server/lib/xcat/plugins/anaconda.pm | 2 +- xCAT-server/lib/xcat/plugins/imgcapture.pm | 324 +++++++++++++++++++++ 5 files changed, 437 insertions(+), 1 deletion(-) create mode 100644 xCAT-client/pods/man1/imgcapture.1.pod create mode 100644 xCAT-server/lib/xcat/plugins/imgcapture.pm diff --git a/xCAT-client/pods/man1/imgcapture.1.pod b/xCAT-client/pods/man1/imgcapture.1.pod new file mode 100644 index 000000000..46d0cb4bd --- /dev/null +++ b/xCAT-client/pods/man1/imgcapture.1.pod @@ -0,0 +1,101 @@ +=head1 NAME + +B - Captures an image from one running diskful Linux node, prepares the rootimg directory, kernel and initial ramdisks for the B/B command to generate the statelite/stateless rootimg. + +=head1 SYNOPSIS + +B node [B<-p>|B<--profile> I] [B<-i> I] [B<-n> I] [B<-V>|B<--verbose>] + +B [B<-h> | B<--help>] | [B<-v> | B<--version>] + +=head1 DESCRIPTION + +The B command will capture an image from one running diskful Linux node, prepares the rootimg directory, kernel and initial rmadisks for the B/B command to generate the statelite/stateless rootimg. + +The B should be one diskful Linux node, managed by the xCAT MN, and the remote shell between MN and the B should have been configured. AIX is not supported. + +The B, B and B attributes for the stateless/statelite image to be created are duplicated from the B's attribute. If the B<-p|--profile> I option is specified, the image will be created under "/>/netboot///>/rootimg". + +The default files/directories excluded in the image are specified by /opt/xcat/share/xcat/netboot//>...imgcapture.exlist; also, you can put your customized file (>...imgcapture.exlist) to /install/custom/netboot/. The directories in the default I<.imgcapture.exlist> file are necessary to capture image from the diskful Linux node managed by xCAT, please don't remove it. + +The image captured will be extracted into the />/netboot/>/>/>/rootimg directory. + +After the B command returns without any errors, you can customize the rootimg and run the B/B command for your own request. + +=head1 OPTIONS + +=over 12 + +=item B<-p|--profile> I + +assign I as the profile of the image to be created. + +=item B<-i> I + +The network interface the diskless node will boot over (e.g. eth0), which is used by the B command to generate initial ramdisks. + +This is optional. + +=item B<-n> I + +The driver modules needed for the network interface, which is used by the B command to generate initial ramdisks. + +This is optional. By default, the B command can provide drivers for the following network interfaces: + +For x86 or x86_64 platform: + + tg3 bnx2 bnx2x e1000 e1000e igb m1x_en + +For ppc64 platform: + + e1000 e1000e igb ibmveth ehea + +For S390x: + + qdio ccwgroup + +If the network interface is not in the above list, you'd better specify the driver modules with this option. + +=item B<-h|--help> + +Display the usage message. + +=item B<-v|--version> + +Display the version. + +=item B<-V|--verbose> + +Verbose output. + +=back + +=head1 RETRUN VALUE + +0 The command completed sucessfully. + +1 An error has occurred. + +=head1 EXAMPLES + +B is one diskful Linux node, which is managed by xCAT. + +1. In order to create the image, run the following command: + + imgcapture node1 + +2. In order to create the image with B as profile, run the command: + + imgcapture node1 -p hpc + +3. Create the image: its profile is B, and the network interface the diskless node will boot over is B, the driver modules for this network interface is B. + + imgcapture node1 -p hpc -i eth0 -n e1000e + +=head1 FILES + +/opt/xcat/bin/imgcapture + +=head1 SEE ALSO + +L, L, L, L, L diff --git a/xCAT-client/xCAT-client.spec b/xCAT-client/xCAT-client.spec index 5c73c447a..723b5519e 100644 --- a/xCAT-client/xCAT-client.spec +++ b/xCAT-client/xCAT-client.spec @@ -143,6 +143,7 @@ ln -sf ../bin/xcatclient $RPM_BUILD_ROOT/%{prefix}/bin/lsflexnode ln -sf ../bin/xcatclient $RPM_BUILD_ROOT/%{prefix}/bin/rmflexnode ln -sf ../bin/xcatclient $RPM_BUILD_ROOT/%{prefix}/bin/mkflexnode ln -sf ../bin/xcatclient $RPM_BUILD_ROOT/%{prefix}/bin/lsslp +ln -sf ../bin/xcatclient $RPM_BUILD_ROOT/%{prefix}/bin/imgcapture ln -sf ../bin/xcatclientnnr $RPM_BUILD_ROOT/%{prefix}/bin/nodegrpch ln -sf ../bin/xcatclientnnr $RPM_BUILD_ROOT/%{prefix}/sbin/tabdump ln -sf ../bin/xcatclientnnr $RPM_BUILD_ROOT/%{prefix}/sbin/tabprune diff --git a/xCAT-server/lib/perl/xCAT/SvrUtils.pm b/xCAT-server/lib/perl/xCAT/SvrUtils.pm index f8d78a43e..f96d9d04a 100644 --- a/xCAT-server/lib/perl/xCAT/SvrUtils.pm +++ b/xCAT-server/lib/perl/xCAT/SvrUtils.pm @@ -477,6 +477,16 @@ sub get_exlist_file_name { return xCAT::SvrUtils::get_file_name($searchpath, "exlist", @_); } +# for the "imgcapture" command + +sub get_imgcapture_exlist_file_name { + my $searchpath = shift; + if ($searchpath and $searchpath =~ m/xCAT::SvrUtils/) { + $searchpath = shift; + } + return xCAT::SvrUtils::get_file_name($searchpath, "imgcapture.exlist", @_); +} + #------------------------------------------------------------------------------- diff --git a/xCAT-server/lib/xcat/plugins/anaconda.pm b/xCAT-server/lib/xcat/plugins/anaconda.pm index 448c8d129..b1ab2ee86 100644 --- a/xCAT-server/lib/xcat/plugins/anaconda.pm +++ b/xCAT-server/lib/xcat/plugins/anaconda.pm @@ -615,7 +615,7 @@ sub mknetboot #} # append the mac address my $mac; - if( $useifname && $machash->{$node}->[0] && $machash->{$node}->[0]->{'mac'}) { + if($machash->{$node}->[0] && $machash->{$node}->[0]->{'mac'}) { # TODO: currently, only "mac" attribute with classic style is used, the "|" delimited string of "macaddress!hostname" format is not used $mac = $machash->{$node}->[0]->{'mac'}; if ( (index($mac, "|") eq -1) and (index($mac, "!") eq -1) ) { diff --git a/xCAT-server/lib/xcat/plugins/imgcapture.pm b/xCAT-server/lib/xcat/plugins/imgcapture.pm new file mode 100644 index 000000000..643dd4342 --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/imgcapture.pm @@ -0,0 +1,324 @@ +#!/usr/bin/perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html + +package xCAT_plugin::imgcapture; + +BEGIN +{ + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; +} + +use lib "$::XCATROOT/lib/perl"; + +use strict; + +use Data::Dumper; # for debug purpose +use Getopt::Long; +use xCAT::MsgUtils; +use xCAT::Utils; +use xCAT::Table; +use File::Path qw(mkpath); + +Getopt::Long::Configure("bundling"); +Getopt::Long::Configure("pass_through"); + +my $verbose = 0; +my $installroot = "/install"; + +sub handled_commands { + return { "imgcapture" => "imgcapture" }; +} + +sub process_request { + my $request = shift; + my $callback = shift; + my $doreq = shift; + + my $node; + if (exists $request->{node}) { + $node = $request->{node}->[0]; + } + + $installroot = xCAT::Utils->getInstallDir(); + @ARGV = @{$request->{arg}} if (defined $request->{arg}); + my $argc = scalar @ARGV; + + my $usage = "Usage: imgcapture [-p | --profile ] [-i ] [-n ] [-V | --verbose] \n imgcapture [-h|--help] \n imgcapture [-v|--version]"; + + my $os; + my $arch; + my $profile; + my $bootif; + my $netdriver; + my $help; + my $version; + + GetOptions( + "profile|p=s" => \$profile, + "i=s" => \$bootif, + 'n=s' => \$netdriver, + "help|h" => \$help, + "version|v" => \$version, + "verbose|V" => \$verbose + ); + + if($version) { + my $version = xCAT::Utils->Version(); + my $rsp = {}; + $rsp->{data}->[0] = $version; + xCAT::MsgUtils->message("D", $rsp, $callback); + return 0; + } + + if($help) { + my $rsp = {}; + $rsp->{data}->[0] = $usage; + xCAT::MsgUtils->message("D", $rsp, $callback); + return 0; + } + + if( ! $node ) { + my $rsp = {}; + $rsp->{data}->[0] = $usage; + xCAT::MsgUtils->message("D", $rsp, $callback); + return 0; + } + + my $nodetypetab = xCAT::Table->new("nodetype"); + my $ref_nodetype = $nodetypetab->getNodeAttribs($node, ['os','arch','profile']); + $os = $ref_nodetype->{os}; + $arch = $ref_nodetype->{arch}; + unless($profile) { + $profile = $ref_nodetype->{profile}; + } + + imgcapture($node, $os, $arch, $profile, $bootif, $netdriver, $callback, $doreq); +} + +sub imgcapture { + my ($node, $os, $arch, $profile, $bootif, $netdriver, $callback, $subreq) = @_; + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = "nodename is $node; os is $os; arch is $arch; profile is $profile"; + $rsp->{data}->[1] = "bootif is $bootif; netdriver is $netdriver"; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + # make sure the "/" partion is on the disk, + my $output = xCAT::Utils->runxcmd({command => ["xdsh"], node => [$node], arg =>["stat / -f |grep Type"]}, $subreq, -1, 1); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{the output of "stat / -f |grep Type" on $node is:}; + foreach my $o (@$output) { + push @{$rsp->{data}}, $o; + } + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + if($::RUNCMD_RC) { #failed + my $rsp = {}; + $rsp->{data}->[0] = qq{The "xdsh" command fails to run on the $node}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + # parse the output of "stat / -f |grep Type", + $output->[0] =~ m/Type:\s+(.*)$/; + my $fstype = $1; + if ($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{The file type is $fstype}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + # make sure the rootfs type is not nfs or tmpfs + if($fstype eq "nfs" or $fstype eq "tmpfs") { + my $rsp = {}; + $rsp->{data}->[0] = qq{This node might not be diskful Linux node, please check it.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + my $distname = $os; + while ( $distname and ( ! -r "$::XCATROOT/share/xcat/netboot/$distname/") ) { + chop($distname); + } + + unless($distname) { + $callback->({error=>["Unable to find $::XCATROOT/share/xcat/netboot directory for $os"], errorcode => [1]}); + return; + } + + my $exlistloc = xCAT::SvrUtils->get_imgcapture_exlist_file_name("$installroot/custom/netboot/$distname", $profile, $os, $arch); + unless ($exlistloc) { + $exlistloc = xCAT::SvrUtils->get_imgcapture_exlist_file_name("$::XCATROOT/share/xcat/netboot/$distname", $profile, $os, $arch); + } + + my $xcat_imgcapture_tmpfile = "/tmp/xcat_imgcapture.$$"; + + my $excludestr = "cd /; find ."; + + if($exlistloc) { + my $exlist; + open $exlist, "<", $exlistloc; + + while(<$exlist>) { + $_ =~ s/^\s+//; + unless($_ =~ m{^#}) { + $excludestr .= qq{ ! -path "$_"}; + } + } + + close $exlist; + } else { + # the following directories must be exluded when capturing the image + my @default_exlist = ("./tmp*", "./proc*", "./sys*", "./dev*", "./xcatpost*", "./install*"); + foreach my $item (@default_exlist) { + $excludestr .= qq{ ! -path "$item"}; + } + } + + $excludestr .= " |cpio -H newc -o |gzip -c - >$xcat_imgcapture_tmpfile"; + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{The excludestr is "$excludestr"}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + # run the command via "xdsh" + + xCAT::Utils->runxcmd({command => ["xdsh"], node => [$node], arg => ["echo -n >$xcat_imgcapture_tmpfile"]}, $subreq, -1, 1); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{running "echo -n > $xcat_imgcapture_tmpfile" on $node}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + if($::RUNCMD_RC) { # the xdsh command fails + my $rsp = {}; + $rsp->{data}->[0] = qq{The "xdsh" command fails to run "echo -n > $xcat_imgcapture_tmpfile" on $node}; + xCAT:MsgUtils->message("E", $rsp, $callback); + return; + } + + xCAT::Utils->runxcmd({command => ["xdsh"], node => [$node], arg => [$excludestr]}, $subreq, -1, 1); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{running "$excludestr" on $node via the "xdsh" command}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + if($::RUNCMD_RC) { # the xdsh command fails + my $rsp = {}; + $rsp->{data}->[0] = qq{The "xdsh" command fails to run "$excludestr" on $node}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + # copy the image captured on $node back via the "scp" command + xCAT::Utils->runcmd("scp $node:$xcat_imgcapture_tmpfile $xcat_imgcapture_tmpfile"); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{Running "scp $node:$xcat_imgcapture_tmpfile $xcat_imgcapture_tmpfile"}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + if($::RUNCMD_RC) { + my $rsp ={}; + $rsp->{data}->[0] = qq{The scp command fails}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + # extract the $xcat_imgcapture_tmpfile file to /install/netboot/$os/$arch/$profile/rootimg + my $rootimgdir = "$installroot/netboot/$os/$arch/$profile/rootimg"; + + # empty the rootimg directory before extracting the image captured on the diskful Linux node + if( -d $rootimgdir ) { + unlink $rootimgdir; + } + mkpath($rootimgdir); + + xCAT::Utils->runcmd("cd $rootimgdir; gzip -cd $xcat_imgcapture_tmpfile|cpio -idum"); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{Extracting the image to $rootimgdir}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + if($::RUNCMD_RC) { + my $rsp = {}; + $rsp->{data}->[0] = qq{fails to run the "gzip -cd xx |cpio -idum" command}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{Creating the spots exluded when capturing on $node...}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + my @spotslist = ("/tmp/", "/proc/", "/sys/", "/dev/"); + + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{The spots to be restored in the image are:}; + foreach (@spotslist) { + push @{$rsp->{data}}, $_; + } + xCAT::MsgUtils->message("D", $rsp, $callback); + } + # create the directories listed in @spotslist in the rootimg + foreach my $path (@spotslist) { + mkpath("$rootimgdir$path"); + } + + # the next step is to call "genimage" + my $platform = getplatform($os); + if( -e "$::XCATROOT/share/xcat/netboot/$platform/genimage" ) { + my $cmd = "$::XCATROOT/share/xcat/netboot/$platform/genimage -o $os -a $arch -p $profile "; + if($bootif) { + $cmd .= "-i $bootif "; + } + if($netdriver) { + $cmd .= "-n $netdriver"; + } + my $rsp = {}; + $rsp->{data}->[0] = qq{Generating kernel and initial ramdisks}; + xCAT::MsgUtils->message("D", $rsp, $callback); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{"The genimage command is: $cmd"}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + xCAT::Utils->runcmd($cmd); + } else { + my $rsp = {}; + $rsp->{data}->[0] = qq{Can't run the "genimage" command for $os}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + return 0; +} + +sub getplatform { + my $os = shift; + my $platform; + if ($os =~ m/rh.*/) { + $platform = "rh"; + } elsif ($os =~ m/centos.*/) { + $platform = "centos"; + } elsif ($os =~ m/fedora.*/) { + $platform = "fedora"; + } elsif ($os =~ m/SL.*/) { + $platform = "SL"; + } elsif ($os =~ m/sles.*/) { + $platform = "sles"; + } elsif ($os =~ m/suse.*/) { + $platform = "suse"; + } + + return $platform; +} + +1;