diff --git a/perl-xCAT/xCAT/NetworkUtils.pm b/perl-xCAT/xCAT/NetworkUtils.pm index ba85bd59a..8f98fc39b 100755 --- a/perl-xCAT/xCAT/NetworkUtils.pm +++ b/perl-xCAT/xCAT/NetworkUtils.pm @@ -2080,4 +2080,179 @@ sub pingNodeStatus { return %status; } +#------------------------------------------------------------------------------- + +=head3 isReservedIP + Description : Validate whether specified string is a reseved IPv4 string. + Arguments : ipstr - the string to be validated. + Returns : 1 - valid reserved String. + 0 - invalid reserved String. +=cut + +#------------------------------------------------------------------------------- +sub isReservedIP +{ + my ($class, $ipstr) = @_; + my @ipnums = split('\.', $ipstr); + if ($ipnums[3] eq "0" || $ipnums[3] eq "255"){ + return 1; + } + return 0; +} + +#------------------------------------------------------------------------------- + +=head3 isValidMAC + Description : Validate whether specified string is a MAC string. + Arguments : macstr - the string to be validated. + Returns : 1 - valid MAC String. + 0 - invalid MAC String. +=cut + +#------------------------------------------------------------------------------- +sub isValidMAC +{ + my ($class, $macstr) = @_; + if ($macstr =~ /^[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}$/){ + return 1; + } + return 0; +} + +#------------------------------------------------------------------------------- + +=head3 isValidHostname + Description : Validate whether specified string is a valid hostname. + Arguments : hostname - the string to be validated. + Returns : 1 - valid hostname String. + 0 - invalid hostname String. +=cut + +#------------------------------------------------------------------------------- +sub isValidHostname +{ + my ($class, $hostname) = @_; + if ($hostname =~ /^[\-a-zA-Z0-9]+$/){ + return 1; + } + return 0; +} + +#------------------------------------------------------------------------------- + +=head3 ip_to_int + Description : convert an IPv4 string into int. + Arguments : ipstr - the IPv4 string. + Returns : ipint - int number +=cut + +#------------------------------------------------------------------------------- +sub ip_to_int +{ + my ($class, $ipstr) = @_; + my $ipint = 0; + my @ipnums = split('\.', $ipstr); + $ipint += $ipnums[0] << 24; + $ipint += $ipnums[1] << 16; + $ipint += $ipnums[2] << 8; + $ipint += $ipnums[3]; + return $ipint; +} + +#------------------------------------------------------------------------------- + +=head3 int_to_ip + Description : convert an int into IPv4 String. + Arguments : ipnit - the input int number. + Returns : ipstr - IPv4 String. +=cut + +#------------------------------------------------------------------------------- +sub int_to_ip +{ + my ($class, $ipint) = @_; + return inet_ntoa(inet_aton($ipint)); +} + +#------------------------------------------------------------------------------- + +=head3 get_allips_in_range + Description : Get all IPs in a IP range, return in a list. + Arguments : $startip - start IP address + $endip - end IP address + $increment - increment factor + $reservflag - A flag for whether we exclude reserved ips or not + Returns : IP list in this range. + Example : + my $startip = "192.168.0.1"; + my $endip = "192.168.0.100"; + xCAT::NetworkUtils->get_allips_in_range($startip, $endip, 1, 1); +=cut + +#------------------------------------------------------------------------------- +sub get_allips_in_range +{ + my $class = shift; + my $startip = shift; + my $endip = shift; + my $increment = shift; + my $reservflag = shift; + my @iplist; + + my $startipnum = xCAT::NetworkUtils->ip_to_int($startip); + my $endipnum = xCAT::NetworkUtils->ip_to_int($endip); + while ($startipnum <= $endipnum){ + my $ip = xCAT::NetworkUtils->int_to_ip($startipnum); + $startipnum += $increment; + # Not return reserved IPs + if ($reservflag){ + if(xCAT::NetworkUtils->isReservedIP($ip)){ + next; + } + } + push (@iplist, $ip); + } + return \@iplist; +} + +#------------------------------------------------------------------------------- + +=head3 get_all_ips + Description : Get all IP addresses from table nics, column nicips. + Arguments : hashref - if not set, will return a reference of list, + if set, will return a reference of hash. + Returns : All IPs reference. +=cut + +#------------------------------------------------------------------------------- +sub get_all_nicips{ + my ($class, $hashref) = @_; + my %allipshash; + my @allipslist; + + my $table = xCAT::Table->new('nics'); + my @entries = $table->getAllNodeAttribs(['nicips']); + foreach (@entries){ + # $_->{nicips} looks like "eth0:ip1,eth1:ip2,bmc:ip3..." + if($_->{nicips}){ + my @nicandiplist = split(',', $_->{nicips}); + # Each record in @nicandiplist looks like "eth0:ip1" + foreach (@nicandiplist){ + my @nicandip = split(':', $_); + if ($hashref){ + $allipshash{$nicandip[1]} = 0; + } else{ + push (@allipslist, $nicandip[1]); + } + } + } + } + if ($hashref){ + return \%allipshash; + } else{ + return \@allipslist; + } +} + + 1; diff --git a/perl-xCAT/xCAT/PCMNodeMgmtUtils.pm b/perl-xCAT/xCAT/PCMNodeMgmtUtils.pm new file mode 100644 index 000000000..909e6419b --- /dev/null +++ b/perl-xCAT/xCAT/PCMNodeMgmtUtils.pm @@ -0,0 +1,527 @@ +# IBM(c) 2012 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT::PCMNodeMgmtUtils; + +use strict; +use warnings; +use Socket; +use File::Path qw/mkpath/; +use File::Temp qw/tempfile/; +require xCAT::Table; +require xCAT::TableUtils; +require xCAT::NodeRange; +require xCAT::NetworkUtils; + + +#-------------------------------------------------------------------------------- + +=head1 xCAT::PCMNodeMgmtkUtils + +=head2 Package Description + +This program module file, is a set of PCM node management utilities. + +=cut + +#------------------------------------------------------------------------------- + +#------------------------------------------------------------------------------- + +=head3 get_allocable_staticips_innet + Description : Get allocable IPs from a network. + Arguments : $netname - network name + $exclude_ips_ref - excluded IPs list reference. + Returns : Reference of allocable IPs list +=cut + +#------------------------------------------------------------------------------- +sub get_allocable_staticips_innet +{ + my $class = shift; + my $netname = shift; + my $exclude_ips_ref = shift; + my %iphash; + my @allocableips; + + foreach (@$exclude_ips_ref){ + $iphash{$_} = 0; + } + + my $networkstab = xCAT::Table->new('networks'); + my $netentry = ($networkstab->getAllAttribsWhere("netname = '$netname'", 'ALL'))[0]; + my ($startip, $endip) = split('-', $netentry->{'staticrange'}); + my $incremental = $netentry->{'staticrangeincrement'}; + my $validipsref; + if ($incremental and $startip and $endip){ + $validipsref = xCAT::NetworkUtils->get_allips_in_range($startip, $endip, $incremental); + } + foreach (@$validipsref){ + if (! exists($iphash{$_})){ + push @allocableips, $_; + } + } + return \@allocableips; +} + +#------------------------------------------------------------------------------- + +=head3 genhosts_with_numric_tmpl + Description : Generate numric hostnames using numric template name. + Arguments : $format - The hostname format string.. + Returns : numric hostname list + Example : + calling genhosts_with_numric_tmpl("compute#NNnode") will return a list like: + ("compute00node", "compute01node", ..."compute98node", "compute99node") +=cut + +#------------------------------------------------------------------------------- +sub genhosts_with_numric_tmpl +{ + my ($class, $format) = @_; + + my ($prefix, $appendix, $len) = xCAT::PCMNodeMgmtUtils->split_hostname($format, 'N'); + return xCAT::PCMNodeMgmtUtils->gen_numric_hostnames($prefix, $appendix, $len); +} + +#------------------------------------------------------------------------------- + +=head3 split_hostname + Description : Split hostname format as prefix, appendix and number length. + Arguments : $format - hostname format + $patt_char - pattern char, we always use "N" to indicate numric pattern + Returns : ($prefix, $appendix, $numlen) + $prefix - the prefix string of hostname format. + $appendix - the appendix string of hostname format + $numlen - The number length in hostname format. + Example : + calling split_hostname("compute#NNnode") will return a list like: + ("compute", "node", 2) +=cut + +#------------------------------------------------------------------------------- +sub split_hostname +{ + my ($class, $format, $patt_char) = @_; + + my $idx = index $format, "#$patt_char"; + my @array_format = split(//, $format); + my $pos = $idx+2; + while ( $pos <= (scalar(@array_format) - 1)){ + if ($array_format[$pos] eq "$patt_char"){ + $pos++; + }else{ + last; + } + } + my $ridx = $pos - 1; + + my $prefix = ""; + $prefix = substr $format, 0, $idx; + my $appendix = ""; + if (($ridx + 1) != scalar(@array_format)){ + $appendix = substr $format, $ridx + 1; + } + return $prefix, $appendix, ($ridx - $idx); +} + +#------------------------------------------------------------------------------- + +=head3 gen_numric_hostnames + Description : Generate numric hostnames. + Arguments : $prefix - The prefix string of the hostname. + $appendix - The appendix string of the hostname. + $len - the numric number length in hostname. + Returns : numric hostname list + Example : + calling gen_numric_hostnames("compute", "node",2) will return a list like: + ("compute00node", "compute01node", ..."compute98node", "compute99node") +=cut + +#------------------------------------------------------------------------------- +sub gen_numric_hostnames +{ + my ($class, $prefix, $appendix, $len) = @_; + my @hostnames; + + my $cnt=0; + my $maxnum = 10 ** $len; + while($cnt < $maxnum) + { + my $fullnum = $maxnum + $cnt; + my $hostname = $prefix.(substr $fullnum, 1).$appendix; + push (@hostnames, $hostname); + $cnt++; + } + return \@hostnames; +} + +#------------------------------------------------------------------------------- + +=head3 get_hostname_format_type + Description : Get hostname format type. + Arguments : $format - hostname format + Returns : hostname format type value: + "numric" - numric hostname format. + "rack" - rack info hostname format. + Example : + calling get_hostname_format_type("compute#NNnode") will return "numric" + calling get_hostname_format_type("compute-#RR-#NN") will return "rack" +=cut + +#------------------------------------------------------------------------------- +sub get_hostname_format_type{ + my ($class, $format) = @_; + my $type; + + my $ridx = index $format, "#R"; + my $nidx = index $format, "#N"; + if ($ridx >= 0){ + $type = "rack"; + } elsif ($nidx >= 0){ + $type = "numric"; + } + return $type; +} + +#------------------------------------------------------------------------------- + +=head3 rackformat_to_numricformat + Description : convert rack hostname format into numric hostname format. + Arguments : $format - rack hostname format + $racknum - rack number. + Returns : numric hostname format. + Example : + calling rackformat_to_numricformat("compute-#RR-#NN", 1) will return "compute-01-#NN" +=cut + +#------------------------------------------------------------------------------- +sub rackformat_to_numricformat{ + my ($class, $format, $racknum) = @_; + my ($prefix, $appendix, $len) = xCAT::PCMNodeMgmtUtils->split_hostname($format, 'R'); + + my $maxnum = 10 ** $len; + my $fullnum = $maxnum + $racknum; + return $prefix.(substr $fullnum, 1).$appendix; +} + +#------------------------------------------------------------------------------- + +=head3 get_netprofile_nic_attrs + Description : Get networkprofile's NIC attributes and return a dict. + Arguments : $netprofilename - network profile name. + Returns : A hash %netprofileattrs for network profile attributes. + keys of %netprofileattrs are nics names, like: ib0, eth0, bmc... + values of %netprofileattrs are attributes of a specific nic, like: + type : nic type + hostnamesuffix: hostname suffix + customscript: custom script for this nic + network: network name for this nic +=cut + +#------------------------------------------------------------------------------- +sub get_netprofile_nic_attrs{ + my $class = shift; + my $netprofilename = shift; + + my $nicstab = xCAT::Table->new( 'nics'); + my $entry = $nicstab->getNodeAttribs("$netprofilename", ['nictypes', 'nichostnamesuffixes', 'niccustomscripts', 'nicnetworks']); + + my %netprofileattrs; + my @nicattrslist; + + if ($entry->{'nictypes'}){ + @nicattrslist = split(",", $entry->{'nictypes'}); + foreach (@nicattrslist){ + my @nicattrs = split(":", $_); + $netprofileattrs{$nicattrs[0]}{'type'} = $nicattrs[1]; + } + } + + if($entry->{'nichostnamesuffixes'}){ + @nicattrslist = split(",", $entry->{'nichostnamesuffixes'}); + foreach (@nicattrslist){ + my @nicattrs = split(":", $_); + $netprofileattrs{$nicattrs[0]}{'hostnamesuffix'} = $nicattrs[1]; + } + } + + if($entry->{'niccustomscripts'}){ + @nicattrslist = split(",", $entry->{'niccustomscripts'}); + foreach (@nicattrslist){ + my @nicattrs = split(":", $_); + $netprofileattrs{$nicattrs[0]}{'customscript'} = $nicattrs[1]; + } + } + + if($entry->{'nicnetworks'}){ + @nicattrslist = split(",", $entry->{'nicnetworks'}); + foreach (@nicattrslist){ + my @nicattrs = split(":", $_); + $netprofileattrs{$nicattrs[0]}{'network'} = $nicattrs[1]; + } + } + + return \%netprofileattrs; +} + +#------------------------------------------------------------------------------- + +=head3 get_netprofile_bmcnet + Description : Get bmc network name of a network profile. + Arguments : $nettmpl - network profile name + Returns : bmc network name of this network profile. +=cut + +#------------------------------------------------------------------------------- +sub get_netprofile_bmcnet{ + my ($class, $netprofilename) = @_; + + my $netprofile_nicshash_ref = xCAT::PCMNodeMgmtUtils->get_netprofile_nic_attrs($netprofilename); + my %netprofile_nicshash = %$netprofile_nicshash_ref; + if (exists $netprofile_nicshash{'bmc'}{"network"}){ + return $netprofile_nicshash{'bmc'}{"network"} + }else{ + return undef; + } +} + +#------------------------------------------------------------------------------- + +=head3 get_netprofile_provisionnet + Description : Get deployment network of a network profile. + Arguments : $nettmpl - network profile name + Returns : deployment network name of this network profile. +=cut + +#------------------------------------------------------------------------------- +sub get_netprofile_provisionnet{ + my ($class, $netprofilename) = @_; + + my $netprofile_nicshash_ref = xCAT::PCMNodeMgmtUtils->get_netprofile_nic_attrs($netprofilename); + my %netprofile_nicshash = %$netprofile_nicshash_ref; + my $restab = xCAT::Table->new('noderes'); + my $installnicattr = $restab->getNodeAttribs($netprofilename, ['installnic']); + my $installnic = $installnicattr->{'installnic'}; + + if ($installnic){ + if (exists $netprofile_nicshash{$installnic}{"network"}){ + return $netprofile_nicshash{$installnic}{"network"} + } + } + return undef; +} + +#------------------------------------------------------------------------------- + +=head3 get_output_filename + Description : Generate a temp file name for placing output details for PCM node management operations. + We make this file generated under /install/ so that clients can access it through http. + Arguments : N/A + Returns : A temp filename placed under /install/pcm/work/ +=cut + +#------------------------------------------------------------------------------- +sub get_output_filename +{ + my $installdir = xCAT::TableUtils->getInstallDir(); + my $pcmworkdir = $installdir."/pcm/work/"; + if (! -d $pcmworkdir) + { + mkpath($pcmworkdir); + } + return tempfile("hostinfo_result_XXXXXXX", DIR=>$pcmworkdir); +} + +#------------------------------------------------------------------------------- + +=head3 get_all_chassis + Description : Get all chassis in system. + Arguments : hashref: if not set, return a array ref. + if set, return a hash ref. + Returns : ref for node list. + Example : + my $arrayref = xCAT::PCMNodeMgmtUtils->get_all_chassis(); + my $hashref = xCAT::PCMNodeMgmtUtils->get_all_chassis(1); +=cut + +#------------------------------------------------------------------------------- +sub get_all_chassis +{ + my $class = shift; + my $hashref = shift; + my %chassishash; + + my @chassis = xCAT::NodeRange::noderange('__Chassis'); + if ($hashref){ + foreach (@chassis){ + $chassishash{$_} = 1; + } + return \%chassishash; + } else{ + return \@chassis; + } +} + +#------------------------------------------------------------------------------- + +=head3 get_allnode_singleattrib_hash + Description : Get all records of a column from a table, then return a hash. + The return hash's keys are the records of this attribute + and values are all set as 1. + Arguments : $tabname - the table name. + $attr - the attribute name. + Returns : Reference of the records hash. +=cut + +#------------------------------------------------------------------------------- +sub get_allnode_singleattrib_hash +{ + my $class = shift; + my $tabname = shift; + my $attr = shift; + my $table = xCAT::Table->new($tabname); + my @entries = $table->getAllNodeAttribs([$attr]); + my %allrecords; + foreach (@entries) { + if ($_->{$attr}){ + $allrecords{$_->{$attr}} = 0; + } + } + return \%allrecords; +} + +#------------------------------------------------------------------------------- + +=head3 acquire_lock + Description : Create lock file for node management plugin so that there is + no multi node mamangement plugins running. + The file content will be the pid of the plugin running process. + Once the plugin process terminated abnormally and lock file not removed, + next time node management plugin runs, it will read the lock file + and check whether this process is running or not. If not, + just remove this lock file and create a new one. + Arguments : N/A + Returns : 1 - create lock success. + 0 - failed, there may be a node management process running. +=cut + +#------------------------------------------------------------------------------- +sub acquire_lock +{ + my $lockfile = "/var/lock/pcm/nodemgmt"; + my $lockdir = "/var/lock/pcm"; + mkdir "$lockdir", 0755 unless -d "$lockdir"; + + if (-e $lockfile) { + open LOCKFILE, "<$lockfile"; + my @lines = ; + my $pid = $lines[0]; + if (-d "/proc/$pid") { + return 0; + } else{ + # the process already not exists, remove lock file. + File::Path->rmtree($lockfile); + } + } + open LOCKFILE, ">$lockfile"; + print LOCKFILE $$; + close LOCKFILE; + return 1; +} + +#------------------------------------------------------------------------------- + +=head3 release_lock + Description : Release lock for node management process. + Arguments : N/A + Returns : N/A +=cut + +#------------------------------------------------------------------------------- +sub release_lock +{ + my $lockfile = "/var/lock/pcm/nodemgmt"; + if (-e $lockfile){ + open LOCKFILE, "<$lockfile"; + my @lines = ; + my $pid = $lines[0]; + close LOCKFILE; + if ($pid ne $$){ + File::Path->rmtree($lockfile); + } + } +} + +#------------------------------------------------------------------------------- + +=head3 get_node_profiles + Description : Get nodelist's profile and return a hash ref. + Arguments : node list. + Returns : nodelist's profile hash. + keys are node names. + values are hash ref. This hash ref is placing the node's profile information: + keys can be followings: "NetworkProfile", "ImageProfile", "HardwareProfile" + values are the profile names. +=cut + +#------------------------------------------------------------------------------- + +sub get_nodes_profiles +{ + my $class = shift; + my $nodelistref = shift; + my %profile_dict; + + my $nodelisttab = xCAT::Table->new('nodelist'); + my $groupshashref = $nodelisttab->getNodesAttribs($nodelistref, ['groups']); + my %groupshash = %$groupshashref; + + foreach (keys %groupshash){ + my $value = $groupshash{$_}; + my $groups = $value->[0]->{'groups'}; + # groups looks like "__Managed,__NetworkProfile_default_cn,__ImageProfile_rhels6.3-x86_64-install-compute" + my @grouplist = split(',', $groups); + my @profilelist = ("NetworkProfile", "ImageProfile", "HardwareProfile"); + foreach my $group (@grouplist){ + foreach my $profile (@profilelist){ + my $idx = index ($group, $profile); + # The Group starts with __, so index will be 2. + if ( $idx == 2 ){ + # The group string will like @NetworkProfile_ + # So, index should +3, 2 for '__', 1 for _. + my $append_index = length($profile) + 3; + $profile_dict{$_}{$profile} = substr $group, $append_index; + last; + } + } + } + } + return \%profile_dict; +} + +#------------------------------------------------------------------------------- + +=head3 get_imageprofile_prov_method + Description : Get A node's provisioning method from its imageprofile attribute. + Arguments : $imgprofilename - imageprofile name + Returns : node's provisioning method: install, netboot...etc +=cut + +#------------------------------------------------------------------------------- +sub get_imageprofile_prov_method +{ + + # For imageprofile, we can get node's provisioning method through: + # nodetype table: node (imageprofile name), provmethod (osimage name) + # osimage table: imagename (osimage name), provmethod (node deploy method: install, netboot...) + my $class = shift; + my $imgprofilename = shift; + + my $nodetypestab = xCAT::Table->new('nodetype'); + my $entry = ($nodetypestab->getAllAttribsWhere("node = '$imgprofilename'", 'ALL' ))[0]; + my $osimgname = $entry->{'provmethod'}; + + my $osimgtab = xCAT::Table->new('osimage'); + my $osimgentry = ($osimgtab->getAllAttribsWhere("imagename = '$osimgname'", 'ALL' ))[0]; + return $osimgentry->{'provmethod'}; +} + diff --git a/xCAT-server/lib/xcat/plugins/00pcmkitbase.pm b/xCAT-server/lib/xcat/plugins/00pcmkitbase.pm new file mode 100644 index 000000000..34a60eef8 --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/00pcmkitbase.pm @@ -0,0 +1,117 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html + +package xCAT_plugin::00pcmkitbase; + +use strict; +use warnings; +require xCAT::Utils; +require xCAT::Table; +require xCAT::PCMNodeMgmtUtils; + +#------------------------------------------------------- + +=head1 + + xCAT plugin, which is also the default PCM kit plugin. + These commands are called by PCM node management commands, + should not be called directly by external. + +=cut + +#------------------------------------------------------- + +#------------------------------------------------------- + +=head3 handled_commands + + Return list of commands handled by this plugin + +=cut + +#------------------------------------------------------- +sub handled_commands { + return { + kitcmd_nodemgmt_add => '00pcmkitbase', + kitcmd_nodemgmt_remove => '00pcmkitbase', + kitcmd_nodemgmt_update => '00pcmkitbase', + kitcmd_nodemgmt_refresh => '00pcmkitbase', + kitcmd_nodemgmt_finished => '00pcmkitbase', + }; + +} + +#------------------------------------------------------- + +=head3 process_request + + Process the command. This is the main call. + +=cut + +#------------------------------------------------------- +sub process_request { + my $request = shift; + my $callback = shift; + my $request_command = shift; + my $command = $request->{command}->[0]; + my $argsref = $request->{arg}; + + my $nodelist = $request->{node}; + + if($command eq 'kitcmd_nodemgmt_add') + { + $request_command->({command=>["makehosts"], node=>$nodelist}); + $request_command->({command=>["makedns"], node=>$nodelist}, arg=>['-n']); + # Work around for makedns bug, it will set umask to 0007. + umask(0022); + $request_command->({command=>["makedhcp"], node=>$nodelist}); + $request_command->({command=>["makeknownhosts"], node=>$nodelist}); + my $firstnode = (@$nodelist)[0]; + my $profileref = xCAT::PCMNodeMgmtUtils->get_nodes_profiles([$firstnode]); + my %profilehash = %$profileref; + if (exists $profilehash{$firstnode}{"ImageProfile"}){ + $request_command->({command=>["nodeset"], node=>$nodelist, arg=>['osimage='.$profilehash{$firstnode}{"ImageProfile"}]}); + } + + } + elsif ($command eq 'kitcmd_nodemgmt_remove'){ + $request_command->({command=>["nodeset"], node=>$nodelist, arg=>['offline']}); + $request_command->({command=>["makeknownhosts"], node=>$nodelist, arg=>['-r']}); + $request_command->({command=>["makedhcp"], node=>$nodelist, arg=>['-d']}); + $request_command->({command=>["makedns"], node=>$nodelist, arg=>['-d']}); + # Work around for makedns bug, it will set umask to 0007. + umask(0022); + $request_command->({command=>["makehosts"], node=>$nodelist, arg=>['-d']}); + } + elsif ($command eq 'kitcmd_nodemgmt_update'){ + $request_command->({command=>["makehosts"], node=>$nodelist}); + $request_command->({command=>["makedns"], node=>$nodelist}, arg=>['-n']); + # Work around for makedns bug, it will set umask to 0007. + umask(0022); + $request_command->({command=>["makedhcp"], node=>$nodelist}); + $request_command->({command=>["makeknownhosts"], node=>$nodelist}); + my $firstnode = (@$nodelist)[0]; + my $profileref = xCAT::PCMNodeMgmtUtils->get_nodes_profiles([$firstnode]); + my %profilehash = %$profileref; + if (exists $profilehash{$firstnode}{"ImageProfile"}){ + $request_command->({command=>["nodeset"], node=>$nodelist, arg=>['osimage='.$profilehash{$firstnode}{"ImageProfile"}]}); + } + } + elsif ($command eq 'kitcmd_nodemgmt_refresh'){ + $request_command->({command=>["makehosts"], node=>$nodelist}); + $request_command->({command=>["makedns"], node=>$nodelist}, arg=>['-n']); + # Work around for makedns bug, it will set umask to 0007. + umask(0022); + $request_command->({command=>["makedhcp"], node=>$nodelist}); + $request_command->({command=>["makeknownhosts"], node=>$nodelist}); + } + elsif ($command eq 'kitcmd_nodemgmt_finished') + { + $request_command->({command=>["makeconservercf"]}); + } + else + { + } +} + +1; diff --git a/xCAT-server/lib/xcat/plugins/pcmnodes.pm b/xCAT-server/lib/xcat/plugins/pcmnodes.pm new file mode 100644 index 000000000..3532be66d --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/pcmnodes.pm @@ -0,0 +1,758 @@ +# IBM(c) 2012 EPL license http://www.eclipse.org/legal/epl-v10.html +#------------------------------------------------------- + +=head1 + + xCAT plugin to support PCM node management + These commands are designed to be called by PCM GUI. + +=cut + +#------------------------------------------------------- +package xCAT_plugin::pcmnodes; + +use strict; +use warnings; +require xCAT::Table; +require xCAT::DBobjUtils; +require xCAT::Utils; +require xCAT::TableUtils; +require xCAT::NetworkUtils; +require xCAT::MsgUtils; +require xCAT::PCMNodeMgmtUtils; + +# Globals. +# These 2 global variables are for storing the parse result of hostinfo file. +# These 2 global varialbes are set in lib xCAT::DBobjUtils->readFileInput. +#%::FILEATTRS; +#@::fileobjnames; + +# All database records. +my %allhostnames; +my %allbmcips; +my %allmacs; +my %allips; +my %allinstallips; +my %allnicips; +my %allracks; +my %allchassis; + +# Define parameters for xcat requests. +my $request; +my $callback; +my $request_command; +my $command; +my $args; +# Put arguments in a hash. +my %args_dict; + +#------------------------------------------------------- + +=head3 handled_commands + + Return list of commands handled by this plugin + +=cut + +#------------------------------------------------------- +sub handled_commands { + return { + addhost_hostfile => 'pcmnodes', + addhost_discover => 'pcmnodes', + removehost => 'pcmnodes', + updatehost => 'pcmnodes', + }; +} + + +#------------------------------------------------------- + +=head3 process_request + + Process the command. This is the main call. + +=cut + +#------------------------------------------------------- +sub process_request { + + my $lock = xCAT::PCMNodeMgmtUtils->acquire_lock(); + unless ($lock){ + setrsp_errormsg("Can not acquire lock, some process is operating node related actions."); + return; + } + $request = shift; + $callback = shift; + $request_command = shift; + $command = $request->{command}->[0]; + $args = $request->{arg}; + + if ($command eq "addhost_hostfile"){ + addhost_hostfile() + } elsif ($command eq "removehost"){ + removehost(); + } elsif ($command eq "updatehost"){ + updatehost(); + } + xCAT::PCMNodeMgmtUtils->release_lock($lock); +} + +#------------------------------------------------------- + +=head3 parse_args + + Description : Parse arguments. We placed arguments into a directory %args_dict + Arguments : args - args of xCAT requests. + Returns : undef - parse succeed. + A string - parse arguments failed, the return value is error message. +=cut + +#----------------------------------------------------- + +sub parse_args{ + foreach my $arg (@$args){ + my @argarray = split(/=/,$arg); + my $arglen = @argarray; + if ($arglen > 2){ + return "Illegal arg $arg specified."; + } + + # translate the profile names into real group names in db. + if($argarray[0] eq "networkprofile"){ + $args_dict{$argarray[0]} = "__NetworkProfile_".$argarray[1]; + } elsif ($argarray[0] eq "imageprofile"){ + $args_dict{$argarray[0]} = "__ImageProfile_".$argarray[1]; + } elsif ($argarray[0] eq "hardwareprofile"){ + $args_dict{$argarray[0]} = "__HardwareProfile_".$argarray[1]; + } else{ + $args_dict{$argarray[0]} = $argarray[1]; + } + } + return undef; +} + +#------------------------------------------------------- + +=head3 addhost_hostfile + + Description : Create nodes by import hostinfo file. + Arguments : N/A + +=cut + +#------------------------------------------------------- +sub addhost_hostfile { + + # Parse arges. + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Import PCM nodes through hostinfo file.\n"); + my $retstr = parse_args(); + if ($retstr){ + setrsp_errormsg($retstr); + return; + } + # Make sure the specified parameters are valid ones. + # TODO: support privisioning template. + my @enabledparams = ('file', 'groups', 'networkprofile', 'hardwareprofile', 'imageprofile'); + foreach my $argname (keys %args_dict){ + if (! grep{ $_ eq $argname} @enabledparams){ + setrsp_errormsg("Illegal attribute $argname specified."); + return; + } + } + # validate hostinfo file. + if (! exists $args_dict{'file'}){ + setrsp_errormsg("No hostinfo file specified."); + return; + } + elsif(! (-e $args_dict{'file'})){ + setrsp_errormsg("The hostinfo file not exists."); + return; + } + + # Get database records: all hostnames, all ips, all racks... + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Getting database records.\n"); + my $recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('nodelist', 'node'); + %allhostnames = %$recordsref; + $recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('ipmi', 'bmc'); + %allbmcips = %$recordsref; + $recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('mac', 'mac'); + %allmacs = %$recordsref; + $recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('hosts', 'ip'); + %allinstallips = %$recordsref; + $recordsref = xCAT::NetworkUtils->get_all_nicips(1); + %allips = %$recordsref; + + # Merge all BMC IPs and install IPs into allips. + %allips = (%allips, %allbmcips, %allinstallips); + + #my $recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('rack', 'rackname'); + #%allracks = %$recordsref; + #my $recordsref = xCAT::PCMNodeMgmtUtils->get_allchassis(1); + #%allchassis = %$recordsref; + + # Generate temporary hostnames for hosts entries in hostfile. + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Generate temporary hostnames.\n"); + my ($retcode_read, $retstr_read) = read_and_generate_hostnames($args_dict{'file'}); + if ($retcode_read != 0){ + setrsp_errormsg($retstr_read); + return; + } + + # Parse and validate the hostinfo string. The real hostnames will be generated here. + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Parsing hostinfo string and validate it.\n"); + my ($hostinfo_dict_ref, $invalid_records_ref) = parse_hosts_string($retstr_read); + my %hostinfo_dict = %$hostinfo_dict_ref; + my @invalid_records = @$invalid_records_ref; + if (@invalid_records){ + setrsp_invalidrecords(\@invalid_records); + return; + } + unless (%hostinfo_dict){ + setrsp_errormsg("No valid host records found in hostinfo file."); + return; + } + + # Create the real hostinfo string in stanza file format. + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Generating new hostinfo string.\n"); + my ($retcode_gen, $retstr_gen) = gen_new_hostinfo_string(\%hostinfo_dict); + unless ($retcode_gen){ + setrsp_errormsg($retstr_gen); + return; + } + # call mkdef to create hosts and then call nodemgmt for node management plugins. + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call mkdef to create pcm nodes.\n"); + $request_command->({command=>["mkdef"], stdin=>[$retstr_gen], arg=>['-z']}); + + my @nodelist = keys %hostinfo_dict; + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call nodemgmt plugins.\n"); + $request_command->({command=>["kitcmd_nodemgmt_add"], node=>\@nodelist}); + $request_command->({command=>["kitcmd_nodemgmt_finished"], node=>\@nodelist}); + setrsp_success(\@nodelist); +} + +#------------------------------------------------------- + +=head3 removehost + + Description : Remove nodes. + Arguments : N/A + +=cut + +#------------------------------------------------------- +sub removehost{ + my $nodes = $request->{node}; + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Remove PCM nodes.\n"); + # For remove nodes, we should call 'nodemgmt' in front of 'noderm' + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call nodemgmt plugins.\n"); + $request_command->({command=>["kitcmd_nodemgmt_remove"], node=>$nodes}); + $request_command->({command=>["kitcmd_nodemgmt_finished"], node=>$nodes}); + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call noderm to remove nodes.\n"); + $request_command->({command=>["noderm"], node=>$nodes}); + setrsp_success($nodes); +} + +#------------------------------------------------------- + +=head3 updatehost + + Description : Update host profiles. + Arguments : N/A + +=cut + +#------------------------------------------------------- +sub updatehost{ + my $nodes = $request->{node}; + my %updated_groups; + + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Update PCM nodes settings.\n"); + # Parse arges. + my $retstr = parse_args(); + if ($retstr){ + setrsp_errormsg($retstr); + return; + } + # Make sure the specified parameters are valid ones. + # TODO: support privisioning template. + my @enabledparams = ('networkprofile', 'hardwareprofile', 'imageprofile'); + foreach my $argname (keys %args_dict){ + if (! grep{ $_ eq $argname} @enabledparams){ + setrsp_errormsg("Illegal attribute $argname specified."); + return; + } + } + + # Get current templates for all nodes. + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Read database to get groups for all nodes.\n"); + my %groupdict; + my $nodelstab = xCAT::Table->new('nodelist'); + my $nodeshashref = $nodelstab->getNodesAttribs($nodes, ['groups']); + my %nodeshash = %$nodeshashref; + my %updatenodeshash; + foreach (keys %nodeshash){ + my @groups; + my $attrshashref = $nodeshash{$_}[0]; + my %attrshash = %$attrshashref; + if ($attrshash{'groups'}){ + @groups = split(/,/, $attrshash{'groups'}); + + my $groupsref; + # Replace the old template name with new specified ones in args_dict + if(exists $args_dict{'networkprofile'}){ + $groupsref = replace_item_in_array(\@groups, "NetworkProfile", $args_dict{'networkprofile'}); + } + if(exists $args_dict{'hardwareprofile'}){ + $groupsref = replace_item_in_array(\@groups, "HardwareProfile", $args_dict{'hardwareprofile'}); + } + if(exists $args_dict{'imageprofile'}){ + $groupsref = replace_item_in_array(\@groups, "ImageProfile", $args_dict{'imageprofile'}); + } + $updatenodeshash{$_}{'groups'} = join (',', @$groupsref); + } + } + + #update DataBase. + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Update database records.\n"); + my $nodetab = xCAT::Table->new('nodelist',-create=>1); + $nodetab->setNodesAttribs(\%updatenodeshash); + $nodetab->close(); + + # call plugins + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call nodemgmt plugins.\n"); + $request_command->({command=>["kitcmd_nodemgmt_update"], node=>$nodes}); + $request_command->({command=>["kitcmd_nodemgmt_finished"], node=>$nodes}); + setrsp_success($nodes); +} + +#------------------------------------------------------- + +=head3 replace_item_in_array + + Description : Replace an item in a list with new value. This item should match specified pattern. + Arguments : arrayref - the list. + pattern - the pattern which the old item must match. + newitem - the updated value. +=cut + +#------------------------------------------------------- +sub replace_item_in_array{ + my $arrayref = shift; + my $pattern = shift; + my $newitem = shift; + + my @newarray; + foreach (@$arrayref){ + if ($_ =~ /__$pattern/){ + next; + } + push (@newarray, $_); + } + push(@newarray, $newitem); + return \@newarray; +} + +#------------------------------------------------------- + +=head3 gen_new_hostinfo_string + + Description : Generate a stanza file format string used for 'mkdef' to create nodes. + Arguments : hostinfo_dict_ref - The reference of hostinfo dict. + Returns : (returnvalue, returnmsg) + returnvalue - 0, stands for generate new hostinfo string failed. + 1, stands for generate new hostinfo string OK. + returnnmsg - error messages if generate failed. + - the new hostinfo string if generate OK. +=cut + +#------------------------------------------------------- +sub gen_new_hostinfo_string{ + my $hostinfo_dict_ref = shift; + my %hostinfo_dict = %$hostinfo_dict_ref; + + # Get free ips list for all networks in network profile. + my @allknownips = keys %allips; + my $netprofileattrsref = xCAT::PCMNodeMgmtUtils->get_netprofile_nic_attrs($args_dict{'networkprofile'}); + my %netprofileattr = %$netprofileattrsref; + my %freeipshash; + foreach (keys %netprofileattr){ + my $netname = $netprofileattr{$_}{'network'}; + if($netname and (! exists $freeipshash{$netname})) { + $freeipshash{$netname} = xCAT::PCMNodeMgmtUtils->get_allocable_staticips_innet($netname, \@allknownips); + } + } + + # Get networkprofile's installip + my $noderestab = xCAT::Table->new('noderes'); + my $networkprofile = $args_dict{'networkprofile'}; + my $nodereshashref = $noderestab->getNodeAttribs($networkprofile, ['installnic']); + my %nodereshash = %$nodereshashref; + my $installnic = $nodereshash{'installnic'}; + + # Get node's provisioning method + my $provmethod = xCAT::PCMNodeMgmtUtils->get_imageprofile_prov_method($args_dict{'imageprofile'}); + + # compose the stanza string for hostinfo file. + my $hostsinfostr = ""; + foreach my $item (keys %hostinfo_dict){ + # Generate IPs for all interfaces. + my %ipshash; + foreach (keys %netprofileattr){ + my $netname = $netprofileattr{$_}{'network'}; + my $freeipsref; + if ($netname){ + $freeipsref = $freeipshash{$netname}; + } + my $nextip = shift @$freeipsref; + if (!$nextip){ + return 0, "No sufficient IP address in network $netname for interface $_"; + }else{ + $ipshash{$_} = $nextip; + $allips{$nextip} = 0; + } + } + my $nicips = ""; + foreach(keys %ipshash){ + $nicips = "$_:$ipshash{$_},$nicips"; + } + $hostinfo_dict{$item}{"nicips"} = $nicips; + + # Generate IP address if no IP specified. + if (! exists $hostinfo_dict{$item}{"ip"}) { + if (exists $ipshash{$installnic}){ + $hostinfo_dict{$item}{"ip"} = $ipshash{$installnic}; + }else{ + return 0, "No sufficient IP address for interface $installnic"; + } + } + $hostinfo_dict{$item}{"objtype"} = "node"; + $hostinfo_dict{$item}{"groups"} = "__Managed"; + if (exists $args_dict{'networkprofile'}){$hostinfo_dict{$item}{"groups"} .= ",".$args_dict{'networkprofile'}} + if (exists $args_dict{'imageprofile'}){$hostinfo_dict{$item}{"groups"} .= ",".$args_dict{'imageprofile'}} + if (exists $args_dict{'hardwareprofile'}){$hostinfo_dict{$item}{"groups"} .= ",".$args_dict{'hardwareprofile'}} + + # Update BMC records. + if (exists $netprofileattr{"bmc"}){ + $hostinfo_dict{$item}{"mgt"} = "ipmi"; + $hostinfo_dict{$item}{"chain"} = 'runcmd=bmcsetup,'.$provmethod; + + if (exists $ipshash{"bmc"}){ + $hostinfo_dict{$item}{"bmc"} = $ipshash{"bmc"}; + } else{ + return 0, "No sufficient IP addresses for BMC"; + } + } else{ + $hostinfo_dict{$item}{"chain"} = $provmethod; + } + + # Generate the hostinfo string. + $hostsinfostr = "$hostsinfostr$item:\n"; + my $itemdictref = $hostinfo_dict{$item}; + my %itemdict = %$itemdictref; + foreach (keys %itemdict){ + $hostsinfostr = "$hostsinfostr $_=\"$itemdict{$_}\"\n"; + } + } + return 1, $hostsinfostr; +} + +#------------------------------------------------------- + +=head3 read_and_generate_hostnames + + Description : Read hostinfo file and generate temporary hostnames for no-hostname specified ones. + Arguments : hostfile - the location of hostinfo file. + Returns : (returnvalue, returnmsg) + returnvalue - 0, stands for a failed return + 1, stands for a success return + returnnmsg - error messages for failed return. + - the contents of the hostinfo string. +=cut + +#------------------------------------------------------- +sub read_and_generate_hostnames{ + my $hostfile = shift; + + # Get 10000 temprary hostnames. + my $freehostnamesref = xCAT::PCMNodeMgmtUtils->gen_numric_hostnames("TMPHOSTS","", 4); + # Auto generate hostnames for "__hostname__" entries. + open(HOSTFILE, $hostfile); + my $filecontent = join("", ); + while ((index $filecontent, "__hostname__:") >= 0){ + my $nexthost = shift @$freehostnamesref; + # no more valid hostnames to assign. + if (! $nexthost){ + return 1, "Can not generate hostname automatically: No more valid hostnames available ."; + } + # This hostname already specified in hostinfo file. + if ((index $filecontent, "$nexthost:") >= 0){ + next; + } + # This hostname should not in database. + if (exists $allhostnames{$nexthost}){ + next; + } + $filecontent =~ s/__hostname__/$nexthost/; + } + close(HOSTFILE); + return 0, $filecontent; +} + +#------------------------------------------------------- + +=head3 parse_hosts_string + + Description : Parse the hostinfo string and validate it. + Arguments : filecontent - The content of hostinfo file. + Returns : (hostinfo_dict, invalid_records) + hostinfo_dict - Reference of hostinfo dict. Key are hostnames and values is an attributes dict. + invalid_records - Reference of invalid records list. +=cut + +#------------------------------------------------------- +sub parse_hosts_string{ + my $filecontent = shift; + my %hostinfo_dict; + my @invalid_records; + + my $nicstab = xCAT::Table->new('nics'); + my $nodehashref = $nicstab->getNodeAttribs($args_dict{'networkprofile'}, ['hostnameformat']); + my $nameformat = $nodehashref->{'hostnameformat'}; + + my $nameformattype = xCAT::PCMNodeMgmtUtils->get_hostname_format_type($nameformat); + my %freehostnames; + + # Parse hostinfo file string. + xCAT::DBobjUtils->readFileInput($filecontent); + + # Record duplicated items. + # We should go through list @::fileobjnames first as %::FILEATTRS is just a hash, + # it not tells whether there are some duplicated hostnames in the hostinfo string. + my %hostnamedict; + foreach my $hostname (@::fileobjnames){ + if (exists $hostnamedict{$hostname}){ + push @invalid_records, [$hostname, "Duplicated hostname defined"]; + } else{ + $hostnamedict{$hostname} = 0; + } + } + # Verify each node entry. + foreach (keys %::FILEATTRS){ + my $errmsg = validate_node_entry($_, $::FILEATTRS{$_}); + if ($errmsg) { + if ($_=~ /^TMPHOSTS/){ + push @invalid_records, ["__hostname__", $errmsg]; + } else{ + push @invalid_records, [$_, $errmsg]; + } + next; + } + + # We need generate hostnames for this entry. + if ($_=~ /^TMPHOSTS/) + { + # rack + numric hostname format, we must specify rack in node's definition. + my $numricformat; + # Need convert hostname format into numric format first. + if ($nameformattype eq "rack"){ + if (! exists $::FILEATTRS{$_}{"rack"}){ + push @invalid_records, ["__hostname__", "No rack info specified. Do specify it because the nameformat contains rack info."]; + next; + } + $numricformat = xCAT::PCMNodeMgmtUtils->rackformat_to_numricformat($nameformat, $::FILEATTRS{$_}{"rack"}); + } else{ + # pure numric hostname format + $numricformat = $nameformat; + } + + # Generate hostnames based on numric hostname format. + if (! exists $freehostnames{$numricformat}){ + $freehostnames{$numricformat} = xCAT::PCMNodeMgmtUtils->genhosts_with_numric_tmpl($numricformat); + } + my $hostnamelistref = $freehostnames{$numricformat}; + my $nexthostname = shift @$hostnamelistref; + while (exists $allhostnames{$nexthostname}){ + $nexthostname = shift @$hostnamelistref; + } + $hostinfo_dict{$nexthostname} = $::FILEATTRS{$_}; + } else{ + $hostinfo_dict{$_} = $::FILEATTRS{$_}; + } + } + return (\%hostinfo_dict, \@invalid_records); +} + +#------------------------------------------------------- + +=head3 validate_node_entry + + Description : Validate a node info hash. + Arguments : node_name - node hostname. + node_entry_ref - Reference of the node info hash. + Returns : errormsg + - undef: stands for no errror. + - valid string: stands for the error message of validation. +=cut + +#------------------------------------------------------- +sub validate_node_entry{ + my $node_name = shift; + my $node_entry_ref = shift; + my %node_entry = %$node_entry_ref; + + # duplicate hostname found in hostinfo file. + if (exists $allhostnames{$node_name}) { + return "Specified hostname $node_name conflicts with database records."; + } + # Must specify either MAC or switch + port. + if (exists $node_entry{"mac"} || + exists $node_entry{"switch"} && exists $node_entry{"port"}){ + } else{ + return "Neither MAC nor switch + port specified"; + } + + if (! xCAT::NetworkUtils->isValidHostname($node_name)){ + return "Specified hostname: $node_name is invalid"; + } + # validate each single value. + foreach (keys %node_entry){ + if ($_ eq "mac"){ + if (exists $allmacs{$node_entry{$_}}){ + return "Specified MAC address $node_entry{$_} conflicts with MACs in database or hostinfo file"; + }elsif(! xCAT::NetworkUtils->isValidMAC($node_entry{$_})){ + return "Specified MAC address $node_entry{$_} is invalid"; + }else{ + $allmacs{$node_entry{$_}} = 0; + } + }elsif ($_ eq "ip"){ + if (exists $allips{$node_entry{$_}}){ + return "Specified IP address $node_entry{$_} conflicts with IPs in database or hostinfo file"; + }elsif((xCAT::NetworkUtils->validate_ip($node_entry{$_}))[0][0] ){ + return "Specified IP address $node_entry{$_} is invalid"; + }elsif(xCAT::NetworkUtils->isReservedIP($node_entry{$_})){ + return "Specified IP address $node_entry{$_} is invalid"; + }else { + #push the IP into allips list. + $allips{$node_entry{$_}} = 0; + } + }elsif ($_ eq "switch"){ + #TODO: xCAT switch discovery enhance: verify whether switch exists. + }elsif ($_ eq "port"){ + #TODO: xCAT switch discovery enhance: verify whether port exists. + }elsif ($_ eq "rack"){ + if (not exists $allracks{$node_entry{$_}}){ + return "Specified rack $node_entry{$_} not defined"; + } + }elsif ($_ eq "chassis"){ + if (not exists $allchassis{$node_entry{$_}}){ + return "Specified chassis $node_entry{$_} not defined"; + } + }elsif ($_ eq "unit"){ + # Not a valid number. + if (!($node_entry{$_} =~ /^\d+$/)){ + return "Specified unit $node_entry{$_} is a invalid number"; + } + }elsif ($_ eq "height"){ + # Not a valid number. + if (!($node_entry{$_} =~ /^\d+$/)){ + return "Specified height $node_entry{$_} is a invalid number"; + } + }else{ + return "Invalid attribute $_ specified"; + } + } + # For blades, don't support specify unit and height. + if(exists $node_entry{"chassis"} ){ + if(exists $node_entry{"unit"}){ + return "Can not specify 'unit' together with 'chassis'"; + } + if(exists $node_entry{"height"}){ + return "can not specify 'height' together with 'chassis'"; + } + } + # push hostinfo into global dicts. + $allhostnames{$node_name} = 0; + return undef; +} + + +#------------------------------------------------------- + +=head3 setrsp_invalidrecords + + Description : Set response for processing invalid host records. + Arguments : recordsref - Refrence of invalid nodes list. + +=cut + +#------------------------------------------------------- +sub setrsp_invalidrecords +{ + my $recordsref = shift; + my $rsp; + my $master=xCAT::TableUtils->get_site_Master(); + + # The total number of invalid records. + $rsp->{invalid_records_num} = scalar @$recordsref; + + # We write details of invalid records into a file. + my ($fh, $filename) = xCAT::PCMNodeMgmtUtils->get_output_filename(); + foreach (@$recordsref){ + my @erroritem = @$_; + print $fh "nodename $erroritem[0], error: $erroritem[1]\n"; + } + close $fh; + # Tells the URL of the details file. + xCAT::MsgUtils->message('S', "Detailed response info placed in file: http://$master/$filename\n"); + $rsp->{details} = "http://$master/$filename"; + $callback->($rsp); +} + +#------------------------------------------------------- + +=head3 setrsp_errormsg + + Description : Set response for error messages. + Arguments : errormsg - Error messages. + +=cut + +#------------------------------------------------------- +sub setrsp_errormsg +{ + my $errormsg = shift; + my $rsp; + xCAT::MsgUtils->message('S', "$errormsg\n"); + $rsp->{error}->[0] = $errormsg; + $callback->($rsp); +} + +#------------------------------------------------------- + +=head3 setrsp_success + + Description : Set response for successfully processed nodes. + Arguments : recordsref - Refrence of nodes list. + +=cut + +#------------------------------------------------------- +sub setrsp_success +{ + my $recordsref = shift; + my $rsp; + my $master=xCAT::TableUtils->get_site_Master(); + + # The total number of success nodes. + $rsp->{success_nodes_num} = scalar @$recordsref; + my ($fh, $filename) = xCAT::PCMNodeMgmtUtils->get_output_filename(); + foreach (@$recordsref){ + print $fh "success: $_\n"; + } + close $fh; + # Tells the URL of the details file. + xCAT::MsgUtils->message('S', "Detailed response info placed in file: http://$master/$filename\n"); + $rsp->{details} = "http://$master/$filename"; + $callback->($rsp); +} +1;