diff --git a/xCAT-server/lib/xcat/plugins/destiny.pm b/xCAT-server/lib/xcat/plugins/destiny.pm index d4c6cfd39..eab73bcfb 100644 --- a/xCAT-server/lib/xcat/plugins/destiny.pm +++ b/xCAT-server/lib/xcat/plugins/destiny.pm @@ -137,7 +137,7 @@ sub setdestiny { if ($ient->{kcmdline}) { $hash->{kcmdline} = $ient->{kcmdline} } $bptab->setNodeAttribs($_,$hash); } - } elsif ($state =~ /^install[=\$]/ or $state eq 'install' or $state =~ /^netboot[=\$]/ or $state eq 'netboot' or $state eq "image" or $state eq "winshell" or $state =~ /^osimage/ or $state eq 'statelite') { + } elsif ($state =~ /^install[=\$]/ or $state eq 'install' or $state =~ /^netboot[=\$]/ or $state eq 'netboot' or $state eq "image" or $state eq "winshell" or $state =~ /^osimage/ or $state =~ /^statelite/) { chomp($state); my $target; if ($state =~ /=/) { diff --git a/xCAT-server/lib/xcat/plugins/litetree.pm b/xCAT-server/lib/xcat/plugins/litetree.pm new file mode 100644 index 000000000..3ee7ae621 --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/litetree.pm @@ -0,0 +1,302 @@ +package xCAT_plugin::litetree; +use xCAT::NodeRange; +use Data::Dumper; +use xCAT::Utils; +use Sys::Syslog; +use xCAT::GlobalDef; +use xCAT::Table; +use Getopt::Long; +Getopt::Long::Configure("bundling"); +Getopt::Long::Configure("pass_through"); + +use strict; +# synchonize files and directories from mulitple sources. + +# object is to return a list of files to be syncronized. +# requesting them. By default we will do read-write files. + + +my $syncdirTab = "litetree"; +my $syncfileTab = "litefile"; +my $errored = 0; + + +sub handled_commands { + # command is syncmount, syncdir is the perl module to use. + return { + litetree => "litetree", + litefile => "litetree", + ilitefile => "litetree" + } +} + +sub usage { + my $command = shift; + my $callback = shift; + my $error = shift; + my $msg; + if($command eq "ilitefile"){ + $msg = "ilitefile +\texample:\n\tilitefile centos5.3-x86_64-compute" + } else{ + $msg = "some general usage string"; + } + + if($error){ + $callback->({error=>[$msg],errorcode=>[$error]}); + }else{ + $callback->({info=>[$msg]}); + } +} + +sub process_request { + my $request = shift; + my $callback = shift; + my $noderange; + my $image; + # the request can come from node or some one running command + # on xCAT mgmt server + # argument could also be image... + + # if request comes from user: + if($request->{node}){ + $noderange = $request->{node}; + + # if request comes from node post script .awk file. + }elsif($request->{'_xcat_clienthost'}){ + $noderange = $request->{'_xcat_clienthost'}; + }else{ + $callback->({error=>["Well Kemosabi, I can't figure out who you are."],errorcode=>[1]}); + return; + } + + if(!$noderange){ + my $usage_string="Missing Noderange\n"; + $callback->({error=>[$usage_string],errorcode=>[1]}); + $request = {}; + return; + } + + my $command = $request->{command}->[0]; + if($command eq "litetree"){ + return syncmount("dir",$request,$callback,$noderange); + }elsif($command eq "litefile"){ + return syncmount("file",$request, $callback,$noderange); + }elsif($command eq "ilitefile"){ + #print Dumper($request); + unless($request->{arg}){ + usage($command, $callback,1); + return 1; + } + return syncmount("image",$request, $callback,$request->{arg}); + }else{ + $callback->({error=>["error in code..."], errorcode=>[127]}); + $request = {}; + return; + } + +} + + +sub syncmount { + my $syncType = shift; + my $request = shift; + my $callback = shift; + # deterimine which node is calling this + # then find out what directories to use. + my $noderange = shift; + my @nodes = @{$noderange}; + my $tab; + if($syncType eq 'dir'){ + $tab = xCAT::Table->new($syncdirTab,-create=>1); + }elsif($syncType =~ /file|image/ ){ + $tab = xCAT::Table->new($syncfileTab,-create=>1); + }else{ + $callback->({error=>["error in code..."], errorcode=>[127]}); + $request = {}; + return; + } + my $ostab; + my %osents; + unless($syncType =~ /image/){ + $ostab = xCAT::Table->new('nodetype'); + %osents = %{$ostab->getNodesAttribs(\@nodes,['profile','os','arch'])}; + } + foreach my $node (@nodes){ + # node may be an image... + my $ent; + if(! $syncType =~ /image/){ + $ent = $osents{$node}->[0]; + unless($ent->{os} && $ent->{arch} && $ent->{profile}){ + $callback->({error=>["$node does not have os, arch, or profile defined in nodetype table"],errorcode=>[1]}); + $request = {}; + return; + } + } + my $fData = getNodeData($syncType,$node,$ent,$tab,$callback); + # now we go through each directory and search for the file. + showSync($syncType,$callback, $node, $fData); + } +} + + +# In most cases the syncdir will be on the management node so +# want to make sure its not us before we mount things. +sub showSync { + my $syncType = shift; # dir or file + my $callback = shift; + my $node = shift; + my $dirs = shift; + + #print Dumper($dirs); + # go through each directory in priority order + #mkdir "/mnt/xcat"; + if($syncType eq "dir"){ + foreach my $priority (sort {$a <=> $b} keys %$dirs){ + # split the nfs server up from the directory: + my ($server, $dir) = split(/:/,$dirs->{$priority}); + if(grep /\$/, $dir){ + $dir = subVar($dir,$node,'dir'); + $dir =~ s/\/\//\//g; + } + + if(grep /\$/, $server){ + $server = subVar($server,$node,'server'); + } + my $mntpnt = $server . ":" . $dir; + # ok, now we have all the mount points. Let's go through them all? + $callback->({info => "$node: $mntpnt"}); + } + }elsif($syncType =~ /file|image/){ + foreach my $file (sort keys %$dirs){ + my $options = $dirs->{$file}; + # persistent,rw + my $out = sprintf("%s: %-13s %s", $node, $options, $file); + $callback->({info => $out}); + } + } + +} + +# some directories will have xCAT database values, like: +# $nodetype.os. If that is the case we need to open up +# the database and look at them. We need to make sure +# we do this sparingly... We don't like tons of hits +# to the database. +sub subVar { + my $dir = shift; + my $node = shift; + my $type = shift; + # parse all the dollar signs... + # if its a directory then it has a / in it, so you have to parse it. + # if its a server, it won't have one so don't worry about it. + my @arr = split("/", $dir); + my $fdir = ""; + foreach my $p (@arr){ + if($p =~ /^\$/){ + # have to sub here: + # get rid of the $ sign. + $p =~ s/\$//g; + # check if p is just the node name: + + if($p eq 'node'){ + # it is so, just return the node. + $fdir .= "/$node"; + }else{ + # ask the xCAT DB what the attribute is. + my ($table, $col) = split('\.', $p); + my $tab = xCAT::Table->new($table); + my $ent = $tab->getNodeAttribs($node,[$col]); + my $val = $ent->{$col}; + unless($val){ + # couldn't find the value!! + $val = "UNDEFINED" + } + if($type eq 'dir'){ + $fdir .= "/$val"; + }else{ + $fdir .= $val; + } + } + }else{ + # no substitution here + $fdir .= "/$p"; + } + } + return $fdir; +} + +# get all the directories or files for given image related to this node. +sub getNodeData { + my $type = shift; + my $node = shift; + my $ent = shift; + my $tab = shift; + my $cb = shift; # callback to print messages!! + # the image name will be something like rhels5.4-x86_64-nfsroot + my $image; + unless($type =~ /image/){ + $image = $ent->{os} . "-" . $ent->{arch} . "-" . $ent->{profile}; + }else{ + $image = $node; + } + my @imageInfo; + my @attrs; + if($type eq "dir"){ + @attrs = ['priority', 'directory']; + }elsif($type =~ /file|image/){ + @attrs = ['file','options']; + }else{ + print "Yikes! error in the code litefile;getNodeData!"; + exit 1; + } + # get the directories with no names + push @imageInfo, $tab->getAttribs({image => ''}, @attrs); + # get the ALL directories + push @imageInfo, $tab->getAttribs({image => 'ALL'}, @attrs); + # get for the image specific directories + push @imageInfo, $tab->getAttribs({image => $image}, @attrs); + # pass back a reference to the directory + + # now we need to sort them + return mergeArrays($type,\@imageInfo,$cb); +} + +sub mergeArrays { + my $type = shift; # file or dir? + my $arr = shift; # array of info from the tables. + my $cb = shift; # callback routine + my $attrs; + if($type eq "dir"){ + foreach(@$arr){ + if($_->{directory} eq ''){ next; } + $attrs->{$_->{priority}} = $_->{directory}; + } + }elsif($type =~ /file|image/){ + foreach(@$arr){ + if($_->{file} eq ''){ next; } + my $o = $_->{options}; + if(!$o){ + $o = "tmpfs,rw"; + } + # TODO: put some logic in here to make sure that ro is alone. + # if type is ro and con, then this is wrong silly! + #if($p eq "ro" and $t eq "con"){ + # my $f = $_->{file}; + # $cb->({info => "#skipping: $f. not allowed to be ro and con"}); + # next; + #} + $attrs->{$_->{file}} = $o; + } + }else{ + print "Yikes! Error in the code in mergeArrays!\n"; + exit 1; + } + #print "mergeArrays...\n"; + #print Dumper($attrs); + return $attrs; +} + +1; +#vim: set ts=2 + diff --git a/xCAT-server/lib/xcat/plugins/statelite.pm b/xCAT-server/lib/xcat/plugins/statelite.pm new file mode 100644 index 000000000..f0fa16047 --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/statelite.pm @@ -0,0 +1,319 @@ +package xCAT_plugin::statelite; +BEGIN +{ + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; +} +use lib "$::XCATROOT/lib/perl"; +use xCAT::Table; +use Getopt::Long; +use File::Basename; +use File::Path; +use File::Copy; +use File::Find; +use Cwd; +use File::Temp; +use xCAT::Utils qw(genpassword); +use xCAT::SvrUtils; +use Data::Dumper; +Getopt::Long::Configure("bundling"); +Getopt::Long::Configure("pass_through"); + +my $cmdname = "liteimg"; +my $statedir = ".statelite"; +my $verbose = "0"; +sub handled_commands { + return { + $cmdname => "statelite" + } +} + +# function to handle request. Basically, get the information +# about the image and then do the action on it. Is that vague enough? +sub process_request { + my $request = shift; + my $callback = shift; + my $doreq = shift; + + my $sitetab = xCAT::Table->new('site'); + my $ent = $sitetab->getAttribs({key=>'installdir'},['value']); + my $installroot = "/install"; + + # get /install directory + if ($ent and $ent->{value}) { + $installroot = $ent->{value}; + } + # if not defined, error out... or should we set it as default? + unless ($installroot) { + $callback->({error=>["No installdir defined in site table"],errorcode=>[1]}); + return; + } + + @ARGV = @{$request->{arg}}; + my $argc = scalar @ARGV; + if ($argc == 0) { + $callback->({info=>["$cmdname -h # this help message\n$cmdname -v # version\n$cmdname -V # verbose\n$cmdname [-p profile] [-a architecture] [-o OS]\n$cmdname imagename"]}); + return; + } + + my $osver; + my $arch; + my $profile; + my $rootimg_dir; + my $destdir; + my $imagename; + + GetOptions( + "profile|p=s" => \$profile, + "arch|a=s" => \$arch, + "osver|o=s" => \$osver, + "help|h" => \$help, + "version|v" => \$version, + "verbose|V" => \$verbose + ); + + if ($version) { + my $version = xCAT::Utils->Version(); + $callback->({info=>[$version]}); + return; + } + if ($help) { + # $callback->({info=>["packimage -h \npackimage -v \npackimage [-p profile] [-a architecture] [-o OS] \npackimage imagename"]}); + $callback->({info=>["imglite... prep an image to be lite"]}); + return; + } + + # if they only gave us the image name: + if (@ARGV > 0) { + $imagename=$ARGV[0]; + if ($arch or $osver or $profile) { + $callback->({error=>["-o, -p and -a options are not allowed when a image name is specified."],errorcode=>[1]}); + return; + } + #load the module in memory + eval {require("$::XCATROOT/lib/perl/xCAT/Table.pm")}; + if ($@) { + $callback->({error=>[$@],errorcode=>[1]}); + return; + } + #get the info from the osimage and linux + + my $osimagetab=xCAT::Table->new('osimage', -create=>1); + if (!$osimagetab) { + # os image doesn't exist in table. + $callback->({error=>["The osimage table cannot be opened."],errorcode=>[1]}); + return; + } + + # open the linux image table to get more attributes... for later. + my $linuximagetab=xCAT::Table->new('linuximage', -create=>1); + if (!$linuximagetab) { + $callback->({error=>["The linuximage table cannot be opened."],errorcode=>[1]}); + return; + } + + # get the os, arch, and profile from the image name table. + (my $ref) = $osimagetab->getAttribs({imagename => $imagename}, 'osvers', 'osarch', 'profile'); + if (!$ref) { + $callback->({error=>["Cannot find image \'$imagename\' from the osimage table."],errorcode=>[1]}); + return; + } + + $osver=$ref->{'osvers'}; + $arch=$ref->{'osarch'}; + $profile=$ref->{'profile'}; + } # end of case where they give us osimage. + + unless ($osver and $arch and $profile ) { + $callback->({error=>["osimage.osvers, osimage.osarch, and osimage.profile and must be specified for the image $imagename in the database, or you must specify -o os -p profile -a arch."],errorcode=>[1]}); + return; + } + + $destdir="$installroot/netboot/$osver/$arch/$profile"; + $rootimg_dir="$destdir/rootimg"; + my $oldpath=cwd(); + + # now we have all the info we need: + # - rootimg_dir + # - osver + # - arch + # - profile + $callback->({info=>["going to modify $rootimg_dir"]}); + + + # now get the files for the node + my @synclist = xCAT::Utils->runcmd("ilitefile $osver-$arch-$profile", 0, 1); + if(!@synclist){ + $callback->({error=>["There are no files to sync for $osver-$arch-$profile. You have to have some files read/write filled out in the synclist table."],errorcode=>[1]}); + return; + } + + # all I care about are the files, not the characteristics at this point: + my @files; + foreach my $line (@synclist){ + foreach (@{$line}){ + my $f = (split(/\s+/, $_))[2]; + if($f =~ /^\//){ + push @files, $f; + }else{ + # make sure each file begins with "/" + $callback->({error=>["$f in litefile does not begin with absolute path! Need a '/' in the beginning"],errorcode=>[1]}); + return; + } + } + } + + liteMe($rootimg_dir,\@files, $callback); + + +} + + +sub liteMe { + # Arg 1: root image dir: /install/netboot/centos5.3/x86_64/compute/rootimg + my $rootimg_dir = shift; + my $files = shift; + # Arg 2: callback ref to make comments... + my $callback = shift; + unless(-d $rootimg_dir){ + $callback->({error=>["no rootimage dir"],errorcode=>[1]}); + return; + } + # snapshot directory for tmpfs and persistent data. + $callback->({info=>["creating $rootimg_dir/$statedir"]}); + mkpath("$rootimg_dir/$statedir/tmpfs"); + # now make a place for all the files. + + + # this loop creates symbolic links for each of the files in the sync list. + # 1. copy original contents if they exist to .default directory + # 2. remove file + # 3. create symbolic link to .statelite + + my $sym = "1"; # sym = 0 means no symlinks, just bindmount + if($sym){ + foreach my $f (@$files){ + # copy the file to /.defaults + my $rif = $rootimg_dir . $f; + my $d = dirname($f); + if( -f "$rif" or -d "$rif"){ + # if its already a link then leave it alone. + unless(-l $rif){ + # mk the directory if it doesn't exist: + $verbose && $callback->({info=>["mkdir -p $rootimg_dir/.default$d"]}); + system("mkdir -p $rootimg_dir/.default$d"); + + # copy the file in place. + $verbose && $callback->({info=>["cp -a $rif $rootimg_dir/.default$d"]}); + system("cp -a $rif $rootimg_dir/.default$d"); + + # remove the real file + $verbose && $callback->({info=>["rm -rf $rif"]}); + system("rm -rf $rif"); + + } + + }else{ + + # in this case the file doesn't exist in the image so we create something to it. + # here we're modifying the read/only image + unless(-d "$rootimg_dir$d"){ + $verbose && $callback->({info=>["mkdir -p $rootimg_dir$d"]}); + system("mkdir -p $rootimg_dir$d"); + } + + unless(-d "$rootimg_dir/.default$d"){ + $verbose && $callback->({info=>["mkdir -p $rootimg_dir/.default$d"]}); + system("mkdir -p $rootimg_dir/.default$d"); + } + + # now make touch the file: + if($f =~ /\/$/){ + # if its a directory, make the directory in .default + $verbose && $callback->({info=>["mkdir -p $rootimg_dir/.default$f"]}); + system("mkdir -p $rootimg_dir/.default$f"); + }else{ + # if its just a file then link it. + $verbose && $callback->({info=>["touch $rootimg_dir/.default$f"]}); + system("touch $rootimg_dir/.default$f"); + } + } + + # now create the spot in tmpfs + $verbose && $callback->({info=>["mkdir -p $rootimg_dir/$statedir/tmpfs$d"]}); + system("mkdir -p $rootimg_dir/$statedir/tmpfs$d"); + + # now for each file, create a symbollic link to /.statelite/tmpfs/ + # strip the last / if its a directory for linking, but be careful! + # doing ln -sf ../../tmp ../tmp when tmp is a directory creates 50 levels of links. + # have to do: + # ln -sf ../../tmp ../../tmp/ <- note the / on the end! + if($f =~ /\/$/){ + $f =~ s/\/$//g; + } + # now do the links. + # link the .default file to the .statelite file and the real file to the .statelite file. + + # ../ for tmpfs + # ../ for .statelite + # the rest are for the paths in the file. + my $l = getRelDir($f); + $verbose && $callback->({info=>["ln -sf ../../$l/.default$f $rootimg_dir/$statedir/tmpfs$f"]}); + system("ln -sfn ../../$l/.default$f $rootimg_dir/$statedir/tmpfs/$f"); + + $verbose && $callback->({info=>["ln -sf $relPath/$statedir/tmpfs$f $rootimg_dir$f"]}); + system("ln -sfn $l/$statedir/tmpfs$f $rootimg_dir$f"); + + } + }else{ + # if we go the bindmount method, create some files + foreach my $f (@$files){ + my $rif = $rootimg_dir . $f; + my $d = dirname($rif); + unless(-e "$rif"){ + # if it doesn't exist, create it on base image: + unless(-d $d){ + #$callback->({info=>["mkdir -p $d"]}); + system("mkdir -p $d"); + } + if($rif =~ /\/$/){ + #$callback->({info=>["mkdir -p $rif"]}); + system("mkdir -p $rif"); + }else{ + #$callback->({info=>["touch $rif"]}); + system("touch $rif"); + } + } + else { + #$callback->({info=>["$rif exists"]}); + } + + } + + } + + # end loop, synclist should now all be in place. + + # now stick the rc files in: + # this is actually a pre-rc file because it gets run before the node boots up all the way. + system("cp -a $::XCATROOT/share/xcat/netboot/add-on/statelite/rc.statelite $rootimg_dir/etc/init.d/statelite"); + +} + +sub getRelDir { + my $f = shift; + $f = dirname($f); + if($f eq "/"){ + return "."; + } + my $l = ""; + + my @arr = split("/", $f); + foreach (1 .. $#arr){ + $l .= "../"; + } + + chop($l); # get rid of last / + return $l +} + +1;