mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 03:12:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			3118 lines
		
	
	
		
			98 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			3118 lines
		
	
	
		
			98 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
 | |
| #####################################################
 | |
| #
 | |
| #  xCAT plugin package to handle various commands that work with the
 | |
| #     xCAT tables
 | |
| #
 | |
| #
 | |
| #####################################################
 | |
| package xCAT_plugin::tabutils;
 | |
| use strict;
 | |
| use warnings;
 | |
| use xCAT::Table;
 | |
| use xCAT::Schema;
 | |
| use Data::Dumper;
 | |
| use xCAT::NodeRange qw/noderange abbreviate_noderange/;
 | |
| use xCAT::Schema;
 | |
| use xCAT::Utils;
 | |
| 
 | |
| #use XML::Simple;
 | |
| use xCAT::TableUtils;
 | |
| use xCAT::MsgUtils;
 | |
| use xCAT::DBobjUtils;
 | |
| use Getopt::Long;
 | |
| my $requestcommand;
 | |
| 
 | |
| 1;
 | |
| 
 | |
| #some quick aliases to table/value
 | |
| my %shortnames = (
 | |
|     groups => [qw(nodelist groups)],
 | |
|     tags   => [qw(nodelist groups)],
 | |
|     mgt    => [qw(nodehm mgt)],
 | |
| 
 | |
|     #switch => [qw(switch switch)],
 | |
| );
 | |
| 
 | |
| #####################################################
 | |
| # Return list of commands handled by this plugin
 | |
| #####################################################
 | |
| sub handled_commands
 | |
| {
 | |
|     return {
 | |
|         gettab     => "tabutils",
 | |
|         tabdump    => "tabutils",
 | |
|         lsxcatd    => "tabutils",
 | |
|         tabprune   => "tabutils",
 | |
|         tabrestore => "tabutils",
 | |
|         tabch      => "tabutils",
 | |
|         nodegrpch  => "tabutils",
 | |
|         nodech     => "tabutils",
 | |
|         nodeadd    => "tabutils",
 | |
|         noderm     => "tabutils",
 | |
|         tabls      => "tabutils",    # not implemented yet
 | |
|         nodels     => "tabutils",
 | |
|         getnodecfg => "tabutils", # not implemented yet (?? this doesn't seem much different from gettab)
 | |
|         addattr                 => "tabutils",    # not implemented yet
 | |
|         delattr                 => "tabutils",    # not implemented yet
 | |
|         chtype                  => "tabutils",    # not implemented yet
 | |
|         nr                      => "tabutils",    # not implemented yet
 | |
|         rnoderange              => "tabutils",    # not implemented yet
 | |
|         tabgrep                 => "tabutils",
 | |
|         getAllEntries           => "tabutils",
 | |
|         getNodesAttribs         => "tabutils",
 | |
|         getTablesAllNodeAttribs => "tabutils",
 | |
|         getTablesNodesAttribs   => "tabutils",
 | |
|         getTablesAllRowAttribs  => "tabutils",
 | |
|         setNodesAttribs         => "tabutils",
 | |
|         delEntries              => "tabutils",
 | |
|         getAttribs              => "tabutils",
 | |
|         setAttribs              => "tabutils",
 | |
|         NodeRange               => "tabutils",
 | |
|         gennr                   => "tabutils"
 | |
|     };
 | |
| }
 | |
| 
 | |
| # Each cmd now returns its own usage inside its function
 | |
| 
 | |
| #####################################################
 | |
| # Process the command
 | |
| #####################################################
 | |
| sub process_request
 | |
