Cleaned up the client code and added man pages for tabdump and tabrestore

git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@477 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
This commit is contained in:
bp-sawyers 2008-02-14 14:25:49 +00:00
parent bc7fc0742d
commit 8c057025b8
9 changed files with 769 additions and 453 deletions

View File

@ -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:
# <xcatrequest>
# <node>
# <name>node1</name>
# <data>
# <desc>node1 desc</desc>
# <contents>node1 contents</contents>
# </data>
# </node>
# <node>
# <name>node2</name>
# <data>
# <desc>node2 desc</desc>
# <contents>node2 contents</contents>
# </data>
# </node>
# </xcatrequest>
#
# 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

View File

@ -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 ( <STDIN> ) {
$data.=$_;
}
while ( <STDIN> ) { $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:
# <xcatrequest>
# <node>
# <name>node1</name>
# <data>
# <desc>node1 desc</desc>
# <contents>node1 contents</contents>
# </data>
# </node>
# <node>
# <name>node2</name>
# <data>
# <desc>node2 desc</desc>
# <contents>node2 contents</contents>
# </data>
# </node>
# </xcatrequest>
#
# 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;

View File

@ -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 ( <STDIN> ) { $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;

View File

@ -0,0 +1,45 @@
=head1 NAME
B<tabdump> - display a database table in csv format.
=head1 SYNOPSIS
I<tabdump [table]>
I<tabdump [? | -h | --help]>
=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<tabdump site>
2. To see what tables exist in the xCAT database:
I<tabdump>
=head1 FILES
/opt/xcat/sbin/tabdump
=head1 NOTES
This command is part of the xCAT software product.

View File

@ -0,0 +1,50 @@
=head1 NAME
B<tabrestore> - replaces the contents of an xCAT database table with the contents in a csv file.
=head1 SYNOPSIS
I<tabrestore table.csv>
I<tabrestore [? | -h | --help]>
=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<tabrestore mp.csv>
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.

View File

@ -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 <tablename>.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:
# <xcatrequest>
# <node>
# <name>node1</name>
# <data>
# <desc>node1 desc</desc>
# <contents>node1 contents</contents>
# </data>
# </node>
# <node>
# <name>node2</name>
# <data>
# <desc>node2 desc</desc>
# <contents>node2 contents</contents>
# </data>
# </node>
# </xcatrequest>
#
# 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;

View File

@ -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.

View File

@ -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.

View File

@ -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 <noderange> [table.column=value] [table.column=value] ...",
noderm => "Usage: noderm <noderange>",
tabdump =>
"Usage: tabdump <tablename>\n where <tablename> is one of the following:\n "
. join("\n ", keys %xCAT::Schema::tabspec),
tabrestore => "Usage: tabrestore <tablename>.csv",
# the usage for tabdump is in the tabdump function
#tabdump => "Usage: tabdump <tablename>\n where <tablename> 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 <tablename>.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;