package xCAT_plugin::litetree; BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; } use lib "$::XCATROOT/lib/perl"; use xCAT::NodeRange; use Data::Dumper; use xCAT::Utils; use Sys::Syslog; use xCAT::GlobalDef; use xCAT::Table; use Getopt::Long; use xCAT::SvrUtils; 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 $synclocTab = "statelite"; my $errored = 0; sub handled_commands { # command is syncmount, syncdir is the perl module to use. return { litetree => "litetree", litefile => "litetree", ilitefile => "litetree", lslite => "litetree" } } sub usage { my $command = shift; my $callback = shift; my $error = shift; my $msg; if ($command eq "ilitefile") { $msg = "Usage: ilitefile \texample:\n\tilitefile centos5.3-x86_64-statelite-compute" } elsif ($command eq "litefile") { $msg = "Usage: litefile \nexample:\n\tlitefile node1"; } elsif ($command eq "litetree") { $msg = "Usage: litetree \nexample:\n\tlitetree node1"; } 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'}) { my @nodenames = noderange($request->{'_xcat_clienthost'}->[0] . "," . $request->{'_xcat_clientfqdn'}->[0]); if (@nodenames) { $noderange = \@nodenames; $request->{node} = $noderange; } } my $command = $request->{command}->[0]; if ($command eq "litetree") { unless ($request->{node}) { usage($command, $callback, 0); return 1; } return syncmount("dir", $request, $callback, $noderange); } elsif ($command eq "litefile") { unless ($request->{node}) { usage($command, $callback, 0); return 1; } return syncmount("file", $request, $callback, $noderange); } elsif ($command eq "ilitefile") { #print Dumper($request); unless ($request->{arg}) { usage($command, $callback, 0); return 1; } return syncmount("image", $request, $callback, $request->{arg}); } elsif ($command eq "lslite") { if (defined $noderange) { &lslite($request, $callback, $noderange); } else { &lslite($request, $callback, undef); } return; } 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); } elsif ($syncType =~ /location/) { $tab = xCAT::Table->new($synclocTab, -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', 'provmethod' ]) }; } foreach my $node (@nodes) { # node may be an image... my $image; my $ent; if ($syncType !~ /image/) { $ent = $osents{$node}->[0]; if (xCAT::Utils->isAIX()) { $image = $ent->{provmethod}; } else { unless ($ent->{os} && $ent->{arch} && $ent->{profile}) { $callback->({ error => ["$node does not have os, arch, or profile defined in nodetype table"], errorcode => [1] }); $request = {}; next; } if ((!$ent->{provmethod}) || ($ent->{provmethod} eq 'statelite') || ($ent->{provmethod} eq 'netboot') || ($ent->{provmethod} eq 'install')) { $image = $ent->{os} . "-" . $ent->{arch} . "-statelite-" . $ent->{profile}; } elsif (($ent->{provmethod} ne 'netboot') && ($ent->{provmethod} ne 'install')) { $image = $ent->{provmethod}; } } } else { $image = $node; } my $fData = getNodeData($syncType, $node, $image, $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; my $mnts; my $first; #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 $mntpnt; my ($server, $dir, $mntopts) = split(/:/, $dirs->{$priority}); # if server is blank then its the directory: unless ($dir) { $dir = $server; $server = ''; } if (grep /\$|#CMD/, $dir) { $dir = xCAT::SvrUtils->subVars($dir, $node, 'dir', $callback); $dir =~ s/\/\//\//g; } $first = $dir; $first =~ s!\/([^/]*)\/.*!$1!; if ($server) { if (grep /\$/, $server) { $server = xCAT::SvrUtils->subVars($server, $node, 'server', $callback); } $mntpnt = $server . ":"; # we have a server and need to make sure we can mount them under unique names if ($mnts->{$first} eq '') { # if the first mount point doesn't have a server then leave it. $mnts->{$first} = $server; } } $mntpnt .= $dir; # add the mount options if we have any if ($mntopts) { $mntpnt .= " :$mntopts"; } # ok, now we have all the mount points. Let's go through them all? if ($::from_lslite == 1) { $callback->({ info => " $priority, $mntpnt" }); } else { $callback->({ info => "$node: $mntpnt" }); } } } elsif ($syncType =~ /file|image/) { foreach my $file (sort keys %$dirs) { my $options = $dirs->{$file}; # persistent,rw my $out; if ($::from_lslite == 1) { $out = sprintf(" %-13s %s", $options, $file); } else { $out = sprintf("%s: %-13s %s", $node, $options, $file); } $callback->({ info => $out }); } } elsif ($syncType =~ /location/) { foreach my $node (sort keys %$dirs) { my $location = $dirs->{$node}; my ($server, $dir) = split(/:/, $location); if (grep /\$|#CMD/, $dir) { $dir = xCAT::SvrUtils->subVars($dir, $node, 'dir', $callback); $dir =~ s/\/\//\//g; } if (grep /\$/, $server) { $server = xCAT::SvrUtils->subVars($server, $node, 'server', $callback); } $location = $server . ":" . $dir; if ($::from_lslite == 1) { $callback->({ info => " $location" }); } else { $callback->({ info => "$node: $location" }); } } } } # get all the directories or files for given image related to this node. sub getNodeData { my $type = shift; my $node = shift; my $image = 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} . "-statelite-" . $ent->{profile}; #}else{ # $image = $node; #} my @imageInfo; my @attrs; my @imagegroupsattr = ('groups'); if ($type eq "dir") { @attrs = ('priority', 'directory', 'mntopts'); } elsif ($type =~ /file|image/) { @attrs = ('file', 'options'); } elsif ($type =~ /location/) { @attrs = ('node', 'statemnt'); } else { print "Yikes! error in the code litefile;getNodeData!"; exit 1; } if ($type eq "location") { # get locations with specific nodes push @imageInfo, $tab->getNodeAttribs($node, @attrs); } else { # Check if this image contains osimage.groups attribute. # if so, means user wants to use specific directories to this image. my $osimagetab = xCAT::Table->new("osimage", -create => 1); my $imagegroups = $osimagetab->getAttribs({ imagename => $image }, @imagegroupsattr); if ($imagegroups and $imagegroups->{groups}) { # get for the image groups specific directories push @imageInfo, $tab->getAttribs({ image => $imagegroups->{groups} }, @attrs); } # These cases should run for all images # 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}:$_->{mntopts}"; } } elsif ($type =~ /file|image/) { my $doesMtabExists = 0; foreach (@$arr) { next if ($_->{file} eq ''); my $o = $_->{options}; unless ($o) { if (xCAT::Utils->isAIX()) { $o = "rw"; # default option if not provided } else { # for compatible reason, the default option is set to "tmpfs,rw" $o = "tmpfs,rw"; } } if ($_->{file} eq "/etc/mtab") { $doesMtabExists = 1; # TODO # let the user know the "link" option should be with # /etc/mtab $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; } if (xCAT::Utils->isLinux()) { if ($doesMtabExists eq 0) { $attrs->{"/etc/mtab"} = "link"; } } } elsif ($type =~ /location/) { foreach (@$arr) { if ($_->{statemnt} eq '') { next; } $attrs->{ $_->{node} } = $_->{statemnt}; } } else { print "Yikes! Error in the code in mergeArrays!\n"; exit 1; } #print "mergeArrays...\n"; #print Dumper($attrs); return $attrs; } #---------------------------------------------------------------------------- =head3 lslite_usage =cut #----------------------------------------------------------------------------- sub lslite_usage { my $callback = shift; my $rsp; push @{ $rsp->{data} }, "\n lslite - Display a summary of the statelite information \n\t\tthat has been defined for a noderange or an image."; push @{ $rsp->{data} }, " Usage: "; push @{ $rsp->{data} }, "\tlslite [-h | --help]"; push @{ $rsp->{data} }, "or"; push @{ $rsp->{data} }, "\tlslite [-V | --verbose] [-i imagename] | [noderange]"; xCAT::MsgUtils->message("I", $rsp, $callback); return 0; } sub lslite { my $request = shift; my $callback = shift; my $noderange = shift; my @image; $::from_lslite = 1; # to control the output format unless ($request->{arg} || defined $noderange) { &lslite_usage($callback); return 1; } # parse the options Getopt::Long::Configure("no_pass_through"); Getopt::Long::Configure("bundling"); if ($request->{arg}) { @ARGV = @{ $request->{arg} }; if ( !GetOptions( 'h|help' => \$::HELP, 'i=s' => \$::OSIMAGE, 'V|verbose' => \$::VERBOSE, ) ) { &lslite_usage($callback); return 1; } } if ($::HELP) { &lslite_usage($callback); return 0; } # handle "lslite -i image1" # use the logic for ilitefile if ($::OSIMAGE) { # make sure the osimage is defined my @imglist = xCAT::DBobjUtils->getObjectsOfType('osimage'); if (!grep(/^$::OSIMAGE$/, @imglist)) { $callback->({ error => ["The osimage named \'$::OSIMAGE\' is not defined."], errorcode => [1] }); return 1; } @image = join(',', $::OSIMAGE); syncmount("image", $request, $callback, \@image); return 0; } # handle "lslite node1" my @nodes; if (defined $noderange) { @nodes = @{$noderange}; if (scalar @nodes) { # get node's osimage/profile my $nttab = xCAT::Table->new('nodetype'); my $nttabdata = $nttab->getNodesAttribs(\@nodes, [ 'node', 'profile', 'os', 'arch', 'provmethod' ]); foreach my $node (@nodes) { my $image; my $data = $nttabdata->{$node}->[0]; if (xCAT::Utils->isAIX()) { if (defined($data->{provmethod})) { $image = $data->{provmethod}; } else { $callback->({ error => ["no provmethod defined for node $node."], errorcode => [1] }); } } else { if ((!$data->{provmethod}) || ($data->{provmethod} eq 'statelite') || ($data->{provmethod} eq 'netboot') || ($data->{provmethod} eq 'install')) { $image = $data->{os} . "-" . $data->{arch} . "-statelite-" . $data->{profile}; } else { $image = $data->{provmethod}; } } $callback->({ info => ">>>Node: $node\n" }); $callback->({ info => "Osimage: $image\n" }); # structure node as ARRAY my @tmpnode = join(',', $node); my @types = ("location", "file", "dir"); foreach my $type (@types) { if ($type eq "location") { # show statelite table $callback->({ info => "Persistent directory (statelite table):" }); } elsif ($type eq "file") { # show litefile table $callback->({ info => "Litefiles (litefile table):" }); } elsif ($type eq "dir") { # show litetree table $callback->({ info => "Litetree path (litetree table):" }); } else { $callback->({ error => ["Invalid type."], errorcode => [1] }); return 1; } syncmount($type, $request, $callback, \@tmpnode); $callback->({ info => "\n" }); } } } } return; } 1; #vim: set ts=2