| {
 | |
|     #use Getopt::Long;
 | |
|     Getopt::Long::Configure("bundling");
 | |
| 
 | |
|     #Getopt::Long::Configure("pass_through");
 | |
|     Getopt::Long::Configure("no_pass_through");
 | |
| 
 | |
|     my $request  = shift;
 | |
|     my $callback = shift;
 | |
|     $requestcommand = shift;
 | |
|     my $nodes   = $request->{node};
 | |
|     my $command = $request->{command}->[0];
 | |
|     my $args    = $request->{arg};
 | |
| 
 | |
|     #unless ($args or $nodes or $request->{data})
 | |
|     #{
 | |
|     #if ($usage{$command})
 | |
|     #{
 | |
|     #$callback->({data => [$usage{$command}]});
 | |
|     #return;
 | |
|     #}
 | |
|     #}
 | |
| 
 | |
|     if ($command eq "nodels")
 | |
|     {
 | |
|         return nodels($nodes, $args, $callback, $request->{emptynoderange}->[0]);
 | |
|     }
 | |
|     elsif ($command eq "rnoderange")
 | |
|     {
 | |
|         return rnoderange($nodes, $args, $callback);
 | |
|     }
 | |
|     elsif ($command eq "noderm" or $command eq "rmnode")
 | |
|     {
 | |
|         return noderm($nodes, $args, $callback);
 | |
|     }
 | |
|     elsif ($command eq "nodeadd" or $command eq "addnode")
 | |
|     {
 | |
|         return nodech($nodes, $args, $callback, 1);
 | |
|     }
 | |
|     elsif ($command eq "nodegrpch" or $command eq "chnodegrp")
 | |
|     {
 | |
|         return nodech($nodes, $args, $callback, "groupch");
 | |
|     }
 | |
|     elsif ($command eq "gennr")
 | |
|     {
 | |
|         return gennr($nodes, $args, $callback);
 | |
|     }
 | |
|     elsif ($command eq "nodech" or $command eq "chnode")
 | |
|     {
 | |
|         return nodech($nodes, $args, $callback, 0);
 | |
|     }
 | |
|     elsif ($command eq "tabrestore")
 | |
|     {
 | |
|         return tabrestore($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "tabdump")
 | |
|     {
 | |
|         return tabdump($args, $callback, $request);
 | |
|     }
 | |
|     elsif ($command eq "lsxcatd")
 | |
|     {
 | |
|         return lsxcatd($args, $callback);
 | |
|     }
 | |
|     elsif ($command eq "tabprune")
 | |
|     {
 | |
|         return tabprune($args, $callback);
 | |
|     }
 | |
|     elsif ($command eq "gettab")
 | |
|     {
 | |
|         return gettab($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "tabgrep")
 | |
|     {
 | |
|         return tabgrep($nodes, $callback);
 | |
|     }
 | |
|     elsif ($command eq "tabch") {
 | |
|         return tabch($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "getAllEntries")
 | |
|     {
 | |
|         return getAllEntries($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "getNodesAttribs")
 | |
|     {
 | |
|         return getNodesAttribs($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "getTablesAllNodeAttribs")
 | |
|     {
 | |
|         return getTablesAllNodeAttribs($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "getTablesNodesAttribs")
 | |
|     {
 | |
|         return getTablesNodesAttribs($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "getTablesAllRowAttribs")
 | |
|     {
 | |
|         return getTablesAllRowAttribs($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "setNodesAttribs")
 | |
|     {
 | |
|         return setNodesAttribs($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "delEntries")
 | |
|     {
 | |
|         return delEntries($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "getAttribs")
 | |
|     {
 | |
|         return getAttribs($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "setAttribs")
 | |
|     {
 | |
|         return setAttribs($request, $callback);
 | |
|     }
 | |
|     elsif ($command eq "NodeRange")
 | |
|     {
 | |
|         return NodeRange($request, $callback);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         print "$command not implemented yet\n";
 | |
|         return (1, "$command not written yet");
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| # Display particular attributes, using query strings.
 | |
| sub gettab
 | |
| {
 | |
|     my $req      = shift;
 | |
|     my $callback = shift;
 | |
|     my $HELP;
 | |
|     my $NOTERSE;
 | |
| 
 | |
|     my $gettab_usage = sub {
 | |
|         my $exitcode = shift @_;
 | |
|         my %rsp;
 | |
|         push @{ $rsp{data} }, "Usage: gettab [-H|--with-fieldname] key=value,...  table.attribute ...";
 | |
|         push @{ $rsp{data} }, "       gettab [-?|-h|--help]";
 | |
|         if ($exitcode) { $rsp{errorcode} = $exitcode; }
 | |
|         $callback->(\%rsp);
 | |
|     };
 | |
| 
 | |
|     # Process arguments
 | |
|     if (!defined($req->{arg})) { $gettab_usage->(1); return; }
 | |
|     @ARGV = @{ $req->{arg} };
 | |
|     if (!GetOptions('h|?|help' => \$HELP, 'H|with-fieldname' => \$NOTERSE)) { $gettab_usage->(1); return; }
 | |
| 
 | |
|     if ($HELP)             { $gettab_usage->(0); return; }
 | |
|     if (scalar(@ARGV) < 2) { $gettab_usage->(1); return; }
 | |
| 
 | |
|     # Get all the key/value pairs into a hash
 | |
|     my $keyspec = shift @ARGV;
 | |
|     my @keypairs = split /,/, $keyspec;
 | |
|     my %keyhash;
 | |
|     foreach (@keypairs)
 | |
|     {
 | |
|         (my $key, my $value) = split /=/, $_;
 | |
|         unless (defined $key) {
 | |
|             $gettab_usage->(1);
 | |
|             return;
 | |
|         }
 | |
|         $keyhash{$key} = $value;
 | |
|     }
 | |
| 
 | |
|     # Group the columns asked for by table (so we can do 1 query per table)
 | |
|     my %tabhash;
 | |
|     my $terse = 2;
 | |
|     if ($NOTERSE) {
 | |
|         $terse = 0;
 | |
|     }
 | |
|     foreach my $tabvalue (@ARGV)
 | |
|     {
 | |
|         $terse--;
 | |
|         (my $table, my $column) = split /\./, $tabvalue;
 | |
|         $tabhash{$table}->{$column} = 1;
 | |
|     }
 | |
| 
 | |
|     #Sanity check the key against all tables in question
 | |
|     foreach my $tabn (keys %tabhash) {
 | |
|         foreach my $kcheck (keys %keyhash) {
 | |
|             unless (grep /^$kcheck$/, @{ $xCAT::Schema::tabspec{$tabn}->{cols} }) {
 | |
|                 $callback->({ error => ["Unkown key $kcheck to $tabn"], errorcode => [1] });
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # Get the requested columns from each table
 | |
|     foreach my $tabn (keys %tabhash)
 | |
|     {
 | |
|         my $tab = xCAT::Table->new($tabn);
 | |
|         (my $ent) = $tab->getAttribs(\%keyhash, keys %{ $tabhash{$tabn} });
 | |
|         if ($ent) {
 | |
|             foreach my $coln (keys %{ $tabhash{$tabn} })
 | |
|             {
 | |
|                 if ($terse > 0) {
 | |
|                     $callback->({ data => [ "" . $ent->{$coln} ] });
 | |
|                 } else {
 | |
|                     $callback->({ data => [ "$tabn.$coln: " . $ent->{$coln} ] });
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         $tab->close;
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub noderm
 | |
| {
 | |
|     my $nodes = shift;
 | |
|     my $args  = shift;
 | |
|     my $cb    = shift;
 | |
|     my $VERSION;
 | |
|     my $HELP;
 | |
| 
 | |
|     my $noderm_usage = sub {
 | |
|         my $exitcode = shift @_;
 | |
|         my %rsp;
 | |
|         push @{ $rsp{data} }, "Usage:";
 | |
|         push @{ $rsp{data} }, "  noderm noderange";
 | |
|         push @{ $rsp{data} }, "  noderm {-v|--version}";
 | |
|         push @{ $rsp{data} }, "  noderm [-?|-h|--help]";
 | |
|         if ($exitcode) { $rsp{errorcode} = $exitcode; }
 | |
|         $cb->(\%rsp);
 | |
|     };
 | |
| 
 | |
|     if ($args) {
 | |
|         @ARGV = @{$args};
 | |
|     } else {
 | |
|         @ARGV = ();
 | |
|     }
 | |
|     if (!GetOptions('h|?|help' => \$HELP, 'v|version' => \$VERSION)) { $noderm_usage->(1); return; }
 | |
| 
 | |
|     if ($HELP) { $noderm_usage->(0); return; }
 | |
| 
 | |
|     if ($VERSION) {
 | |
|         my %rsp;
 | |
|         my $version = xCAT::Utils->Version();
 | |
|         $rsp{data}->[0] = "$version";
 | |
|         $cb->(\%rsp);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (!$nodes) { $noderm_usage->(1); return; }
 | |
| 
 | |
|     #my $sitetab = xCAT::Table->new('site');
 | |
|     #my $pdhcp = $sitetab->getAttribs({key=>'pruneservices'},['value']);
 | |
|     my @entries = xCAT::TableUtils->get_site_attribute("pruneservices");
 | |
|     my $t_entry = $entries[0];
 | |
|     if (defined($t_entry) and $t_entry !~ /n(\z|o)/i) {
 | |
|         $requestcommand->({ command => ['makedhcp'], node => $nodes, arg => ['-d'] });
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
|     # Build the argument list for using the -d option of nodech to do our work for us
 | |
|     my @tablist = ("-d");
 | |
|     foreach (keys %{xCAT::Schema::tabspec})
 | |
|     {
 | |
|         if (grep /^node$/, @{ $xCAT::Schema::tabspec{$_}->{cols} })
 | |
|         {
 | |
|             push @tablist, $_;
 | |
|         }
 | |
|     }
 | |
|     if (scalar(@$nodes)) {
 | |
|         for my $nn (@$nodes) {
 | |
|             my $nt = xCAT::DBobjUtils->getnodetype($nn);
 | |
|             if ($nt and $nt =~ /^(cec|frame)$/) {
 | |
|                 my $cnodep = xCAT::DBobjUtils->getchildren($nn);
 | |
|                 if ($cnodep) {
 | |
|                     my $cnode = join ',', @$cnodep;
 | |
|                     if ($cnode)
 | |
|                     {
 | |
|                         my $rsp;
 | |
|                         $rsp->{data}->[0] =
 | |
| "Removed a $nt node, please remove these nodes belongs to it manually: $cnode \n";
 | |
|                         xCAT::MsgUtils->message("I", $rsp, $cb);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     nodech($nodes, \@tablist, $cb, 0);
 | |
| }
 | |
| #
 | |
| # restores the table from the input CSV file.  Default deletes the table rows and
 | |
| # replaces with the rows in the file
 | |
| # If -a flag is input then it adds the rows from the CSV file to the table.
 | |
| #
 | |
| sub tabrestore
 | |
| {
 | |
|     # the usage for tabrestore is in the tabrestore client cmd
 | |
| 
 | |
|     #request->{data} is an array of CSV formatted lines
 | |
|     my $request = shift;
 | |
|     my $cb      = shift;
 | |
|     my $table   = $request->{table}->[0];
 | |
|     my $addrows = $request->{addrows}->[0];
 | |
| 
 | |
|     # do not allow teal tables
 | |
|     if ($table =~ /^x_teal/) {
 | |
|         $cb->({ error => "$table is not supported in tabrestore. Use Teal maintenance commands. ", errorcode => 1 });
 | |
|         return 1;
 | |
|     }
 | |
|     my $tab = xCAT::Table->new($table, -create => 1, -autocommit => 0);
 | |
|     unless ($tab) {
 | |
|         $cb->({ error => "Unable to open $table", errorcode => 4 });
 | |
|         return;
 | |
|     }
 | |
|     if (!defined($addrows)) {    # this is a replace not add rows
 | |
|         $tab->delEntries();      #Yes, delete *all* entries
 | |
|     }
 | |
|     my $header = shift @{ $request->{data} };
 | |
|     unless ($header =~ /^#/) {
 | |
|         $cb->({ error => "Data missing header line starting with #", errorcode => 1 });
 | |
|         return;
 | |
|     }
 | |
|     $header =~ s/"//g;           #Strip " from overzealous CSV apps
 | |
|     $header =~ s/^#//;
 | |
|     $header =~ s/\s+$//;
 | |
|     my @colns = split(/,/, $header);
 | |
|     my $tcol;
 | |
|     foreach $tcol (@colns) { #validate the restore data has no invalid column names
 | |
|         unless (grep /^$tcol\z/, @{ $xCAT::Schema::tabspec{$table}->{cols} }) {
 | |
|             $cb->({ error => "The header line indicates that column '$tcol' should exist, which is not defined in the schema for '$table'", errorcode => 1 });
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         #print Dumper(grep /^$tcol\z/,@{$xCAT::Schema::tabspec{$table}->{cols}});
 | |
|     }
 | |
| 
 | |
|     #print "We passed it!\n";
 | |
|     my $line;
 | |
|     my $rollback = 0;
 | |
| 
 | |
|     my @tmp = $tab->getAutoIncrementColumns(); #get the columns that are auto increment by DB.
 | |
|     my %auto_cols = ();
 | |
|     foreach (@tmp) { $auto_cols{$_} = 1; }
 | |
| 
 | |
| 
 | |
|     my $linenumber;
 | |
|     my $linecount = scalar(@{ $request->{data} });
 | |
| 
 | |
|   LINE: for ($linenumber = 0 ; $linenumber < $linecount ; $linenumber++)
 | |
|     {
 | |
|         $line = @{ $request->{data} }[$linenumber];
 | |
|         $line =~ s/\s+$//;
 | |
|         my $origline = $line;                  #save for error reporting
 | |
|         my %record;
 | |
|         my $col;
 | |
|         foreach $col (@colns)
 | |
|         {
 | |
|             if ($line =~ /^,/ or $line eq "")
 | |
|             {    #Match empty, or end of line that is empty
 | |
|                  #TODO: should we detect when there weren't enough CSV fields on a line to match colums?
 | |
|                 if (!exists($auto_cols{$col})) {
 | |
|                     $record{$col} = undef;
 | |
|                 }
 | |
|                 $line =~ s/^,//;
 | |
|             }
 | |
|             elsif ($line =~ /^[^,]*"/)
 | |
|             {    # We have stuff in quotes... pain...
 | |
|                     #I don't know what I'm doing, so I'll do it a hard way....
 | |
|                 if ($line !~ /^"/)
 | |
|                 {
 | |
|                     $rollback = 1;
 | |
|                     $cb->(
 | |
|                         {
 | |
|                             error =>
 | |
| "CSV missing opening \" for record with \" characters on line $linenumber, character "
 | |
|                               . index($origline, $line) . ": $origline", errorcode => 4
 | |
|                         }
 | |
|                     );
 | |
|                     next LINE;
 | |
|                 }
 | |
|                 my $offset = 1;
 | |
|                 my $nextchar;
 | |
|                 my $ent;
 | |
|                 while (not defined $ent)
 | |
|                 {
 | |
|                     $offset = index($line, '"', $offset);
 | |
|                     $offset++;
 | |
| 
 | |
|                     if ($offset <= 0)
 | |
|                     {    #the matching quote is not on this line of the file
 | |
| 
 | |
|                         if ($linenumber < $linecount)
 | |
|                         { #it's not the end of the world, we have more lines to check
 | |
| 
 | |
|                             my $continuedline = @{ $request->{data} }[ ++$linenumber ];
 | |
|                             $offset = length($line);
 | |
|                             $line .= "\n" . $continuedline;
 | |
|                             $line =~ s/\s+$//;
 | |
|                         }
 | |
|                         else
 | |
|                         { #the matching quote was not found before the end of the file
 | |
| 
 | |
|                             #MALFORMED CSV, request rollback, report an error
 | |
|                             $rollback = 1;
 | |
|                             $cb->(
 | |
|                                 {
 | |
|                                     error =>
 | |
| "CSV unmatched \" in record on line $linenumber, character "
 | |
|                                       . index($origline, $line) . ": $origline", errorcode => 4
 | |
|                                 }
 | |
|                             );
 | |
|                             next LINE;
 | |
|                         }
 | |
|                     }
 | |
|                     else
 | |
|                     {    #the next quote was on the current line
 | |
| 
 | |
|                         $nextchar = substr($line, $offset, 1);
 | |
| 
 | |
|                         if ($nextchar eq '"')
 | |
|                         { #the case of 2 double quotes.  ignore them and move on
 | |
|                             $offset++;
 | |
|                         }
 | |
|                         elsif ($offset eq length($line) or $nextchar eq ',')
 | |
|                         { #hit the end of the line or at least the end of the column
 | |
|                             $ent = substr($line, 0, $offset, '');
 | |
|                             $line =~ s/^,//;
 | |
|                             chop $ent;
 | |
|                             $ent = substr($ent, 1);
 | |
|                             $ent =~ s/""/"/g;
 | |
|                             if (!exists($auto_cols{$col}))
 | |
|                             {
 | |
|                                 $record{$col} = $ent;
 | |
|                             }
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             $cb->(
 | |
|                                 {
 | |
|                                     error =>
 | |
| "CSV unescaped \" in record on line $linenumber, character "
 | |
|                                       . index($origline, $line) . ": $origline", errorcode => 4
 | |
|                                 }
 | |
|                             );
 | |
|                             $rollback = 1;
 | |
|                             next LINE;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             elsif ($line =~ /^([^,]+)/)
 | |
|             {    #easiest case, no Text::Balanced needed..
 | |
|                 if (!exists($auto_cols{$col}))
 | |
|                 {
 | |
|                     $record{$col} = $1;
 | |
|                 }
 | |
|                 $line =~ s/^([^,]+)(,|$)//;
 | |
|             }
 | |
|         }
 | |
|         if ($line)
 | |
|         {
 | |
|             $rollback = 1;
 | |
|             $cb->({ error => "Too many fields on line $linenumber: $origline | $line", errorcode => 4 });
 | |
|             next LINE;
 | |
|         }
 | |
| 
 | |
|         #check for error from DB and rollback
 | |
|         my @rc = $tab->setAttribs(\%record, \%record);
 | |
|         if (not defined($rc[0]))
 | |
|         {
 | |
|             $rollback = 1;
 | |
|             $cb->({ error => "DB error " . $rc[1] . " with line $linenumber: " . $origline, errorcode => 4 });
 | |
|         }
 | |
|     }
 | |
|     if ($rollback)
 | |
|     {
 | |
|         $tab->rollback();
 | |
|         $tab->close;
 | |
|         undef $tab;
 | |
|         return;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         $tab->commit;    #Made it all the way here, commit
 | |
|     }
 | |
| }
 | |
| 
 | |
| # Display a list of tables, or a specific table in CSV format
 | |
| sub tabdump
 | |
| {
 | |
|     my $args    = shift;
 | |
|     my $cb      = shift;
 | |
|     my $request = shift;
 | |
|     my $table   = "";
 | |
|     my $HELP;
 | |
|     my $DESC;
 | |
|     my $OPTW;
 | |
|     my $VERSION;
 | |
|     my $FILENAME;
 | |
|     my $NUMBERENTRIES;
 | |
| 
 | |
|     my $tabdump_usage = sub {
 | |
|         my $exitcode = shift @_;
 | |
|         my %rsp;
 | |
|         push @{ $rsp{data} }, "Usage: tabdump [-d] [table]";
 | |
|         push @{ $rsp{data} }, "       tabdump      [table]";
 | |
|         push @{ $rsp{data} }, "       tabdump [-f <filename>]  [table]";
 | |
|         push @{ $rsp{data} }, "       tabdump [-n <# of records>]  [auditlog | eventlog]";
 | |
|         push @{ $rsp{data} }, "       tabdump [-w attr==val [-w attr=~val] ...] [table]";
 | |
|         push @{ $rsp{data} }, "       tabdump [-w attr==val [-w attr=~val] ...] [-f <filename>] [table]";
 | |
|         push @{ $rsp{data} }, "       tabdump [-?|-h|--help]";
 | |
|         push @{ $rsp{data} }, "       tabdump {-v|--version}";
 | |
|         push @{ $rsp{data} }, "       tabdump ";
 | |
|         if ($exitcode) { $rsp{errorcode} = $exitcode; }
 | |
|         $cb->(\%rsp);
 | |
|     };
 | |
| 
 | |
|     # Process arguments
 | |
|     if ($args) {
 | |
|         @ARGV = @{$args};
 | |
|     }
 | |
|     Getopt::Long::Configure("posix_default");
 | |
|     Getopt::Long::Configure("no_gnu_compat");
 | |
|     Getopt::Long::Configure("bundling");
 | |
| 
 | |
| 
 | |
|     if (!GetOptions(
 | |
|             'h|?|help'  => \$HELP,
 | |
|             'v|version' => \$VERSION,
 | |
|             'n|lines=i' => \$NUMBERENTRIES,
 | |
|             'd'         => \$DESC,
 | |
|             'f=s'       => \$FILENAME,
 | |
|             'w=s@'      => \$OPTW,
 | |
|         )
 | |
|       )
 | |
|     { $tabdump_usage->(1);
 | |
|         return;
 | |
|     }
 | |
|     if ($VERSION) {
 | |
|         my %rsp;
 | |
|         my $version = xCAT::Utils->Version();
 | |
|         $rsp{data}->[0] = "$version";
 | |
|         $cb->(\%rsp);
 | |
|         return;
 | |
|     }
 | |
|     if ($FILENAME and $FILENAME !~ /^\//) { $FILENAME =~ s/^/$request->{cwd}->[0]\//; }
 | |
| 
 | |
| 
 | |
|     if ($HELP) { $tabdump_usage->(0); return; }
 | |
| 
 | |
|     if (($NUMBERENTRIES) && ($DESC)) {
 | |
|         $cb->({ error => "You  cannot use the -n and -d flag together. ", errorcode => 1 });
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if (($NUMBERENTRIES) && ($OPTW)) {
 | |
|         $cb->({ error => "You  cannot use the -n and -w flag together. ", errorcode => 1 });
 | |
|         return 1;
 | |
|     }
 | |
|     if (($NUMBERENTRIES) && ($FILENAME)) {
 | |
|         $cb->({ error => "You  cannot use the -n and -f flag together. ", errorcode => 1 });
 | |
|         return 1;
 | |
|     }
 | |
|     if (scalar(@ARGV) > 1) { $tabdump_usage->(1); return; }
 | |
| 
 | |
|     my %rsp;
 | |
| 
 | |
|     # If no arguments given, we display a list of the tables
 | |
|     if (!scalar(@ARGV)) {
 | |
| 
 | |
|         # if -f filename give but no table name, display error
 | |
|         if ($FILENAME) {
 | |
|             $cb->({ error => "table name missing from the command input. ", errorcode => 1 });
 | |
|             return 1;
 | |
|         }
 | |
|         if ($DESC) {    # display the description of each table
 | |
|             my $tab = xCAT::Table->getDescriptions();
 | |
|             foreach my $key (keys %$tab) {
 | |
|                 my $space = (length($key) < 7 ? "\t\t" : "\t");
 | |
|                 push @{ $rsp{data} }, "$key:$space" . $tab->{$key} . "\n";
 | |
|             }
 | |
|         }
 | |
|         else { push @{ $rsp{data} }, xCAT::Table->getTableList(); } # if no descriptions, just display the list of table names
 | |
|         @{ $rsp{data} } = sort @{ $rsp{data} };
 | |
|         if ($DESC && scalar(@{ $rsp{data} })) { chop($rsp{data}->[ scalar(@{ $rsp{data} }) - 1 ]); } # remove the final newline
 | |
|         $cb->(\%rsp);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # get the table name
 | |
|     $table = $ARGV[0];
 | |
| 
 | |
|     # if -n  can only be the auditlog or eventlog
 | |
|     if ($NUMBERENTRIES) {
 | |
|         if (!($table =~ /^auditlog/) && (!($table =~ /^eventlog/))) {
 | |
|             $cb->({ error => "$table table is not supported in tabdump -n. You may only use this option on the auditlog or the eventlog.", errorcode => 1 });
 | |
|             return 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # do not allow teal tables
 | |
|     if ($table =~ /^x_teal/) {
 | |
|         $cb->({ error => "$table table is not supported in tabdump. Use Teal maintenance commands. ", errorcode => 1 });
 | |
|         return 1;
 | |
|     }
 | |
|     if ($DESC) {    # only show the attribute descriptions, not the values
 | |
|         my $schema = xCAT::Table->getTableSchema($table);
 | |
|         if (!$schema) { $cb->({ error => "table $table does not exist.", errorcode => 1 }); return; }
 | |
|         my $desc = $schema->{descriptions};
 | |
|         foreach my $c (@{ $schema->{cols} }) {
 | |
|             my $space = (length($c) < 7 ? "\t\t" : "\t");
 | |
|             push @{ $rsp{data} }, "$c:$space" . $desc->{$c} . "\n";
 | |
|         }
 | |
|         if (scalar(@{ $rsp{data} })) { chop($rsp{data}->[ scalar(@{ $rsp{data} }) - 1 ]); } # remove the final newline
 | |
|         $cb->(\%rsp);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     my $tabh = xCAT::Table->new($table);
 | |
| 
 | |
|     my $tabdump_header = sub {
 | |
|         my $header = "#" . join(",", @_);
 | |
|         push @{ $rsp{data} }, $header;
 | |
|     };
 | |
| 
 | |
|     # If the table does not exist yet (because its never been written to),
 | |
|     # at least show the header (the column names)
 | |
|     unless ($tabh)
 | |
|     {
 | |
|         if (defined($xCAT::Schema::tabspec{$table}))
 | |
|         {
 | |
|             $tabdump_header->(@{ $xCAT::Schema::tabspec{$table}->{cols} });
 | |
|             $cb->(\%rsp);
 | |
|             return;
 | |
|         }
 | |
|         $cb->({ error => "No such table: $table", errorcode => 1 });
 | |
|         return 1;
 | |
|     }
 | |
|     #
 | |
|     # if tabdump -n <number of recs> auditlog|eventlog
 | |
|     #
 | |
|     if (defined $NUMBERENTRIES) {
 | |
|         my $rc = tabdump_numberentries($table, $cb, $NUMBERENTRIES);
 | |
|         return $rc;
 | |
|     }
 | |
| 
 | |
|     my $recs;
 | |
|     my @ents;
 | |
|     my @attrarray;
 | |
|     if (!($FILENAME)) {    # not dumping to a file
 | |
|         if (!($OPTW)) {    # if no -w flag to filter, then get all
 | |
|             $recs = $tabh->getAllEntries("all");
 | |
|         } else {           # filter entries
 | |
|             foreach my $w (@{$OPTW}) {    # get each attr=val
 | |
|                 push @attrarray, $w;
 | |
|             }
 | |
|             @ents = $tabh->getAllAttribsWhere(\@attrarray, 'ALL');
 | |
|             @$recs = ();
 | |
|             foreach my $e (@ents) {
 | |
|                 push @$recs, $e;
 | |
|             }
 | |
|         }
 | |
|         my $rec;
 | |
|         unless (@$recs)    # table exists, but is empty.  Show header.
 | |
|         {
 | |
|             if (defined($xCAT::Schema::tabspec{$table}))
 | |
|             {
 | |
|                 $tabdump_header->(@{ $xCAT::Schema::tabspec{$table}->{cols} });
 | |
|                 $cb->(\%rsp);
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         #Display all the rows of the table  the order of the columns in the schema
 | |
|         output_table($table, $cb, $tabh, $recs);
 | |
|     } else {               # dump to file
 | |
| 
 | |
|         my $rc1 = 0;
 | |
|         my $fh;
 | |
| 
 | |
|         # check to see if you can open the file
 | |
|         unless (open($fh, " > $FILENAME")) {
 | |
|             $cb->({ error => "Error on tabdump of $table to $FILENAME. Unable to open the file for write. ", errorcode => 1 });
 | |
|             return 1;
 | |
|         }
 | |
|         close $fh;
 | |
|         if (!($OPTW)) {    # if no -w flag to filter, then get all
 | |
|             $rc1 = $tabh->writeAllEntries($FILENAME);
 | |
|         } else {           # filter entries
 | |
|             foreach my $w (@{$OPTW}) {    # get each attr=val
 | |
|                 push @attrarray, $w;
 | |
|             }
 | |
|             $rc1 = $tabh->writeAllAttribsWhere(\@attrarray, $FILENAME);
 | |
|         }
 | |
|         if ($rc1 != 0) {
 | |
|             $cb->({ error => "Error on tabdump of $table to $FILENAME ", errorcode => 1 });
 | |
|             return 1;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| #
 | |
| #  display input number of records for the table requested tabdump -n
 | |
| #  note currently only supports auditlog and eventlog
 | |
| #
 | |
| sub tabdump_numberentries {
 | |
|     my $table         = shift;
 | |
|     my $cb            = shift;
 | |
|     my $numberentries = shift;    # either number of records to display
 | |
| 
 | |
|     my $VERBOSE = shift;
 | |
|     my $rc      = 0;
 | |
|     my $tab     = xCAT::Table->new($table);
 | |
|     unless ($tab) {
 | |
|         $cb->({ error => "Unable to open $table", errorcode => 4 });
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     #determine recid to show all records after
 | |
|     my $RECID;
 | |
|     my $attrrecid = "recid";
 | |
|     my $values    = $tab->getMAXMINEntries($attrrecid);
 | |
|     my $max       = $values->{"max"};
 | |
|     if (defined($values->{"max"})) {
 | |
|         $RECID = $values->{"max"} - $numberentries;
 | |
|         $rc = tabdump_recid($table, $cb, $RECID, $attrrecid);
 | |
| 
 | |
|     } else {
 | |
|         my %rsp;
 | |
|         push @{ $rsp{data} }, "Nothing to display from $table.";
 | |
|         $rsp{errorcode} = $rc;
 | |
|         $cb->(\%rsp);
 | |
|     }
 | |
|     return $rc;
 | |
| }
 | |
| 
 | |
| #  Display requested recored
 | |
| #  if rec id  does not exist error
 | |
| sub tabdump_recid {
 | |
|     my $table = shift;
 | |
|     my $cb    = shift;
 | |
|     my $recid = shift;
 | |
|     my $rc    = 0;
 | |
| 
 | |
|     # check which database so can build the correct Where clause
 | |
|     my $tab = xCAT::Table->new($table);
 | |
|     unless ($tab) {
 | |
|         $cb->({ error => "Unable to open $table", errorcode => 4 });
 | |
|         return 1;
 | |
|     }
 | |
|     my $DBname = xCAT::Utils->get_DBName;
 | |
|     my @recs;
 | |
|     my $attrrecid = "recid";
 | |
| 
 | |
|     # display the output
 | |
|     if ($DBname =~ /^DB2/) {
 | |
|         @recs = $tab->getAllAttribsWhere("\"$attrrecid\">$recid", 'ALL');
 | |
|     } else {
 | |
|         @recs = $tab->getAllAttribsWhere("$attrrecid>$recid", 'ALL');
 | |
|     }
 | |
|     output_table($table, $cb, $tab, \@recs);
 | |
|     return $rc;
 | |
| }
 | |
| 
 | |
| # Display information from the daemon.
 | |
| #
 | |
| sub lsxcatd
 | |
| {
 | |
|     my $args = shift;
 | |
|     my $cb   = shift;
 | |
|     my $HELP;
 | |
|     my $VERSION;
 | |
|     my $DATABASE;
 | |
|     my $NODETYPE;
 | |
|     my $ALL;
 | |
|     my $rc = 0;
 | |
| 
 | |
|     my $lsxcatd_usage = sub {
 | |
|         my $exitcode = shift @_;
 | |
|         my %rsp;
 | |
|         push @{ $rsp{data} }, "       lsxcatd [-v|--version]";
 | |
|         push @{ $rsp{data} }, "       lsxcatd [-h|--help]";
 | |
|         push @{ $rsp{data} }, "       lsxcatd [-d|--database]";
 | |
|         push @{ $rsp{data} }, "       lsxcatd [-t|--nodetype]";
 | |
|         push @{ $rsp{data} }, "       lsxcatd [-a|--all]";
 | |
|         if ($exitcode) { $rsp{errorcode} = $exitcode; }
 | |
|         $cb->(\%rsp);
 | |
|     };
 | |
| 
 | |
|     # Process arguments
 | |
|     if ($args) {
 | |
|         @ARGV = @{$args};
 | |
|     }
 | |
|     if (scalar(@ARGV) == 0) { $lsxcatd_usage->(1); return; }
 | |
|     if (!GetOptions('h|?|help' => \$HELP,
 | |
|             'v|version'  => \$VERSION,
 | |
|             'a|all'      => \$ALL,
 | |
|             't|type'     => \$NODETYPE,
 | |
|             'd|database' => \$DATABASE))
 | |
|     { $lsxcatd_usage->(1); return; }
 | |
| 
 | |
|     if ($HELP) { $lsxcatd_usage->(0); return; }
 | |
| 
 | |
|     # Version
 | |
|     if ($VERSION || $ALL) {
 | |
|         my %rsp;
 | |
|         my $version = xCAT::Utils->Version();
 | |
|         $rsp{data}->[0] = "$version";
 | |
|         $cb->(\%rsp);
 | |
|         if (!$ALL) {
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # nodetype  MN or SN
 | |
|     if ($NODETYPE || $ALL) {
 | |
|         my %rsp;
 | |
|         if (xCAT::Utils->isMN()) {    # if on Management Node
 | |
|             $rsp{data}->[0] = "This is a Management Node";
 | |
|             $cb->(\%rsp);
 | |
|         }
 | |
|         if (xCAT::Utils->isServiceNode()) {    # if on Management Node
 | |
|             $rsp{data}->[0] = "This is a Service Node";
 | |
|             $cb->(\%rsp);
 | |
|         }
 | |
|         if (!$ALL) {
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # no arguments error
 | |
|     my $xcatcfg;
 | |
|     my %rsp;
 | |
|     if ($DATABASE || $ALL) {
 | |
|         $xcatcfg = xCAT::Table->get_xcatcfg();
 | |
| 
 | |
|         if ($xcatcfg =~ /^SQLite:/) {    # SQLite just return SQlite
 | |
|             $rsp{data}->[0] = "dbengine=SQLite";
 | |
|             $cb->(\%rsp);
 | |
| 
 | |
|         }
 | |
|         if ($xcatcfg =~ /^DB2:/) {       # for DB2 , get schema name
 | |
|             my @parts = split('\|', $xcatcfg);
 | |
|             my $cfgloc = $parts[0] . "|" . $parts[1];
 | |
|             my $instance;
 | |
|             $instance = $parts[1];
 | |
|             my ($db2, $databasename) = split(':', $parts[0]);
 | |
|             my $xcatdbhome = xCAT::Utils->getHomeDir("xcatdb");
 | |
|             $rsp{data}->[0] = "cfgloc=$cfgloc";
 | |
|             $rsp{data}->[1] = "dbengine=$db2";
 | |
|             $rsp{data}->[2] = "dbinstance=$instance";
 | |
|             $rsp{data}->[3] = "dbname=$databasename";
 | |
|             $rsp{data}->[4] = "dbloc=$xcatdbhome";
 | |
| 
 | |
|             $cb->(\%rsp);
 | |
| 
 | |
|         }
 | |
|         if (($xcatcfg =~ /^mysql:/) || ($xcatcfg =~ /^Pg:/)) {
 | |
|             my @parts = split('\|', $xcatcfg);
 | |
|             my $cfgloc = $parts[0] . "|" . $parts[1];
 | |
|             my ($host,   $addr)            = split('host=', $parts[0]);
 | |
|             my ($engine, $databasenamestr) = split(':',     $host);
 | |
|             my ($db,     $databasename)    = split('=',     $databasenamestr);
 | |
|             chop $databasename;
 | |
| 
 | |
|             $rsp{data}->[0] = "cfgloc=$cfgloc";
 | |
|             $rsp{data}->[1] = "dbengine=$engine";
 | |
|             $rsp{data}->[2] = "dbname=$databasename";
 | |
|             $rsp{data}->[3] = "dbhost=$addr";
 | |
|             $rsp{data}->[4] = "dbadmin=$parts[1]";
 | |
|             $cb->(\%rsp);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return $rc;
 | |
| }
 | |
| 
 | |
| # Prune records from the eventlog or auditlog or all records.
 | |
| #  Only supports eventlog and auditlog
 | |
| sub tabprune
 | |
| {
 | |
|     my $args = shift;
 | |
|     my $cb   = shift;
 | |
|     my $HELP;
 | |
|     my $VERSION;
 | |
|     my $ALL;
 | |
|     my $NUMBERENTRIES;
 | |
|     my $PERCENT;
 | |
|     my $VERBOSE;
 | |
|     my $RECID;
 | |
|     my $NUMBERDAYS;
 | |
|     my $rc = 0;
 | |
| 
 | |
|     my $tabprune_usage = sub {
 | |
|         my $exitcode = shift @_;
 | |
|         my %rsp;
 | |
|         push @{ $rsp{data} }, "Usage: tabprune <tablename> [-V] -a";
 | |
|         push @{ $rsp{data} }, "       tabprune <tablename> [-V] -n <# of records>";
 | |
|         push @{ $rsp{data} }, "       tabprune <tablename> [-V] -i <recid>";
 | |
|         push @{ $rsp{data} }, "       tabprune <tablename> [-V] -p <percent>";
 | |
|         push @{ $rsp{data} }, "       tabprune <tablename> [-V] -d <# of days>";
 | |
|         push @{ $rsp{data} }, "       tabprune [-h|--help]";
 | |
|         push @{ $rsp{data} }, "       tabprune [-v|--version]";
 | |
|         push @{ $rsp{data} }, "       tables supported:eventlog,auditlog,unless -a which supports all tables";
 | |
|         push @{ $rsp{data} }, "       -d option only supported for eventlog,auditlog";
 | |
|         if ($exitcode) { $rsp{errorcode} = $exitcode; }
 | |
|         $cb->(\%rsp);
 | |
|     };
 | |
| 
 | |
|     # Process arguments
 | |
|     if ($args) {
 | |
|         @ARGV = @{$args};
 | |
|     }
 | |
|     if (!GetOptions('h|?|help' => \$HELP,
 | |
|             'v|version'   => \$VERSION,
 | |
|             'V'           => \$VERBOSE,
 | |
|             'p|percent=i' => \$PERCENT,
 | |
|             'd|days=i'    => \$NUMBERDAYS,
 | |
|             'i|recid=s'   => \$RECID,
 | |
|             'a'           => \$ALL,
 | |
|             'n|number=i'  => \$NUMBERENTRIES))
 | |
|     { $tabprune_usage->(1); return; }
 | |
| 
 | |
|     if ($HELP) { $tabprune_usage->(0); return; }
 | |
| 
 | |
|     # Version
 | |
|     if ($VERSION) {
 | |
|         my %rsp;
 | |
|         my $version = xCAT::Utils->Version();
 | |
|         $rsp{data}->[0] = "$version";
 | |
|         $cb->(\%rsp);
 | |
|         return;
 | |
|     }
 | |
|     if (scalar(@ARGV) > 1) { $tabprune_usage->(1); return; }
 | |
|     my $table = $ARGV[0];
 | |
|     if (!(defined $table)) {
 | |
|         my %rsp;
 | |
|         $rsp{data}->[0] = "Table name required.";
 | |
|         $rsp{errorcode} = 1;
 | |
|         $cb->(\%rsp);
 | |
|         return 1;
 | |
| 
 | |
|     }
 | |
|     $table =~ s/\s*//g;    # remove blanks
 | |
|     if (($table ne "eventlog") && ($table ne "auditlog") && ($table ne "isnm_perf") && ($table ne "isnm_perf_sum") && (!$ALL)) {
 | |
|         my %rsp;
 | |
|         $rsp{data}->[0] = "Table $table not supported, see tabprune -h for supported tables.";
 | |
|         $rsp{errorcode} = 1;
 | |
|         $cb->(\%rsp);
 | |
|         return 1;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     # only support days option for eventlog and auditlog
 | |
|     if (($table ne "eventlog") && ($table ne "auditlog") && (defined $NUMBERDAYS)) {
 | |
|         my %rsp;
 | |
|         $rsp{data}->[0] = "Table $table not supported for the -d option.";
 | |
|         $rsp{errorcode} = 1;
 | |
|         $cb->(\%rsp);
 | |
|         return 1;
 | |
| 
 | |
|     }
 | |
|     if ((!(defined $PERCENT)) && (!(defined $NUMBERDAYS)) && (!(defined $RECID)) && (!(defined $ALL)) && (!(defined $NUMBERENTRIES))) {
 | |
|         my %rsp;
 | |
|         $rsp{data}->[0] = "One option -p or -i or -n or -a or -d must be supplied.";
 | |
|         $rsp{errorcode} = 1;
 | |
|         $cb->(\%rsp);
 | |
|         return 1;
 | |
| 
 | |
|     }
 | |
|     if ((defined $PERCENT) && ((defined $RECID) || (defined $ALL) || (defined $NUMBERENTRIES))) {
 | |
|         my %rsp;
 | |
|         $rsp{data}->[0] = "Only one option -p or -i or -n or -a maybe used at a time.";
 | |
|         $rsp{errorcode} = 1;
 | |
|         $cb->(\%rsp);
 | |
|         return 1;
 | |
| 
 | |
|     }
 | |
|     if ((defined $RECID) && ((defined $PERCENT) || (defined $ALL) || (defined $NUMBERENTRIES))) {
 | |
|         my %rsp;
 | |
|         $rsp{data}->[0] = "Only one option -p or -i or -n or -a maybe used at a time.";
 | |
|         $rsp{errorcode} = 1;
 | |
|         $cb->(\%rsp);
 | |
|         return 1;
 | |
|     }
 | |
|     if ((defined $ALL) && ((defined $PERCENT) || (defined $RECID) || (defined $NUMBERENTRIES))) {
 | |
|         my %rsp;
 | |
|         $rsp{data}->[0] = "Only one option -p or -i or -n or -a maybe used at a time.";
 | |
|         $rsp{errorcode} = 1;
 | |
|         $cb->(\%rsp);
 | |
|         return 1;
 | |
|     }
 | |
|     if ((defined $NUMBERENTRIES) && ((defined $PERCENT) || (defined $RECID) || (defined $ALL))) {
 | |
|         my %rsp;
 | |
|         $rsp{data}->[0] = "Only one option -p or -i or -n or -a maybe used at a time.";
 | |
|         $rsp{errorcode} = 1;
 | |
|         $cb->(\%rsp);
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     # determine the attribute name of the recid
 | |
|     my $attrrecid;
 | |
|     if (($table eq "eventlog") || ($table eq "auditlog")) {
 | |
|         $attrrecid = "recid";
 | |
|     } else {
 | |
|         if ($table eq "isnm_perf") { # if ISNM   These tables are really not supported in 2.8 or later
 | |
|             $attrrecid = "perfid";
 | |
|         } else {
 | |
|             $attrrecid = "period";    # isnm_perf_sum table
 | |
|         }
 | |
|     }
 | |
|     if (defined $ALL) {
 | |
|         $rc = tabprune_all($table, $cb, $attrrecid, $VERBOSE);
 | |
|     }
 | |
|     if (defined $NUMBERENTRIES) {
 | |
|         $rc = tabprune_numberentries($table, $cb, $NUMBERENTRIES, "n", $attrrecid, $VERBOSE);
 | |
|     }
 | |
|     if (defined $PERCENT) {
 | |
|         $rc = tabprune_numberentries($table, $cb, $PERCENT, "p", $attrrecid, $VERBOSE);
 | |
|     }
 | |
|     if (defined $RECID) {
 | |
|         $rc = tabprune_recid($table, $cb, $RECID, $attrrecid, $VERBOSE);
 | |
|     }
 | |
|     if (defined $NUMBERDAYS) {
 | |
|         $rc = tabprune_numberdays($table, $cb, $NUMBERDAYS, $attrrecid, $VERBOSE);
 | |
|     }
 | |
|     if (!($VERBOSE)) {    # not putting out changes
 | |
|         my %rsp;
 | |
|         push @{ $rsp{data} }, "tabprune of $table complete.";
 | |
|         $rsp{errorcode} = $rc;
 | |
|         $cb->(\%rsp);
 | |
|     }
 | |
|     return $rc;
 | |
| }
 | |
| 
 | |
| sub tabprune_all {
 | |
|     my $table     = shift;
 | |
|     my $cb        = shift;
 | |
|     my $attrrecid = shift;
 | |
|     my $VERBOSE   = shift;
 | |
|     my $rc        = 0;
 | |
|     my $tab       = xCAT::Table->new($table);
 | |
|     unless ($tab) {
 | |
|         $cb->({ error => "Unable to open  $table", errorcode => 4 });
 | |
|         return 1;
 | |
|     }
 | |
|     if ($VERBOSE) {    # will output change to std
 | |
|         my $recs = $tab->getAllEntries("all");
 | |
|         output_table($table, $cb, $tab, $recs);
 | |
|     }
 | |
| 
 | |
|     $tab->delEntries();    #Yes, delete *all* entries
 | |
|     $tab->commit;          #  commit
 | |
|     return $rc;
 | |
| }
 | |
| 
 | |
| #  prune input number of records for the table
 | |
| #  if number of entries > number than in the table, then remove all
 | |
| #  this handles the number of records or percentage to delete
 | |
| sub tabprune_numberentries {
 | |
|     my $table         = shift;
 | |
|     my $cb            = shift;
 | |
|     my $numberentries = shift;    # either number of entries or percent to
 | |
|                                   # remove based on the flag
 | |
|     my $flag          = shift;    # (n or p flag)
 | |
|     my $attrrecid     = shift;
 | |
|     my $VERBOSE       = shift;
 | |
|     my $rc            = 0;
 | |
|     my $tab = xCAT::Table->new($table);
 | |
| 
 | |
|     unless ($tab) {
 | |
|         $cb->({ error => "Unable to open $table", errorcode => 4 });
 | |
|         return 1;
 | |
|     }
 | |
|     my $RECID;
 | |
|     my $values = $tab->getMAXMINEntries($attrrecid);
 | |
|     if ((defined($values->{"max"})) && (defined($values->{"min"}))) {
 | |
|         my $largerid = $values->{"max"};
 | |
|         my $smallrid = $values->{"min"};
 | |
|         if ($flag eq "n") {    # deleting number of records
 | |
|              #get the smalled recid and add number to delete, that is where to start removing
 | |
|             $RECID = $smallrid + $numberentries;
 | |
|         } else {    # flag must be percentage
 | |
|              #take largest and smallest recid and percentage and determine the recid
 | |
|              # that will remove the requested percentage.   If some are missing in the
 | |
|              # middle due to tabedit,  we are not worried about it.
 | |
| 
 | |
|             my $totalnumberrids = $largerid - $smallrid + 1;
 | |
|             my $percent         = $numberentries / 100;
 | |
|             my $percentage      = $totalnumberrids * $percent;
 | |
|             my $cnt = sprintf("%d", $percentage);    # round to whole number
 | |
|             $RECID = $smallrid + $cnt;    # get recid to remove all before
 | |
|         }
 | |
| 
 | |
|         # Now prune starting at $RECID
 | |
|         $rc = tabprune_recid($table, $cb, $RECID, $attrrecid, $VERBOSE);
 | |
|     } else {
 | |
|         my %rsp;
 | |
|         push @{ $rsp{data} }, "Nothing to prune from $table.";
 | |
|         $rsp{errorcode} = $rc;
 | |
|         $cb->(\%rsp);
 | |
|     }
 | |
|     return $rc;
 | |
| }
 | |
| 
 | |
| #  prune all entries up to the record id input
 | |
| #  if rec id  does not exist error
 | |
| sub tabprune_recid {
 | |
|     my $table     = shift;
 | |
|     my $cb        = shift;
 | |
|     my $recid     = shift;
 | |
|     my $attrrecid = shift;
 | |
|     my $VERBOSE   = shift;
 | |
|     my $rc        = 0;
 | |
| 
 | |
|     # check which database so can build the correct Where clause
 | |
|     my $tab = xCAT::Table->new($table);
 | |
|     unless ($tab) {
 | |
|         $cb->({ error => "Unable to open $table", errorcode => 4 });
 | |
|         return 1;
 | |
|     }
 | |
|     my $DBname = xCAT::Utils->get_DBName;
 | |
| 
 | |
|     # display the output
 | |
|     my @recs;
 | |
|     if ($VERBOSE) {    # need to get all attributes
 | |
|         if ($DBname =~ /^DB2/) {
 | |
|             @recs = $tab->getAllAttribsWhere("\"$attrrecid\"<$recid", 'ALL');
 | |
|         } else {
 | |
|             @recs = $tab->getAllAttribsWhere("$attrrecid<$recid", 'ALL');
 | |
|         }
 | |
|         output_table($table, $cb, $tab, \@recs);
 | |
|     }
 | |
|     my @ents;
 | |
|     if ($DBname =~ /^DB2/) {
 | |
|         @ents = $tab->getAllAttribsWhere("\"$attrrecid\"<$recid", $attrrecid);
 | |
|     } else {
 | |
|         @ents = $tab->getAllAttribsWhere("$attrrecid<$recid", $attrrecid);
 | |
|     }
 | |
| 
 | |
|     # delete them
 | |
|     foreach my $rid (@ents) {
 | |
|         $tab->delEntries($rid);
 | |
|     }
 | |
|     $tab->commit;
 | |
|     return $rc;
 | |
| }
 | |
| 
 | |
| #  prune all record up to number of days from today
 | |
| sub tabprune_numberdays {
 | |
|     my $table      = shift;
 | |
|     my $cb         = shift;
 | |
|     my $numberdays = shift;
 | |
|     my $attrrecid  = shift;
 | |
|     my $VERBOSE    = shift;
 | |
|     my $rc         = 0;
 | |
| 
 | |
|     # check which database so can build the correct Where clause
 | |
|     my $tab = xCAT::Table->new($table);
 | |
|     unless ($tab) {
 | |
|         $cb->({ error => "Unable to open $table", errorcode => 4 });
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     # get number of seconds in the day count
 | |
|     my $numbersecs = ($numberdays * 86400);
 | |
| 
 | |
|     # get time now
 | |
|     my $timenow     = time;
 | |
|     my $secsdaysago = $timenow - $numbersecs;
 | |
| 
 | |
|     # Format like the database table timestamp record
 | |
|     my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
 | |
|       localtime($secsdaysago);
 | |
|     my $daysago = sprintf("%04d-%02d-%02d %02d:%02d:%02d",
 | |
|         $year + 1900, $mon + 1, $mday,
 | |
|         $hour, $min, $sec);
 | |
| 
 | |
|     # delete all records before # days ago
 | |
| 
 | |
|     # display the output
 | |
|     # get field name for the table
 | |
|     my $timeattr = "audittime";    # default auditlog, most used
 | |
|     if ($table eq "eventlog") {
 | |
|         $timeattr = "eventtime";
 | |
|     }
 | |
|     my @attrarray;
 | |
|     push @attrarray, "$timeattr<$daysago";
 | |
|     my @recs;
 | |
|     if ($VERBOSE) {                # need to get all attributes
 | |
|         @recs = $tab->getAllAttribsWhere(\@attrarray, 'ALL');
 | |
|         output_table($table, $cb, $tab, \@recs);
 | |
|     }
 | |
|     my @ents = $tab->getAllAttribsWhere(\@attrarray, $attrrecid);
 | |
| 
 | |
|     # delete them
 | |
|     foreach my $rid (@ents) {
 | |
|         $tab->delEntries($rid);
 | |
|     }
 | |
|     $tab->commit;
 | |
|     return $rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| #  Dump table records to  stdout.
 | |
| sub output_table {
 | |
|     my $table = shift;
 | |
|     my $cb    = shift;
 | |
|     my $tabh  = shift;
 | |
|     my $recs  = shift;
 | |
|     my %rsp;
 | |
|     my $tabdump_header = sub {
 | |
|         my $header = "#" . join(",", @_);
 | |
|         push @{ $rsp{data} }, $header;
 | |
|     };
 | |
| 
 | |
|     # Display all the rows of the table in order of the columns in the schema
 | |
|     $tabdump_header->(@{ $tabh->{colnames} });
 | |
|     foreach my $rec (@$recs)
 | |
|     {
 | |
|         my $line = '';
 | |
|         foreach (@{ $tabh->{colnames} })
 | |
|         {
 | |
|             if (defined $rec->{$_})
 | |
|             {
 | |
|                 $rec->{$_} =~ s/"/""/g;
 | |
|                 $line = $line . '"' . $rec->{$_} . '",';
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 $line .= ',';
 | |
|             }
 | |
|         }
 | |
|         $line =~ s/,$//;    # remove the extra comma at the end
 | |
|         push @{ $rsp{data} }, $line;
 | |
|     }
 | |
|     $cb->(\%rsp);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| sub getTableColumn {
 | |
|     my $string = shift;
 | |
|     if ($shortnames{$string}) {
 | |
|         return @{ $shortnames{$string} };
 | |
|     }
 | |
|     unless ($string =~ /\./) {
 | |
|         return undef;
 | |
|     }
 | |
|     return split /\./, $string, 2;
 | |
| }
 | |
| 
 | |
| #   sub groupch
 | |
| #   {
 | |
| #       my $args = shift;
 | |
| #       my $nodech_usage = sub
 | |
| #       {
 | |
| #           my $exitcode = shift @_;
 | |
| #           my $cmdname = "groupch";
 | |
| #           my %rsp;
 | |
| #           push @{$rsp{data}}, "Usage: $cmdname <group1,group2,...> table.column=value [...]";
 | |
| #           push @{$rsp{data}}, "       $cmdname {-v | --version}";
 | |
| #           push @{$rsp{data}}, "       $cmdname [-? | -h | --help]";
 | |
| #           if ($exitcode) { $rsp{errorcode} = $exitcode; }
 | |
| #           $callback->(\%rsp);
 | |
| #       };
 | |
| #       my @args = @{$args};
 | |
| #       unless (scalar @args >= 2) {
 | |
| #           $nodech_usage->(1);
 | |
| #           return;
 | |
| #       }
 | |
| #       my @groups = split /,/,shift @args;
 | |
| #       foreach (@args)
 | |
| #       {
 | |
| #   #       if ($deletemode)
 | |
| #   #       {
 | |
| #   #           if (m/[=\.]/)   # in delete mode they can only specify tables names
 | |
| #   #           {
 | |
| #   #               $callback->({error => [". and = not valid in delete mode."],errorcode=>1});
 | |
| #   #               next;
 | |
| #   #           }
 | |
| #   #           $tables{$_} = 1;
 | |
| #   #           next;
 | |
| #   #       }
 | |
| #           unless (m/=/ or m/!~/)
 | |
| #           {
 | |
| #               $callback->({error => ["Malformed argument $_ ignored."],errorcode=>1});
 | |
| #               next;
 | |
| #           }
 | |
| #           my $stable;
 | |
| #           my $scolumn;
 | |
| #           #Check for selection criteria
 | |
| #           if (m/^[^=]*==/) {
 | |
| #               ($temp,$value)=split /==/,$_,2;
 | |
| #               ($stable,$scolumn)=getTableColumn($temp);
 | |
| #               $criteria{$stable}->{$scolumn}=[$value,'match'];
 | |
| 
 | |
| #               next; #Is a selection criteria, not an assignment specification
 | |
| #           } elsif (m/^[^=]*!=/) {
 | |
| #               ($temp,$value)=split /!=/,$_,2;
 | |
| #               ($stable,$scolumn)=getTableColumn($temp);
 | |
| #               $criteria{$stable}->{$scolumn}=[$value,'natch'];
 | |
| #               next; #Is a selection criteria, not an assignment specification
 | |
| #           } elsif (m/^[^=]*=~/) {
 | |
| #               ($temp,$value)=split /=~/,$_,2;
 | |
| #               ($stable,$scolumn)=getTableColumn($temp);
 | |
| #               $value =~ s/^\///;
 | |
| #               $value =~ s/\/$//;
 | |
| #               $criteria{$stable}->{$scolumn}=[$value,'regex'];
 | |
| #               next; #Is a selection criteria, not an assignment specification
 | |
| #           } elsif (m/^[^=]*!~/) {
 | |
| #               ($temp,$value)=split /!~/,$_,2;
 | |
| #               ($stable,$scolumn)=getTableColumn($temp);
 | |
| #               $value =~ s/^\///;
 | |
| #               $value =~ s/\/$//;
 | |
| #               $criteria{$stable}->{$scolumn}=[$value,'negex'];
 | |
| #               next; #Is a selection criteria, not an assignment specification
 | |
| #           }
 | |
| #           #Now definitely an assignment
 | |
| #
 | |
| #           ($temp, $value) = split('=', $_, 2);
 | |
| #           $value =~ s/^@//; #Allow the =@ operator to exist for an unambiguous assignmenet operator
 | |
| #                             #So before, table.column==value meant set to =value, now it would be matching value
 | |
| #                             #the new way would be table.column=@=value to be unambiguous
 | |
| #                             #now a value like '@hi' would be set with table.column=@@hi
 | |
| #           if ($value eq '') { #If blank, force a null entry to override group settings
 | |
| #               $value = '|^.*$||';
 | |
| #           }
 | |
| #           my $op = '=';
 | |
| #           if ($temp =~ /,$/)
 | |
| #           {
 | |
| #               $op = ',=';
 | |
| #               chop($temp);
 | |
| #           }
 | |
| #           elsif ($temp =~ /\^$/)
 | |
| #           {
 | |
| #               $op = '^=';
 | |
| #               chop($temp);
 | |
| #           }
 | |
| 
 | |
| #           my $table;
 | |
| #           if ($shortnames{$temp})
 | |
| #           {
 | |
| #               ($table, $column) = @{$shortnames{$temp}};
 | |
| #           }
 | |
| #           else
 | |
| #           {
 | |
| #               ($table, $column) = split('\.', $temp, 2);
 | |
| #           }
 | |
| #           unless (grep /$column/,@{$xCAT::Schema::tabspec{$table}->{cols}}) {
 | |
| #                $callback->({error=>"$table.$column not a valid table.column description",errorcode=>[1]});
 | |
| #                return;
 | |
| #           }
 | |
| 
 | |
| #           # Keep a list of the value/op pairs, in case there is more than 1 per table.column
 | |
| #           #$tables{$table}->{$column} = [$value, $op];
 | |
| #           push @{$tables{$table}->{$column}}, ($value, $op);
 | |
| #       }
 | |
| #   }
 | |
| 
 | |
| 
 | |
| #
 | |
| #  Process the nodech command, also used by the XML setNodesAttribs utility
 | |
| #  in tabutils
 | |
| #
 | |
| sub nodech
 | |
| {
 | |
|     my $nodes    = shift;
 | |
|     my $args     = shift;
 | |
|     my $callback = shift;
 | |
|     my $addmode  = shift;
 | |
|     my $groupmode;
 | |
|     if ($addmode eq "groupch") {
 | |
|         $addmode   = 0;
 | |
|         $groupmode = 1;
 | |
|     }
 | |
|     my $VERSION;
 | |
|     my $HELP;
 | |
|     my $deletemode;
 | |
|     my $grptab;
 | |
|     my @grplist;
 | |
| 
 | |
|     my $nodech_usage = sub
 | |
|     {
 | |
|         my $exitcode  = shift @_;
 | |
|         my $addmode   = shift @_;
 | |
|         my $groupmode = shift @_;
 | |
|         my $cmdname = $addmode ? 'nodeadd' : ($groupmode ? 'nodegrpch' : 'nodech');
 | |
|         my %rsp;
 | |
|         if ($addmode) {
 | |
|             push @{ $rsp{data} }, "Usage: $cmdname <noderange> groups=<groupnames> [table.column=value] [...]";
 | |
|         } elsif ($groupmode) {
 | |
|             push @{ $rsp{data} }, "Usage: $cmdname <group1,group2,...> [table.column=value] [...]";
 | |
|         } else {
 | |
|             push @{ $rsp{data} }, "Usage: $cmdname <noderange> table.column=value [...]";
 | |
|             push @{ $rsp{data} }, "       $cmdname {-d | --delete} <noderange> <table> [...]";
 | |
|         }
 | |
|         push @{ $rsp{data} }, "       $cmdname {-v | --version}";
 | |
|         push @{ $rsp{data} }, "       $cmdname [-? | -h | --help]";
 | |
|         if ($exitcode) { $rsp{errorcode} = $exitcode; }
 | |
|         $callback->(\%rsp);
 | |
|     };
 | |
| 
 | |
|     if ($args) {
 | |
|         @ARGV = @{$args};
 | |
|     } else {
 | |
|         @ARGV = ();
 | |
|     }
 | |
|     my %options = ('h|?|help' => \$HELP, 'v|version' => \$VERSION);
 | |
|     if (!$addmode) { $options{'d|delete'} = \$deletemode; }
 | |
|     if (!GetOptions(%options)) {
 | |
|         $nodech_usage->(1, $addmode, $groupmode);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Help
 | |
|     if ($HELP) {
 | |
|         $nodech_usage->(0, $addmode, $groupmode);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Version
 | |
|     if ($VERSION) {
 | |
|         my %rsp;
 | |
|         my $version = xCAT::Utils->Version();
 | |
|         $rsp{data}->[0] = "$version";
 | |
|         $callback->(\%rsp);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Note: the noderange comes through in $arg (and therefore @ARGV) for nodeadd,
 | |
|     # because it is linked to xcatclientnnr, since the nodes specified in the noderange
 | |
|     # do not exist yet.  The nodech cmd is linked to xcatclient, so its noderange is
 | |
|     # put in $nodes instead of $args.
 | |
|     if (scalar(@ARGV) < (1 + $addmode)) { $nodech_usage->(1, $addmode); return; }
 | |
|     my @groups;
 | |
| 
 | |
|     if ($addmode)
 | |
|     {
 | |
|         my $nr = shift @ARGV;
 | |
|         $nodes = [ noderange($nr, 0) ];
 | |
|         unless ($nodes) {
 | |
|             $callback->({ error => "No noderange to add.\n", errorcode => 1 });
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         my $invalidnodename = ();
 | |
|         foreach my $node (@$nodes) {
 | |
|             if ($node =~ /[A-Z]/) {
 | |
|                 $invalidnodename .= ",$node";
 | |
|             }
 | |
|         }
 | |
|         if ($invalidnodename) {
 | |
|             $invalidnodename =~ s/,//;
 | |
|             $callback->({ warning => "The node name \'$invalidnodename\' contains capital letters which may not be resolved correctly by the dns server." });
 | |
|         }
 | |
|     } elsif ($groupmode) {
 | |
|         @groups = split /,/, shift @ARGV;
 | |
|     }
 | |
|     my $column;
 | |
|     my $value;
 | |
|     my $temp;
 | |
|     my %tables;
 | |
|     my %criteria = ();
 | |
|     my $tab;
 | |
| 
 | |
|     #print Dumper($deletemode);
 | |
|     foreach (@ARGV)
 | |
|     {
 | |
|         if ($deletemode)
 | |
|         {
 | |
|             if (m/[=\.]/)    # in delete mode they can only specify tables names
 | |
|             {
 | |
|                 $callback->({ error => [". and = not valid in delete mode."], errorcode => 1 });
 | |
|                 next;
 | |
|             }
 | |
|             $tables{$_} = 1;
 | |
|             next;
 | |
|         }
 | |
|         unless (m/=/ or m/!~/)
 | |
|         {
 | |
|             $callback->({ error => ["Malformed argument $_ ignored."], errorcode => 1 });
 | |
|             next;
 | |
|         }
 | |
|         my $stable;
 | |
|         my $scolumn;
 | |
| 
 | |
|         #Check for selection criteria
 | |
|         if (m/^[^=]*==/) {
 | |
|             ($temp, $value) = split /==/, $_, 2;
 | |
|             ($stable, $scolumn) = getTableColumn($temp);
 | |
|             $criteria{$stable}->{$scolumn} = [ $value, 'match' ];
 | |
| 
 | |
|             next;    #Is a selection criteria, not an assignment specification
 | |
|         } elsif (m/^[^=]*!=/) {
 | |
|             ($temp, $value) = split /!=/, $_, 2;
 | |
|             ($stable, $scolumn) = getTableColumn($temp);
 | |
|             $criteria{$stable}->{$scolumn} = [ $value, 'natch' ];
 | |
|             next;    #Is a selection criteria, not an assignment specification
 | |
|         } elsif (m/^[^=]*=~/) {
 | |
|             ($temp, $value) = split /=~/, $_, 2;
 | |
|             ($stable, $scolumn) = getTableColumn($temp);
 | |
|             $value =~ s/^\///;
 | |
|             $value =~ s/\/$//;
 | |
|             $criteria{$stable}->{$scolumn} = [ $value, 'regex' ];
 | |
|             next;    #Is a selection criteria, not an assignment specification
 | |
|         } elsif (m/^[^=]*!~/) {
 | |
|             ($temp, $value) = split /!~/, $_, 2;
 | |
|             ($stable, $scolumn) = getTableColumn($temp);
 | |
|             $value =~ s/^\///;
 | |
|             $value =~ s/\/$//;
 | |
|             $criteria{$stable}->{$scolumn} = [ $value, 'negex' ];
 | |
|             next;    #Is a selection criteria, not an assignment specification
 | |
|         }
 | |
| 
 | |
|         #Now definitely an assignment
 | |
| 
 | |
|         ($temp, $value) = split('=', $_, 2);
 | |
|         $value =~ s/^@//; #Allow the =@ operator to exist for an unambiguous assignmenet operator
 | |
|          #So before, table.column==value meant set to =value, now it would be matching value
 | |
|          #the new way would be table.column=@=value to be unambiguous
 | |
|          #now a value like '@hi' would be set with table.column=@@hi
 | |
|         if ($value eq '') { #If blank, force a null entry to override group settings
 | |
|             $value = '|^.*$||';
 | |
|         }
 | |
|         my $op = '=';
 | |
|         if ($temp =~ /,$/)
 | |
|         {
 | |
|             $op = ',=';
 | |
|             chop($temp);
 | |
|         }
 | |
|         elsif ($temp =~ /\^$/)
 | |
|         {
 | |
|             $op = '^=';
 | |
|             chop($temp);
 | |
|         }
 | |
| 
 | |
|         my $table;
 | |
|         if ($shortnames{$temp})
 | |
|         {
 | |
|             ($table, $column) = @{ $shortnames{$temp} };
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             ($table, $column) = split('\.', $temp, 2);
 | |
|         }
 | |
|         unless (grep /$column/, @{ $xCAT::Schema::tabspec{$table}->{cols} }) {
 | |
|             $callback->({ error => "$table.$column not a valid table.column description", errorcode => [1] });
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         # Keep a list of the value/op pairs, in case there is more than 1 per table.column
 | |
|         #$tables{$table}->{$column} = [$value, $op];
 | |
|         push @{ $tables{$table}->{$column} }, ($value, $op);
 | |
|     }
 | |
|     my %nodehash;
 | |
|     if (keys %criteria) {
 | |
|         foreach (@$nodes) {
 | |
|             $nodehash{$_} = 1;
 | |
|         }
 | |
|     }
 | |
|     foreach $tab (keys %criteria) {
 | |
|         my $tabhdl  = xCAT::Table->new($tab, -create => 1, -autocommit => 0);
 | |
|         my @columns = keys %{ $criteria{$tab} };
 | |
|         my $tabhash = $tabhdl->getNodesAttribs($nodes, \@columns);
 | |
|         my $node;
 | |
|         my $col;
 | |
|         my $rec;
 | |
|         foreach $node (@$nodes) {
 | |
|             foreach $rec (@{ $tabhash->{$node} }) {
 | |
|                 foreach $col (@columns) {
 | |
|                     my $value = $criteria{$tab}->{$col}->[0];
 | |
|                     unless (defined $value) {
 | |
|                         $value = "";
 | |
|                     }
 | |
|                     my $matchtype = $criteria{$tab}->{$col}->[1];
 | |
|                     if ($matchtype eq 'match' and not($rec->{$col} eq $value) or
 | |
|                         $matchtype eq 'natch' and ($rec->{$col} eq $value)   or
 | |
|                         $matchtype eq 'regex' and ($rec->{$col} !~ /$value/) or
 | |
|                         $matchtype eq 'negex' and ($rec->{$col} =~ /$value/)) {
 | |
|                         delete $nodehash{$node};
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         $nodes = [ keys %nodehash ];
 | |
|     }
 | |
|     foreach $tab (keys %tables)
 | |
|     {
 | |
|         my $tabhdl = xCAT::Table->new($tab, -create => 1, -autocommit => 0);
 | |
|         if ($tabhdl)
 | |
|         {
 | |
|             my $changed = 0;
 | |
|             my @entities;
 | |
|             if ($groupmode) {
 | |
|                 @entities = @groups;
 | |
|             } else {
 | |
|                 @entities = @$nodes;
 | |
|             }
 | |
|             my $entity;
 | |
|             foreach $entity (@entities) {
 | |
|                 if ($deletemode) {
 | |
|                     $tabhdl->delEntries({ 'node' => $entity });
 | |
|                     $changed = 1;
 | |
|                 } else {
 | |
| 
 | |
|                     #$tabhdl->setNodeAttribs($_,$tables{$tab});
 | |
|                     my %uhsh;
 | |
|                     foreach (keys %{ $tables{$tab} }) # for each column specified for this table
 | |
|                     {
 | |
|                         #my $op  = $tables{$tab}->{$_}->[1];
 | |
|                         #my $val = $tables{$tab}->{$_}->[0];
 | |
|                         my @valoppairs = @{ $tables{$tab}->{$_} };    #Deep copy
 | |
|                         while (scalar(@valoppairs)) { # alternating list of value and op for this table.column
 | |
|                             my $val = shift @valoppairs;
 | |
|                             my $op  = shift @valoppairs;
 | |
|                             my $key = $_;
 | |
| 
 | |
|                             # When changing the groups of the node, check whether the new group
 | |
|                             # is a dynamic group.
 | |
|                             if (($key eq 'groups') && ($op eq '=')) {
 | |
|                                 if ($groupmode) {
 | |
|                                     $callback->({ error => "Group membership is not changeable via nodegrpch", errorcode => 1 });
 | |
|                                     return;
 | |
|                                 }
 | |
|                                 if (scalar(@grplist) == 0) { # Do not call $grptab->getAllEntries for each node, performance issue.
 | |
|                                     $grptab = xCAT::Table->new('nodegroup');
 | |
|                                     if ($grptab) {
 | |
|                                         @grplist = @{ $grptab->getAllEntries() };
 | |
|                                     }
 | |
|                                 }
 | |
|                                 my @grps = split(/,/, $val);
 | |
|                                 foreach my $grp (@grps) {
 | |
|                                     foreach my $grpdef_ref (@grplist) {
 | |
|                                         my %grpdef = %$grpdef_ref;
 | |
|                                         if (($grpdef{'groupname'} eq $grp) && ($grpdef{'grouptype'} eq 'dynamic')) {
 | |
|                                             my %rsp;
 | |
|                                             $rsp{data}->[0] = "nodegroup $grp is a dynamic node group, should not add a node into a dynamic node group statically.\n";
 | |
|                                             $callback->(\%rsp);
 | |
|                                         }
 | |
|                                     }
 | |
|                                 }
 | |
|                             }
 | |
|                             if ($op eq '=') {
 | |
|                                 $uhsh{$key} = $val;
 | |
|                             } elsif ($op eq ',=') {    #splice assignment
 | |
|                                 if ($groupmode) {
 | |
|                                     $callback->({ error => ",= and ^= are not supported by nodegrpch", errorcode => 1 });
 | |
|                                     return;
 | |
|                                 }
 | |
|                                 my $curval = $uhsh{$key}; # in case it was already set
 | |
|                                 if (!defined($curval)) {
 | |
|                                     my $cent = $tabhdl->getNodeAttribs($entity, [$key]);
 | |
|                                     if ($cent) { $curval = $cent->{$key}; }
 | |
|                                 }
 | |
|                                 if ($curval) {
 | |
|                                     my @vals = split(/,/, $curval);
 | |
|                                     unless (grep /^$val$/, @vals) {
 | |
|                                         unshift @vals, $val;
 | |
|                                         my $newval = join(',', @vals);
 | |
|                                         $uhsh{$key} = $newval;
 | |
|                                     }
 | |
|                                 } else {
 | |
|                                     $uhsh{$key} = $val;
 | |
|                                 }
 | |
|                             } elsif ($op eq '^=') {
 | |
|                                 if ($groupmode) {
 | |
|                                     $callback->({ error => ",= and ^= are not supported by nodegrpch", errorcode => 1 });
 | |
|                                     return;
 | |
|                                 }
 | |
|                                 my $curval = $uhsh{$key}; # in case it was already set
 | |
|                                 if (!defined($curval)) {
 | |
|                                     my $cent = $tabhdl->getNodeAttribs($entity, [$key]);
 | |
|                                     if ($cent) { $curval = $cent->{$key}; }
 | |
|                                 }
 | |
|                                 if ($curval) {
 | |
|                                     my @vals = split(/,/, $curval);
 | |
|                                     if (grep /^$val$/, @vals) { #only bother if there
 | |
|                                         @vals = grep(!/^$val$/, @vals);
 | |
|                                         my $newval = join(',', @vals);
 | |
|                                         $uhsh{$key} = $newval;
 | |
|                                     }
 | |
|                                 }  #else, what they asked for is the case alredy
 | |
|                             }
 | |
|                         }    # end of while @valoppairs
 | |
|                     }    # end of foreach column specified for this table
 | |
| 
 | |
|                     if (keys %uhsh)
 | |
|                     {
 | |
|                         if ($groupmode) {
 | |
|                             my $nodekey = "node";
 | |
|                             if (defined $xCAT::Schema::tabspec{$tab}->{nodecol}) {
 | |
|                                 $nodekey = $xCAT::Schema::tabspec{$tab}->{nodecol}
 | |
|                             }
 | |
|                             my %clrhash; #First, we prepare to clear all nodes of their overrides on these columns
 | |
|                             foreach (keys %uhsh) {
 | |
|                                 if ($_ eq $nodekey) { next; } #skip attempts to manipulate 'node' type columns in a groupch
 | |
|                                 $clrhash{$_} = "";
 | |
|                             }
 | |
|                             $tabhdl->setAttribs({ $nodekey => $entity }, \%uhsh);
 | |
|                             $changed = 1;
 | |
|                             $nodes   = [ noderange($entity) ];
 | |
|                             unless (scalar @$nodes) { next; }
 | |
|                             $tabhdl->setNodesAttribs($nodes, \%clrhash);
 | |
|                             $changed = 1;
 | |
|                         } else {
 | |
|                             my @rc = $tabhdl->setNodeAttribs($entity, \%uhsh);
 | |
|                             $changed = 1;
 | |
|                             if (not defined($rc[0])) {
 | |
|                                 $callback->({ error => "DB error " . $rc[1], errorcode => 1 });
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             if ($changed) {
 | |
|                 $tabhdl->commit;
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             $callback->(
 | |
|                 { error => ["ERROR: Unable to open table $tab in configuration"], errorcode => 1 }
 | |
|             );
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| # gennr linked to xcatclientnnr and is used to generate a list of nodes
 | |
| # external to the database.
 | |
| sub gennr {
 | |
|     my $nodes    = shift;
 | |
|     my $args     = shift;
 | |
|     my $callback = shift;
 | |
|     @ARGV = @{$args};
 | |
|     my $nr = shift @ARGV;
 | |
|     $nodes = [ noderange($nr, 0) ];
 | |
|     my %rsp;    # for output.
 | |
|     foreach (@$nodes) {
 | |
| 
 | |
|         #print $_ . "\n";
 | |
|         push @{ $rsp{data} }, $_;
 | |
| 
 | |
|     }
 | |
|     $callback->(\%rsp);
 | |
| }
 | |
| 
 | |
| sub tabgrep
 | |
| {
 | |
|     my $node = shift;
 | |
|     my @tablist;
 | |
|     my $callback = shift;
 | |
| 
 | |
|     if (!defined($node) || !scalar(@$node)) {
 | |
|         my %rsp;
 | |
|         push @{ $rsp{data} }, "Usage: tabgrep nodename";
 | |
|         push @{ $rsp{data} }, "       tabgrep [-?|-h|--help]";
 | |
|         $rsp{errorcode} = 1;
 | |
|         $callback->(\%rsp);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     foreach (keys %{xCAT::Schema::tabspec})
 | |
|     {
 | |
|         if (grep /^node$/, @{ $xCAT::Schema::tabspec{$_}->{cols} })
 | |
|         {
 | |
|             push @tablist, $_;
 | |
|         }
 | |
|     }
 | |
|     foreach (@tablist)
 | |
|     {
 | |
|         my $tab = xCAT::Table->new($_);
 | |
|         unless ($tab) { next; }
 | |
|         if ($tab and $tab->getNodeAttribs($node->[0], ["node"]))
 | |
|         {
 | |
|             $callback->({ data => [$_] });
 | |
|         }
 | |
|         $tab->close;
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| sub rnoderange
 | |
| {
 | |
|     my $nodes    = shift;
 | |
|     my $args     = shift;
 | |
|     my $callback = shift;
 | |
|     my $data     = abbreviate_noderange($nodes);
 | |
|     if ($data) {
 | |
|         $callback->({ data => [$data] });
 | |
|     }
 | |
| }
 | |
| #####################################################
 | |
| #  nodels command
 | |
| #####################################################
 | |
| sub nodels
 | |
| {
 | |
|     my $nodes     = shift;
 | |
|     my $args      = shift;
 | |
|     my $callback  = shift;
 | |
|     my $noderange = shift;
 | |
|     unless ($nodes) {
 | |
|         $nodes = [];
 | |
|     }
 | |
| 
 | |
|     my $VERSION;
 | |
|     my $HELP;
 | |
| 
 | |
|     my $nodels_usage = sub
 | |
|     {
 | |
|         my $exitcode = shift @_;
 | |
|         my %rsp;
 | |
|         push @{ $rsp{data} }, "Usage:";
 | |
|         push @{ $rsp{data} }, "  nodels [noderange] [-b|--blame] [-H|--with-fieldname] [table.attribute | shortname] [-S][...]";
 | |
|         push @{ $rsp{data} }, "  nodels {-v|--version}";
 | |
|         push @{ $rsp{data} }, "  nodels [-?|-h|--help]";
 | |
|         if ($exitcode) { $rsp{errorcode} = $exitcode; }
 | |
|         $callback->(\%rsp);
 | |
|     };
 | |
| 
 | |
|     if ($args) {
 | |
|         @ARGV = @{$args};
 | |
|     } else {
 | |
|         @ARGV = ();
 | |
|     }
 | |
|     my $NOTERSE;
 | |
|     my $ATTRIBUTION;
 | |
|     my $HIDDEN;
 | |
| 
 | |
|     if (!GetOptions('h|?|help' => \$HELP, 'H|with-fieldname' => \$NOTERSE, 'b|blame' => \$ATTRIBUTION, 'v|version' => \$VERSION, 'S' => \$HIDDEN)) { $nodels_usage->(1); return; }
 | |
| 
 | |
|     # Help
 | |
|     if ($HELP) { $nodels_usage->(0); return; }
 | |
| 
 | |
|     # Version
 | |
|     if ($VERSION)
 | |
|     {
 | |
|         my %rsp;
 | |
|         my $version = xCAT::Utils->Version();
 | |
|         $rsp{data}->[0] = "$version";
 | |
|         $callback->(\%rsp);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # TODO -- Parse command arguments
 | |
|     #  my $opt;
 | |
|     #  my %attrs;
 | |
|     #  foreach $opt (@ARGV) {
 | |
|     #     if ($opt =~ /^group/) {
 | |
|     #     }
 | |
|     #  }
 | |
|     my $argc  = @ARGV;
 | |
|     my $terse = 2;
 | |
|     if ($NOTERSE) {
 | |
|         $terse = 0;
 | |
|     }
 | |
| 
 | |
|     if (@$nodes > 0 or $noderange)
 | |
|     { #Make sure that there are zero nodes *and* that a noderange wasn't requested
 | |
|                     # TODO - gather data for each node
 | |
|                     #        for now just return the flattened list of nodes)
 | |
|         my $rsp;    #build up fewer requests, be less chatty
 | |
| 
 | |
|         #-S will make nodels not show FSPs and BPAs
 | |
|         my @newnodes = ();
 | |
|         if (!defined($HIDDEN))
 | |
|         {
 | |
|             my $listtab = xCAT::Table->new('nodelist');
 | |
|             if ($listtab) {
 | |
|                 my $listHash = $listtab->getNodesAttribs(\@$nodes, ['hidden']);
 | |
|                 foreach my $rnode (@$nodes) {
 | |
|                     unless (defined($listHash->{$rnode}->[0]->{hidden})) {
 | |
|                         push(@newnodes, $rnode);
 | |
|                     } elsif ($listHash->{$rnode}->[0]->{hidden} != 1) {
 | |
|                         push(@newnodes, $rnode);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             $nodes = \@newnodes;
 | |
|         }
 | |
| 
 | |
|         if ($argc)
 | |
|         {
 | |
|             my %tables;
 | |
|             foreach (@ARGV)
 | |
|             {
 | |
|                 my $table;
 | |
|                 my $column;
 | |
|                 my $value;
 | |
|                 my $matchtype;
 | |
|                 my $temp = $_;
 | |
|                 if ($temp =~ /^[^=]*\!=/) {
 | |
|                     ($temp, $value) = split /!=/, $temp, 2;
 | |
|                     $matchtype = 'natch';
 | |
|                 }
 | |
|                 elsif ($temp =~ /^[^=]*=~/) {
 | |
|                     ($temp, $value) = split /=~/, $temp, 2;
 | |
|                     $value =~ s/^\///;
 | |
|                     $value =~ s/\/$//;
 | |
|                     $matchtype = 'regex';
 | |
|                 }
 | |
|                 elsif ($temp =~ /[^=]*==/) {
 | |
|                     ($temp, $value) = split /==/, $temp, 2;
 | |
|                     $matchtype = 'match';
 | |
|                 }
 | |
|                 elsif ($temp =~ /[^=]*!~/) {
 | |
|                     ($temp, $value) = split /!~/, $temp, 2;
 | |
|                     $value =~ s/^\///;
 | |
|                     $value =~ s/\/$//;
 | |
|                     $matchtype = 'negex';
 | |
|                 }
 | |
|                 if ($shortnames{$temp})
 | |
|                 {
 | |
|                     ($table, $column) = @{ $shortnames{$temp} };
 | |
|                     $terse--;
 | |
|                 } elsif ($temp =~ /\./) {
 | |
|                     ($table, $column) = split('\.', $temp, 2);
 | |
|                     $terse--;
 | |
|                 } elsif ($xCAT::Schema::tabspec{$temp}) {
 | |
|                     $terse = 0;
 | |
|                     $table = $temp;
 | |
|                     foreach my $column (@{ $xCAT::Schema::tabspec{$table}->{cols} }) {
 | |
|                         unless (grep /^$column$/, @{ $tables{$table} }) {
 | |
|                             push @{ $tables{$table} }, [ $column, "$temp.$column" ];
 | |
|                         }
 | |
|                     }
 | |
|                     next;
 | |
|                 } else {
 | |
|                     $callback->({ error => "$temp not a valid table.column description", errorcode => [1] });
 | |
|                     next;
 | |
|                 }
 | |
| 
 | |
| 
 | |
|                 unless (grep /$column/, @{ $xCAT::Schema::tabspec{$table}->{cols} }) {
 | |
|                     $callback->({ error => "$table.$column not a valid table.column description", errorcode => [1] });
 | |
|                     next;
 | |
|                 }
 | |
|                 unless (grep /^$column$/, @{ $tables{$table} })
 | |
|                 {
 | |
|                     push @{ $tables{$table} },
 | |
|                       [ $column, $temp, $value, $matchtype ]; #Mark this as something to get
 | |
|                 }
 | |
|             }
 | |
|             my $tab;
 | |
|             my %noderecs;
 | |
|             my %filterednodes    = ();
 | |
|             my %mustdisplaynodes = ();
 | |
|             my %forcedisplaykeys = ();
 | |
|             foreach $tab (keys %tables)
 | |
|             {
 | |
|                 my $tabh = xCAT::Table->new($tab);
 | |
|                 unless ($tabh) { next; }
 | |
| 
 | |
|                 #print Dumper($tables{$tab});
 | |
|                 my $node;
 | |
|                 my %labels;
 | |
|                 my %values;
 | |
|                 my %matchtypes;
 | |
|                 my @cols = ();
 | |
|                 foreach (@{ $tables{$tab} })
 | |
|                 {
 | |
|                     push @cols, $_->[0];
 | |
|                     $labels{ $_->[0] } = $_->[1]; #Remember user supplied discreptions and use them
 | |
|                     if (not defined $values{ $_->[0] }) { #If selection criteria not previously specified
 | |
|                         $values{ $_->[0] } = $_->[2]; #assign selection criteria
 | |
|                     } elsif (not defined $_->[2]) { #we already have selection criteria, but this field isn't that
 | |
|                         $forcedisplaykeys{ $_->[0] } = 1; #allow switch.switch=~switch switch.switch, for example
 | |
|                     } else { #User attempted multiple selection criteria on the same field, bail
 | |
|                         $callback->({ error => [ "Multiple selection critera for " . $labels{ $_->[0] } ] });
 | |
|                         return;
 | |
|                     }
 | |
|                     if (not defined $matchtypes{ $_->[0] }) {
 | |
|                         $matchtypes{ $_->[0] } = $_->[3];
 | |
|                     }
 | |
|                 }
 | |
|                 my $nodekey = "node";
 | |
|                 if (defined $xCAT::Schema::tabspec{$tab}->{nodecol}) {
 | |
|                     $nodekey = $xCAT::Schema::tabspec{$tab}->{nodecol}
 | |
|                 }
 | |
| 
 | |
|                 my $removenodecol = 1;
 | |
|                 if (grep /^$nodekey$/, @cols) {
 | |
|                     $removenodecol = 0;
 | |
|                 }
 | |
|                 my $rechash = $tabh->getNodesAttribs($nodes, \@cols, withattribution => $ATTRIBUTION);
 | |
|                 foreach $node (@$nodes)
 | |
|                 {
 | |
|                     my @cols;
 | |
|                     my $recs = $rechash->{$node}; #$tabh->getNodeAttribs($node, \@cols);
 | |
|                     my %satisfiedreqs = ();
 | |
|                     foreach my $rec (@$recs) {
 | |
| 
 | |
|                         foreach (sort keys %$rec)
 | |
|                         {
 | |
|                             if ($_ eq '!!xcatgroupattribution!!') { next; }
 | |
|                             if ($_ eq $nodekey and $removenodecol) { next; }
 | |
|                             $satisfiedreqs{$_} = 1;
 | |
|                             my %datseg = ();
 | |
|                             if (defined $values{$_}) {
 | |
|                                 my $criteria = $values{$_}; #At least vim highlighting makes me worry about syntax in regex
 | |
|                                 if ($matchtypes{$_} eq 'match' and not($rec->{$_} eq $criteria) or
 | |
|                                     $matchtypes{$_} eq 'natch' and ($rec->{$_} eq $criteria) or
 | |
|                                     $matchtypes{$_} eq 'regex' and ($rec->{$_} !~ /$criteria/) or
 | |
|                                     $matchtypes{$_} eq 'negex' and ($rec->{$_} =~ /$criteria/)) {
 | |
| 
 | |
|                                     #unless ($rec->{$_} eq $values{$_}) {
 | |
|                                     $filterednodes{$node} = 1;
 | |
|                                     next;
 | |
|                                 }
 | |
|                                 $mustdisplaynodes{$node} = 1;
 | |
|                                 unless ($forcedisplaykeys{$_}) { next; } #skip if only specified once on command line
 | |
|                             }
 | |
|                             unless ($terse > 0) {
 | |
|                                 $datseg{data}->[0]->{desc} = [ $labels{$_} ];
 | |
|                             }
 | |
|                             if ($rec->{'!!xcatgroupattribution!!'} and $rec->{'!!xcatgroupattribution!!'}->{$_}) {
 | |
|                                 $datseg{data}->[0]->{contents} = [ $rec->{$_} . " (inherited from group " . $rec->{'!!xcatgroupattribution!!'}->{$_} . ")" ];
 | |
|                             } else {
 | |
|                                 $datseg{data}->[0]->{contents} = [ $rec->{$_} ];
 | |
|                             }
 | |
|                             $datseg{name} = [$node]; #{}->{contents} = [$rec->{$_}];
 | |
|                             push @{ $noderecs{$node} }, \%datseg;
 | |
|                         }
 | |
|                     }
 | |
|                     foreach (keys %labels) {
 | |
|                         unless (defined $satisfiedreqs{$_}) {
 | |
|                             my %dataseg;
 | |
|                             if (defined $values{$_}) {
 | |
|                                 my $criteria = $values{$_};
 | |
|                                 if ($matchtypes{$_} eq 'match' and not("" eq $criteria) or
 | |
|                                     $matchtypes{$_} eq 'natch' and ("" eq $criteria) or
 | |
|                                     $matchtypes{$_} eq 'regex' and ("" !~ /$criteria/) or
 | |
|                                     $matchtypes{$_} eq 'negex' and ("" =~ /$criteria/)) {
 | |
| 
 | |
|                                     #unless ("" eq $values{$_}) {
 | |
|                                     $filterednodes{$node} = 1;
 | |
|                                     next;
 | |
|                                 }
 | |
|                                 $mustdisplaynodes{$node} = 1;
 | |
|                                 unless ($forcedisplaykeys{$_}) { next; }
 | |
|                             }
 | |
|                             $dataseg{name} = [$node];
 | |
|                             unless ($terse > 0) {
 | |
|                                 $dataseg{data}->[0]->{desc} = [ $labels{$_} ];
 | |
|                             }
 | |
|                             $dataseg{data}->[0]->{contents} = [""];
 | |
|                             push @{ $noderecs{$node} }, \%dataseg;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 #$rsp->{node}->[0]->{data}->[0]->{desc}->[0] = $_;
 | |
|                 #$rsp->{node}->[0]->{data}->[0]->{contents}->[0] = $_;
 | |
|                 $tabh->close();
 | |
|                 undef $tabh;
 | |
|             }
 | |
|             foreach (keys %mustdisplaynodes) {
 | |
|                 if ($filterednodes{$_} or defined $noderecs{$_}) {
 | |
|                     next;
 | |
|                 }
 | |
|                 $noderecs{$_} = [ { name => [$_] } ];
 | |
|             }
 | |
|             foreach (keys %filterednodes) {
 | |
|                 delete $noderecs{$_};
 | |
|             }
 | |
|             foreach (sort (keys %noderecs))
 | |
|             {
 | |
|                 push @{ $rsp->{"node"} }, @{ $noderecs{$_} };
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             foreach (sort @$nodes)
 | |
|             {
 | |
|                 my $noderec;
 | |
|                 $noderec->{name}->[0] = ($_);
 | |
|                 push @{ $rsp->{node} }, $noderec;
 | |
|             }
 | |
|         }
 | |
|         $callback->($rsp);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
| 
 | |
|         # no noderange specified on command line, return list of all nodes
 | |
|         my $nodelisttab;
 | |
|         if ($nodelisttab = xCAT::Table->new("nodelist"))
 | |
|         {
 | |
|             my @attribs = ("node");
 | |
|             my @ents    = $nodelisttab->getAllAttribs(@attribs);
 | |
|             my @nodes;
 | |
|             foreach (@ents) {
 | |
|                 if ($_->{node}) {
 | |
|                     push @nodes, $_->{node};
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             #-S will make nodels not show FSPs and BPAs
 | |
|             my @newnodes = ();
 | |
|             if (!defined($HIDDEN))
 | |
|             {
 | |
|                 my $listtab = xCAT::Table->new('nodelist');
 | |
|                 if ($listtab) {
 | |
|                     my $listHash = $listtab->getNodesAttribs(\@nodes, ['hidden']);
 | |
|                     foreach my $rnode (@nodes) {
 | |
|                         unless (defined($listHash->{$rnode}->[0]->{hidden})) {
 | |
|                             push(@newnodes, $rnode);
 | |
|                         } elsif ($listHash->{$rnode}->[0]->{hidden} != 1) {
 | |
|                             push(@newnodes, $rnode);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 @nodes = ();
 | |
|                 foreach (@newnodes) {
 | |
|                     push(@nodes, $_);
 | |
|                 }
 | |
|             }
 | |
|             @nodes = sort { $a cmp $b } @nodes;
 | |
|             foreach (@nodes) {
 | |
|                 my $rsp;
 | |
| 
 | |
|                 #if ($_)
 | |
|                 #{
 | |
|                 $rsp->{node}->[0]->{name}->[0] = ($_);
 | |
| 
 | |
|                 #              $rsp->{node}->[0]->{data}->[0]->{contents}->[0]="$_->{node} node contents";
 | |
|                 #              $rsp->{node}->[0]->{data}->[0]->{desc}->[0]="$_->{node} node desc";
 | |
|                 $callback->($rsp);
 | |
| 
 | |
|                 #}
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| #########
 | |
| #  tabch
 | |
| #########
 | |
| 
 | |
| sub tabch {
 | |
|     my $req      = shift;
 | |
|     my $callback = shift;
 | |
| 
 | |
|     # tabch usages message
 | |
|     my $tabch_usage = sub {
 | |
|         my $exitcode = shift @_;
 | |
|         my %rsp;
 | |
|         push @{ $rsp{data} }, "Usage: tabch";
 | |
|         push @{ $rsp{data} }, "       To add or update rows for tables:";
 | |
|         push @{ $rsp{data} }, "       tabch keycolname=keyvalue[,keycolname=keyvalue...] tablename.colname=newvalue [tablename.colname=newvalue...]";
 | |
|         push @{ $rsp{data} }, "       To delete rows from tables:";
 | |
|         push @{ $rsp{data} }, "       tabch -d|--delete keycolname=keyvalue[,keycolname=keyvalue...] tablename [tablename...]";
 | |
|         push @{ $rsp{data} }, "         keycolname=keyvalue   a column name-and-value pair ";
 | |
|         push @{ $rsp{data} }, "         that identifies the rows in a table to be changed.";
 | |
|         push @{ $rsp{data} }, "         tablename.colname=newvalue ";
 | |
|         push @{ $rsp{data} }, "         the new value for the specified row and column of the table.";
 | |
|         push @{ $rsp{data} }, "       tabch [-h|--help]";
 | |
|         push @{ $rsp{data} }, "       tabch [-v|--version]";
 | |
|         if ($exitcode) { $rsp{errorcode} = $exitcode; }
 | |
|         $callback->(\%rsp);
 | |
|     };
 | |
| 
 | |
|     # check for parameters
 | |
|     if (!defined($req->{arg})) { $tabch_usage->(1); return; }
 | |
|     @ARGV = @{ $req->{arg} };
 | |
| 
 | |
|     # options can be bundled up like -vV
 | |
|     Getopt::Long::Configure("bundling");
 | |
|     $Getopt::Long::ignorecase = 0;
 | |
|     my $delete;
 | |
|     my $help;
 | |
|     my $version;
 | |
| 
 | |
|     # parse the options
 | |
|     if (
 | |
|         !GetOptions(
 | |
|             'd|delete'  => \$delete,
 | |
|             'h|help'    => \$help,
 | |
|             'v|version' => \$version,
 | |
|         )
 | |
|       )
 | |
|     {
 | |
|         $tabch_usage->(1);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if ($help) { $tabch_usage->(0); return; }
 | |
| 
 | |
|     # display the version statement if -v or --verison is specified
 | |
|     if ($version)
 | |
|     {
 | |
|         my %rsp;
 | |
|         my $version = xCAT::Utils->Version();
 | |
|         $rsp{data}->[0] = "tabch :$version";
 | |
|         $callback->(\%rsp);
 | |
|         exit(0);
 | |
|     }
 | |
| 
 | |
|     # now start processing the input
 | |
| 
 | |
|     my $target = shift @ARGV;
 | |
|     unless ($target)
 | |
|     {
 | |
| 
 | |
|         $tabch_usage->(1); return;
 | |
|     }
 | |
|     my %tables;
 | |
|     my %keyhash = ();
 | |
|     my @keypairs = split(/,/, $target);
 | |
|     if ($keypairs[0] !~ /([^\.\=]+)\.([^\.\=]+)\=(.+)/)
 | |
|     {
 | |
|         foreach (@keypairs)
 | |
|         {
 | |
|             m/(.*)=(.*)/;
 | |
|             my $key = $1;
 | |
|             my $val = $2;
 | |
|             if (!defined($key) || !defined($val))
 | |
|             {
 | |
|                 my %rsp;
 | |
|                 $rsp{data}->[0] = "Incorrect argument \"$_\".\n";
 | |
|                 $rsp{data}->[1] = "Check man tabch or tabch -h\n";
 | |
|                 $callback->(\%rsp);
 | |
|                 return 1;
 | |
|             }
 | |
|             $keyhash{$key} = $val;
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         unshift(@ARGV, $target);
 | |
|     }
 | |
| 
 | |
|     if ($delete)
 | |
|     {
 | |
| 
 | |
|         #delete option is specified
 | |
|         my @tables_to_del = @ARGV;
 | |
|         if (@tables_to_del == 0)
 | |
|         {
 | |
|             my %rsp;
 | |
|             $rsp{data}->[0] = "Missing table name.\n";
 | |
|             $rsp{data}->[1] = "Check man tabch or tabch -h\n";
 | |
|             $callback->(\%rsp);
 | |
|             return 1;
 | |
|         }
 | |
|         for (@tables_to_del)
 | |
|         {
 | |
|             $tables{$_} = xCAT::Table->new($_, -create => 1, -autocommit => 0);
 | |
|             $tables{$_}->delEntries(\%keyhash);
 | |
|             $tables{$_}->commit;
 | |
|         }
 | |
|     }
 | |
|     else {
 | |
|         #update or create option
 | |
|         my %tableupdates;
 | |
|         for (@ARGV) {
 | |
|             my $temp;
 | |
|             my $table;
 | |
|             my $column;
 | |
|             my $value;
 | |
| 
 | |
|             ($table, $temp) = split('\.', $_, 2);
 | |
| 
 | |
|             #try to create the entry if it doesn't exist
 | |
|             unless ($tables{$table}) {
 | |
|                 my $tab = xCAT::Table->new($table, -create => 1, -autocommit => 0);
 | |
|                 if ($tab) {
 | |
|                     $tables{$table} = $tab;
 | |
|                 } else {
 | |
|                     my %rsp;
 | |
|                     $rsp{data}->[0] = "Table $table does not exist.\n";
 | |
|                     $callback->(\%rsp);
 | |
|                     return 1;
 | |
| 
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             #splice assignment
 | |
|             if (grep /\+=/, $temp) {
 | |
|                 ($column, $value) = split('\+=', $temp, 2);
 | |
| 
 | |
|                 #grab the current values to check against
 | |
|                 my ($attrHash) = $tables{$table}->getAttribs(\%keyhash, $column);
 | |
|                 my @existing = split(",", $attrHash->{$column});
 | |
| 
 | |
|                 #if it has values, merge the new and old ones together so no dupes
 | |
|                 if (@existing) {
 | |
|                     my @values = split(",", $value);
 | |
|                     my %seen   = ();
 | |
|                     my @uniq   = ();
 | |
|                     my $item;
 | |
| 
 | |
|                     foreach $item (@existing, @values) {
 | |
|                         unless ($seen{$item}) {
 | |
| 
 | |
|                             # if we get here, we have not seen it before
 | |
|                             $seen{$item} = 1;
 | |
|                             push(@uniq, $item);
 | |
|                         }
 | |
|                     }
 | |
|                     $value = join(",", @uniq);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             #normal non-splicing assignment
 | |
|             else {
 | |
|                 ($column, $value) = split("=", $temp, 2);
 | |
|             }
 | |
|             unless (grep /$column/, @{ $xCAT::Schema::tabspec{$table}->{cols} }) {
 | |
|                 $callback->({ error => "$table.$column not a valid table.column description", errorcode => [1] });
 | |
|                 return;
 | |
|             }
 | |
|             $tableupdates{$table}{$column} = $value;
 | |
|         }
 | |
| 
 | |
|         #commit all the changes
 | |
|         my $rollback;
 | |
|         foreach (keys %tables) {
 | |
|             if (exists($tableupdates{$_})) {
 | |
|                 my @rc = $tables{$_}->setAttribs(\%keyhash, \%{ $tableupdates{$_} });
 | |
|                 if (not defined($rc[0]))
 | |
|                 {
 | |
|                     $rollback = 1;
 | |
|                     $callback->({ error => "DB error " . $rc[1], errorcode => [4] });
 | |
|                 }
 | |
|             }
 | |
|             if ($rollback)
 | |
|             {
 | |
|                 $tables{$_}->rollback();
 | |
|                 $tables{$_}->close;
 | |
|                 undef $tables{$_};
 | |
|                 return;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 $tables{$_}->commit;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| #
 | |
| # getAllEntries
 | |
| #
 | |
| # Read all the rows from the input table name and returns the response, so
 | |
| # that the XML will look like this
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>getAllEntries</command>
 | |
| #<table>nodelist</table>
 | |
| #</xcatrequest>
 | |
| 
 | |
| 
 | |
| #<xcatresponse>
 | |
| #<row>
 | |
| #<attr1>value1</attr1>
 | |
| #.
 | |
| #.
 | |
| #.
 | |
| #<attrN>valueN</attrN>
 | |
| #</row>
 | |
| #.
 | |
| #.
 | |
| #.
 | |
| #</xcatresponse>
 | |
| #
 | |
| #
 | |
| sub getAllEntries
 | |
| {
 | |
|     my $request   = shift;
 | |
|     my $cb        = shift;
 | |
|     my $command   = $request->{command}->[0];
 | |
|     my $tablename = $request->{table}->[0];
 | |
|     my $tab       = xCAT::Table->new($tablename);
 | |
|     my %rsp;
 | |
|     my $recs = $tab->getAllEntries("all");
 | |
|     unless (@$recs)    # table exists, but is empty.  Show header.
 | |
|     {
 | |
| 
 | |
|         if (defined($xCAT::Schema::tabspec{$tablename}))
 | |
|         {
 | |
|             my $header = "#";
 | |
|             my @array  = @{ $xCAT::Schema::tabspec{$tablename}->{cols} };
 | |
|             foreach my $arow (@array) {
 | |
|                 $header .= $arow;
 | |
|                 $header .= ",";
 | |
|             }
 | |
|             chop $header;
 | |
|             push @{ $rsp{row} }, $header;
 | |
|             $cb->(\%rsp);
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     my %noderecs;
 | |
|     foreach my $rec (@$recs) {
 | |
|         my %datseg = ();
 | |
|         foreach my $key (keys %$rec) {
 | |
|             $datseg{$key} = $rec->{$key};
 | |
|         }
 | |
|         push @{ $noderecs{"row"} }, \%datseg;
 | |
|     }
 | |
|     push @{ $rsp{"row"} }, @{ $noderecs{"row"} };
 | |
| 
 | |
|     # for checkin XML created
 | |
|     #my  $xmlrec=XMLout(\%rsp,RootName=>'xcatresponse',NoAttr=>1,KeyAttr=>[]);
 | |
|     $cb->(\%rsp);
 | |
| 
 | |
|     return;
 | |
| }
 | |
| 
 | |
| # getNodesAttribs
 | |
| # Read the array of  attributes for the noderange  from the input table.
 | |
| # If the <attr>ALL</attr> is input then read all the attributes
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>getNodesAttribs</command>
 | |
| #<table>nodelist</table>
 | |
| #<noderange>blade01-blade02</noderange>
 | |
| #<attr>groups</attr>
 | |
| #<attr>status</attr>
 | |
| #</xcatrequest>
 | |
| #
 | |
| #<xcatresponse>
 | |
| #<node>
 | |
| #<name>nodename</name>
 | |
| #<attr1>value1</attr1>
 | |
| #.
 | |
| #.
 | |
| #.
 | |
| #<attrN>valueN</attrN>
 | |
| #</node>
 | |
| #.
 | |
| #.
 | |
| #.
 | |
| #</xcatresponse>
 | |
| #
 | |
| #
 | |
| sub getNodesAttribs
 | |
| {
 | |
|     my $request   = shift;
 | |
|     my $cb        = shift;
 | |
|     my $node      = $request->{node};
 | |
|     my $command   = $request->{command}->[0];
 | |
|     my $tablename = $request->{table}->[0];
 | |
|     my $attr      = $request->{attr};
 | |
|     my $tab       = xCAT::Table->new($tablename);
 | |
|     my @nodes     = @$node;
 | |
|     my @attrs     = @$attr;
 | |
|     my %rsp;
 | |
|     my %noderecs;
 | |
| 
 | |
|     if (grep (/ALL/, @attrs)) {  # read the  schema and build array of all attrs
 | |
|         @attrs = ();
 | |
|         my $schema = xCAT::Table->getTableSchema($tablename);
 | |
|         my $desc   = $schema->{descriptions};
 | |
|         foreach my $c (@{ $schema->{cols} }) {
 | |
| 
 | |
|             # my $space = (length($c)<7 ? "\t\t" : "\t");
 | |
|             push @attrs, $c;
 | |
|         }
 | |
|     }
 | |
|     my $rechash = $tab->getNodesAttribs(\@nodes, \@attrs);
 | |
|     foreach my $node (@nodes) {
 | |
|         my $recs = $rechash->{$node};
 | |
|         foreach my $rec (@$recs) {
 | |
|             my %datseg = ();
 | |
|             $datseg{name} = [$node];
 | |
|             foreach my $key (keys %$rec) {
 | |
|                 if ($key ne "node") {   # do not put in the added node attribute
 | |
|                     $datseg{$key} = [ $rec->{$key} ];
 | |
|                 }
 | |
|             }
 | |
|             push @{ $noderecs{$node} }, \%datseg;
 | |
|         }
 | |
|         push @{ $rsp{"node"} }, @{ $noderecs{$node} };
 | |
| 
 | |
|     }
 | |
| 
 | |
|     # for checkin XML created
 | |
|     #my  $xmlrec=XMLout(\%rsp,RootName=>'xcatresponse',NoAttr=>1,KeyAttr=>[]);
 | |
|     $cb->(\%rsp);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| # getTablesAllNodeAttribs
 | |
| # Read  all the nodes from the input tables and get the input attributes
 | |
| # or get ALL attributes, if the word ALL is used.
 | |
| # If the <attr>ALL</attr> is input then read all the attributes
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>getTablesAllNodeAttribs</command>
 | |
| #<table>
 | |
| #<tablename>nodelist</tablename>
 | |
| #<attr>groups</attr>
 | |
| #<attr>status</attr>
 | |
| #</table>
 | |
| #<table>
 | |
| #<tablename>nodetype</tablename>
 | |
| #<attr>ALL</attr>
 | |
| #</table>
 | |
| #   .
 | |
| #   .
 | |
| #   .
 | |
| #</xcatrequest>
 | |
| #
 | |
| #<xcatresponse>
 | |
| #<table>
 | |
| #<tablename>tablename1</tablename>
 | |
| #<node>
 | |
| #<name>n1</name>
 | |
| #<attr1>value1</attr1>
 | |
| #<attr2>value1</attr2>
 | |
| #.
 | |
| #<attrN>valueN</attrN>
 | |
| #</node>
 | |
| #</table>
 | |
| #   .
 | |
| #   .
 | |
| #   .
 | |
| #</xcatresponse>
 | |
| #
 | |
| sub getTablesAllNodeAttribs
 | |
| {
 | |
|     my $request = shift;
 | |
|     my $cb      = shift;
 | |
|     my $command = $request->{command}->[0];
 | |
|     my %rsp;
 | |
| 
 | |
|     # process each table in the request
 | |
|     my $tables = $request->{table};
 | |
|     foreach my $tabhash (@$tables) {
 | |
| 
 | |
|         my $tablename = $tabhash->{tablename}->[0];
 | |
|         my $attr      = $tabhash->{attr};
 | |
|         my @attrs     = @$attr;
 | |
|         my $tab       = xCAT::Table->new($tablename);
 | |
|         my %noderecs;
 | |
|         my $recs;
 | |
| 
 | |
|         # build the table name record
 | |
|         @{ $noderecs{table}->[0]->{tablename} } = $tablename;
 | |
| 
 | |
|         # if request for ALL attributes
 | |
|         if (grep (/ALL/, @attrs)) { # read the  schema and build array of all attrs
 | |
|             @attrs = ();
 | |
|             my $schema = xCAT::Table->getTableSchema($tablename);
 | |
|             my $desc   = $schema->{descriptions};
 | |
|             foreach my $c (@{ $schema->{cols} }) {
 | |
| 
 | |
|                 # my $space = (length($c)<7 ? "\t\t" : "\t");
 | |
|                 push @attrs, $c;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # read all the nodes and their attributes in this table
 | |
|         my @nodeentries = $tab->getAllNodeAttribs(\@attrs);
 | |
|         foreach my $node (@nodeentries) {
 | |
| 
 | |
|             # build the node entrys
 | |
|             my %datseg = ();
 | |
|             $datseg{name} = $node->{node};
 | |
|             foreach my $at (@attrs) {
 | |
| 
 | |
|                 # if the attribute has a value and is not the node attribute
 | |
|                 if (($node->{$at}) && ($at ne "node")) {
 | |
|                     $datseg{$at} = $node->{$at};
 | |
|                 }
 | |
|             }
 | |
|             push @{ $noderecs{table}->[0]->{node} }, \%datseg;
 | |
|         }
 | |
|         push @{ $rsp{"table"} }, @{ $noderecs{table} };
 | |
|     }    # end of all table processing
 | |
| 
 | |
|     # for checkin XML created
 | |
|     #my  $xmlrec=XMLout(\%rsp,RootName=>'xcatresponse',NoAttr=>1,KeyAttr=>[]);
 | |
|     $cb->(\%rsp);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| # getTablesNodesAttribs
 | |
| # Read the nodes in the noderange from the input tables
 | |
| # and get the input attributes
 | |
| # or get ALL attributes, if the word ALL is used.
 | |
| # If the <attr>ALL</attr> is input then read all the attributes
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>getTablesNodesAttribs</command>
 | |
| #<noderange>blade01-blade10</noderange>
 | |
| #<table>
 | |
| #<tablename>nodelist</tablename>
 | |
| #<attr>groups</attr>
 | |
| #<attr>status</attr>
 | |
| #</table>
 | |
| #<table>
 | |
| #<tablename>nodetype</tablename>
 | |
| #<attr>ALL</attr>
 | |
| #</table>
 | |
| #   .
 | |
| #   .
 | |
| #   .
 | |
| #</xcatrequest>
 | |
| #
 | |
| #<xcatresponse>
 | |
| #<table>
 | |
| #<tablename>tablename1</tablename>
 | |
| #<node>
 | |
| #<name>n1</name>
 | |
| #<attr1>value1</attr1>
 | |
| #<attr2>value1</attr2>
 | |
| #.
 | |
| #<attrN>valueN</attrN>
 | |
| #</node>
 | |
| #</table>
 | |
| #   .
 | |
| #   .
 | |
| #   .
 | |
| #</xcatresponse>
 | |
| #
 | |
| sub getTablesNodesAttribs
 | |
| {
 | |
|     my $request = shift;
 | |
|     my $cb      = shift;
 | |
|     my $command = $request->{command}->[0];
 | |
|     my %rsp;
 | |
| 
 | |
|     # process each table in the request
 | |
|     my $tables = $request->{table};
 | |
|     my $node   = $request->{node};
 | |
|     my @nodes  = @$node;
 | |
|     foreach my $tabhash (@$tables) {
 | |
| 
 | |
|         my $tablename = $tabhash->{tablename}->[0];
 | |
|         my $attr      = $tabhash->{attr};
 | |
|         my @attrs     = @$attr;
 | |
|         my $tab       = xCAT::Table->new($tablename);
 | |
|         my %noderecs;
 | |
|         my $recs;
 | |
| 
 | |
|         # build the table name record
 | |
|         #@{$noderecs{table}->[0]->{tablename}} = $tablename;
 | |
|         # if request for ALL attributes
 | |
|         if (grep (/ALL/, @attrs)) { # read the  schema and build array of all attrs
 | |
|             @attrs = ();
 | |
|             my $schema = xCAT::Table->getTableSchema($tablename);
 | |
|             my $desc   = $schema->{descriptions};
 | |
|             foreach my $c (@{ $schema->{cols} }) {
 | |
| 
 | |
|                 # my $space = (length($c)<7 ? "\t\t" : "\t");
 | |
|                 push @attrs, $c;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # read the nodes and their attributes in this table
 | |
|         my $rechash = $tab->getNodesAttribs(\@nodes, \@attrs);
 | |
|         foreach my $node (@nodes) {
 | |
|             my $recs = $rechash->{$node};
 | |
|             foreach my $rec (@$recs) {
 | |
|                 my %datseg = ();
 | |
|                 $datseg{name} = [$node];
 | |
|                 foreach my $key (keys %$rec) {
 | |
|                     if ($key ne "node") { # do not put in the added node attribute
 | |
|                         $datseg{$key} = [ $rec->{$key} ];
 | |
|                     }
 | |
|                 }
 | |
|                 push @{ $noderecs{table}->[0]->{node} }, \%datseg;
 | |
|             }
 | |
| 
 | |
|         }
 | |
|         @{ $noderecs{table}->[0]->{tablename} } = $tablename;
 | |
|         push @{ $rsp{"table"} }, @{ $noderecs{table} };
 | |
|     }    # end of all table processing
 | |
| 
 | |
|     # for checkin XML created
 | |
|     #my  $xmlrec=XMLout(\%rsp,RootName=>'xcatresponse',NoAttr=>1,KeyAttr=>[]);
 | |
|     $cb->(\%rsp);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| # getTablesALLRowAttribs
 | |
| # Read  all the rows from the input non-Node key'd
 | |
| # tables and get the input attributes
 | |
| # or get ALL attributes, if the word ALL is used.
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>getTablesALLRowAttribs</command>
 | |
| #<table>
 | |
| #<tablename>osimage</tablename>
 | |
| #<attr>imagename</attr>
 | |
| #<attr>synclists</attr>
 | |
| #</table>
 | |
| #<table>
 | |
| #<tablename>linuximage</tablename>
 | |
| #<attr>ALL</attr>
 | |
| #</table>
 | |
| #   .
 | |
| #   .
 | |
| #   .
 | |
| #</xcatrequest>
 | |
| #
 | |
| #<xcatresponse>
 | |
| #<table>
 | |
| #<tablename>osimage</tablename>
 | |
| #<row>
 | |
| #<synclists>value1</synclists>
 | |
| #</row>
 | |
| #<row>
 | |
| #.
 | |
| #.
 | |
| #</row>
 | |
| #</table>
 | |
| #<table>
 | |
| #<tablename>linuximage</tablename>
 | |
| #<row>
 | |
| #<imagename>value</imagename>
 | |
| #<template>value</template>
 | |
| #.
 | |
| #.
 | |
| #</row>
 | |
| #<row>
 | |
| #.
 | |
| #.
 | |
| #</row>
 | |
| #</table>
 | |
| #</xcatresponse>
 | |
| #.
 | |
| #.
 | |
| #
 | |
| sub getTablesAllRowAttribs
 | |
| {
 | |
|     my $request = shift;
 | |
|     my $cb      = shift;
 | |
|     my $command = $request->{command}->[0];
 | |
|     my %rsp;
 | |
| 
 | |
|     # process each table in the request
 | |
|     my $tables = $request->{table};
 | |
|     foreach my $tabhash (@$tables) {
 | |
| 
 | |
|         my $tablename = $tabhash->{tablename}->[0];
 | |
|         my $attr      = $tabhash->{attr};
 | |
|         my @attrs     = @$attr;
 | |
|         my $tab       = xCAT::Table->new($tablename);
 | |
|         my %tblrecs;
 | |
| 
 | |
|         # build the table name record
 | |
|         @{ $tblrecs{table}->[0]->{tablename} } = $tablename;
 | |
| 
 | |
|         # if request for ALL attributes
 | |
|         if (grep (/ALL/, @attrs)) { # read the  schema and build array of all attrs
 | |
|             @attrs = ();
 | |
|             my $schema = xCAT::Table->getTableSchema($tablename);
 | |
|             my $desc   = $schema->{descriptions};
 | |
|             foreach my $c (@{ $schema->{cols} }) {
 | |
| 
 | |
|                 # my $space = (length($c)<7 ? "\t\t" : "\t");
 | |
|                 push @attrs, $c;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # read all the attributes in this table
 | |
|         my @recs = $tab->getAllAttribs(@attrs);
 | |
|         foreach my $rec (@recs) {
 | |
|             my %datseg = ();
 | |
|             foreach my $key (keys %$rec) {
 | |
|                 $datseg{$key} = $rec->{$key};
 | |
|             }
 | |
|             push @{ $tblrecs{table}->[0]->{row} }, \%datseg;
 | |
|         }
 | |
|         push @{ $rsp{"table"} }, @{ $tblrecs{table} };
 | |
|     }    # end of all table processing
 | |
|          # for checkin XML created
 | |
|      # my  $xmlrec=XMLout(\%rsp,RootName=>'xcatresponse',NoAttr=>1,KeyAttr=>[]);
 | |
|     $cb->(\%rsp);
 | |
|     return;
 | |
| }
 | |
| #
 | |
| # setNodesAttribs - setNodesAttribs
 | |
| # Sets Nodes attributes for noderange for each of the tables supplied
 | |
| # Example of XML in for this routine
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>setNodesAttribs</command>
 | |
| #<noderange>blade01-blade02</noderange>
 | |
| #<arg>
 | |
| #   <table>
 | |
| #      <name>nodelist</name>
 | |
| #      <attr>
 | |
| #         <groups>test</groups>
 | |
| #         <comments> This is a another testx</comments>
 | |
| #      </attr>
 | |
| #   </table>
 | |
| #   <table>
 | |
| #      <name>nodetype</name>
 | |
| #      <attr>
 | |
| #         <os>Redhat2</os>
 | |
| #         <comments> This is a another testy</comments>
 | |
| #      </attr>
 | |
| #   </table>
 | |
| #</arg>
 | |
| #</xcatrequest>
 | |
| #
 | |
| sub setNodesAttribs
 | |
| {
 | |
|     my $request   = shift;
 | |
|     my $cb        = shift;
 | |
|     my $node      = $request->{node};           # added by Client.pm
 | |
|     my $noderange = $request->{noderange};
 | |
|     my $command   = $request->{command}->[0];
 | |
|     my %rsp;
 | |
|     my $args   = $request->{arg};
 | |
|     my $tables = $args->[0]->{table};
 | |
| 
 | |
|     # take input an build a request for the nodech function
 | |
|     my $newrequest;
 | |
|     $newrequest->{noderange} = $request->{noderange};
 | |
|     $newrequest->{command}->[0] = "nodech";
 | |
|     foreach my $table (@$tables) {
 | |
|         my $tablename = $table->{name}->[0];
 | |
|         my %keyhash;
 | |
|         my $attrs = $table->{attr};
 | |
|         foreach my $attrhash (@$attrs) {
 | |
|             foreach my $key (keys %$attrhash) {
 | |
|                 my $tblattr = $tablename;
 | |
|                 $tblattr .= ".$key=";
 | |
|                 $tblattr .= $table->{attr}->[0]->{$key}->[0];
 | |
|                 push(@{ $newrequest->{arg} }, $tblattr);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # nodech will open the table and do all the work
 | |
|     if (@$node) {
 | |
|         &nodech(\@$node, $newrequest->{arg}, $cb, 0);
 | |
|     } else {
 | |
|         my $rsp = { errorcode => 1, error => "No nodes in noderange" };
 | |
|         $cb->(\%rsp);
 | |
|     }
 | |
|     return;
 | |
| }
 | |
| #
 | |
| # delEntries
 | |
| # Deletes the table entry based on the input attributes
 | |
| # The attributes and AND'd to together to form the delete request
 | |
| # DELETE FROM nodelist WHERE ("groups" = "compute1,test" AND "status" = "down")
 | |
| # Example of XML in for this routine
 | |
| #
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>delEntries</command>
 | |
| #<table>
 | |
| #      <name>nodelist</name>
 | |
| #      <attr>
 | |
| #         <groups>compute1,test</groups>
 | |
| #         <status>down</status>
 | |
| #      </attr>
 | |
| #</table>
 | |
| #  .
 | |
| #  .
 | |
| #<table>
 | |
| #  .
 | |
| #  .
 | |
| #  .
 | |
| #</table>
 | |
| #</xcatrequest>
 | |
| #
 | |
| # To delete all entries in a table, you input no attributes
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>delEntries</command>
 | |
| #<table>
 | |
| #      <name>nodelist</name>
 | |
| #</table>
 | |
| #</xcatrequest>
 | |
| 
 | |
| 
 | |
| sub delEntries
 | |
| {
 | |
|     my $request = shift;
 | |
|     my $cb      = shift;
 | |
|     my $command = $request->{command}->[0];
 | |
|     my %rsp;
 | |
|     my $tables = $request->{table};
 | |
|     foreach my $table (@$tables) {
 | |
|         my $tablename = $table->{name}->[0];
 | |
|         my $tab       = xCAT::Table->new($tablename);
 | |
|         my %keyhash;
 | |
|         my $attrs = $table->{attr};
 | |
|         foreach my $attrhash (@$attrs) {
 | |
|             foreach my $key (keys %$attrhash) {
 | |
|                 $keyhash{$key} = $attrhash->{$key}->[0];
 | |
|             }
 | |
|         }
 | |
|         if (%keyhash) {    # delete based on requested attributes
 | |
|             $tab->delEntries(\%keyhash);    #Yes, delete *all* entries
 | |
|         } else {                            # delete all entries
 | |
|             $tab->delEntries();             #delete *all* entries
 | |
|         }
 | |
|         $tab->commit;                       #  commit
 | |
|     }
 | |
|     return;
 | |
| }
 | |
| 
 | |
| # getAttribs
 | |
| # Read and returns array of  attributes for the key  from the input table.
 | |
| #  and attributes input.  Use "ALL" in the <attr>ALL</attr> for all attributes
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>getAttribs</command>
 | |
| #<table>site</table>
 | |
| #<keys>
 | |
| #  <key>domain</key>
 | |
| #</keys>
 | |
| #<attr>value</attr>
 | |
| #<attr>comments</attr>
 | |
| #</xcatrequest>
 | |
| #
 | |
| #
 | |
| #<xcatresponse>
 | |
| #<value>{domain value}</value>
 | |
| #<comments>This is a comment</comments>
 | |
| #</xcatresponse>
 | |
| sub getAttribs
 | |
| {
 | |
|     my $request   = shift;
 | |
|     my $cb        = shift;
 | |
|     my $command   = $request->{command}->[0];
 | |
|     my $tablename = $request->{table}->[0];
 | |
|     my $attr      = $request->{attr};
 | |
|     my @attrs     = @$attr;
 | |
|     my $tab       = xCAT::Table->new($tablename);
 | |
|     my %rsp;
 | |
|     my %keyhash;
 | |
| 
 | |
|     # if request for ALL attributes
 | |
|     if (grep (/ALL/, @attrs)) {  # read the  schema and build array of all attrs
 | |
|         @attrs = ();
 | |
|         my $schema = xCAT::Table->getTableSchema($tablename);
 | |
|         my $desc   = $schema->{descriptions};
 | |
|         foreach my $c (@{ $schema->{cols} }) {
 | |
| 
 | |
|             # my $space = (length($c)<7 ? "\t\t" : "\t");
 | |
|             push @attrs, $c;
 | |
|         }
 | |
|     }
 | |
|     foreach my $k (keys %{ $request->{keys}->[0] }) {
 | |
|         $keyhash{$k} = $request->{keys}->[0]->{$k}->[0];
 | |
|     }
 | |
|     my $recs = $tab->getAttribs(\%keyhash, \@attrs);
 | |
| 
 | |
|     if ($recs) {
 | |
|         my %attrhash = %$recs;
 | |
|         foreach my $k (keys %attrhash) {
 | |
| 
 | |
|             push @{ $rsp{$k} }, $recs->{$k};
 | |
|         }
 | |
|     }
 | |
|     $cb->(\%rsp);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| # setAttribs
 | |
| # Set the  attributes for the key(s) input in the table.
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>setAttribs</command>
 | |
| #<table>site</table>
 | |
| #<keys>
 | |
| #  <key>domain</key>
 | |
| #</keys>
 | |
| #<attr>
 | |
| #  <value>cluster.net</value>
 | |
| #  <comments>This is a comment</comments>
 | |
| #</xcatrequest>
 | |
| #
 | |
| #
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>setAttribs</command>
 | |
| #<table>networks</table>
 | |
| #<keys>
 | |
| #  <net>10.0.1.0</net>
 | |
| #  <mask>255.255.255.0</mask>
 | |
| #</keys>
 | |
| #<attr>
 | |
| #  <netname>mynet</netname>
 | |
| #  <gateway>10.0.1.254</gateway>
 | |
| #</attr>
 | |
| #</xcatrequest>
 | |
| 
 | |
| sub setAttribs
 | |
| {
 | |
|     my $request   = shift;
 | |
|     my $cb        = shift;
 | |
|     my $command   = $request->{command}->[0];
 | |
|     my $tablename = $request->{table}->[0];
 | |
|     my $tab       = xCAT::Table->new($tablename);
 | |
|     my %rsp;
 | |
|     my %keyhash;
 | |
|     my %attrhash;
 | |
| 
 | |
|     foreach my $k (keys %{ $request->{keys}->[0] }) {
 | |
|         $keyhash{$k} = $request->{keys}->[0]->{$k}->[0];
 | |
|     }
 | |
|     foreach my $a (keys %{ $request->{attr}->[0] }) {
 | |
|         $attrhash{$a} = $request->{attr}->[0]->{$a}->[0];
 | |
|     }
 | |
|     $tab->setAttribs(\%keyhash, \%attrhash);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| # noderange
 | |
| # Expands the input noderange into a list of nodes.
 | |
| #<xcatrequest>
 | |
| #<clienttype>PCM</clienttype>
 | |
| #<command>noderange</command>
 | |
| #<noderange>compute1-compute2</noderange>
 | |
| #</xcatrequest>
 | |
| #<xcatresponse>
 | |
| #<node>nodename1</node>
 | |
| # .
 | |
| # .
 | |
| #<node>nodenamern1</node>
 | |
| #</xcatresponse>
 | |
| sub NodeRange
 | |
| {
 | |
|     my $request = shift;
 | |
|     my $cb      = shift;
 | |
|     my $command = $request->{command}->[0];
 | |
|     my %rsp;
 | |
|     my $node  = $request->{node};
 | |
|     my @nodes = @$node;
 | |
|     foreach my $node (@nodes) {
 | |
|         push @{ $rsp{"node"} }, $node;
 | |
| 
 | |
|     }
 | |
|     $cb->(\%rsp);
 | |
|     return;
 | |
| }
 |