diff --git a/perl-xCAT/xCAT/PCMNodeMgmtUtils.pm b/perl-xCAT/xCAT/PCMNodeMgmtUtils.pm index 909e6419b..8aa5ac0eb 100644 --- a/perl-xCAT/xCAT/PCMNodeMgmtUtils.pm +++ b/perl-xCAT/xCAT/PCMNodeMgmtUtils.pm @@ -6,6 +6,7 @@ use warnings; use Socket; use File::Path qw/mkpath/; use File::Temp qw/tempfile/; +use Fcntl qw(:flock); require xCAT::Table; require xCAT::TableUtils; require xCAT::NodeRange; @@ -67,6 +68,7 @@ sub get_allocable_staticips_innet =head3 genhosts_with_numric_tmpl Description : Generate numric hostnames using numric template name. Arguments : $format - The hostname format string.. + $rank - The start number. Returns : numric hostname list Example : calling genhosts_with_numric_tmpl("compute#NNnode") will return a list like: @@ -76,10 +78,10 @@ sub get_allocable_staticips_innet #------------------------------------------------------------------------------- sub genhosts_with_numric_tmpl { - my ($class, $format) = @_; + my ($class, $format, $rank) = @_; my ($prefix, $appendix, $len) = xCAT::PCMNodeMgmtUtils->split_hostname($format, 'N'); - return xCAT::PCMNodeMgmtUtils->gen_numric_hostnames($prefix, $appendix, $len); + return xCAT::PCMNodeMgmtUtils->gen_numric_hostnames($prefix, $appendix, $len, $rank); } #------------------------------------------------------------------------------- @@ -139,10 +141,13 @@ sub split_hostname #------------------------------------------------------------------------------- sub gen_numric_hostnames { - my ($class, $prefix, $appendix, $len) = @_; + my ($class, $prefix, $appendix, $len, $rank) = @_; my @hostnames; + my $cnt = 0; - my $cnt=0; + if ($rank){ + $cnt = $rank; + } my $maxnum = 10 ** $len; while($cnt < $maxnum) { @@ -392,39 +397,56 @@ sub get_allnode_singleattrib_hash #------------------------------------------------------------------------------- =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. + Description : Create lock file for PCM plugins so that there is + no multi instance of plugins running at same time. + The lock file content will be the pid of the plugin running process. + Using perl's flock to achive this. + Note: we can not judge whether PCM discovering is running or only + through acquire_lock("nodemgmt") + We must also call is_discover_started() + Arguments : action name: for example: nodemgmt, imageprofile...etc We'll generate + a lock file named as /var/lock/pcm/$action. + Returns : -1 - Acquire lock failed. + fh of lock file - the filehandler of lock file. =cut #------------------------------------------------------------------------------- sub acquire_lock { - my $lockfile = "/var/lock/pcm/nodemgmt"; + my $class = shift; + my $action = shift; my $lockdir = "/var/lock/pcm"; - mkdir "$lockdir", 0755 unless -d "$lockdir"; + my $lockfile = "$lockdir/$action"; - 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); - } + mkdir "$lockdir", 0755 unless -d "$lockdir"; + open my $fh, ">$lockfile"; + # use flock, non-blocking mode while acquiring a lock. + my $lockret = flock($fh, LOCK_EX|LOCK_NB); + if(! $lockret){ + close $fh; + return -1; + } + + print $fh $$; + return $fh; +} + +#------------------------------------------------------------------------------- + +=head3 is_discover_started + Description : Judge whether PCM discovering is running or not. + Arguments : NA + Returns : 1 - Discover is running + 0 - Discover is not started. +=cut + +#------------------------------------------------------------------------------- +sub is_discover_started +{ + my @sitevalues = xCAT::TableUtils->get_site_attribute("__PCMDiscover"); + if (! $sitevalues[0]){ + return 0; } - open LOCKFILE, ">$lockfile"; - print LOCKFILE $$; - close LOCKFILE; return 1; } @@ -432,23 +454,18 @@ sub acquire_lock =head3 release_lock Description : Release lock for node management process. - Arguments : N/A - Returns : N/A + Arguments : fh - the lock file handler. + Returns : return value of flock. + True - release lock succeed. + False - release lock failed. =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); - } - } + my $class = shift; + my $lockfh = shift; + return flock($lockfh, LOCK_UN|LOCK_NB); } #------------------------------------------------------------------------------- diff --git a/xCAT-server/lib/xcat/plugins/00pcmkitbase.pm b/xCAT-server/lib/xcat/plugins/00pcmkitbase.pm index e11a239bd..0e087dc0b 100644 --- a/xCAT-server/lib/xcat/plugins/00pcmkitbase.pm +++ b/xCAT-server/lib/xcat/plugins/00pcmkitbase.pm @@ -70,56 +70,95 @@ sub process_request { my $nodelist = $request->{node}; my $retref; + my $rsp; if($command eq 'kitcmd_nodemgmt_add') { + $rsp->{info}->[0] = "[PCM nodes mgmt]Updating hosts entries"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makehosts"], node=>$nodelist}, $request_command, 0, 1); - $retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist, arg=>['-n']}, $request_command, 0, 1); + $rsp->{info}->[0] = "[PCM nodes mgmt]Updating DNS entries"; + $::CALLBACK->($rsp); + $retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist}, $request_command, 0, 1); # Work around for makedns bug, it will set umask to 0007. #umask(0022); + $rsp->{info}->[0] = "[PCM nodes mgmt]Update DHCP entries"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makekdhcp"], node=>$nodelist}, $request_command, 0, 1); + $rsp->{info}->[0] = "[PCM nodes mgmt]Update known hosts"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makeknownhosts"], node=>$nodelist}, $request_command, 0, 1); my $firstnode = (@$nodelist)[0]; my $profileref = xCAT::PCMNodeMgmtUtils->get_nodes_profiles([$firstnode]); my %profilehash = %$profileref; if (exists $profilehash{$firstnode}{"ImageProfile"}){ + $rsp->{info}->[0] = "[PCM nodes mgmt]Update nodes' boot settings"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["nodeset"], node=>$nodelist, arg=>['osimage='.$profilehash{$firstnode}{"ImageProfile"}]}, $request_command, 0, 1); } } elsif ($command eq 'kitcmd_nodemgmt_remove'){ $retref = xCAT::Utils->runxcmd({command=>["nodeset"], node=>$nodelist, arg=>['offline']}, $request_command, 0, 1); + $rsp->{info}->[0] = "[PCM nodes mgmt]Update known hosts"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makeknownhosts"], node=>$nodelist, arg=>['-r']}, $request_command, 0, 1); + $rsp->{info}->[0] = "[PCM nodes mgmt]Update DHCP entries"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makekdhcp"], node=>$nodelist, arg=>['-d']}, $request_command, 0, 1); + $rsp->{info}->[0] = "[PCM nodes mgmt]Updating DNS entries"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist, arg=>['-d']}, $request_command, 0, 1); # Work around for makedns bug, it will set umask to 0007. #umask(0022); + $rsp->{info}->[0] = "[PCM nodes mgmt]Updating hosts entries"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makehosts"], node=>$nodelist, arg=>['-d']}, $request_command, 0, 1); } elsif ($command eq 'kitcmd_nodemgmt_update'){ + $rsp->{info}->[0] = "[PCM nodes mgmt]Updating hosts entries"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makehosts"], node=>$nodelist}, $request_command, 0, 1); - $retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist, arg=>['-n']}, $request_command, 0, 1); + $rsp->{info}->[0] = "[PCM nodes mgmt]Updating DNS entries"; + $::CALLBACK->($rsp); + $retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist}, $request_command, 0, 1); # Work around for makedns bug, it will set umask to 0007. #umask(0022); + $rsp->{info}->[0] = "[PCM nodes mgmt]Update DHCP entries"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makekdhcp"], node=>$nodelist}, $request_command, 0, 1); + $rsp->{info}->[0] = "[PCM nodes mgmt]Update known hosts"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makeknownhosts"], node=>$nodelist}, $request_command, 0, 1); my $firstnode = (@$nodelist)[0]; my $profileref = xCAT::PCMNodeMgmtUtils->get_nodes_profiles([$firstnode]); my %profilehash = %$profileref; if (exists $profilehash{$firstnode}{"ImageProfile"}){ + $rsp->{info}->[0] = "[PCM nodes mgmt]Update nodes' boot settings"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["nodeset"], node=>$nodelist, arg=>['osimage='.$profilehash{$firstnode}{"ImageProfile"}]}, $request_command, 0, 1); } } elsif ($command eq 'kitcmd_nodemgmt_refresh'){ + $rsp->{info}->[0] = "[PCM nodes mgmt]Updating hosts entries"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makehosts"], node=>$nodelist}, $request_command, 0, 1); - $retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist, arg=>['-n']}, $request_command, 0, 1); + $rsp->{info}->[0] = "[PCM nodes mgmt]Updating DNS entries"; + $::CALLBACK->($rsp); + $retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist}, $request_command, 0, 1); # Work around for makedns bug, it will set umask to 0007. #umask(0022); + $rsp->{info}->[0] = "[PCM nodes mgmt]Update DHCP entries"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makekdhcp"], node=>$nodelist}, $request_command, 0, 1); + $rsp->{info}->[0] = "[PCM nodes mgmt]Update known hosts"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makeknownhosts"], node=>$nodelist}, $request_command, 0, 1); } elsif ($command eq 'kitcmd_nodemgmt_finished') { + $rsp->{info}->[0] = "[PCM nodes mgmt]Updating conserver configuration files"; + $::CALLBACK->($rsp); $retref = xCAT::Utils->runxcmd({command=>["makeconservercf"]}, $request_command, 0, 1); } else diff --git a/xCAT-server/lib/xcat/plugins/pcmnodes.pm b/xCAT-server/lib/xcat/plugins/pcmnodes.pm index 80c99ba02..ab7e42f3e 100644 --- a/xCAT-server/lib/xcat/plugins/pcmnodes.pm +++ b/xCAT-server/lib/xcat/plugins/pcmnodes.pm @@ -61,6 +61,10 @@ sub handled_commands { addhost_discover => 'pcmnodes', removehost => 'pcmnodes', updatehost => 'pcmnodes', + pcmdiscover_start => 'pcmnodes', + pcmdiscover_stop => 'pcmnodes', + pcmdiscover_nodels => 'pcmnodes', + findme => 'pcmnodes', }; } @@ -76,16 +80,29 @@ sub handled_commands { #------------------------------------------------------- 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; + $::CALLBACK = $callback; $request_command = shift; $command = $request->{command}->[0]; $args = $request->{arg}; + + + my $lockfh = xCAT::PCMNodeMgmtUtils->acquire_lock("nodemgmt"); + if ($lockfh == -1){ + setrsp_errormsg("Can not acquire lock, some process is operating node related actions."); + return; + } + + # These commands should make sure no discover is running. + if (grep{ $_ eq $command} ("addhost_hostfile", "removehost", "updatehost")){ + my $discover_running = xCAT::PCMNodeMgmtUtils->is_discover_started(); + if ($discover_running){ + setrsp_errormsg("Can not run command $command as PCM discover is running."); + xCAT::PCMNodeMgmtUtils->release_lock($lockfh); + return; + } + } if ($command eq "addhost_hostfile"){ addhost_hostfile() @@ -93,8 +110,17 @@ sub process_request { removehost(); } elsif ($command eq "updatehost"){ updatehost(); + } elsif ($command eq "pcmdiscover_start"){ + pcmdiscover_start(); + } elsif ($command eq "pcmdiscover_nodels"){ + pcmdiscover_nodels(); + } elsif ($command eq "findme"){ + findme(); + } elsif ($command eq "pcmdiscover_stop"){ + pcmdiscover_stop(); } - xCAT::PCMNodeMgmtUtils->release_lock($lock); + + xCAT::PCMNodeMgmtUtils->release_lock($lockfh); } #------------------------------------------------------- @@ -157,39 +183,49 @@ sub parse_args{ sub addhost_hostfile { # Parse arges. - xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Import PCM nodes through hostinfo file.\n"); + setrsp_infostr("[PCM nodes mgmt]Import PCM nodes through hostinfo file."); 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'); + my @enabledparams = ('file', 'groups', 'networkprofile', 'hardwareprofile', 'imageprofile', 'hostnameformat'); 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; + # Mandatory arguments. + foreach (('file','networkprofile', 'imageprofile', 'hostnameformat')){ + if(! exists($args_dict{$_})){ + setrsp_errormsg("Mandatory parameter $_ not specified."); + return; + } } - elsif(! (-e $args_dict{'file'})){ + + if(! (-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"); + setrsp_infostr("[PCM nodes mgmt]Getting database records."); 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; + # MAC records looks like: "01:02:03:04:05:0E!node5│01:02:03:05:0F!node6-eth1". We want to get the real mac addres. + foreach (keys %allmacs){ + my @hostentries = split(/\|/, $_); + foreach my $hostandmac ( @hostentries){ + my ($macstr, $machostname) = split("!", $hostandmac); + $allmacs{$macstr} = 0; + } + } $recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('hosts', 'ip'); %allinstallips = %$recordsref; $recordsref = xCAT::NetworkUtils->get_all_nicips(1); @@ -198,13 +234,14 @@ sub addhost_hostfile { # 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; + #TODO: can not use getallnode to get rack infos. + #$recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('rack', 'rackname'); + %allracks = (); + $recordsref = xCAT::PCMNodeMgmtUtils->get_all_chassis(1); + %allchassis = %$recordsref; # Generate temporary hostnames for hosts entries in hostfile. - xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Generate temporary hostnames.\n"); + setrsp_infostr("[PCM nodes mgmt]Generate temporary hostnames."); my ($retcode_read, $retstr_read) = read_and_generate_hostnames($args_dict{'file'}); if ($retcode_read != 0){ setrsp_errormsg($retstr_read); @@ -212,7 +249,7 @@ sub addhost_hostfile { } # 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"); + setrsp_infostr("[PCM nodes mgmt]Parsing hostinfo string and validate it."); my ($hostinfo_dict_ref, $invalid_records_ref) = parse_hosts_string($retstr_read); my %hostinfo_dict = %$hostinfo_dict_ref; my @invalid_records = @$invalid_records_ref; @@ -226,18 +263,18 @@ sub addhost_hostfile { } # Create the real hostinfo string in stanza file format. - xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Generating new hostinfo string.\n"); + setrsp_infostr("[PCM nodes mgmt]Generating new hostinfo string."); 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"); + setrsp_infostr("[PCM nodes mgmt]call mkdef to create pcm nodes."); my $retref = xCAT::Utils->runxcmd({command=>["mkdef"], stdin=>[$retstr_gen], arg=>['-z']}, $request_command, 0, 1); my @nodelist = keys %hostinfo_dict; - xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call nodemgmt plugins.\n"); + setrsp_infostr("[PCM nodes mgmt]call nodemgmt plugins."); $retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_add"], node=>\@nodelist}, $request_command, 0, 1); $retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_finished"], node=>\@nodelist}, $request_command, 0, 1); setrsp_success(\@nodelist); @@ -255,12 +292,12 @@ sub addhost_hostfile { #------------------------------------------------------- sub removehost{ my $nodes = $request->{node}; - xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Remove PCM nodes.\n"); + setrsp_infostr("[PCM nodes mgmt]Remove PCM nodes."); # For remove nodes, we should call 'nodemgmt' in front of 'noderm' - xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call nodemgmt plugins.\n"); + setrsp_infostr("[PCM nodes mgmt]call nodemgmt plugins."); my $retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_remove"], node=>$nodes}, $request_command, 0, 1); $retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_finished"], node=>$nodes}, $request_command, 0, 1); - xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call noderm to remove nodes.\n"); + setrsp_infostr("[PCM nodes mgmt]call noderm to remove nodes."); $retref = xCAT::Utils->runxcmd({command=>["noderm"], node=>$nodes}, $request_command, 0, 1); setrsp_success($nodes); } @@ -279,7 +316,7 @@ sub updatehost{ my $nodes = $request->{node}; my %updated_groups; - xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Update PCM nodes settings.\n"); + setrsp_infostr("[PCM nodes mgmt]Update PCM nodes settings."); # Parse arges. my $retstr = parse_args(); if ($retstr){ @@ -287,7 +324,6 @@ sub updatehost{ 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){ @@ -297,7 +333,7 @@ sub updatehost{ } # Get current templates for all nodes. - xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Read database to get groups for all nodes.\n"); + setrsp_infostr("[PCM nodes mgmt]Read database to get groups for all nodes."); my %groupdict; my $nodelstab = xCAT::Table->new('nodelist'); my $nodeshashref = $nodelstab->getNodesAttribs($nodes, ['groups']); @@ -326,13 +362,13 @@ sub updatehost{ } #update DataBase. - xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Update database records.\n"); + setrsp_infostr("[PCM nodes mgmt]Update database records."); 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"); + setrsp_infostr("[PCM nodes mgmt]call nodemgmt plugins."); my $retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_update"], node=>$nodes}, $request_command, 0, 1); $retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_finished"], node=>$nodes}, $request_command, 0, 1); setrsp_success($nodes); @@ -340,6 +376,314 @@ sub updatehost{ #------------------------------------------------------- +=head3 pcmdiscover_start + + Description : Start PCM discovery. If already started, return a failure. + User should specify networkprofile, hardwareprofile, + imageprofile, hostnameformat, rack, chassis, height and u so + that node's IP address will be generated automatcially + according to networkprofile, node's hardware settings will + be set according to hardware profile, node's os settings will + be set according to image profile, node's hostname will be + set according to hostnameformat and rank. And other node's + attribs will also be set according to rack, chassis, height and u. + Arguments : N/A + +=cut + +#------------------------------------------------------- +sub pcmdiscover_start{ + # Parse arges. + setrsp_infostr("[PCM nodes mgmt]PCM discovery started."); + my $retstr = parse_args(); + if ($retstr){ + setrsp_errormsg($retstr); + return; + } + + my @enabledparams = ('networkprofile', 'hardwareprofile', 'imageprofile', 'hostnameformat', 'rank', 'rack', 'chassis', 'height', 'u'); + foreach my $argname (keys %args_dict){ + if (! grep{ $_ eq $argname} @enabledparams){ + setrsp_errormsg("Illegal attribute $argname specified."); + return; + } + } + # mandatory arguments. + foreach my $key ('networkprofile', 'imageprofile', 'hostnameformat'){ + if (! exists $args_dict{$key}){ + setrsp_errormsg("argument $key must be specified"); + return; + } + } + + # Read DB to confirm the discover is not started yet. + my @sitevalues = xCAT::TableUtils->get_site_attribute("__PCMDiscover"); + if ($sitevalues[0]){ + setrsp_errormsg("PCM node discovery already started."); + return; + } + + # save discover args into table site. + my $valuestr = ""; + foreach (keys %args_dict){ + if($args_dict{$_}){ + $valuestr .= "$_:$args_dict{$_},"; + } + } + + my $sitetab = xCAT::Table->new('site',-create=>1); + $sitetab->setAttribs({"key" => "__PCMDiscover"}, {"value" => "$valuestr"}); + $sitetab->close(); + +} + +#------------------------------------------------------- + +=head3 pcmdiscover_stop + + Description : Stop PCM auto discover. This action will remove the + dababase flags. + Arguments : N/A + +=cut + +#------------------------------------------------------ +sub pcmdiscover_stop{ + # Read DB to confirm the discover is started. + my @sitevalues = xCAT::TableUtils->get_site_attribute("__PCMDiscover"); + if (! $sitevalues[0]){ + setrsp_errormsg("PCM node discovery not started yet."); + return; + } + + # remove site table records: discover flag. + my $sitetab=xCAT::Table->new("site"); + my %keyhash; + $keyhash{'key'} = "__PCMDiscover"; + $sitetab->delEntries(\%keyhash); + $sitetab->commit(); + + # Update node's attributes, remove from gruop "__PCMDiscover". + # we'll call rmdef so that node's groupinfo in table nodelist will be updated automatically. + my @nodes = xCAT::NodeRange::noderange('__PCMDiscover'); + if (@nodes){ + # There are some nodes discvoered. + my $retref = xCAT::Utils->runxcmd({command=>["rmdef"], arg=>["-t", "group", "-o", "__PCMDiscover"]}, $request_command, 0, 1); + } +} + +#------------------------------------------------------- + +=head3 pcmdiscover_nodels + + Description : List all discovered PCM nodes. + Arguments : N/A + +=cut + +#------------------------------------------------------- +sub pcmdiscover_nodels{ + # Read DB to confirm the discover is started. + my @sitevalues = (); + @sitevalues = xCAT::TableUtils->get_site_attribute("__PCMDiscover"); + if (! $sitevalues[0]){ + setrsp_errormsg("PCM node discovery not started yet."); + return; + } + + my @nodes = xCAT::NodeRange::noderange('__PCMDiscover'); + my $mactab = xCAT::Table->new("mac"); + my $macsref = $mactab->getNodesAttribs(\@nodes, ['mac']); + my $nodelisttab = xCAT::Table->new("nodelist"); + my $statusref = $nodelisttab->getNodesAttribs(\@nodes, ['status']); + + my %rsp = (); + my %rspentry = (); + foreach (@nodes){ + if (! $_){ + next; + } + $rspentry{"node"} = $_; + #TODO: get provisioning mac. + $rspentry{"mac"} = $macsref->{$_}->[0]->{"mac"}; + + if ($statusref->{$_}->[0]){ + $rspentry{"status"} = $statusref->{$_}->[0]; + } else{ + $rspentry{"status"} = "defined"; + } + $callback->(\%rspentry); + } +} + +#------------------------------------------------------- + +=head3 findme + + Description : The default interface for node discovery. + We must implement this method so that + PCM nodes's findme request can be answered + while PCM discovery is running. + Arguments : N/A + +=cut + +#------------------------------------------------------- +sub findme{ + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]PCM discover: Start.\n"); + # Read DB to confirm the discover is started. + my @sitevalues = xCAT::TableUtils->get_site_attribute("__PCMDiscover"); + if (! @sitevalues){ + setrsp_errormsg("PCM node discovery not started yet."); + return; + } + + # We store node profiles in site table, key is "__PCMDiscover" + my @profilerecords = split(',', $sitevalues[0]); + foreach (@profilerecords){ + if ($_){ + my ($profilename, $profilevalue) = split(':', $_); + if ($profilename and $profilevalue){ + $args_dict{$profilename} = $profilevalue; + } + } + } + + # Get database records: all hostnames, all ips, all racks... + # To improve performance, we should initalize a daemon later?? + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]PCM discover: 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; + foreach (keys %allmacs){ + my @hostentries = split(/\|/, $_); + foreach my $hostandmac ( @hostentries){ + my ($macstr, $machostname) = split("!", $hostandmac); + $allmacs{$macstr} = 0; + } + } + $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); + + #$recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('rack', 'rackname'); + #%allracks = %$recordsref; + %allracks = (); + $recordsref = xCAT::PCMNodeMgmtUtils->get_all_chassis(1); + %allchassis = %$recordsref; + + my @enabledparams = ('networkprofile', 'hardwareprofile', 'imageprofile', 'hostnameformat', 'rack', 'chassis', 'u', 'height', 'rank'); + foreach my $argname (keys %args_dict){ + if (! grep{ $_ eq $argname} @enabledparams){ + setrsp_errormsg("Illegal attribute $argname specified."); + return; + } + } + # mandatory arguments. + foreach my $key ('networkprofile', 'imageprofile', 'hostnameformat'){ + if (! exists $args_dict{$key}){ + setrsp_errormsg("argument $key must be specified"); + return; + } + } + + # set default value for rack, startunit and height if not specified. + if (exists $args_dict{'rack'}){ + if (! exists $allracks{$args_dict{'rack'}}){ + setrsp_errormsg("Specified rack $args_dict{'rack'} not defined"); + return; + } + }else{ + # set default rack. + #TODO : how to set default rack. + } + if (!exists $args_dict{'u'}){ + $args_dict{'u'} = 1; + } + if (!exists $args_dict{'height'}){ + $args_dict{'height'} = 1; + } + # chassis jdugement. + if (exists $args_dict{'chassis'}){ + if (! exists $allchassis{$args_dict{'chassis'}}){ + setrsp_errormsg("Specified chassis $args_dict{'chassis'} not defined"); + return; + } + } + + # Get discovered client IP and MAC + my $ip = $request->{'_xcat_clientip'}; + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]PCM discover: _xcat_clientip is $ip.\n"); + my $mac = ''; + my $arptable = `/sbin/arp -n`; + my @arpents = split /\n/,$arptable; + foreach (@arpents) { + if (m/^($ip)\s+\S+\s+(\S+)\s/) { + $mac=$2; + last; + } + } + if (! $mac){ + setrsp_errormsg("[PCM nodes mgmt]PCM discover: Can not get mac address of this node."); + return; + } + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]PCM discover: mac is $mac.\n"); + if ( exists $allmacs{$mac}){ + setrsp_errormsg("Discovered MAC $mac already exists in database."); + return; + } + + # Assign TMPHOSTS9999 as a temporary hostname, in parse_hsots_string, + # it will detect this and arrange a real hostname for it. + my $raw_hostinfo_str = "TMPHOSTS9999:\n mac=$mac\n"; + # Append rack, chassis, unit, height into host info string. + foreach my $key ('rack', 'chassis', 'u', 'height'){ + if(exists($args_dict{$key})){ + $raw_hostinfo_str .= " $key=$args_dict{$key}\n"; + } + } + my ($hostinfo_dict_ref, $invalid_records_ref) = parse_hosts_string($raw_hostinfo_str); + my %hostinfo_dict = %$hostinfo_dict_ref; + # Create the real hostinfo string in stanza file format. + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]PCM discover: Generating new hostinfo string.\n"); + my ($retcode_gen, $retstr_gen) = gen_new_hostinfo_string($hostinfo_dict_ref); + 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"); + my $retref = xCAT::Utils->runxcmd({command=>["mkdef"], stdin=>[$retstr_gen], arg=>['-z']}, $request_command, 0, 1); + # increase unit automatically. + $args_dict{'u'} = $args_dict{'u'} + $args_dict{'height'}; + + my @nodelist = keys %hostinfo_dict; + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call nodemgmt plugins.\n"); + $retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_add"], node=>\@nodelist}, $request_command, 0, 1); + $retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_finished"], node=>\@nodelist}, $request_command, 0, 1); + + # call discover to notify client. + xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call discovered request.\n"); + $request->{"command"} = ["discovered"]; + $request->{"node"} = \@nodelist; + $retref = xCAT::Utils->runxcmd($request, $request_command, 0, 1); + + # Set discovered flag. + my $nodegroupstr = $hostinfo_dict{$nodelist[0]}{"groups"}; + my $nodelstab = xCAT::Table->new('nodelist',-create=>1); + $nodelstab->setNodeAttribs($nodelist[0],{groups=>$nodegroupstr.",__PCMDiscover"}); + $nodelstab->close(); +} + +#------------------------------------------------------- + =head3 replace_item_in_array Description : Replace an item in a list with new value. This item should match specified pattern. @@ -528,9 +872,7 @@ sub parse_hosts_string{ 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 $nameformat = $args_dict{'hostnameformat'}; my $nameformattype = xCAT::PCMNodeMgmtUtils->get_hostname_format_type($nameformat); my %freehostnames; @@ -580,7 +922,11 @@ sub parse_hosts_string{ # Generate hostnames based on numric hostname format. if (! exists $freehostnames{$numricformat}){ - $freehostnames{$numricformat} = xCAT::PCMNodeMgmtUtils->genhosts_with_numric_tmpl($numricformat); + my $rank = 0; + if (exists($args_dict{'rank'})){ + $rank = $args_dict{'rank'}; + } + $freehostnames{$numricformat} = xCAT::PCMNodeMgmtUtils->genhosts_with_numric_tmpl($numricformat, $rank); } my $hostnamelistref = $freehostnames{$numricformat}; my $nexthostname = shift @$hostnamelistref; @@ -651,19 +997,28 @@ sub validate_node_entry{ }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"; } + # rack must be specified with chassis or unit + height. + if (exists $node_entry{"chassis"}){ + } elsif (exists $node_entry{"height"} and exists $node_entry{"u"}){ + } else { + return "Rack must be specified together with chassis or height + u "; + } }elsif ($_ eq "chassis"){ if (not exists $allchassis{$node_entry{$_}}){ return "Specified chassis $node_entry{$_} not defined"; } - }elsif ($_ eq "unit"){ + # Chassis must not be specified with unit and height. + if (exists $node_entry{"height"} and exists $node_entry{"u"}){ + return "Chassis should not be specified together with height + u"; + } + }elsif ($_ eq "u"){ # Not a valid number. if (!($node_entry{$_} =~ /^\d+$/)){ - return "Specified unit $node_entry{$_} is a invalid number"; + return "Specified u $node_entry{$_} is a invalid number"; } }elsif ($_ eq "height"){ # Not a valid number. @@ -674,15 +1029,6 @@ sub validate_node_entry{ 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; @@ -706,6 +1052,7 @@ sub setrsp_invalidrecords my $master=xCAT::TableUtils->get_site_Master(); # The total number of invalid records. + $rsp->{error} = "Some error records detected"; $rsp->{invalid_records_num} = scalar @$recordsref; # We write details of invalid records into a file. @@ -740,6 +1087,26 @@ sub setrsp_errormsg $callback->($rsp); } +#------------------------------------------------------- + +=head3 setrsp_infostr + + Description : Set response for a info string. + Arguments : infostr - The info string.. + +=cut + +#------------------------------------------------------- +sub setrsp_infostr +{ + my $infostr = shift; + my $rsp; + xCAT::MsgUtils->message('S', "$infostr\n"); + $rsp->{info}->[0] = $infostr; + $callback->($rsp); +} + + #------------------------------------------------------- =head3 setrsp_success