2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-30 17:46:38 +00:00
xcat-core/perl-xCAT/xCAT/DBobjUtils.pm
chenglch 831a3b6e04 Make tables which contain nodecol column inheritable (#2785)
Actually, the subroutine getNodeAttribs already fetch the attributes
related to the groups, but the `defobj` related method ignore these
attributes as it is not node column although nodecol is set. This
patch treat nodecol column as node to make the attributes in these
table inheritable.

fix-issue: #2450
2017-04-18 01:46:45 -05:00

2669 lines
96 KiB
Perl
Executable File

#!/usr/bin/env perl -w
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
#####################################################
#
# Utility subroutines that can be used to manage xCAT data object
# definitions.
#
#
#####################################################
package xCAT::DBobjUtils;
require xCAT::NodeRange;
require xCAT::Schema;
require xCAT::Table;
require xCAT::Utils;
require xCAT::MsgUtils;
require xCAT::NetworkUtils;
require xCAT::ServiceNodeUtils;
use strict;
# IPv6 not yet implemented - need Socket6
use Socket;
#----------------------------------------------------------------------------
=head3 getObjectsOfType
Get a list of data objects of the given type.
Arguments:
Returns:
undef
@objlist - list of objects of this type
Globals:
Error:
Example:
Comments:
@objlist = xCAT::DBobjUtils->getObjectsOfType($type);
=cut
#-----------------------------------------------------------------------------
sub getObjectsOfType
{
my ($class, $type) = @_;
my @objlist;
# special case for site table
if ($type eq 'site') {
push(@objlist, 'clustersite');
return @objlist;
}
# The database may be changed between getObjectsOfType calls
# do not use cache %::saveObjList if --nocache is specified
if ($::saveObjList{$type} && !$::opt_nc) {
@objlist = @{ $::saveObjList{$type} };
} else {
# get the key for this type object
# ex. for "network" type the key is "netname"
# get the data type spec from Schema.pm
my $datatype = $xCAT::Schema::defspec{$type};
# get the key for this type object
# ex. for "network" type the key is "netname"
my $objkey = $datatype->{'objkey'};
my $table;
my $tabkey;
foreach my $this_attr (@{ $datatype->{'attrs'} }) {
my $attr = $this_attr->{attr_name};
if ($attr eq $objkey) {
# get the table & key for to lookup
# get the actual attr name to use in the table
# - may be different then the attr name used for the object.
($table, $tabkey) = split('\.', $this_attr->{tabentry});
last;
}
}
# get the whole table and add each entry in the objkey column
# to the list of objects.
my @TableRowArray = xCAT::DBobjUtils->getDBtable($table);
foreach (@TableRowArray) {
push(@objlist, $_->{$tabkey});
}
# if this is type "group" we need to check the nodelist table
my @nodeGroupList = ();
if ($type eq 'group') {
my $table = "nodelist";
my @TableRowArray = xCAT::DBobjUtils->getDBtable($table);
foreach (@TableRowArray) {
my @tmplist = split(',', $_->{'groups'});
push(@nodeGroupList, @tmplist);
}
foreach my $n (@nodeGroupList) {
if (!grep(/^$n$/, @objlist)) {
push(@objlist, $n);
}
}
}
@{ $::saveObjList{$type} } = @objlist;
}
return @objlist;
}
#----------------------------------------------------------------------------
=head3 getobjattrs
Get data from tables
$type_hash: objectname=>objtype hash
$attrs_ref: only get the specific attributes,
this can be useful especially for performance considerations
Arguments:
Returns:
undef
hash ref - (ex. $tabhash{$table}{$objname}{$attr} = $value)
Globals:
Error:
Example:
%tabhash = xCAT::DBobjUtils->getobjattrs(\%typehash);
Comments:
For now - only support tables that have 'node' as key !!!
=cut
#-----------------------------------------------------------------------------
sub getobjattrs
{
my $class = shift;
my $ref_hash = shift;
my @attrs;
# The $attrs is an optional argument
if (ref $_[0]) {
@attrs = @{ shift() };
}
my %typehash = %$ref_hash;
my %tableattrs;
my %tabhash;
# get a list of object names for each type
my %objtypelist;
foreach my $objname (sort (keys %typehash)) {
# get list of objects for each type
# $objtypelist{$typehash{$objname}}=$objname;
push @{ $objtypelist{ $typehash{$objname} } }, $objname;
}
# go through each object type and look up all the info for each object
foreach my $objtype (keys %objtypelist) {
# only do node type for now
if ($objtype eq 'node') {
# find the list of tables and corresponding attrs
# - for this object type
# get the object type decription from Schema.pm
my $datatype = $xCAT::Schema::defspec{$objtype};
foreach my $this_attr (@{ $datatype->{'attrs'} }) {
my $attr = $this_attr->{attr_name};
if (scalar(@attrs) > 0) { # Only query specific attributes
if (!grep(/^$attr$/, @attrs)) {
next; # This attribute is not needed
}
}
# table_attr is the attr that actually appears in the
# table which could possibly be different then the attr
# used in the node def
# ex. 'nodetype.arch'
my ($lookup_table, $table_attr) = split('\.', $this_attr->{tabentry});
if (!grep(/^$table_attr$/, @{ $tableattrs{$lookup_table} })) {
push @{ $tableattrs{$lookup_table} }, $table_attr;
}
}
# foreach table look up the list of attrs for this
# list of object names
foreach my $table (keys %tableattrs) {
# open the table
# with autocommit => 0, it does not work on Ubuntu running mysql
my $thistable = xCAT::Table->new($table, -create => 1, -autocommit => 1);
if (!$thistable) {
my $rsp;
$rsp->{data}->[0] = "Could not open the \'$table\' table.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
next;
}
my @objlist = @{ $objtypelist{$objtype} };
my $rec = $thistable->getNodesAttribs(\@objlist, @{ $tableattrs{$table} });
# fill in %tabhash with any values that are set
foreach my $n (@objlist) {
my $tmp1 = $rec->{$n}->[0];
foreach $a (@{ $tableattrs{$table} }) {
if (defined($tmp1->{$a})) {
$tabhash{$table}{$n}{$a} = $tmp1->{$a};
#print "obj = $n, table = $table, attr =$a, val = $tabhash{$table}{$n}{$a}\n";
} else {
# Add a has been searched flag to improve the performance
$tabhash{$table}{$n}{ "$a" . "_hassearched" } = 1;
}
}
}
#$thistable->commit;
}
}
}
return %tabhash;
}
#----------------------------------------------------------------------------
=head3 getobjdefs
Get object definitions from the DB.
$type_hash: objectname=>objtype hash
$verbose: optional
$attrs_ref: only get the specific attributes,
this can be useful especially for performance considerations
$chname_ref: used to get the table entries for changing the object name
Arguments:
Returns:
undef - error
hash ref - $objecthash{objectname}{attrname} = value
Globals:
Error:
Example:
To use create hash for objectname and object type
ex. $objhash{$obj} = $type;
- then call as follows:
%myhash = xCAT::DBobjUtils->getobjdefs(\%objhash);
Comments:
=cut
#-----------------------------------------------------------------------------
sub getobjdefs
{
my ($class, $hash_ref, $verbose, $attrs_ref, $chname_ref) = @_;
my %objhash;
my %typehash = %$hash_ref;
my %tabhash;
my @attrs;
if (ref($attrs_ref)) {
@attrs = @$attrs_ref;
}
@::foundTableList = ();
if ($::ATTRLIST eq "none") {
# just return the list of obj names
foreach my $objname (sort (keys %typehash)) {
my $type = $typehash{$objname};
$objhash{$objname}{'objtype'} = $type;
}
return %objhash;
}
# see if we need to get any objects of type 'node'
my $getnodes = 0;
foreach my $objname (keys %typehash) {
if ($typehash{$objname} eq 'node') {
$getnodes = 1;
}
}
# if so then get node info from tables now
# still may need to look up values in some tables using
# other keys - also need to figure out what tables to take
# values from when using 'only_if' - see below
# - but this saves lots of time
if ($getnodes) {
if (scalar(@attrs) > 0) { # Only get specific attributes of the node
# find the onlyif key for the attributes
REDO: my $datatype = $xCAT::Schema::defspec{'node'};
foreach my $this_attr (@{ $datatype->{'attrs'} }) {
my $attr = $this_attr->{attr_name};
if (exists($this_attr->{only_if})) {
my ($onlyif_key, $onlyif_value) = split('\=', $this_attr->{only_if});
if (!grep (/^$onlyif_key$/, @attrs)) {
push @attrs, $onlyif_key;
goto REDO;
}
}
}
%tabhash = xCAT::DBobjUtils->getobjattrs(\%typehash, \@attrs);
} else {
%tabhash = xCAT::DBobjUtils->getobjattrs(\%typehash);
}
}
# Classify the nodes with type
my %type_obj = ();
foreach my $objname (keys %typehash) {
push @{ $type_obj{ $typehash{$objname} } }, $objname;
}
foreach my $objtype (sort (keys %type_obj)) {
if ($objtype eq 'site') {
my @TableRowArray = xCAT::DBobjUtils->getDBtable('site');
foreach my $objname (sort @{ $type_obj{$objtype} }) {
if (@TableRowArray) {
my $foundinfo = 0;
foreach (@TableRowArray) {
if ($_->{key}) {
if (defined($_->{value})) {
$foundinfo++;
if ($verbose == 1) {
$objhash{$objname}{ $_->{key} } = "$_->{value}\t(Table:site - Key:$_->{key})";
} else {
$objhash{$objname}{ $_->{key} } = $_->{value};
}
}
}
}
if ($foundinfo) {
$objhash{$objname}{'objtype'} = 'site';
}
} else {
my $rsp;
$rsp->{data}->[0] = "Could not read the \'$objname\' object from the \'site\' table.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
}
}
} elsif ($objtype eq 'monitoring') {
# need a special case for the monitoring table
# - need to check the monsetting table for entries that contain
# the same name as the monitoring table entry.
my @TableRowArray = xCAT::DBobjUtils->getDBtable('monsetting');
foreach my $objname (sort @{ $type_obj{$objtype} }) {
if (@TableRowArray) {
my $foundinfo = 0;
foreach (@TableRowArray) {
if ($_->{name} eq $objname) {
if ($_->{key}) {
if (defined($_->{value})) {
$foundinfo++;
if ($verbose == 1) {
$objhash{$objname}{ $_->{key} } = "$_->{value}\t(Table:monsetting)";
} else {
$objhash{$objname}{ $_->{key} } = $_->{value};
}
}
}
}
}
if ($foundinfo) {
$objhash{$objname}{'objtype'} = 'monitoring';
}
} else {
my $rsp;
$rsp->{data}->[0] = "Could not read the \'$objname\' object from the \'monsetting\' table.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
}
}
} elsif (($objtype eq 'auditlog') || ($objtype eq 'eventlog')) {
# Special case for auditlog/eventlog
# All the auditlog/eventlog attributes are in auditlog/eventlog table,
# Do not need to read the table multiple times for each attribute.
# The auditlog/eventlog is likely be very big over time,
# performance is a big concern with the general logic
my @TableRowArray = xCAT::DBobjUtils->getDBtable($objtype);
foreach my $objname (sort @{ $type_obj{$objtype} }) {
if (@TableRowArray) {
my $foundinfo = 0;
foreach my $entry (@TableRowArray) {
if ($entry->{recid} eq $objname) {
foreach my $k (keys %{$entry}) {
# recid is the object name, do not need to be in the attributes list
if ($k eq 'recid') { next; }
if (defined($entry->{$k})) {
$foundinfo++;
if ($verbose == 1) {
$objhash{$objname}{$k} = "$entry->{$k}\t(Table:$objtype - Key:$k)";
} else {
$objhash{$objname}{$k} = $entry->{$k};
}
}
}
if ($foundinfo) {
$objhash{$objname}{'objtype'} = $objtype;
}
# There should not be multiple entries with the same recid
last;
} # end if($entry->
} # end foreach my $entry
} # end if(@TableTowArray
} # end foreach my $objname
} else {
# get the object type decription from Schema.pm
my $datatype = $xCAT::Schema::defspec{$objtype};
# get the key to look for, for this object type
my $objkey = $datatype->{'objkey'};
# go through the list of valid attrs
foreach my $this_attr (@{ $datatype->{'attrs'} }) {
my $ent;
my $attr = $this_attr->{attr_name};
# skip the key attr ???
if ($attr eq $objkey) {
next;
}
# skip the attributes that does not needed for node type
if ($getnodes) {
if (scalar(@attrs) > 0 && !grep(/^$attr$/, @attrs)) {
next;
}
}
# OK - get the info needed to access the DB table
# - i.e. table name, key name, attr names
# need the actual table attr name corresponding
# to the object attr name
# ex. noderes.nfsdir
my ($tab, $tabattr) = split('\.', $this_attr->{tabentry});
foreach my $objname (sort @{ $type_obj{$objtype} }) {
# get table lookup info from Schema.pm
# !!!! some tables depend on the value of certain attrs
# we need to look up attrs in the correct order or we will
# not be able to determine what tables to look
# in for some attrs.
if (exists($this_attr->{only_if})) {
my ($check_attr, $check_value) = split('\=', $this_attr->{only_if});
# if the object value is not the value we need
# to match then try the next only_if value
next if (!($objhash{$objname}{$check_attr} =~ /\b$check_value\b/));
}
$objhash{$objname}{'objtype'} = $objtype;
my %tabentry = ();
# def commands need to support multiple keys in one table
# the subroutine parse_access_tabentry is used for supporting multiple keys
my $rc = xCAT::DBobjUtils->parse_access_tabentry($objname,
$this_attr->{access_tabentry}, \%tabentry);
if ($rc != 0) {
my $rsp;
$rsp->{data}->[0] =
"access_tabentry \'$this_attr->{access_tabentry}\' is not valid.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
next;
}
#
# Only allow one table in the access_tabentry
# use multiple tables to look up tabentry does not make any sense
my $lookup_table = $tabentry{'lookup_table'};
my $intabhash = 0;
my $notsearched = 0;
foreach my $lookup_attr (keys %{ $tabentry{'lookup_attrs'} }) {
# Check whether the attribute is already in %tabhash
# The %tabhash is for performance considerations
my $tabspec = $xCAT::Schema::tabspec{$lookup_table};
my $nodecol = $tabspec->{'nodecol'} if defined($tabspec->{'nodecol'});
if (($lookup_attr eq 'node' && $objtype eq 'node') || (defined($nodecol) && $objtype eq 'node')) {
if (defined($tabhash{$lookup_table}{$objname}{$tabattr})) {
if ($verbose == 1) {
$objhash{$objname}{$attr} = "$tabhash{$lookup_table}{$objname}{$tabattr}\t(Table:$lookup_table - Key:$lookup_attr - Column:$tabattr)";
} else {
$objhash{$objname}{$attr} = $tabhash{$lookup_table}{$objname}{$tabattr};
}
if (defined $chname_ref) {
push @{ $chname_ref->{$lookup_table} }, ($tabentry{'lookup_attrs'}, $lookup_attr);
}
$intabhash = 1;
last;
} elsif (!defined($tabhash{$lookup_table}{$objname}{ "$tabattr" . "_hassearched" })) {
$notsearched = 1;
}
} else {
$notsearched = 1;
}
}
# Not in tabhash,
# Need to lookup the table
if ($intabhash == 0 && $notsearched == 1) {
# look up attr values
my @rows = xCAT::DBobjUtils->getDBtable($lookup_table);
if (@rows) {
foreach my $rowent (@rows) {
my $match = 1;
my $matchedattr;
# Again, multiple keys support needs the "foreach"
foreach my $lookup_attr (keys %{ $tabentry{'lookup_attrs'} }) {
if ($rowent->{$lookup_attr} ne $tabentry{'lookup_attrs'}{$lookup_attr}) {
$match = 0;
last;
}
}
if ($match == 1) {
if ($verbose == 1) {
my @lookup_attrs = keys %{ $tabentry{'lookup_attrs'} };
$objhash{$objname}{$attr} = "$rowent->{$tabattr}\t(Table:$lookup_table - Key: @lookup_attrs - Column:$tabattr)";
} else {
$objhash{$objname}{$attr} = $rowent->{$tabattr};
}
if (defined $chname_ref) {
push @{ $chname_ref->{$lookup_table} }, ($tabentry{'lookup_attrs'}, (keys %{ $tabentry{'lookup_attrs'} })[0]);
}
} #end if ($match...
} #end foreach
} # end if (defined...
} #end if ($intabhash...
}
}
}
} #foreach my $objtype
return %objhash;
}
#----------------------------------------------------------------------------
=head3 getDBtable
Get a DB table, cache it , & return list of rows from the table.
Arguments:
Returns:
undef - error
@rows - of table
Globals:
Error:
Example:
call as follows
my @TableRowArray= xCAT::DBobjUtils->getDBtable($tablename);
Comments:
=cut
#-----------------------------------------------------------------------------
sub getDBtable
{
my ($class, $table) = @_;
my @rows = [];
# save this table info - in case this subr gets called multiple times
# --nocache flag specifies not to use cahe
if (grep(/^$table$/, @::foundTableList) && !$::opt_nc) {
# already have this
@rows = @{ $::TableHash{$table} };
} else {
# need to get info from DB
my $thistable = xCAT::Table->new($table, -create => 1);
if (!$thistable) {
return undef;
}
#@rows = $thistable->getTable;
@rows = @{ $thistable->getAllEntries() };
# !!!! this routine returns rows even if the table is empty!!!!!!
# keep track of the fact that we checked this table
# - even if it's empty!
push(@::foundTableList, $thistable->{tabname});
@{ $::TableHash{$table} } = @rows;
#$thistable->commit;
} # end if not cached
if (@rows) {
return @rows;
} else {
return undef;
}
}
#----------------------------------------------------------------------------
=head3 setobjdefs
Set the object definitions in the DB.
- Handles the Schema lookup and updating the DB tables.
Arguments:
Returns:
1 - error
0 - OK
Globals:
Error:
Example:
To use:
-create hash for objectname and object type
ex. $objhash{$object}{$attribute} = value;
-then call as follows:
if (xCAT::DBobjUtils->setobjdefs(\%objhash) != 0)
Comments:
=cut
#-----------------------------------------------------------------------------
sub setobjdefs
{
my ($class, $hash_ref) = @_;
my %objhash = %$hash_ref;
my %settableref;
my $ret = 0;
my %allupdates;
my $setattrs = 0;
# get the attr=vals for these objects from the DB - if any
# - so we can figure out where to put additional attrs
# The getobjdefs call was in the foreach loop,
# it caused mkdef/chdef performance issue,
# so it is moved out of the foreach loop
my %DBhash;
my @attrs;
foreach my $objname (keys %objhash) {
my $type = $objhash{$objname}{objtype};
$DBhash{$objname} = $type;
@attrs = keys %{ $objhash{$objname} };
}
my %DBattrvals;
%DBattrvals = xCAT::DBobjUtils->getobjdefs(\%DBhash, 0, \@attrs);
# for each object figure out:
# - what tables to update
# - which table attrs correspond to which object attrs
# - what the keys are for each table
# update the tables a row at a time
foreach my $objname (keys %objhash) {
# get attr=val that are set in the DB ??
my $type = $objhash{$objname}{objtype};
# handle the monitoring table as a special case !!!!!
if ($type eq 'monitoring') {
# Get the names of the attrs stored in monitoring table
# get the object type decription from Schema.pm
my $datatype = $xCAT::Schema::defspec{$type};
# get a list of valid attr names
# for this type object
my @attrlist;
foreach my $entry (@{ $datatype->{'attrs'} }) {
push(@attrlist, $entry->{'attr_name'});
}
# open the tables (monitoring and monsetting)
my $montable = xCAT::Table->new('monitoring', -create => 1, -autocommit => 0);
if (!$montable) {
my $rsp;
$rsp->{data}->[0] = "Could not set the \'$montable\' table.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
return 1;
}
# open the table
my $monsettable = xCAT::Table->new('monsetting', -create => 1, -autocommit => 0);
if (!$monsettable) {
my $rsp;
$rsp->{data}->[0] = "Could not set the \'$monsettable\' table.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
return 1;
}
my %keyhash;
my %updates;
foreach my $attr (keys %{ $objhash{$objname} }) {
my $val;
if ($attr eq 'objtype') {
next;
}
# determine the value if we have plus or minus
if ($::plus_option) {
# add new to existing - at the end - comma separated
if (defined($DBattrvals{$objname}{$attr})) {
$val = "$DBattrvals{$objname}{$attr},$objhash{$objname}{$attr}";
} else {
$val = "$objhash{$objname}{$attr}";
}
} elsif ($::minus_option) {
# remove the specified list of values from the current
# attr values.
if ($DBattrvals{$objname}{$attr}) {
# get the list of attrs to remove
my @currentList = split(/,/, $DBattrvals{$objname}{$attr});
my @minusList = split(/,/, $objhash{$objname}{$attr});
# make a new list without the one specified
my $first = 1;
my $newlist;
foreach my $i (@currentList) {
chomp $i;
if (!grep(/^$i$/, @minusList)) {
# set new groups list for node
if (!$first) {
$newlist .= ",";
}
$newlist .= $i;
$first = 0;
}
}
$val = $newlist;
}
} else {
#just set the attr to what was provided! - replace
$val = $objhash{$objname}{$attr};
}
if (grep(/^$attr$/, @attrlist)) {
# if the attr belong in the monitoring tabel
%keyhash = (name => $objname);
%updates = ($attr => $val);
$montable->setAttribs(\%keyhash, \%updates);
} else {
# else it belongs in the monsetting table
$keyhash{name} = $objname;
$keyhash{key} = $attr;
$updates{value} = $val;
$monsettable->setAttribs(\%keyhash, \%updates);
}
}
$montable->commit;
$monsettable->commit;
next;
} #if ($type eq 'monitoring')
# handle the site table as a special case !!!!!
if ($type eq 'site') {
# open the table
my $thistable =
xCAT::Table->new('site', -create => 1, -autocommit => 0);
if (!$thistable) {
my $rsp;
$rsp->{data}->[0] = "Could not set the \'$thistable\' table.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
return 1;
}
foreach my $attr (keys %{ $objhash{$objname} }) {
if ($attr eq 'objtype') {
next;
}
my %keyhash;
$keyhash{key} = $attr;
my $val;
if ($::plus_option) {
# add new to existing - at the end - comma separated
if (defined($DBattrvals{$objname}{$attr})) {
$val =
"$DBattrvals{$objname}{$attr},$objhash{$objname}{$attr}";
} else {
$val = "$objhash{$objname}{$attr}";
}
} elsif ($::minus_option) {
# remove the specified list of values from the current
# attr values.
if ($DBattrvals{$objname}{$attr}) {
# get the list of attrs to remove
my @currentList = split(/,/, $DBattrvals{$objname}{$attr});
my @minusList = split(/,/, $objhash{$objname}{$attr});
# make a new list without the one specified
my $first = 1;
my $newlist;
foreach my $i (@currentList) {
chomp $i;
if (!grep(/^$i$/, @minusList)) {
# set new groups list for node
if (!$first) {
$newlist .= ",";
}
$newlist .= $i;
$first = 0;
}
}
$val = $newlist;
}
} else {
#just set the attr to what was provided! - replace
$val = $objhash{$objname}{$attr};
}
if ($val eq "") { # delete the line
$thistable->delEntries(\%keyhash);
} else { # change the attr
my %updates;
$updates{value} = $val;
my ($rc, $str) = $thistable->setAttribs(\%keyhash, \%updates);
if (!defined($rc)) {
if ($::verbose) {
my $rsp;
$rsp->{data}->[0] =
"Could not set the \'$attr\' attribute of the \'$objname\' object in the xCAT database.";
$rsp->{data}->[1] =
"Error returned is \'$str->errstr\'.";
xCAT::MsgUtils->message("I", $rsp, $::callback);
}
$ret = 1;
}
}
}
$thistable->commit;
next;
} #if ($type eq 'site')
#
# handle the rest of the object types
#
# get the object type decription from Schema.pm
my $datatype = $xCAT::Schema::defspec{$type};
# get the object key to look for, for this object type
my $objkey = $datatype->{'objkey'};
# get a list of valid attr names
# for this type object
my %attrlist;
foreach my $entry (@{ $datatype->{'attrs'} }) {
#push(@{$attrlist{$type}}, $entry->{'attr_name'});
$attrlist{$type}{ $entry->{'attr_name'} } = 1;
}
my @attrprovided = ();
# check FINALATTRS to see if all the attrs are valid
foreach my $attr (keys %{ $objhash{$objname} }) {
if ($attr eq $objkey) {
next;
}
if ($attr eq "objtype") {
# objtype not stored in object definition
next;
}
if (!defined($attrlist{$type}{$attr})) {
if ($::verbose) {
my $rsp;
$rsp->{data}->[0] =
"\'$attr\' is not a valid attribute for type \'$type\'.";
$rsp->{data}->[1] = "Skipping to the next attribute.";
xCAT::MsgUtils->message("I", $rsp, $::callback);
}
next;
}
push(@attrprovided, $attr);
}
# we need to figure out what table to
# store each attr
# And we must do this in the order given in defspec!!
my @setattrlist = ();
my %checkedattrs;
my $invalidattr;
foreach my $this_attr (@{ $datatype->{'attrs'} }) {
my %keyhash;
my %updates;
my %tabentry;
my ($lookup_table, $lookup_attr, $lookup_data);
my $attr_name = $this_attr->{attr_name};
if ($attr_name eq $objkey) {
next;
}
# if we have a value for this attribute then process it
# - otherwise go to the next attr
if (defined($objhash{$objname}{$attr_name})) {
# check the defspec to see where this attr goes
# the table for this attr might depend on the
# value of some other attr
# need to check the only_if entries to find one where the
# other attr value matches what we have
# ex. like if I want to set hdwctrlpoint I will have
# to match the right value for mgtmethod
if (exists($this_attr->{only_if})) {
my ($check_attr, $check_value) =
split('\=', $this_attr->{only_if});
# if my attr value for the attr to check doesn't
# match this then try the next one
# ex. say I want to set hdwctrlpoint, the table
# will depend on the mgtmethod attr - so I need
# to find the 'only_if' that matches the value
# specified for that attr (ex. mgtmethod=hmc)
# need to check the attrs we are setting for the object
# as well as the attrs for this object that may be
# already set in DB
if (!($objhash{$objname}{$check_attr}) && !($DBattrvals{$objname}{$check_attr})) {
# if I didn't already check for this attr
my $rsp;
if (!defined($checkedattrs{$attr_name})) {
push @{ $rsp->{data} }, "Cannot set the \'$attr_name\' attribute unless a value is provided for \'$check_attr\'.";
foreach my $tmp_attr (@{ $datatype->{'attrs'} }) {
my $attr = $tmp_attr->{attr_name};
if ($attr eq $check_attr) {
my ($tab, $at) = split(/\./, $tmp_attr->{tabentry});
my $schema = xCAT::Table->getTableSchema($tab);
my $desc = $schema->{descriptions}->{$at};
push @{ $rsp->{data} }, "$check_attr => $desc";
}
}
}
xCAT::MsgUtils->message("I", $rsp, $::callback);
$checkedattrs{$attr_name} = 1;
if ($invalidattr->{$attr_name}->{valid} != 1) {
$invalidattr->{$attr_name}->{valid} = 0;
$invalidattr->{$attr_name}->{condition} = "\'$check_attr=$check_value\'";
}
next;
}
if (!($objhash{$objname}{$check_attr} =~ /\b$check_value\b/) && !($DBattrvals{$objname}{$check_attr} =~ /\b$check_value\b/)) {
if ($invalidattr->{$attr_name}->{valid} != 1) {
$invalidattr->{$attr_name}->{valid} = 0;
$invalidattr->{$attr_name}->{condition} = "\'$check_attr=$check_value\'";
}
next;
}
}
$invalidattr->{$attr_name}->{valid} = 1;
# get the info needed to write to the DB table
#
# get the actual attr name to use in the table
# - may be different then the attr name used for the object.
my $ntab;
($ntab, $::tabattr) = split('\.', $this_attr->{tabentry});
my $rc = xCAT::DBobjUtils->parse_access_tabentry($objname,
$this_attr->{access_tabentry}, \%tabentry);
if ($rc != 0) {
my $rsp;
$rsp->{data}->[0] =
"access_tabentry \'$this_attr->{access_tabentry}\' is not valid.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
next;
}
$lookup_table = $tabentry{'lookup_table'};
# Set the lookup criteria for this attribute into %allupdates
# the key is 'lookup_attrs'
foreach my $lookup_attr (keys %{ $tabentry{'lookup_attrs'} }) {
$allupdates{$lookup_table}{$objname}{$attr_name}{'lookup_attrs'}{$lookup_attr}
= $tabentry{'lookup_attrs'}{$lookup_attr};
}
} else {
next;
}
my $val;
my $delim = ',';
if (($type eq 'group') && ($DBattrvals{$objname}{'grouptype'} eq 'dynamic')) {
# dynamic node group selection string use "::" as delimiter
$delim = '::';
}
if ($::plus_option) {
# add new to existing - at the end - comma separated
if (defined($DBattrvals{$objname}{$attr_name})) {
# add the attr into the list if it's not already in the list!
# and avoid the duplicate values
my @DBattrarray = split(/$delim/, $DBattrvals{$objname}{$attr_name});
my @objhasharray = split(/$delim/, $objhash{$objname}{$attr_name});
foreach my $objattr (@objhasharray) {
if (!grep(/^\Q$objattr\E$/, @DBattrarray)) {
push @DBattrarray, $objattr;
}
}
$val = join($delim, @DBattrarray);
} else {
$val = "$objhash{$objname}{$attr_name}";
}
} elsif ($::minus_option) {
# remove the specified list of values from the current
# attr values.
if ($DBattrvals{$objname}{$attr_name}) {
# get the list of attrs to remove
my @currentList =
split(/$delim/, $DBattrvals{$objname}{$attr_name});
my @minusList = split(/$delim/, $objhash{$objname}{$attr_name});
foreach my $em (@minusList) {
if (!(grep { $_ eq $em } @currentList)) {
if (($::opt_t eq 'group') && ($DBattrvals{$objname}{'grouptype'} ne 'dynamic')) {
my $rsp;
$rsp->{data}->[0] = "$objname is not a member of \'$em\'.";
xCAT::MsgUtils->message("W", $rsp, $::callback);
} else {
my $rsp;
$rsp->{data}->[0] = "$em is not in the attribute of \'$attr_name\' for the \'$objname\' definition.";
xCAT::MsgUtils->message("W", $rsp, $::callback);
}
}
}
# make a new list without the one specified
my $first = 1;
my $newlist;
foreach my $i (@currentList) {
chomp $i;
if (!grep(/^\Q$i\E$/, @minusList)) {
# set new list for node
if (!$first) {
$newlist .= "$delim";
}
$newlist .= $i;
$first = 0;
}
}
$val = $newlist;
}
} else {
#just set the attr to what was provided! - replace
$val = $objhash{$objname}{$attr_name};
}
# Set the values into %allupdates
# the key is 'tabattrs'
$allupdates{$lookup_table}{$objname}{$attr_name}{'tabattrs'}{$::tabattr} = $val;
$setattrs = 1;
push(@setattrlist, $attr_name);
} # end - foreach attribute
my $rsp;
foreach my $att (keys %$invalidattr) {
if ($invalidattr->{$att}->{valid} != 1) {
my $tt = $invalidattr->{$att}->{valid};
push @{ $rsp->{data} }, "Cannot set the attr=\'$att\' attribute unless $invalidattr->{$att}->{condition}.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
}
}
# TODO - need to get back to this
if (0) {
#
# check to see if all the attrs got set
#
my @errlist;
foreach $a (@attrprovided) {
# is this attr was not set then add it to the error list
if (!grep(/^$a$/, @setattrlist)) {
push(@errlist, $a);
$ret = 2;
}
}
if ($ret == 2) {
my $rsp;
$rsp->{data}->[0] = "Could not set the following attributes for the \'$objname\' definition in the xCAT database: \'@errlist\'";
xCAT::MsgUtils->message("E", $rsp, $::callback);
}
}
} # end - foreach object
#==========================================================#
#%allupdates structure:
# for command: chdef -t node -o node1 groups=all
# usercomment=ddee passwd.HMC=HMC
# passwd.admin=cluster passwd.general=abc123
# the %allupdates will be:
#0 'ppcdirect'
#1 HASH(0x12783d30)
# 'node1' => HASH(0x12783cc4)
# 'passwd.HMC' => HASH(0x12783ed4)
# 'lookup_attrs' => HASH(0x12783f70)
# 'hcp' => 'node1'
# 'username' => 'HMC'
# 'tabattrs' => HASH(0x12783e8c)
# 'password' => 'HMC'
# 'passwd.admin' => HASH(0x12783c64)
# 'lookup_attrs' => HASH(0x12784000)
# 'hcp' => 'node1'
# 'username' => 'admin'
# 'tabattrs' => HASH(0x12783f64)
# 'password' => 'cluster'
# 'passwd.general' => HASH(0x12783a6c)
# 'lookup_attrs' => HASH(0x12784198)
# 'hcp' => 'node1'
# 'username' => 'general'
# 'tabattrs' => HASH(0x12783aa8)
# 'password' => 'abc123'
#2 'nodelist'
#3 HASH(0x127842b8)
# 'node1' => HASH(0x12784378)
# 'groups' => HASH(0x12784090)
# 'lookup_attrs' => HASH(0x127844bc)
# 'node' => 'node1'
# 'tabattrs' => HASH(0x1277fd34)
# 'groups' => 'all'
# 'usercomment' => HASH(0x12784318)
# 'lookup_attrs' => HASH(0x12780550)
# 'node' => 'node1'
# 'tabattrs' => HASH(0x127842f4)
# 'comments' => 'ddee'
#=================================================================#
# now set the attribute values in the tables
# - handles all except site, monitoring & monsetting for now
if ($setattrs) {
foreach my $table (keys %allupdates) {
# get the keys for this table
my $schema = xCAT::Table->getTableSchema($table);
my $keys = $schema->{keys};
# open the table
my $thistable = xCAT::Table->new($table, -create => 1, -autocommit => 0);
if (!$thistable) {
my $rsp;
$rsp->{data}->[0] = "Could not set the \'$thistable\' table.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
return 1;
}
# Special case for the postscripts table
# Does not set the postscripts to the postscripts table
# if the postscripts already in xcatdefaults
# for code logic, it will be clearer to put the special case into defch,
# but putting it into defch will introduce additional table access for postscripts table.
# accessing table is time consuming.
if ($table eq "postscripts") {
my $xcatdefaultsps;
my $xcatdefaultspbs;
my @TableRowArray = xCAT::DBobjUtils->getDBtable('postscripts');
if (@TableRowArray) {
foreach my $tablerow (@TableRowArray) {
if (($tablerow->{node} eq 'xcatdefaults') && !($tablerow->{disable})) {
$xcatdefaultsps = $tablerow->{postscripts};
$xcatdefaultspbs = $tablerow->{postbootscripts};
last;
}
}
}
my @xcatdefps = split(/,/, $xcatdefaultsps);
my @xcatdefpbs = split(/,/, $xcatdefaultspbs);
foreach my $obj (keys %{ $allupdates{$table} }) {
if ($obj eq 'xcatdefaults') {
#xcatdefaults can be treated as a node?
next;
}
my @newps;
if (defined($allupdates{$table}{$obj}{'postscripts'})
&& defined($allupdates{$table}{$obj}{'postscripts'}{'tabattrs'}{'postscripts'})) {
foreach my $tempps (split(/,/, $allupdates{$table}{$obj}{'postscripts'}{'tabattrs'}{'postscripts'})) {
if (grep(/^$tempps$/, @xcatdefps)) {
my $rsp;
$rsp->{data}->[0] = "$obj: postscripts \'$tempps\' is already included in the \'xcatdefaults\'.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
} else {
push @newps, $tempps;
}
}
$allupdates{$table}{$obj}{'postscripts'}{'tabattrs'}{'postscripts'} = join(',', @newps);
}
my @newpbs;
if (defined($allupdates{$table}{$obj}{'postbootscripts'})
&& defined($allupdates{$table}{$obj}{'postbootscripts'}{'tabattrs'}{'postbootscripts'})) {
foreach my $temppbs (split(/,/, $allupdates{$table}{$obj}{'postbootscripts'}{'tabattrs'}{'postbootscripts'})) {
if (grep(/^$temppbs$/, @xcatdefpbs)) {
my $rsp;
$rsp->{data}->[0] = "$obj: postbootscripts \'$temppbs\' is already included in the \'xcatdefaults\'.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
} else {
push @newpbs, $temppbs;
}
}
$allupdates{$table}{$obj}{'postbootscripts'}{'tabattrs'}{'postbootscripts'} = join(',', @newpbs);
}
}
}
my $commit_manually = 0;
my %node_updates;
OBJ: foreach my $obj (keys %{ $allupdates{$table} }) {
my %keyhash;
my %updates;
my $firsttime = 1;
ROW: foreach my $row (keys %{ $allupdates{$table}{$obj} }) {
# make sure we have a value for each key
foreach my $k (@$keys) {
if (!$allupdates{$table}{$obj}{$row}{'lookup_attrs'}) {
my $rsp;
$rsp->{data}->[0] = "\nMissing required attribute values for the \'$obj\' object. The required attributes are: @$keys";
xCAT::MsgUtils->message("E", $rsp, $::callback);
$ret = 1;
next ROW;
}
}
if ($firsttime) {
# lookup keys in %hashkey
# ex. $keyhash{'hcp'} = node1
foreach my $key (keys %{ $allupdates{$table}{$obj}{$row}{'lookup_attrs'} }) {
$keyhash{$key} = $allupdates{$table}{$obj}{$row}{'lookup_attrs'}{$key};
}
$firsttime = 0;
} else {
# check if the look_attrs is the same as the %keyhash
foreach my $key (keys %{ $allupdates{$table}{$obj}{$row}{'lookup_attrs'} }) {
# The lookup_attrs can be different for tables with more than one keys such as ppcdirect
if ((scalar(keys %keyhash) != scalar(keys %{ $allupdates{$table}{$obj}{$row}{'lookup_attrs'} }))
|| !defined($keyhash{$key})
|| ($keyhash{$key} ne $allupdates{$table}{$obj}{$row}{'lookup_attrs'}{$key})) {
# different keys, set the existing attributes into database
# update the %keyhash and clean up the %updates hash
if (%updates) {
$commit_manually = 1;
my ($rc, $str) = $thistable->setAttribs(\%keyhash, \%updates);
}
foreach my $key (keys %{ $allupdates{$table}{$obj}{$row}{'lookup_attrs'} }) {
$keyhash{$key} = $allupdates{$table}{$obj}{$row}{'lookup_attrs'}{$key};
}
%updates = ();
}
}
}
# set values in %updates
# ex. $updates{'groups'} = 'all,lpar'
foreach my $attr (keys %{ $allupdates{$table}{$obj}{$row}{'tabattrs'} }) {
if (scalar(keys %keyhash) == 0 && $keyhash{'node'} && $keyhash{'node'} eq "node") {
$node_updates{$obj}{$attr} = $allupdates{$table}{$obj}{$row}{'tabattrs'}{$attr};
} else {
$updates{$attr} = $allupdates{$table}{$obj}{$row}{'tabattrs'}{$attr};
}
}
} #end foreach my $row
# only uses the setAttribs to set attribute one by one when the obj type is NOT 'node'
if (%updates) {
$commit_manually = 1;
my ($rc, $str) = $thistable->setAttribs(\%keyhash, \%updates);
}
} #end foreach my $obj
if ($commit_manually) {
$thistable->commit;
}
if (%node_updates) {
$thistable->setNodesAttribs(\%node_updates);
}
} #end forach my $table
}
return $ret;
}
#----------------------------------------------------------------------------
=head3 rmobjdefs
Remove object definitions from the DB.
Arguments:
Returns:
0 - OK
1 - error
Globals:
Error:
Example:
To use create hash for object name and object type
ex. $objhash{$obj} = $type;
- then call as follows:
xCAT::DBobjUtils->rmobjdefs(\%objhash);
Comments:
=cut
#-----------------------------------------------------------------------------
sub rmobjdefs
{
my ($class, $hash_ref) = @_;
my %tablehash;
my %typehash = %$hash_ref;
# get the attr=vals for these objects so we know how to
# find what tables have to be modified
foreach my $objname (sort (keys %typehash)) {
my $type = $typehash{$objname};
# special handling for site table
if ($type eq 'site') {
my %DBattrvals = xCAT::DBobjUtils->getobjdefs(\%typehash);
my $thistable =
xCAT::Table->new('site', -create => 1, -autocommit => 0);
my %keyhash;
foreach my $attr (keys %{ $DBattrvals{$objname} }) {
# ex. key = attr
$keyhash{key} = $attr;
$thistable->delEntries(\%keyhash);
}
$thistable->commit();
next;
}
# get the object type decription from Schema.pm
my $datatype = $xCAT::Schema::defspec{$type};
# go through the list of valid attrs
# - need to delete the row with a $key value of $objname from $table
# - make a hash containing $delhash{$table}{$key}= $objname
foreach my $this_attr (@{ $datatype->{'attrs'} }) {
my $attr = $this_attr->{attr_name};
# get table lookup info from Schema.pm
# def commands need to support multiple keys in one table
# the subroutine parse_access_tabentry is used for supporting multiple keys
my %tabentry = ();
my $rc = xCAT::DBobjUtils->parse_access_tabentry($objname, $this_attr->{access_tabentry}, \%tabentry);
if ($rc != 0) {
my $rsp;
$rsp->{data}->[0] =
"access_tabentry \'$this_attr->{access_tabentry}\' is not valid.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
next;
}
# Only allow one table in the access_tabentry
# use multiple tables to look up tabentry does not make any sense
my $lookup_table = $tabentry{'lookup_table'};
# The attr_name is the *def attribute name instead of db column
my $attr_name = $this_attr->{'attr_name'};
# we'll need table name, object name, attribute name and the lookup entries
# put this info in a hash - we'll process it later - below
foreach my $lookup_attr (keys %{ $tabentry{'lookup_attrs'} }) {
$tablehash{$lookup_table}{$objname}{$attr_name}{$lookup_attr}
= $tabentry{'lookup_attrs'}{$lookup_attr};
}
}
}
#=============================================#
# The tablehash looks like this
# DB<5> x %tablehash
# 'bootparams'
# HASH(0x1280828c)
# 'node1' => HASH(0x127bca50)
# 'addkcmdline' => HASH(0x127fb114)
# 'node' => 'node1'
# 'initrd' => HASH(0x127bcb40)
# 'node' => 'node1'
# 'kcmdline' => HASH(0x127fb24c)
# 'node' => 'node1'
# 'kernel' => HASH(0x127b2e80)
# 'node' => 'node1'
# 'testfsp' => HASH(0x1280e71c)
# 'addkcmdline' => HASH(0x1280e7a0)
# 'node' => 'testfsp'
# 'initrd' => HASH(0x1280e740)
# 'node' => 'testfsp'
# 'kcmdline' => HASH(0x1280e77c)
# 'node' => 'testfsp'
# 'kernel' => HASH(0x1280e758)
# 'node' => 'testfsp'
#...
# 'ppcdirect'
# HASH(0x1278fe1c)
# 'node1' => HASH(0x12808370)
# 'passwd.HMC' => HASH(0x128083e8)
# 'hcp' => 'node1'
# 'username' => 'HMC'
# 'passwd.admin' => HASH(0x128081c0)
# 'hcp' => 'node1'
# 'username' => 'admin'
# 'passwd.general' => HASH(0x128075d8)
# 'hcp' => 'node1'
# 'username' => 'general'
# 'testfsp' => HASH(0x12790620)
# 'passwd.HMC' => HASH(0x1280ee84)
# 'hcp' => 'testfsp'
# 'username' => 'HMC'
# 'passwd.admin' => HASH(0x128082f8)
# 'hcp' => 'testfsp'
# 'username' => 'admin'
# 'passwd.general' => HASH(0x1280843c)
# 'hcp' => 'testfsp'
# 'username' => 'general'
#...
##=========================================================#
# now for each table - clear the entry
foreach my $table (keys %tablehash) {
my @all_keyhash;
my $thistable =
xCAT::Table->new($table, -create => 1, -autocommit => 0);
foreach my $obj (keys %{ $tablehash{$table} }) {
my %keyhash;
foreach my $attr (keys %{ $tablehash{$table}{$obj} }) {
foreach my $key (keys %{ $tablehash{$table}{$obj}{$attr} }) {
#multiple keys support
if (defined($keyhash{$key}) && ($keyhash{$key} ne $tablehash{$table}{$obj}{$attr}{$key})) {
my %tmpkeyhash;
# copy hash
foreach my $hashkey (keys %keyhash) {
$tmpkeyhash{$hashkey} = $keyhash{$hashkey};
}
push @all_keyhash, \%tmpkeyhash;
#$thistable->delEntries(\@all_keyhash);
}
# ex. $keyhash{node}=c68m3hvp01
$keyhash{$key} = $tablehash{$table}{$obj}{$attr}{$key};
}
}
push @all_keyhash, \%keyhash;
}
# ex. delete the c68m3hvp01 entry of the node column in the
# nodelist table
$thistable->delEntries(\@all_keyhash);
$thistable->commit();
}
return 0;
}
#----------------------------------------------------------------------------
=head3 readFileInput
Process the command line input piped in from a file.
(Support stanza or xml format.)
Arguments:
Returns:
0 - OK
1 - error
Globals:
Error:
Example:
Comments:
Set @::fileobjtypes, @::fileobjnames, %::FILEATTRS
(i.e.- $::FILEATTRS{objname}{attr}=val)
=cut
#-----------------------------------------------------------------------------
sub readFileInput
{
my ($class, $filedata) = @_;
my ($objectname, $junk1, $junk2);
@::fileobjnames = ();
my @lines = split /\n/, $filedata;
my $header = $lines[0];
# to do
#if ($header =~/<xCAT data object stanza file>/) {
# do stanza file parsing
# } elsis ($header =~/<xCAT data object XML file>/) {
# do XML parsing
#}
my $look_for_colon = 1; # start with first line that has a colon
my $objtype;
foreach my $l (@lines) {
# skip blank and comment lines
next if ($l =~ /^\s*$/ || $l =~ /^\s*#/);
# see if it's a stanza name
if (grep(/:\s*$/, $l)) {
$look_for_colon = 0; # ok - we have a colon
# Remove any trailing whitespace
$l =~ s/\s*$//;
# IPv6 network names could be something like fd59::/64
# Use all the characters before the last ":" as the object name
# .* means greedy regular expression
$l =~ /^(.*):(.*?)$/;
($objectname, $junk2) = ($1, $2);
# if $junk2 is defined or there's an =
if ($junk2 || grep(/=/, $objectname)) {
# error - invalid header $line in node definition file
# skipping to next node stanza
# Skipping to the next valid header.
$look_for_colon++;
next;
}
$objectname =~ s/^\s*//; # Remove any leading whitespace
$objectname =~ s/\s*$//; # Remove any trailing whitespace
# could have different default stanzas for different object types
if ($objectname =~ /default/) {
($junk1, $objtype) = split(/-/, $objectname);
if ($objtype) {
$objectname = 'default';
}
next;
}
push(@::fileobjnames, $objectname);
} elsif (($l =~ /^\s*(.*?)\s*=\s*(.*)\s*/) && (!$look_for_colon)) {
my $attr = $1;
my $val = $2;
$attr =~ s/^\s*//; # Remove any leading whitespace
$attr =~ s/\s*$//; # Remove any trailing whitespace
$val =~ s/^\s*//;
$val =~ s/\s*$//;
# remove spaces and quotes so createnode won't get upset
$val =~ s/^\s*"\s*//;
$val =~ s/\s*"\s*$//;
if ($objectname eq "default") {
# set the default for this attribute
$::defAttrs{$objtype}{$attr} = $val;
} else {
# set the value in the hash for this object
$::FILEATTRS{$objectname}{$attr} = $val;
# if the attr being set is "objtype" then check
# to see if we have any defaults set for this type
# the objtype should be the first etntry in each stanza
# so after we set the defaults they will be overwritten
# by any values that appear in the rest of the stanza
if ($attr eq 'objtype') {
push(@::fileobjtypes, $val);
# $val will be the object type ex. site, node etc.
foreach my $a (keys %{ $::defAttrs{$val} }) {
# set the default values for this object hash
$::FILEATTRS{$objectname}{$a} = $::defAttrs{$val}{$a};
}
}
}
} else {
# error - invalid line in node definition file
$look_for_colon++;
}
} # end while - go to next line
return 0;
}
#----------------------------------------------------------------------------
=head3 getGroupMembers
Get the list of members for the specified group.
Arguments:
Returns:
undef - error
$members - comma-separated list of group members
Globals:
Error:
Example:
To use:
- create hash for objectname and and attr values (need group
name (object), and grouptype & members attr values at a
minimum.)
ex. $objhash{$obj}{$attr} = value;
- then call as follows:
xCAT::DBobjUtils->getGroupMembers($objectname, \%objhash);
Comments:
=cut
#-----------------------------------------------------------------------------
sub getGroupMembers
{
my ($class, $objectname, $hash_ref) = @_;
my $members;
my %objhash = %$hash_ref;
# set 'static' as the dafault of nodetype
if (!defined($objhash{$objectname}{'grouptype'}) ||
$objhash{$objectname}{'grouptype'} eq "") {
$objhash{$objectname}{'grouptype'} = 'static';
}
if ($objhash{$objectname}{'grouptype'} eq 'static') {
my $table = "nodelist";
my @TableRowArray = xCAT::DBobjUtils->getDBtable($table);
my $first = 1;
foreach (@TableRowArray) {
# if find the group name in the "groups" attr value then add the
# node name to the member list
#if ($_->{'groups'} =~ /$objectname/)
my @nodeGroupList = split(',', $_->{'groups'});
if (grep(/^$objectname$/, @nodeGroupList)) {
chomp($_->{'node'});
if (!$first) {
$members .= ",";
}
$members .= $_->{'node'};
$first = 0;
}
}
} elsif ($objhash{$objectname}{'grouptype'} eq 'dynamic') {
# find all nodes that satisfy the criteria specified in "wherevals"
# value
my %whereHash;
my %tabhash;
# remove spaces and quotes so createnode won't get upset
#$val =~ s/^\s*"\s*//;
#$val =~ s/\s*"\s*$//;
my @tmpWhereList = split('::', $objhash{$objectname}{'wherevals'});
my $rc = xCAT::Utils->parse_selection_string(\@tmpWhereList, \%whereHash);
if ($rc != 0) {
my $rsp;
$rsp->{data}->[0] =
"The \'-w\' option has an incorrect attr*val pair.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
}
# see what nodes have these attr=values
# get a list of all nodes
my @tmplist = xCAT::DBobjUtils->getObjectsOfType('node');
# create a hash of obj names and types
my %tmphash;
foreach my $n (@tmplist) {
$tmphash{$n} = 'node';
}
# Only get the specific attributes of the node
my @whereattrs = keys %whereHash;
my %nodeattrhash = xCAT::DBobjUtils->getobjdefs(\%tmphash, 0, \@whereattrs);
# The attribute 'node' can be used as a key of selection string,
# however, the 'node' attribute is not included in the getobjdefs hash
foreach my $objname (keys %nodeattrhash) {
$nodeattrhash{$objname}{'node'} = $objname;
}
my $first = 1;
foreach my $objname (keys %nodeattrhash) {
if (xCAT::Utils->selection_string_match(\%nodeattrhash, $objname, \%whereHash)) {
chomp($objname);
if (!$first) {
$members .= ",";
}
$members .= $objname;
$first = 0;
}
}
}
return $members;
}
#----------------------------------------------------------------------------
=head3 getNetwkInfo
Get the network info from the database for a list of nodes.
Arguments:
Returns:
undef
hash ref - ex. $nethash{nodename}{networks attr name} = value
Globals:
Error:
Example:
%nethash = xCAT::DBobjUtils->getNetwkInfo(\@targetnodes);
Comments:
=cut
#-----------------------------------------------------------------------------
sub getNetwkInfo
{
my ($class, $ref_nodes) = @_;
my @nodelist = @$ref_nodes;
my %nethash;
my @attrnames;
# get the current list of network attrs (networks table columns)
my $datatype = $xCAT::Schema::defspec{'network'};
foreach my $a (@{ $datatype->{'attrs'} }) {
my $attr = $a->{attr_name};
push(@attrnames, $attr);
}
# read the networks table
my @TableRowArray = xCAT::DBobjUtils->getDBtable('networks');
if (!@TableRowArray) {
return undef;
}
# for each node - get the network info
foreach my $node (@nodelist) {
# get, check, split the node IP
my $IP = xCAT::NetworkUtils->getipaddr($node);
chomp $IP;
unless (($IP =~ /\d+\.\d+\.\d+\.\d+/) || ($IP =~ /:/)) {
next;
}
my ($ia, $ib, $ic, $id) = split('\.', $IP);
# check the entries of the networks table
# - if the bitwise AND of the IP and the netmask gives you
# the "net" name then that is the entry you want.
foreach (@TableRowArray) {
my $NM = $_->{'mask'};
my $net = $_->{'net'};
chomp $NM;
chomp $net;
if (xCAT::NetworkUtils->ishostinsubnet($IP, $NM, $net)) {
# fill in the hash -
foreach my $attr (@attrnames) {
if (defined($_->{$attr})) {
$nethash{$node}{$attr} = $_->{$attr};
}
}
if ($nethash{$node}{'gateway'} eq '<xcatmaster>') {
if (xCAT::NetworkUtils->ip_forwarding_enabled()) {
$nethash{$node}{'gateway'} = xCAT::NetworkUtils->my_ip_in_subnet($net, $NM);
} else {
$nethash{$node}{'gateway'} = '';
}
$nethash{$node}{'myselfgw'} = 1;
# For hwctrl commands, it is possible that this subroutine is called
# on MN instead of SN, if the hcp SN is not set
if (xCAT::Utils->isMN() && !$nethash{$node}{'gateway'}) {
# does not have ip address in this subnet,
# use the node attribute 'xcatmaster' or site.master
my @nodes = ("$node");
my $sn = xCAT::ServiceNodeUtils->get_ServiceNode(\@nodes, "xcat", "Node");
my $snkey = (keys %{$sn})[0];
my $gw = xCAT::NetworkUtils->getipaddr($snkey);
# two possible cases when this code is run:
# 1. flat cluster: ip forwarding is not enabled on MN
# 2. hw ctrl in hierarchy cluster, in which HCP SN is not set
# in either case, MN itself should not be the gateway
if (xCAT::NetworkUtils->thishostisnot($gw)) {
$nethash{$node}{'gateway'} = $gw;
}
}
}
next;
}
}
} #end - for each node
return %nethash;
}
#----------------------------------------------------------------------------
=head3 parse_access_tabentry
Parse the access_tabentry field in Schema.pm.
We needs to support multiple keys in the table
Arguments:
$objname: objectname=>objtype hash
$access_tabentry: the access_tabentry defined in Schema.pm
$tabentry_ref: return the parsed result through this hash ref
The structure of the hash is:
{
'lookup_tables' => <table_name>
'lookup_attrs' =>
{
'attr1' => 'val1'
'attr2' => 'val2'
...
}
}
Returns:
0 - success
1 - failed
Globals:
Error:
Example:
To parse the access_tabentry field
my $rc = xCAT::DBobjUtils->parse_access_tabentry($objname, $this_attr->{access_tabentry}, \%tabentry);
Comments:
=cut
#-----------------------------------------------------------------------------
sub parse_access_tabentry()
{
my ($class, $objname, $access_tabentry, $tabentry_ref) = @_;
# ex. 'nodelist.node', 'attr:node'
foreach my $ent (split('::', $access_tabentry)) {
# ex. 'nodelist.node', 'attr:node'
my ($lookup_key, $lookup_value) = split('\=', $ent);
# ex. 'nodelist', 'node'
my ($lookup_table, $lookup_attr) = split('\.', $lookup_key);
# ex. 'attr', 'node'
my ($lookup_type, $lookup_data) = split('\:', $lookup_value);
if (!defined($tabentry_ref->{'lookup_table'})) {
$tabentry_ref->{'lookup_table'} = $lookup_table;
}
# Only support one lookup table in the access_tabentry
# Do we need to support multiple tables in one access_tabentry ????
# has not seen any requirement...
if ($lookup_table ne $tabentry_ref->{'lookup_table'}) {
my $rsp;
$rsp->{data}->[0] =
"The access_tabentry \"$access_tabentry\" is not valid, can not specify more than one tables to look up.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
return 1;
}
if ($lookup_type eq 'attr') {
# TODO: may need to update in the future
# for now, the "val" in attr:val in
# Schema.pm can only be the object name
# In the future, even if we need to change here,
# be caution about the performance
# looking up table is time consuming
$tabentry_ref->{'lookup_attrs'}->{$lookup_attr} = $objname;
} elsif ($lookup_type eq 'str') {
$tabentry_ref->{'lookup_attrs'}->{$lookup_attr} = $lookup_data;
} else {
my $rsp;
$rsp->{data}->[0] =
"The access_tabentry \"$access_tabentry\" is not valid, the lookup type can only be 'attr' or 'str'.";
xCAT::MsgUtils->message("E", $rsp, $::callback);
return 1;
}
}
return 0;
}
#-------------------------------------------------------------------------------
=head3 getchildren
Arguments:
single hostname
optional port number
Returns:
array of fsp/bpa hostnames (IPs)
if specified port, it will just return nodes within the port.
Globals:
%PPCHASH - HASH of nodename -> array of ip addresses
where the nodetype is fsp or bpa
Error:
$::RUNCMD_RC = 1;
Writes error to syslog
Example:
$c1 = getchildren($nodetocheck);
$c1 = getchildren($nodetocheck,$port);
Comments:
none
=cut
#-------------------------------------------------------------------------------
my %PPCHASH;
sub getchildren
{
my $parent = shift;
if (($parent) && ($parent =~ /xCAT::/)) {
$parent = shift;
}
$::RUNCMD_RC = 0;
my $port = shift;
my @tabletype = qw(ppc zvm);
my @children = ();
my @children_port = ();
if (!%PPCHASH) {
my $ppctab = xCAT::Table->new('ppc');
unless ($ppctab) { # cannot open the table return with error
xCAT::MsgUtils->message('S', "getchildren:Unable to open ppc table.\n");
$::RUNCMD_RC = 1;
return undef;
}
my @ps = $ppctab->getAllNodeAttribs([ 'node', 'parent', 'nodetype', 'hcp' ]);
foreach my $entry (@ps) {
my $p = $entry->{parent};
my $c = $entry->{node};
my $t = $entry->{nodetype};
if ($p and $c) {
if ($t) { # the nodetype exists in the ppc table, use it
if ($t eq 'fsp' or $t eq 'bpa') {
# build hash of ppc.parent -> ppc.node
push @{ $PPCHASH{$p} }, $c;
} elsif ($t eq 'blade') {
push @{ $PPCHASH{$c} }, $entry->{hcp};
}
} else { # go look in the nodetype table to find nodetype
my $type = getnodetype($c, "ppc");
if ($type eq 'fsp' or $type eq 'bpa') {
# build hash of ppc.parent -> ppc.node
push @{ $PPCHASH{$p} }, $c;
} elsif ($type eq "blade") {
push @{ $PPCHASH{$c} }, $entry->{hcp};
}
}
} # not $p and $c
}
# Find parent in the hash and build return values
foreach (@{ $PPCHASH{$parent} }) {
push @children, $_;
}
} else {
if (exists($PPCHASH{$parent})) {
foreach (@{ $PPCHASH{$parent} }) {
push @children, $_;
}
}
}
# if port not input
if (!defined($port)) {
return \@children;
} else {
if (@children) {
my $vpdtab = xCAT::Table->new('vpd');
unless ($vpdtab) { # cannot open the table return with error
xCAT::MsgUtils->message('S', "getchildren:Unable to open vpd table.\n");
$::RUNCMD_RC = 1;
return undef;
}
my $sides = $vpdtab->getNodesAttribs(\@children, ['side']);
if (!$sides) {
return undef;
}
foreach my $n (@children) {
my $nside = $sides->{$n}->[0];
if ($nside->{side} =~ /$port/) {
push @children_port, $n;
}
}
return \@children_port;
} else { # no children
return undef;
}
}
}
#-------------------------------------------------------------------------------
=head3 getnodetype
Query ppc table, if no type found query nodetype table
Arguments:
An array of nodenames or 1 nodename
Returns:
If the input is an array, it returns a hash,
for the nodes that can't get node type, it will be 'node' => undef;
If the input is not an array, it returns the value of type,
for the node that can't get node type, it will be undef;
Globals:
%NODETYPEHASH
Error:
$::RUNCMD_RC = 1;
Errors written to syslog
Example:
$type = xCAT::DBobjUtils->getnodetype($node, "ppc");
$type = xCAT::DBobjUtils->getnodetype($node);
$typerefer = xCAT::DBobjUtils->getnodetype(\@nodes, "PPC");
$typerefer = xCAT::DBobjUtils->getnodetype(\@nodes);
Comments:
none
=cut
#-------------------------------------------------------------------------------
my %NODETYPEHASH;
sub getnodetype
{
my $nodes = shift;
if (($nodes) && ($nodes =~ /xCAT::/)) {
$nodes = shift;
}
my $table = shift;
my $rsp;
my @tabletype = qw(ppc zvm);
my %typehash;
my %tablehash;
$::RUNCMD_RC = 0;
my @failnodes;
my @failnodes1;
######################################################################
# if the table arg is set, go to the specified table first
# if can't get anything from the specified table, go to nodetype table
######################################################################
if ($table) {
my $nodetypetab = xCAT::Table->new($table);
unless ($nodetypetab) {
xCAT::MsgUtils->message('S', "getnodetype:Unable to open $table table.\n");
$::RUNCMD_RC = 1;
if ($nodes =~ /^ARRAY/) {
foreach my $tn (@$nodes) {
$typehash{$tn} = undef;
}
} else {
$typehash{$nodes} = undef;
}
return \%typehash;
}
############################################
# if the input node arg is an array,
# query table and don't use the global hash
############################################
if ($nodes =~ /^ARRAY/) {
my $nodetypes = $nodetypetab->getNodesAttribs($nodes, ['nodetype']);
foreach my $tn (@$nodes) {
my $gottype = $nodetypes->{$tn}->[0]->{'nodetype'};
if ($gottype) {
$NODETYPEHASH{$tn} = $gottype;
$typehash{$tn} = $gottype;
} else {
push @failnodes, $tn;
}
}
################################################
# for the failed nodes, go to nodetype table
################################################
if (@failnodes) {
my $typetable = xCAT::Table->new('nodetype');
unless ($typetable) { # cannot open the table return with error
xCAT::MsgUtils->message('S', "getnodetype:Unable to open nodetype table.\n");
$::RUNCMD_RC = 1;
foreach my $tn (@failnodes) {
$typehash{$tn} = undef;
}
} else {
my $nodetypes = $nodetypetab->getNodesAttribs(\@failnodes, ['nodetype']);
foreach my $tn (@failnodes) {
if ($nodetypes->{$tn}->[0]) {
$NODETYPEHASH{$tn} = $nodetypes->{$tn}->[0]->{'nodetype'};
$typehash{$tn} = $nodetypes->{$tn}->[0]->{'nodetype'};
} else {
push @failnodes1, $tn;
$typehash{$tn} = undef;
}
##################################################
# give error msg for the nodes can't get nodetype
##################################################
}
if (@failnodes1) {
my $nodelist = join(",", @failnodes1);
xCAT::MsgUtils->message('S', "getnodetype:Can't find these nodes' type: $nodelist.\n");
}
}
}
#####################
# return the result
#####################
return \%typehash;
} else {
############################################
# if the input node arg is not an array,
# query table and use the global hash first
############################################
if ($NODETYPEHASH{$nodes}) {
return $NODETYPEHASH{$nodes};
} else {
my $typep = $nodetypetab->getNodeAttribs($nodes, ['nodetype']);
if ($typep->{nodetype}) {
$NODETYPEHASH{$nodes} = $typep->{nodetype};
return $typep->{nodetype};
} else { #if not find in the specified table, go to nodetype table
my $typetable = xCAT::Table->new('nodetype');
unless ($typetable) { # cannot open the table return with error
xCAT::MsgUtils->message('S', "getnodetype:Unable to open nodetype table.\n");
$::RUNCMD_RC = 1;
return undef;
}
my $typep = $typetable->getNodeAttribs($nodes, ['nodetype']);
if ($typep->{nodetype}) {
$NODETYPEHASH{$nodes} = $typep->{nodetype};
return $typep->{nodetype};
} else {
return undef;
}
}
}
}
} else {
######################################################################
# if the table arg is not set, go to the nodetype table first
# if can't get anything from the specified table, go to nodetype table
######################################################################
my $nodetypetab = xCAT::Table->new('nodetype');
unless ($nodetypetab) {
xCAT::MsgUtils->message('S', "getnodetype:Unable to open $table table.\n");
$::RUNCMD_RC = 1;
if ($nodes =~ /^ARRAY/) {
foreach my $tn (@$nodes) {
$typehash{$tn} = undef;
}
} else {
$typehash{$nodes} = undef;
}
return \%typehash;
}
############################################
# if the input node arg is an array,
# query table and don't use the global hash
############################################
if ($nodes =~ /^ARRAY/) {
my $nodetypes = $nodetypetab->getNodesAttribs($nodes, ['nodetype']);
foreach my $tn (@$nodes) {
my $gottype = $nodetypes->{$tn}->[0]->{'nodetype'};
if ($gottype) {
if ($gottype =~ /,/) { #if find ppc,osi
my @tbty = split /,/, $gottype;
foreach my $ttable (@tbty) {
if (grep(/$ttable/, @tabletype)) {
$tablehash{$tn} = $ttable;
last;
}
}
} elsif (grep(/$gottype/, @tabletype)) { #if find ppc or zvm
$tablehash{$tn} = $gottype;
} else {
$NODETYPEHASH{$tn} = $gottype;
$typehash{$tn} = $gottype;
}
} else {
$typehash{$tn} = undef;
}
}
################################################
# for the failed nodes, go to related tables
################################################
if (%tablehash) {
foreach my $ttable (@tabletype) {
my @nodegroup;
foreach my $fnode (keys %tablehash) {
if ($tablehash{$fnode} eq $ttable) {
push @nodegroup, $fnode;
}
}
next unless (@nodegroup);
my $typetable = xCAT::Table->new($ttable);
unless ($typetable) {
my $failnodes = join(",", @nodegroup);
xCAT::MsgUtils->message('S', "getnodetype:Unable to open $ttable table, can't find $failnodes type.\n");
$::RUNCMD_RC = 1;
foreach (@nodegroup) {
$typehash{$_} = undef;
}
} else {
my $typep = $typetable->getNodesAttribs(\@nodegroup, ['nodetype']);
foreach my $fn (@nodegroup) {
if ($typep->{$fn}->[0]->{'nodetype'}) {
$typehash{$fn} = $typep->{$fn}->[0]->{'nodetype'};
$NODETYPEHASH{$fn} = $typep->{$fn}->[0]->{'nodetype'};
} else {
$typehash{$fn} = undef;
}
}
}
}
}
return \%typehash;
} else { # if not an array
if ($NODETYPEHASH{$nodes}) {
return $NODETYPEHASH{$nodes};
} else {
my $typep = $nodetypetab->getNodeAttribs($nodes, ["nodetype"]);
if ($typep->{nodetype} and !(grep(/$typep->{nodetype}/, @tabletype))) {
$NODETYPEHASH{$nodes} = $typep->{nodetype};
return $typep->{nodetype};
} elsif (grep(/$typep->{nodetype}/, @tabletype)) {
my $typetable = xCAT::Table->new($typep->{nodetype});
unless ($typetable) {
xCAT::MsgUtils->message('S', "getnodetype:Unable to open nodetype table.\n");
$::RUNCMD_RC = 1;
return undef;
}
my $typep = $typetable->getNodeAttribs($nodes, ["nodetype"]);
if ($typep->{nodetype}) {
$NODETYPEHASH{$nodes} = $typep->{nodetype};
return $typep->{nodetype};
} else {
return undef;
}
}
}
}
}
}
#-------------------------------------------------------------------------------
=head3 getcecchildren
returns cec of the specified frame,
Arguments:
frame name
Returns:
Array of cec hostnames
Globals:
%PARENT_CHILDREN_CEC
Error:
none
Example:
@frame_members = getcecchildren($frame);
Comments:
none
=cut
#-------------------------------------------------------------------------------
my %PARENT_CHILDREN_CEC;
sub getcecchildren
{
my $parent = shift;
if (($parent) && ($parent =~ /xCAT::/)) {
$parent = shift;
}
my @children = ();
if (!%PARENT_CHILDREN_CEC) {
my $ppctab = xCAT::Table->new('ppc');
unless ($ppctab) { # cannot open the table return with error
xCAT::MsgUtils->message('S', "getcecchildren:Unable to open ppc table.\n");
$::RUNCMD_RC = 1;
return undef;
}
if ($ppctab) {
my @ps = $ppctab->getAllNodeAttribs([ 'node', 'parent', 'nodetype' ]);
foreach my $entry (@ps) {
my $p = $entry->{parent};
my $c = $entry->{node};
my $t = $entry->{nodetype};
if ($p and $c) {
if ($t) { # the nodetype exists in the ppc table, use it
if ($t eq 'cec') {
# build hash of ppc.parent -> ppc.node
push @{ $PARENT_CHILDREN_CEC{$p} }, $c;
}
} else { # go look in the nodetype table to find nodetype
my $type = getnodetype($c);
if ($type eq 'cec') {
push @{ $PARENT_CHILDREN_CEC{$p} }, $c;
}
}
}
}
# find a match for the parent and build the return array
foreach (@{ $PARENT_CHILDREN_CEC{$parent} }) {
push @children, $_;
}
return \@children;
}
} else { # already built the HASH
if (exists($PARENT_CHILDREN_CEC{$parent})) {
foreach (@{ $PARENT_CHILDREN_CEC{$parent} }) {
push @children, $_;
}
return \@children;
}
}
return undef;
}
#-------------------------------------------------------------------------------
=head3 judge_node
judge the node is a real FSP/BPA,
use to distinguish if the data is defined in xCAT 2.5 or later
Arguments:
node name
Returns:
0 is a fake FSP/BPA,defined in xCAT 2.5
1 is a real FSP/BPA,defined in xCAT 2.6 or later
Error:
none
Example:
$result = judge_node($nodetocheck);
Comments:
none
=cut
#-------------------------------------------------------------------------------
sub judge_node
{
my $node = shift;
if (($node) && ($node =~ /xCAT::/)) {
$node = shift;
}
my $type = shift;
my $flag = 0;
my $parenttype;
my $nodeparent;
my $ppctab = xCAT::Table->new('ppc');
if ($ppctab) {
$nodeparent = $ppctab->getNodeAttribs($node, ["parent"]);
if ($nodeparent and $nodeparent->{parent}) {
$parenttype = getnodetype($nodeparent->{parent});
}
}
if ($type =~ /^fsp$/) {
if ($parenttype =~ /^cec$/) {
$flag = 1;
} else {
$flag = 0;
}
}
if ($type =~ /^bpa$/) {
if ($parenttype =~ /^frame$/) {
$flag = 1;
} else {
$flag = 0;
}
}
return $flag;
}
#-------------------------------------------------------------------------------
=head3 expandnicsattr
Expand the nics related attributes into the readable format,
for example, the nicsips=eth0!1.1.1.1|2.1.1.1,eth1!3.1.1.1|4.1.1.1
expanded format:
nicsips.eth0=1.1.1.1|2.1.1.1
nicsips.eth1=3.1.1.1|4.1.1.1
Arguments:
nicsattr value, like niccsips=eth0!1.1.1.1|2.1.1.1,eth1!3.1.1.1|4.1.1.1
node name, like frame10node10
nicnames: only return the value for specific nics, like "eth0,eth1"
Returns:
expanded format, like:
nicsips.eth0=1.1.1.1|2.1.1.1
nicsips.eth1=3.1.1.1|4.1.1.1
Error:
none
Example:
my $nicsstr = xCAT::DBobjUtils->expandnicsattr($attrval);
Comments:
none
=cut
#-------------------------------------------------------------------------------
sub expandnicsattr()
{
my $nicstr = shift;
if (($nicstr) && ($nicstr =~ /xCAT::/)) {
$nicstr = shift;
}
my $node = shift;
my $nicnames = shift;
my $ret;
$nicstr =~ /^(.*?)=(.*?)$/;
#Attribute: nicips, nichostnamesuffix, etc.
my $nicattr = $1;
# Value: eth0!1.1.1.1|2.1.1.1,eth1!3.1.1.1|4.1.1.1
my $nicval = $2;
#if there is regrex in nicips
if (($nicval) && ($nicval =~ /^\|(.*?)\|$/)) {
#$nicval Value: |node(d+)|eth0!192.1.1.($1+10)| or
# |eth0!192.1.1.($1+10),bond0!10.28.41.($1+10)|
#In the lsdef, remove the ^| and |$ before displaying
$nicval =~ s/(^\||\|$)//g;
#$nicval Value: node(d+)|eth0!192.1.1.($1+10)
if (($nicval) && ($nicval =~ /\|/)) {
my ($str1, $str2) = split('\|', $nicval);
#$nivval Value: eth0!192.1.1.($1+10)
$nicval = $str2;
}
}
# $nicarr[0]: eth0!1.1.1.1|2.1.1.1
# $nicarr[1]: eth1!3.1.1.1|4.1.1.1
my @nicarr = split(/,/, $nicval);
foreach my $nicentry (@nicarr) {
#nicentry: eth0!1.1.1.1|2.1.1.1
# $nicv[0]: eth0
# $nicv[1]: 1.1.1.1|2.1.1.1
my @nicv = split(/!/, $nicentry);
# only return nic* attr for these specific nics
if ($nicnames) {
my @nics = split(/,/, $nicnames);
if ($nicv[0]) {
# Do not need to return the nic attr for this nic
if (!grep(/^$nicv[0]$/, @nics)) {
next;
}
}
}
$nicv[1]= xCAT::Table::transRegexAttrs($node, $nicv[1]);
# ignore the line that does not have nicname or value
if ($nicv[0] && $nicv[1]) {
$ret .= " $nicattr.$nicv[0]=$nicv[1]\n";
}
}
chomp($ret);
return $ret;
}
#-------------------------------------------------------------------------------
=head3 collapsenicsattr
Collapse the nics related attributes into the database format,
for example,
nicsips.eth0=1.1.1.1|2.1.1.1
nicsips.eth1=3.1.1.1|4.1.1.1
the collapsed format:
nicsips=eth0!1.1.1.1|2.1.1.1,eth1!3.1.1.1|4.1.1.1
The collapse will be done against the hash %::FILEATTRS or %::CLIATTRS,
remove the nicips.thx attributes from %::FILEATTRS or %::CLIATTRS,
add the collapsed info nicips into %::FILEATTRS or %::CLIATTRS.
Arguments:
$::FILEATTRS{$objname} or $::CLIATTRS{$objname}
$objname
Returns:
None, update %::FILEATTRS or %::CLIATTRS directly
Error:
none
Example:
xCAT::DBobjUtils->collapsenicsattr($nodeattrhash);
Comments:
none
=cut
#-------------------------------------------------------------------------------
sub collapsenicsattr()
{
my $nodeattrhash = shift;
if (($nodeattrhash) && ($nodeattrhash =~ /xCAT::/)) {
$nodeattrhash = shift;
}
my $objname = shift;
my %nicattrs = ();
foreach my $nodeattr (keys %{$nodeattrhash}) {
# e.g nicips.eth0
# do not need to handle nic attributes without the postfix .ethx,
# it will be overwritten by the attributes with the postfix .ethx,
if ($nodeattr =~ /^(nic\w+)\.(.*)$/) {
if ($1 && $2) {
# chdef <noderange> nicips.eth2= to remove the definition for eth2
# in this case, the $nodeattrhash->{'nicips.eth0'} is blank
# $nicattrs{nicips}{eth0} = "1.1.1.1|1.2.1.1"
$nicattrs{$1}{$2} = $nodeattrhash->{$nodeattr};
# remove nicips.eth0 from the %::FILEATTRS
delete $nodeattrhash->{$nodeattr};
}
}
}
# $nicattrs{'nicips'}{'eth0'} = "1.1.1.1|1.2.1.1"
# $nicattrs{'nicips'}{'eth1'} = "2.1.1.1|2.2.1.1"
foreach my $nicattr (keys %nicattrs) {
my @tmparray = ();
foreach my $nicname (keys %{ $nicattrs{$nicattr} }) {
# eth0!1.1.1.1|1.2.1.1
if ($nicattrs{$nicattr}{$nicname}) {
push @tmparray, "$nicname!$nicattrs{$nicattr}{$nicname}";
}
}
# eth0!1.1.1.1|1.2.1.1,eth1!2.1.1.1|2.2.1.1
$nodeattrhash->{$nicattr} = join(',', @tmparray);
}
}
1;