diff --git a/perl-xCAT-2.0/xCAT/Client.pm b/perl-xCAT-2.0/xCAT/Client.pm
index 10dcc6db2..d803027c8 100644
--- a/perl-xCAT-2.0/xCAT/Client.pm
+++ b/perl-xCAT-2.0/xCAT/Client.pm
@@ -27,19 +27,20 @@ use Storable qw(dclone);
my $xcathost='localhost:3001';
my $plugins_dir;
my %resps;
+my $EXITCODE; # save the bitmask of all exit codes returned by calls to handle_response()
1;
#################################
# submit_request will take an xCAT command and pass it to the xCAT
# server for execution.
-#
+#
# If the XCATBYPASS env var is set, the connection to the server/daemon
# will be bypassed and the plugin will be called directly. If it is
-# set to one or more directories (separated by ":"), all perl modules
-# in those directories will be loaded in as plugins (for duplicate
-# commands, last one in wins). If it is set to any other value
-# (e.g. "yes", "default", whatever string you want) the default plugin
+# set to one or more directories (separated by ":"), all perl modules
+# in those directories will be loaded in as plugins (for duplicate
+# commands, last one in wins). If it is set to any other value
+# (e.g. "yes", "default", whatever string you want) the default plugin
# directory /opt/xcat/lib/perl/xCAT_plugin will be used.
#
# Input:
@@ -54,12 +55,12 @@ my %resps;
# }
# Callback - A subroutine ref that will be called to process the output
# from the plugin.
-#
+#
# NOTE: The request hash will get converted to XML when passed to the
# xcatd daemon, and will get converted back to a hash before being
# passed to the plugin. The XMLin ForceArray option is used to
# force all XML constructs to be arrays so that the plugin code
-# and callback routines can access the data consistently.
+# and callback routines can access the data consistently.
# The input request and the response hash created by the plugin should
# always create hashes with array values.
#################################
@@ -72,6 +73,7 @@ sub submit_request {
unless ($keyfile) { $keyfile = $ENV{HOME}."/.xcat/client-key.pem"; }
unless ($certfile) { $certfile = $ENV{HOME}."/.xcat/client-cert.pem"; }
unless ($cafile) { $cafile = $ENV{HOME}."/.xcat/ca.pem"; }
+ $xCAT::Client::EXITCODE = 0; # clear out exit code before invoking the plugin
# If XCATBYPASS is set, invoke the plugin process_request method directly
@@ -128,12 +130,6 @@ sub submit_request {
if ($response =~ m/<\/xcatresponse>/) {
$rsp = XMLin($response,SuppressEmpty=>undef,ForceArray=>1);
$response='';
- if ($rsp->{warning}) {
- printf ("Warning: ".$rsp->{warning}->[0]."\n");
- }
- if ($rsp->{error}) {
- printf "Error: ". $rsp->{error}->[0]."\n";
- }
$callback->($rsp);
if ($rsp->{serverdone}) {
last;
@@ -143,10 +139,10 @@ sub submit_request {
###################################
# scan_plugins
-# will load all plugin perl modules and build a list of supported
+# will load all plugin perl modules and build a list of supported
# commands
#
-# NOTE: This is copied from xcatd (last merge 10/3/07).
+# NOTE: This is copied from xcatd (last merge 10/3/07).
# Will eventually move to using common source....
###################################
sub scan_plugins {
@@ -184,9 +180,9 @@ sub scan_plugins {
###################################
# plugin_command
-# will invoke the correct plugin
+# will invoke the correct plugin
#
-# NOTE: This is copied from xcatd (last merge 10/3/07).
+# NOTE: This is copied from xcatd (last merge 10/3/07).
# Will eventually move to using common source....
###################################
sub plugin_command {
@@ -333,7 +329,7 @@ sub plugin_command {
# do_request
# called from a plugin to execute another xCAT plugin command internally
#
-# NOTE: This is copied from xcatd (last merge 10/3/07).
+# NOTE: This is copied from xcatd (last merge 10/3/07).
# Will eventually move to using common source....
###################################
sub do_request {
@@ -402,7 +398,160 @@ sub build_response {
-}
+} # end of submit_request()
+
+
+
+##########################################
+# handle_response is a default callback that can be passed into submit_response()
+# It is invoked repeatedly by submit_response() to print out the data returned by
+# the plugin.
+#
+# The normal flow is:
+# -> client cmd (e.g. nodels, which is just a link to xcatclient)
+# -> xcatclient
+# -> submit_request()
+# -> send xml request to xcatd
+# -> xcatd
+# -> process_request() of the plugin
+# <- plugin callback
+# <- xcatd
+# <- xcatd sends xml response to client
+# <- submit_request() read response
+# <- handle_response() prints responses and saves exit codes
+# <- xcatclient gets exit code and exits
+#
+# But in XCATBYPASS mode, the flow is:
+# -> client cmd (e.g. nodels, which is just a link to xcatclient)
+# -> xcatclient
+# -> submit_request()
+# -> process_request() of the plugin
+# <- handle_response() prints responses and saves exit codes
+# <- xcatclient gets exit code and exits
+#
+# Format of the response hash:
+# {data => [ 'data str1', 'data str2', '...' ] }
+#
+# Results are printed as:
+# data str1
+# data str2
+#
+# or:
+# {data => [ {desc => [ 'desc1' ],
+# contents => [ 'contents1' ] },
+# {desc => [ 'desc2 ],
+# contents => [ 'contents2' ] }
+# :
+# ] }
+# NOTE: In this format, only the data array can have more than one
+# element. All other arrays are assumed to be a single element.
+# Results are printed as:
+# desc1: contents1
+# desc2: contents2
+#
+# or:
+# {node => [ {name => ['node1'],
+# data => [ {desc => [ 'node1 desc' ],
+# contents => [ 'node1 contents' ] } ] },
+# {name => ['node2'],
+# data => [ {desc => [ 'node2 desc' ],
+# contents => [ 'node2 contents' ] } ] },
+# :
+# ] }
+# NOTE: Only the node array can have more than one element.
+# All other arrays are assumed to be a single element.
+#
+# This was generated from the corresponding XML:
+#
+#
+# node1
+#
+# node1 desc
+# node1 contents
+#
+#
+#
+# node2
+#
+# node2 desc
+# node2 contents
+#
+#
+#
+#
+# Results are printed as:
+# node_name: desc: contents
+##########################################
+sub handle_response {
+ my $rsp = shift;
+ # Handle errors
+ if ($rsp->{errorcode}) {
+ if (ref($rsp->{errorcode}) eq 'ARRAY') { foreach my $ecode (@{$rsp->{errorcode}}) { $xCAT::Client::EXITCODE |= $ecode; } }
+ else { $xCAT::Client::EXITCODE |= $rsp->{errorcode}; } # assume it is a non-reference scalar
+ }
+ if ($rsp->{warning}) {
+ if (ref($rsp->{warning}) eq 'ARRAY') { print ("Warning: " . $rsp->{warning}->[0] . "\n"); }
+ else { print ("Warning: ".$rsp->{warning}."\n"); }
+ }
+ if ($rsp->{error}) {
+ if (ref($rsp->{error}) eq 'ARRAY') { print ("Error: " . $rsp->{error}->[0] . "\n"); }
+ else { print ("Error: ".$rsp->{error}."\n"); }
+ }
+
+ # Handle {node} structure
+ if ($rsp->{node}) {
+ my $nodes=($rsp->{node});
+ my $node;
+ foreach $node (@$nodes) {
+ my $desc=$node->{name}->[0];
+ if ($node->{errorcode}) {
+ if (ref($node->{errorcode}) eq 'ARRAY') { foreach my $ecode (@{$node->{errorcode}}) { $xCAT::Client::EXITCODE |= $ecode; } }
+ else { $xCAT::Client::EXITCODE |= $node->{errorcode}; } # assume it is a non-reference scalar
+ }
+ if ($node->{data}) {
+ if (ref(\($node->{data}->[0])) eq 'SCALAR') {
+ $desc=$desc.": ".$node->{data}->[0];
+ } else {
+ if ($node->{data}->[0]->{desc}) {
+ $desc=$desc.": ".$node->{data}->[0]->{desc}->[0];
+ }
+ if ($node->{data}->[0]->{contents}) {
+ $desc="$desc: ".$node->{data}->[0]->{contents}->[0];
+ }
+ }
+ }
+ if ($desc) {
+ print "$desc\n";
+ }
+ }
+ }
+
+ # Handle {data} structure with no nodes
+ if ($rsp->{data}) {
+ my $data=($rsp->{data});
+ my $data_entry;
+ foreach $data_entry (@$data) {
+ my $desc;
+ if (ref(\($data_entry)) eq 'SCALAR') {
+ $desc=$data_entry;
+ } else {
+ if ($data_entry->{desc}) {
+ $desc=$data_entry->{desc}->[0];
+ }
+ if ($data_entry->{contents}) {
+ if ($desc) {
+ $desc="$desc: ".$data_entry->{contents}->[0];
+ } else {
+ $desc=$data_entry->{contents}->[0];
+ }
+ }
+ }
+ if ($desc) {
+ print "$desc\n";
+ }
+ }
+ }
+} # end of handle_response
diff --git a/xCAT-client-2.0/bin/xcatclient b/xCAT-client-2.0/bin/xcatclient
index e7867dc54..932ebad63 100755
--- a/xCAT-client-2.0/bin/xcatclient
+++ b/xCAT-client-2.0/bin/xcatclient
@@ -1,175 +1,44 @@
#!/usr/bin/env perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
-BEGIN
-{
- $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr';
-}
+
+# Used as a standard client cmd that can be used for many of the xcat cmds.
+# It grabs the arguments, noderange, and stdin and then submits the request to
+# xcatd and waits for responses. Most of the client/server communication is
+# contained in Client.pm.
+
+# To use this, sym link your cmd name to this script.
+
+BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr'; }
use lib "$::XCATROOT/lib/perl";
use Cwd;
-use IO::Socket::SSL;
-use IO::Socket::INET;
+#use IO::Socket::SSL;
+#use IO::Socket::INET;
use File::Basename;
-use Data::Dumper;
-use xCAT::Client submit_request;
+#use Data::Dumper;
+use xCAT::Client;
+
my $bname = basename($0);
-
-#########################################
-# Main
-# Build hash and submit request
-#########################################
-
-my $exitcode = 0; #var to store exit code that results from responses
my $cmdref;
-if($bname =~ /xcatclient/) {
- $cmdref->{command}->[0]=shift @ARGV;
-} elsif ($bname =~/^(.*$)/) {
- $cmdref->{command}->[0] = $1;
-} else {
- printf("Bad usage\n");
- exit(1);
-}
+if ($bname =~ /xcatclient/) { $cmdref->{command}->[0]=shift @ARGV; } # xcatclient was invoked directly and the 1st arg is cmd name that is used to locate the plugin
+else { $cmdref->{command}->[0] = $bname; } # the cmd was sym linked to xcatclient
$cmdref->{cwd}->[0] = cwd();
if (-p STDIN) {
my $data;
- while ( ) {
- $data.=$_;
- }
+ while ( ) { $data.=$_; }
$cmdref->{stdin}->[0]=$data;
}
+# Consider the 1st non-hyphen arg to be the noderange. All others (before and after) go on the arg list.
my $arg=shift(@ARGV);
while ($arg =~ /^-/) {
push (@{$cmdref->{arg}}, $arg);
$arg=shift(@ARGV);
}
-if ($arg ne "NO_NODE_RANGE") {
+if ($arg ne "NO_NODE_RANGE") {
$cmdref->{noderange}->[0]=$arg;
}
-foreach (@ARGV) { push (@{$cmdref->{arg}}, $_ ); }
-#$cmdref->{arg}=\@ARGV;
-xCAT::Client::submit_request($cmdref,\&handle_response);
-exit $exitcode;
-
-
-##########################################
-# handle_response is the callback that is
-# invoked to print out the data returned by
-# the plugin.
-#
-# Format of the response hash:
-# {data => [ 'data str1', 'data str2', '...' ] }
-#
-# Results are printed as:
-# data str1
-# data str2
-#
-# or:
-# {data => [ {desc => [ 'desc1' ],
-# contents => [ 'contents1' ] },
-# {desc => [ 'desc2 ],
-# contents => [ 'contents2' ] }
-# :
-# ] }
-# NOTE: In this format, only the data array can have more than one
-# element. All other arrays are assumed to be a single element.
-# Results are printed as:
-# desc1: contents1
-# desc2: contents2
-#
-# or:
-# {node => [ {name => ['node1'],
-# data => [ {desc => [ 'node1 desc' ],
-# contents => [ 'node1 contents' ] } ] },
-# {name => ['node2'],
-# data => [ {desc => [ 'node2 desc' ],
-# contents => [ 'node2 contents' ] } ] },
-# :
-# ] }
-# NOTE: Only the node array can have more than one element.
-# All other arrays are assumed to be a single element.
-#
-# This was generated from the corresponding HTML:
-#
-#
-# node1
-#
-# node1 desc
-# node1 contents
-#
-#
-#
-# node2
-#
-# node2 desc
-# node2 contents
-#
-#
-#
-#
-# Results are printed as:
-# node_name: desc: contents
-##########################################
-sub handle_response {
- my $rsp = shift;
- # Handle {node} structure
- if ($rsp->{errorcode}) {
- foreach my $ecode (@{$rsp->{errorcode}}) {
- $exitcode |= $ecode;
- }
- }
- if ($rsp->{node}) {
- my $nodes=($rsp->{node});
- my $node;
- foreach $node (@$nodes) {
- my $desc=$node->{name}->[0];
- if ($node->{errorcode}) {
- foreach my $ecode (@{$node->{errorcode}}) {
- $exitcode |= $ecode;
- }
- }
- if ($node->{data}) {
- if (ref(\($node->{data}->[0])) eq 'SCALAR') {
- $desc=$desc.": ".$node->{data}->[0];
- } else {
- if ($node->{data}->[0]->{desc}) {
- $desc=$desc.": ".$node->{data}->[0]->{desc}->[0];
- }
- if ($node->{data}->[0]->{contents}) {
- $desc="$desc: ".$node->{data}->[0]->{contents}->[0];
- }
- }
- }
- if ($desc) {
- print "$desc\n";
- }
- }
- }
-
- # Handle {data} structure with no nodes
- if ($rsp->{data}) {
- my $data=($rsp->{data});
- my $data_entry;
- foreach $data_entry (@$data) {
- my $desc;
- if (ref(\($data_entry)) eq 'SCALAR') {
- $desc=$data_entry;
- } else {
- if ($data_entry->{desc}) {
- $desc=$data_entry->{desc}->[0];
- }
- if ($data_entry->{contents}) {
- if ($desc) {
- $desc="$desc: ".$data_entry->{contents}->[0];
- } else {
- $desc=$data_entry->{contents}->[0];
- }
- }
- }
- if ($desc) {
- print "$desc\n";
- }
- }
- }
-}
+push (@{$cmdref->{arg}}, @ARGV);
+xCAT::Client::submit_request($cmdref,\&xCAT::Client::handle_response);
+exit $xCAT::Client::EXITCODE;
diff --git a/xCAT-client-2.0/bin/xcatclientnnr b/xCAT-client-2.0/bin/xcatclientnnr
index 3b1f60448..3a41c0763 100755
--- a/xCAT-client-2.0/bin/xcatclientnnr
+++ b/xCAT-client-2.0/bin/xcatclientnnr
@@ -1,25 +1,32 @@
#!/usr/bin/env perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
+
+# Used as a standard client cmd that can be used for xcat cmds that do not have
+# noderange as an argument. See xcatclient for additional documentation.
+
+# To use this, sym link your cmd name to this script.
+
+BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr'; }
+use lib "$::XCATROOT/lib/perl";
+use Cwd;
use File::Basename;
+use xCAT::Client;
-#######################################
-# It handles the commands without noderanges.
-#######################################
my $bname = basename($0);
-my $cmd;
-if($bname =~ /xcatclientnnr/) {
- $cmd = shift @ARGV;
-} elsif ($bname =~/^(.*$)/) {
- $cmd = $1;
-} else {
- printf("Bad usage\n");
- exit(1);
+my $cmdref;
+if ($bname =~ /xcatclientnnr/) { $cmdref->{command}->[0]=shift @ARGV; } # xcatclientnnr was invoked directly and the 1st arg is cmd name that is used to locate the plugin
+else { $cmdref->{command}->[0] = $bname; } # the cmd was sym linked to xcatclientnnr
+$cmdref->{cwd}->[0] = cwd();
+
+if (-p STDIN) {
+ my $data;
+ while ( ) { $data.=$_; }
+ $cmdref->{stdin}->[0]=$data;
}
-exec("xcatclient $cmd NO_NODE_RANGE @ARGV 2>&1");
-#if (system("/usr/bin/xcatclient $cmd NO_NODE_RANGE @ARGV 2>&1") != 0) {
-# print "$cmd failed: $?\n";
-#}
-#exit;
+push (@{$cmdref->{arg}}, @ARGV);
+
+xCAT::Client::submit_request($cmdref,\&xCAT::Client::handle_response);
+exit $xCAT::Client::EXITCODE;
diff --git a/xCAT-client-2.0/pods/man1/tabdump.1.pod b/xCAT-client-2.0/pods/man1/tabdump.1.pod
new file mode 100644
index 000000000..85e32e602
--- /dev/null
+++ b/xCAT-client-2.0/pods/man1/tabdump.1.pod
@@ -0,0 +1,45 @@
+=head1 NAME
+
+B - display a database table in csv format.
+
+=head1 SYNOPSIS
+
+I
+
+I
+
+=head1 DESCRIPTION
+
+The tabdump command displays the header and all the rows of the specified table in csv format.
+Only one table can be specified. If no table is specified, the list of existing
+tables will be displayed.
+
+=head1 OPTIONS
+
+B<-?|-h|--help> Display usage message.
+
+=head1 RETURN VALUE
+
+0 The command completed successfully.
+
+1 An error has occurred.
+
+=head1 EXAMPLES
+
+1. To display the contents of the site table:
+
+I
+
+2. To see what tables exist in the xCAT database:
+
+I
+
+=head1 FILES
+
+/opt/xcat/sbin/tabdump
+
+=head1 NOTES
+
+This command is part of the xCAT software product.
+
+
diff --git a/xCAT-client-2.0/pods/man1/tabrestore.1.pod b/xCAT-client-2.0/pods/man1/tabrestore.1.pod
new file mode 100644
index 000000000..d729207bb
--- /dev/null
+++ b/xCAT-client-2.0/pods/man1/tabrestore.1.pod
@@ -0,0 +1,50 @@
+=head1 NAME
+
+B - replaces the contents of an xCAT database table with the contents in a csv file.
+
+=head1 SYNOPSIS
+
+I
+
+I
+
+=head1 DESCRIPTION
+
+The tabrestore command reads the contents of the specified file and puts its data
+in the corresponding table in the xCAT database. Any existing rows in that table
+are replaced. The file must be in csv format. It could be created by tabdump.
+Only one table can be specified.
+
+This command can be used to copy the example table entries in /opt/xcat/share/xcat/templates/e1350
+into the xCAT database.
+
+=head1 OPTIONS
+
+B<-?|-h|--help> Display usage message.
+
+=head1 RETURN VALUE
+
+0 The command completed successfully.
+
+1 An error has occurred.
+
+=head1 EXAMPLES
+
+1. To put rows into the mp table:
+
+I
+
+The file mp.csv could contain something like:
+
+#node,mpa,id,comments,disable
+"blade","|\D+(\d+)|amm(($1-1)/14+1)|","|\D+(\d+)|(($1-1)%14+1)|",,
+
+=head1 FILES
+
+/opt/xcat/sbin/tabrestore
+
+=head1 NOTES
+
+This command is part of the xCAT software product.
+
+
diff --git a/xCAT-client-2.0/sbin/tabrestore b/xCAT-client-2.0/sbin/tabrestore
index 44d08eeff..1c1ff1f2b 100755
--- a/xCAT-client-2.0/sbin/tabrestore
+++ b/xCAT-client-2.0/sbin/tabrestore
@@ -1,166 +1,46 @@
#!/usr/bin/env perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
-#Just like xcatclient, but needs to read a file in and pass it as $request->data
-BEGIN
-{
- $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr';
-}
+
+# Just like xcatclient, but needs to read a file in and pass it as $request->data
+
+BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr'; }
use lib "$::XCATROOT/lib/perl";
-use IO::Socket::SSL;
-use IO::Socket::INET;
use File::Basename;
-use Data::Dumper;
-use xCAT::Client submit_request;
-my $bname = basename($0);
+use xCAT::Client;
+use Getopt::Long;
-#########################################
-# Main
-# Build hash and submit request
-#########################################
+sub usage {
+ print "Usage: tabrestore .csv\n";
+ print " tabrestore [-?|-h|--help]\n";
+ exit $_[0];
+}
+#my $bname = basename($0);
my $cmdref;
-my $exitcode=0;
$cmdref->{command}->[0] = "tabrestore";
+# Get the options
+my $HELP;
+if (!GetOptions('h|?|help' => \$HELP)) { usage(1); }
my $arg=shift(@ARGV);
while ($arg =~ /^-/) {
push (@{$cmdref->{arg}}, $arg);
$arg=shift(@ARGV);
}
-unless ($arg) {
- printf("Usage: tabrestore [tablename].csv\n");
- exit(1);
-}
+unless ($arg) { usage(2); } # no filename specified
+
+# Open the specified table file and put its contents in the data key
my $filename = $arg;
-unless (-r $filename) {
- printf("Unable to open $arg for reading.\n");
- exit(1);
-}
my $tabname = basename($filename);
$tabname =~ s/\..*//;
$cmdref->{table}->[0] = $tabname;
my $fh;
-open($fh,$filename);
+unless (open($fh,$filename)) { print "Error: Unable to open $arg for reading.\n"; exit 3; }
while (<$fh>) {
push @{$cmdref->{data}},$_;
}
-foreach (@ARGV) { push (@{$cmdref->{arg}}, $_ ); }
-#$cmdref->{arg}=\@ARGV;
-xCAT::Client::submit_request($cmdref,\&handle_response);
-exit $exitcode;
+push (@{$cmdref->{arg}}, @ARGV); # get the rest of the arguments
-
-##########################################
-# handle_response is the callback that is
-# invoked to print out the data returned by
-# the plugin.
-#
-# Format of the response hash:
-# {data => [ 'data str1', 'data str2', '...' ] }
-#
-# Results are printed as:
-# data str1
-# data str2
-#
-# or:
-# {data => [ {desc => [ 'desc1' ],
-# contents => [ 'contents1' ] },
-# {desc => [ 'desc2 ],
-# contents => [ 'contents2' ] }
-# :
-# ] }
-# NOTE: In this format, only the data array can have more than one
-# element. All other arrays are assumed to be a single element.
-# Results are printed as:
-# desc1: contents1
-# desc2: contents2
-#
-# or:
-# {node => [ {name => ['node1'],
-# data => [ {desc => [ 'node1 desc' ],
-# contents => [ 'node1 contents' ] } ] },
-# {name => ['node2'],
-# data => [ {desc => [ 'node2 desc' ],
-# contents => [ 'node2 contents' ] } ] },
-# :
-# ] }
-# NOTE: Only the node array can have more than one element.
-# All other arrays are assumed to be a single element.
-#
-# This was generated from the corresponding HTML:
-#
-#
-# node1
-#
-# node1 desc
-# node1 contents
-#
-#
-#
-# node2
-#
-# node2 desc
-# node2 contents
-#
-#
-#
-#
-# Results are printed as:
-# node_name: desc: contents
-##########################################
-sub handle_response {
- my $rsp = shift;
- # Handle {node} structure
- if ($rsp->{node}) {
- my $nodes=($rsp->{node});
- my $node;
- foreach $node (@$nodes) {
- my $desc=$node->{name}->[0];
- if ($node->{data}) {
- if (ref(\($node->{data}->[0])) eq 'SCALAR') {
- $desc=$desc.": ".$node->{data}->[0];
- } else {
- if ($node->{data}->[0]->{desc}) {
- $desc=$desc.": ".$node->{data}->[0]->{desc}->[0];
- }
- if ($node->{data}->[0]->{contents}) {
- $desc="$desc: ".$node->{data}->[0]->{contents}->[0];
- }
- }
- }
- if ($desc) {
- print "$desc\n";
- }
- }
- }
-
- # Handle {data} structure with no nodes
- if ($rsp->{error}) {
- $exitcode=1;
- }
- if ($rsp->{data}) {
- my $data=($rsp->{data});
- my $data_entry;
- foreach $data_entry (@$data) {
- my $desc;
- if (ref(\($data_entry)) eq 'SCALAR') {
- $desc=$data_entry;
- } else {
- if ($data_entry->{desc}) {
- $desc=$data_entry->{desc}->[0];
- }
- if ($data_entry->{contents}) {
- if ($desc) {
- $desc="$desc: ".$data_entry->{contents}->[0];
- } else {
- $desc=$data_entry->{contents}->[0];
- }
- }
- }
- if ($desc) {
- print "$desc\n";
- }
- }
- }
-}
+xCAT::Client::submit_request($cmdref,\&xCAT::Client::handle_response);
+exit $xCAT::Client::EXITCODE;
\ No newline at end of file
diff --git a/xCAT-client-2.0/share/man/man1/tabdump.1 b/xCAT-client-2.0/share/man/man1/tabdump.1
new file mode 100644
index 000000000..b501b90c1
--- /dev/null
+++ b/xCAT-client-2.0/share/man/man1/tabdump.1
@@ -0,0 +1,167 @@
+.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. | will give a
+.\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to
+.\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C'
+.\" expand to `' in nroff, nothing in troff, for use with C<>.
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.if \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. nr % 0
+. rr F
+.\}
+.\"
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "TABDUMP.1 1"
+.TH TABDUMP.1 1 "2008-02-13" "perl v5.8.8" "User Contributed Perl Documentation"
+.SH "NAME"
+\&\fBtabdump\fR \- display a database table in csv format.
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fItabdump [table]\fR
+.PP
+\&\fItabdump [? | \-h | \-\-help]\fR
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+The tabdump command displays the header and all the rows of the specified table in csv format.
+Only one table can be specified. If no table is specified, the list of existing
+tables will be displayed.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+\&\fB\-?|\-h|\-\-help\fR Display usage message.
+.SH "RETURN VALUE"
+.IX Header "RETURN VALUE"
+0 The command completed successfully.
+.PP
+1 An error has occurred.
+.SH "EXAMPLES"
+.IX Header "EXAMPLES"
+1. To display the contents of the site table:
+.PP
+\&\fItabdump site\fR
+.PP
+2. To see what tables exist in the xCAT database:
+.PP
+\&\fItabdump\fR
+.SH "FILES"
+.IX Header "FILES"
+/opt/xcat/sbin/tabdump
+.SH "NOTES"
+.IX Header "NOTES"
+This command is part of the xCAT software product.
diff --git a/xCAT-client-2.0/share/man/man1/tabrestore.1 b/xCAT-client-2.0/share/man/man1/tabrestore.1
new file mode 100644
index 000000000..5f5041e9c
--- /dev/null
+++ b/xCAT-client-2.0/share/man/man1/tabrestore.1
@@ -0,0 +1,172 @@
+.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. | will give a
+.\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to
+.\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C'
+.\" expand to `' in nroff, nothing in troff, for use with C<>.
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.if \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. nr % 0
+. rr F
+.\}
+.\"
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "TABRESTORE.1 1"
+.TH TABRESTORE.1 1 "2008-02-14" "perl v5.8.8" "User Contributed Perl Documentation"
+.SH "NAME"
+\&\fBtabrestore\fR \- replaces the contents of an xCAT database table with the contents in a csv file.
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fItabrestore table.csv\fR
+.PP
+\&\fItabrestore [? | \-h | \-\-help]\fR
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+The tabrestore command reads the contents of the specified file and puts its data
+in the corresponding table in the xCAT database. Any existing rows in that table
+are replaced. The file must be in csv format. It could be created by tabdump.
+Only one table can be specified.
+.PP
+This command can be used to copy the example table entries in /opt/xcat/share/xcat/templates/e1350
+into the xCAT database.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+\&\fB\-?|\-h|\-\-help\fR Display usage message.
+.SH "RETURN VALUE"
+.IX Header "RETURN VALUE"
+0 The command completed successfully.
+.PP
+1 An error has occurred.
+.SH "EXAMPLES"
+.IX Header "EXAMPLES"
+1. To put rows into the mp table:
+.PP
+\&\fItabrestore mp.csv\fR
+.PP
+The file mp.csv could contain something like:
+.PP
+#node,mpa,id,comments,disable
+\&\*(L"blade\*(R",\*(L"|\eD+(\ed+)|amm(($1\-1)/14+1)|\*(R",\*(L"|\eD+(\ed+)|(($1\-1)%14+1)|\*(R",,
+.SH "FILES"
+.IX Header "FILES"
+/opt/xcat/sbin/tabrestore
+.SH "NOTES"
+.IX Header "NOTES"
+This command is part of the xCAT software product.
diff --git a/xCAT-server-2.0/lib/xcat/plugins/tabutils.pm b/xCAT-server-2.0/lib/xcat/plugins/tabutils.pm
index 02727044b..bcb18cccc 100644
--- a/xCAT-server-2.0/lib/xcat/plugins/tabutils.pm
+++ b/xCAT-server-2.0/lib/xcat/plugins/tabutils.pm
@@ -4,31 +4,13 @@
# xCAT plugin package to handle various commands that work with the
# xCAT tables
#
-# Supported commands:
-# nodeadd
-# nodels
-# nodech
-# tabdump
-# tabrestore
-# noderm
-# To be implemented:
-# gettab
-# chtab
-# tabls
-# getnodecfg (?? this doesn't seem much different from gettab)
-# nr
-# These were xCAT 1.2 commands. Are they still useful in xCAT 1.3?
-# addattr
-# delattr
-# chtype
-#
#####################################################
package xCAT_plugin::tabutils;
use xCAT::Table;
use xCAT::Schema;
use Data::Dumper;
use xCAT::NodeRange;
-use xCAT::Schema; #noderm will need to build list of tables..
+use xCAT::Schema;
#use Getopt::Long qw(GetOptionsFromArray);
@@ -51,17 +33,17 @@ sub handled_commands
gettab => "tabutils",
tabdump => "tabutils",
tabrestore => "tabutils",
- tabch => "tabutils",
+ tabch => "tabutils", # not implemented yet
nodech => "tabutils",
nodeadd => "tabutils",
noderm => "tabutils",
- tabls => "tabutils",
+ tabls => "tabutils", # not implemented yet
nodels => "tabutils",
- getnodecfg => "tabutils",
- addattr => "tabutils",
- delattr => "tabutils",
- chtype => "tabutils",
- nr => "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
tabgrep => "tabutils"
};
}
@@ -72,45 +54,15 @@ my %usage = (
nodeadd =>
"Usage: nodeadd [table.column=value] [table.column=value] ...",
noderm => "Usage: noderm ",
- tabdump =>
- "Usage: tabdump \n where is one of the following:\n "
- . join("\n ", keys %xCAT::Schema::tabspec),
- tabrestore => "Usage: tabrestore .csv",
+ # the usage for tabdump is in the tabdump function
+ #tabdump => "Usage: tabdump \n where is one of the following:\n " . join("\n ", keys %xCAT::Schema::tabspec),
+ # the usage for tabrestore is in the tabrestore client cmd
+ #tabrestore => "Usage: tabrestore .csv",
);
#####################################################
# Process the command
#####################################################
-sub gettab
-{
- my $req = shift;
- my $callback = shift;
- my $keyspec = shift @{$req->{arg}};
- my @keypairs = split /,/, $keyspec;
- my %keyhash;
- foreach (@keypairs)
- {
- (my $key, my $value) = split /=/, $_;
- $keyhash{$key} = $value;
- }
- my %tabhash;
- foreach my $tabvalue (@{$req->{arg}})
- {
- (my $table, my $column) = split /\./, $tabvalue;
- $tabhash{$table}->{$column} = 1;
- }
- foreach my $tabn (keys %tabhash)
- {
- my $tab = xCAT::Table->new($tabn);
- (my $ent) = $tab->getAttribs(\%keyhash, keys %{$tabhash{$tabn}});
- foreach my $coln (keys %{$tabhash{$tabn}})
- {
- $callback->({data => ["$tabn.$coln:" . $ent->{$coln}]});
- }
- $tab->close;
- }
-}
-
sub process_request
{
use Getopt::Long;
@@ -141,11 +93,11 @@ sub process_request
}
elsif ($command eq "nodeadd" or $command eq "addnode")
{
- return chnode($nodes, $args, $callback, 1);
+ return nodech($nodes, $args, $callback, 1);
}
- elsif ($command eq "nodech" or $command eq "chnode")
+ elsif ($command eq "nodech" or $command eq "nodech")
{
- return chnode($nodes, $args, $callback, 0);
+ return nodech($nodes, $args, $callback, 0);
}
elsif ($command eq "tabrestore")
{
@@ -171,6 +123,36 @@ sub process_request
}
+sub gettab
+{
+ my $req = shift;
+ my $callback = shift;
+ my $keyspec = shift @{$req->{arg}};
+ my @keypairs = split /,/, $keyspec;
+ my %keyhash;
+ foreach (@keypairs)
+ {
+ (my $key, my $value) = split /=/, $_;
+ $keyhash{$key} = $value;
+ }
+ my %tabhash;
+ foreach my $tabvalue (@{$req->{arg}})
+ {
+ (my $table, my $column) = split /\./, $tabvalue;
+ $tabhash{$table}->{$column} = 1;
+ }
+ foreach my $tabn (keys %tabhash)
+ {
+ my $tab = xCAT::Table->new($tabn);
+ (my $ent) = $tab->getAttribs(\%keyhash, keys %{$tabhash{$tabn}});
+ foreach my $coln (keys %{$tabhash{$tabn}})
+ {
+ $callback->({data => ["$tabn.$coln:" . $ent->{$coln}]});
+ }
+ $tab->close;
+ }
+}
+
sub noderm
{
my $nodes = shift;
@@ -219,11 +201,12 @@ sub noderm
push @tablist, $_;
}
}
- chnode($nodes, \@tablist, $cb, 0);
+ nodech($nodes, \@tablist, $cb, 0);
}
sub tabrestore
{
+ # the usage for tabrestore is in the tabrestore client cmd
#request->{data} is an array of CSV formatted lines
my $request = shift;
@@ -231,9 +214,8 @@ sub tabrestore
my $table = $request->{table}->[0];
my $linenumber = 1;
my $tab = xCAT::Table->new($table, -create => 1, -autocommit => 0);
- unless ($tab)
- {
- $cb->({error => "Unable to open $table"});
+ unless ($tab) {
+ $cb->({error => "Unable to open $table",errorcode=>4});
return;
}
$tab->delEntries(); #Yes, delete *all* entries
@@ -269,8 +251,7 @@ sub tabrestore
{
error =>
"CSV missing opening \" for record with \" characters on line $linenumber, character "
- . index($origline, $line)
- . ": $origline"
+ . index($origline, $line) . ": $origline", errorcode=>4
}
);
next LINE;
@@ -291,8 +272,7 @@ sub tabrestore
{
error =>
"CSV unmatched \" in record on line $linenumber, character "
- . index($origline, $line)
- . ": $origline"
+ . index($origline, $line) . ": $origline", errorcode=>4
}
);
next LINE;
@@ -317,8 +297,7 @@ sub tabrestore
{
error =>
"CSV unescaped \" in record on line $linenumber, character "
- . index($origline, $line)
- . ": $origline"
+ . index($origline, $line) . ": $origline", errorcode=>4
}
);
$rollback = 1;
@@ -335,12 +314,7 @@ sub tabrestore
if ($line)
{
$rollback = 1;
- $cb->(
- {
- error =>
- "Too many fields on line $linenumber: $origline | $line"
- }
- );
+ $cb->({error => "Too many fields on line $linenumber: $origline | $line", errorcode=>4});
next LINE;
}
@@ -349,14 +323,7 @@ sub tabrestore
if (not defined($rc[0]))
{
$rollback = 1;
- $cb->(
- {
- error => "DB error "
- . $rc[1]
- . " with line $linenumber: "
- . $origline
- }
- );
+ $cb->({error => "DB error " . $rc[1] . " with line $linenumber: " . $origline, errorcode=>4});
}
}
if ($rollback)
@@ -374,69 +341,79 @@ sub tabrestore
sub tabdump
{
-
- #TODO: need to return header for not-yet existing, but schemad tabs
- #TODO: schema defined column order.
my $args = shift;
my $cb = shift;
my $table = "";
- foreach (@$args)
- {
- unless (/^-/)
- {
- if ($table)
- {
- return 1; #TODO: Error, usage
- }
- $table = $_;
- }
+ my $HELP;
+
+ sub tabdump_usage {
+ my $exitcode = shift @_;
+ my %rsp;
+ push @{$rsp{data}}, "Usage: tabdump [table]";
+ push @{$rsp{data}}, " tabdump [-?|-h|--help]";
+ if ($exitcode) { $rsp{errorcode} = $exitcode; }
+ $cb->(\%rsp);
}
- my $tabh = xCAT::Table->new($table);
+
+ # Process arguments
+ @ARGV = @{$args};
+ if (!GetOptions('h|?|help' => \$HELP)) { tabdump_usage(1); return; }
+
+ if ($HELP) { tabdump_usage(0); return; }
+ if (scalar(@ARGV)>1) { tabdump_usage(1); return; }
+
my %rsp;
+ # If no arguments given, we display a list of the tables
+ if (!scalar(@ARGV)) {
+ push @{$rsp{data}}, keys %xCAT::Schema::tabspec;
+ $cb->(\%rsp);
+ return;
+ }
+
+ $table = $ARGV[0];
+ my $tabh = xCAT::Table->new($table);
+
+ sub tabdump_header {
+ 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}))
{
- my $header = join ",", @{$xCAT::Schema::tabspec{$table}->{cols}};
- $header = "#" . $header;
- push @{$rsp{data}}, $header;
- $cb->(\%rsp);
+ tabdump_header(@{$xCAT::Schema::tabspec{$table}->{cols}});
+ $cb->(\%rsp);
return;
}
- $cb->({error => "No such table: $table"});
+ $cb->({error => "No such table: $table",errorcode=>1});
return 1;
}
+
my $recs = $tabh->getAllEntries();
my $rec;
- my $firstline = 1;
- unless (@$recs)
+ unless (@$recs) # table exists, but is empty. Show header.
{
if (defined($xCAT::Schema::tabspec{$table}))
{
- my $header = join ",", @{$xCAT::Schema::tabspec{$table}->{cols}};
- $header = "#" . $header;
- push @{$rsp{data}}, $header;
- $cb->(\%rsp);
+ tabdump_header(@{$xCAT::Schema::tabspec{$table}->{cols}});
+ $cb->(\%rsp);
return;
}
}
+ # Display all the rows of the table in the order of the columns in the schema
+ tabdump_header(@{$tabh->{colnames}});
foreach $rec (@$recs)
{
my $line = '';
- if ($firstline)
- {
- $firstline = 0;
- $line = join ",", @{$tabh->{colnames}};
- $line = "#" . $line;
- push @{$rsp{data}}, $line;
- $line = '';
- }
foreach (@{$tabh->{colnames}})
{
- $rec->{$_} =~ s/"/""/g;
if (defined $rec->{$_})
{
+ $rec->{$_} =~ s/"/""/g;
$line = $line . '"' . $rec->{$_} . '",';
}
else
@@ -444,14 +421,14 @@ sub tabdump
$line .= ',';
}
}
- $line =~ s/,$//;
+ $line =~ s/,$//; # remove the extra comma at the end
$line = $line . $lineappend;
push @{$rsp{data}}, $line;
}
$cb->(\%rsp);
}
-sub chnode
+sub nodech
{
my $nodes = shift;
my $args = shift;