mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-05-21 19:22:05 +00:00
3158 lines
100 KiB
Perl
3158 lines
100 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 => ["Unknown 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;
|
|
}
|
|
my $keys = xCAT::Table::buildWhereClause(\@attrarray, "1");
|
|
if (ref($keys) ne 'ARRAY') {
|
|
$cb->({ error => ["$keys"], errorcode => [1] });
|
|
return;
|
|
} else {
|
|
foreach my $k (@$keys) {
|
|
unless (grep /$k/, @{ $xCAT::Schema::tabspec{$table}->{cols} }) {
|
|
$cb->({ error => ["No column \"$k\" in table \"$table\""], errorcode => [1] });
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
@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 $nodenum;
|
|
|
|
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);
|
|
$nodenum = scalar (@$nodes);
|
|
}
|
|
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);
|
|
|
|
#}
|
|
}
|
|
$nodenum = scalar (@nodes);
|
|
}
|
|
}
|
|
my $rsp_info;
|
|
$rsp_info->{numofnodes}->[0] = $nodenum;
|
|
$callback->($rsp_info);
|
|
|
|
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";
|
|
$rsp{errorcode}->[0] = 1;
|
|
$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";
|
|
$rsp{errorcode}->[0] = 1;
|
|
$callback->(\%rsp);
|
|
return 1;
|
|
}
|
|
|
|
for (@tables_to_del)
|
|
{
|
|
my $tab = xCAT::Table->new($_, -create => 1, -autocommit => 0);
|
|
unless ($tab) {
|
|
my %rsp;
|
|
$rsp{data}->[0] = "Table $_ does not exist.";
|
|
$rsp{errorcode}->[0] = 1;
|
|
$callback->(\%rsp);
|
|
next;
|
|
}
|
|
$tab->delEntries(\%keyhash);
|
|
$tab->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";
|
|
$rsp{errorcode}->[0] = 1;
|
|
$callback->(\%rsp);
|
|
return 1;
|
|
|
|
}
|
|
}
|
|
my $err_found = 0;
|
|
for my $k (keys %keyhash) {
|
|
unless (grep /$k/, @{ $xCAT::Schema::tabspec{$table}->{cols} }) {
|
|
$callback->({ error => ["No column \"$k\" in table \"$table\""], errorcode => [1] });
|
|
$err_found = 1;
|
|
}
|
|
}
|
|
if ($err_found) {
|
|
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 1;
|
|
}
|
|
$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;
|
|
}
|