#!/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_c) { @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 my $thistable = xCAT::Table->new($table, -create => 1, -autocommit => 0); 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 if ( ($lookup_attr eq 'node') && ($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_c) { # already have this @rows = @{$::TableHash{$table}}; } else { # need to get info from DB my $thistable = xCAT::Table->new($table, -create => 1, -autocommit => 0); 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} ne 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} ne 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} ne 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 =~//) { # do stanza file parsing # } elsis ($header =~//) { # 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 ($objectname, $junk2) = split(/:/, $l); # 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 '') { 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]; $nethash{$node}{'gateway'} = xCAT::NetworkUtils->getipaddr($snkey); } } 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' => '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']); 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; } } else { # go look in the nodetype table to find nodetype my $type = getnodetype($c); if ( $type eq 'fsp' or $type eq 'bpa') { # build hash of ppc.parent -> ppc.node push @{$PPCHASH{$p}}, $c; } } } # 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 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 $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; # $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; } } } # 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+)\.(\w+)$/) { if ($1 && $2) { # $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 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;