2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-21 19:22:05 +00:00
2024-05-07 16:19:25 +02:00

1692 lines
57 KiB
Perl

#!/usr/bin/env perl
## IBM(c) 20013 EPL license http://www.eclipse.org/legal/epl-v10.html
#
# This plugin is used to handle the sequencial discovery. During the discovery,
# the nodes should be powered on one by one, sequencial discovery plugin will
# discover the nodes one by one and define them to xCAT DB.
# For the new discovered node but NOT handled by xCAT plugin,
# it will be recorded to discoverydata table.
#
package xCAT_plugin::seqdiscovery;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use strict;
use Getopt::Long;
use XML::Simple;
$XML::Simple::PREFERRED_PARSER = 'XML::Parser';
use lib "$::XCATROOT/lib/perl";
use xCAT::NodeRange;
use xCAT::Table;
use xCAT::NetworkUtils;
use xCAT::MsgUtils;
use xCAT::Utils;
use xCAT::DiscoveryUtils;
use xCAT::NodeRange qw/noderange/;
require xCAT::data::ibmhwtypes;
use Time::HiRes qw(gettimeofday sleep);
sub handled_commands {
return {
findme => 'seqdiscovery',
nodediscoverstart => 'seqdiscovery',
nodediscoverstop => 'seqdiscovery',
nodediscoverls => 'seqdiscovery',
nodediscoverstatus => 'seqdiscovery',
nodediscoverdef => 'seqdiscovery',
}
}
=head3 findme
Handle the request form node to map and define the request to a node
=cut
sub findme {
my $request = shift;
my $callback = shift;
my $subreq = shift;
my @SEQdiscover = xCAT::TableUtils->get_site_attribute("__SEQDiscover");
my @PCMdiscover = xCAT::TableUtils->get_site_attribute("__PCMDiscover");
my @ZVMDiscover = xCAT::TableUtils->get_site_attribute("__ZVMDiscover");
if (defined($request->{discoverymethod}) and defined($request->{discoverymethod}->[0]) and ($request->{discoverymethod}->[0] ne 'undef')) {
# The findme request had been processed by other module, just return
return;
}
unless ($SEQdiscover[0]) {
return;
}
# Get the parameters for the sequential discovery
my %param;
my @params = split(',', $SEQdiscover[0]);
foreach (@params) {
my ($name, $value) = split('=', $_);
$param{$name} = $value;
}
my $mac;
my $ip = $request->{'_xcat_clientip'};
if (defined $request->{nodetype} and $request->{nodetype}->[0] eq 'virtual') {
xCAT::MsgUtils->message("S", "xcat.discovery.seqdiscovery: ($request->{_xcat_clientmac}->[0]) Error: virtual machines is not supported");
return;
}
my $arptable;
if (-x "/usr/sbin/arp") {
$arptable = `/usr/sbin/arp -n`;
}
else {
$arptable = `/sbin/arp -n`;
}
my @arpents = split /\n/, $arptable;
foreach (@arpents) {
if (m/^($ip)\s+\S+\s+(\S+)\s/) {
$mac = $2;
last;
}
}
unless ($mac) {
xCAT::MsgUtils->message("S", "xcat.discovery.seqdiscovery: ($request->{_xcat_clientmac}->[0]) Error: Could not find mac of the $ip");
return;
}
# check whether the mac could map to a node
my $mactab = xCAT::Table->new('mac');
unless ($mactab) {
xCAT::MsgUtils->message("S", "Discovery Error: Could not open table: mac.");
}
my $node;
my @macs = $mactab->getAllAttribs('node', 'mac');
# for each entry: 34:40:b5:be:db:b0!*NOIP*|34:40:b5:be:db:b0!*NOIP*
foreach my $macnode (@macs) {
my @macents = split('\|', $macnode->{'mac'});
foreach my $macent (@macents) {
my ($usedmac) = split('!', $macent);
if ($usedmac =~ /$mac/i) {
$node = $macnode->{'node'};
last;
}
}
}
my @allnodes;
unless ($node) {
# get a free node
@allnodes = getfreenodes($param{'noderange'}, "all");
if (@allnodes) {
$node = $allnodes[0];
}
}
my $bmc_node = undef;
my @bmc_nodes = ();
if ($request->{'mtm'}->[0] and $request->{'serial'}->[0]) {
my $mtms = $request->{'mtm'}->[0] . "*" . $request->{'serial'}->[0];
my $tmp_nodes = $::XCATVPDHASH{$mtms};
foreach (@$tmp_nodes) {
if ($::XCATMPHASH{$_}) {
push @bmc_nodes, $_;
}
}
}
if ($request->{'bmcmac'}->[0]) {
my $bmcmac = lc($request->{'bmcmac'}->[0]);
$bmcmac =~ s/\://g;
my $tmp_node = "node-$bmcmac";
push @bmc_nodes, $tmp_node if ($::XCATMPHASH{$tmp_node});
}
$bmc_node = join(",", @bmc_nodes);
if ($node) {
my $skiphostip;
my $skipbmcip;
my $bmcname;
# check the host ip and bmc
my $hosttab = xCAT::Table->new('hosts');
unless ($hosttab) {
xCAT::MsgUtils->message("S", "Discovery Error: Could not open table: hosts.");
}
my $hostip = getpredefips([$node], "host");
foreach (keys %$hostip) {
if ($hostip->{$_} eq "$node") {
$skiphostip = 1;
}
}
my $ipmitab = xCAT::Table->new('ipmi');
unless ($ipmitab) {
xCAT::MsgUtils->message("S", "Discovery Error: Could not open table: ipmi.");
}
# check the bmc definition in the ipmi table
my $ipmient = $ipmitab->getNodeAttribs($node, ['bmc']);
if (defined($ipmient->{'bmc'})) {
$bmcname = $ipmient->{'bmc'};
if ($bmcname =~ /\d+\.\d+\.\d+\.\d+/) {
$skipbmcip = 1;
} else {
my $bmcip = getpredefips([$node], "bmc");
foreach (keys %$bmcip) {
if ($bmcip->{$_} eq $bmcname) {
$skipbmcip = 1;
}
}
}
}
# set the host ip if the node does not have
unless ($skiphostip) {
my $hostip = getfreeips($param{'hostiprange'}, \@allnodes, "host");
unless ($hostip) {
nodediscoverstop($callback, undef, "host ips");
return;
}
$hosttab->setNodeAttribs($node, { ip => $hostip });
$hosttab->commit();
}
# set the bmc ip if the node does not have
unless ($skipbmcip) {
unless ($bmcname) {
# set the default bmc name
$bmcname = $node . "-bmc";
}
my $bmcip = getfreeips($param{'bmciprange'}, \@allnodes, "bmc");
unless ($bmcip) {
nodediscoverstop($callback, undef, "bmc ips");
return;
}
# for auto created bmc, just add it to hosts.otherinterfaces instead of adding a new bmc node
my $otherif = $hosttab->getNodeAttribs($node, ['otherinterfaces']);
my $updateotherif;
if ($otherif && defined($otherif->{'otherinterfaces'})) {
$updateotherif .= ",$bmcname:$bmcip";
} else {
$updateotherif = "$bmcname:$bmcip";
}
$hosttab->setNodeAttribs($node, { otherinterfaces => $updateotherif });
$hosttab->commit();
# set the bmc to the ipmi table
$ipmitab->setNodeAttribs($node, { bmc => $bmcname });
$ipmitab->commit();
}
# update the host ip pair to /etc/hosts, it's necessary for discovered and makedhcp commands
my @newhosts = ($node, $bmcname);
if (@newhosts) {
my $req;
$req->{command} = ['makehosts'];
$req->{node} = \@newhosts;
$subreq->($req);
# run makedns only when -n flag was specified with nodediscoverstart,
# dns=yes in site.__SEQDiscover
if (defined($param{'dns'}) && ($param{'dns'} eq 'yes'))
{
my $req;
$req->{command} = ['makedns'];
$req->{node} = \@newhosts;
$subreq->($req);
}
}
# set the specific attributes from parameters
my $updateparams;
my %setpos;
if (defined($param{'rack'})) {
$setpos{'rack'} = $param{'rack'};
}
if (defined($param{'chassis'})) {
$setpos{'chassis'} = $param{'chassis'};
}
if (defined($param{'height'})) {
$setpos{'height'} = $param{'height'};
}
if (defined($param{'unit'})) {
$setpos{'u'} = $param{'unit'};
if (defined($param{'height'})) {
$param{'unit'} += $param{'height'};
} else {
$param{'unit'} += 1;
}
$updateparams = 1;
}
if (keys %setpos) {
my $postab = xCAT::Table->new('nodepos');
unless ($postab) {
xCAT::MsgUtils->message("S", "Discovery Error: Could not open table: nodepos.");
}
$postab->setNodeAttribs($node, \%setpos);
$postab->close();
}
if ($updateparams) {
my $textparam;
foreach my $name (keys %param) {
$textparam .= "$name=$param{$name},";
}
$textparam =~ s/,\z//;
# Update the discovery parameters to the site.__SEQDiscover which will be used by nodediscoverls/status/stop and findme,
my $sitetab = xCAT::Table->new("site");
$sitetab->setAttribs({ "key" => "__SEQDiscover" }, { "value" => "$textparam" });
$sitetab->close();
}
#set the groups for the node
my $nltab = xCAT::Table->new('nodelist');
unless ($nltab) {
xCAT::MsgUtils->message("S", "Discovery Error: Could not open table: nodelist.");
}
if (defined($param{'groups'})) {
$nltab->setNodeAttribs($node, { groups => $param{'groups'} });
} else {
# just set the groups attribute when there was no groups value was set
my $nlent = $nltab->getNodeAttribs($node, ['groups']);
if (!$nlent || !$nlent->{'groups'}) {
$nltab->setNodeAttribs($node, { groups => "all" });
}
}
# update node groups with pre-defined groups
if (defined($param{'mtm'})) {
my @list = ();
my $tmp_group = xCAT::data::ibmhwtypes::parse_group($param{'mtm'});
if (defined($tmp_group)) {
xCAT::TableUtils->updatenodegroups($node, $nltab, $tmp_group);
}
}
# set the mgt for the node
my $hmtab = xCAT::Table->new('nodehm');
unless ($hmtab) {
xCAT::MsgUtils->message("S", "Discovery Error: Could not open table: nodehm.");
}
my $hment = $hmtab->getNodeAttribs($node, ['mgt']);
if (!$hment || !$hment->{'mgt'}) {
$hmtab->setNodeAttribs($node, { mgt => "ipmi" });
}
my $chaintab = xCAT::Table->new('chain');
# Could not open chain table, but do not fail the discovery process
if (!$chaintab) {
xCAT::MsgUtils->message("S", "Error: could not open chain table");
} else {
my $chainent = $chaintab->getNodeAttribs($node, ['chain']);
my $nodechain = '';
my $origchain = '';
if (defined($chainent->{'chain'})) {
$nodechain = $chainent->{'chain'};
$origchain = $nodechain;
}
# If the bmciprange=xxx is specified with nodediscoverstart command,
# add the runcmd=bmcsetup at the beginning of the chain attribute
# the skipbmcsetup could be used to skip the bmcsetup for the node
if ($param{'bmciprange'} && !$param{'skipbmcsetup'}) {
if (!$nodechain) {
$nodechain = "runcmd=bmcsetup";
} else {
# do not add duplicate runcmd=bmcsetup in the chain attribute
if ($nodechain !~ /runcmd=bmcsetup/) {
$nodechain = "runcmd=bmcsetup," . $nodechain;
}
}
} # end if $param{'bmciprange'}
# Remove the runcmd=bmcsetup from chain if skipbmcsetup is specified
# this is useful for predefined configuration, or attributes inherit from groups
if ($param{'skipbmcsetup'}) {
if ($nodechain =~ /runcmd=bmcsetup/) {
$nodechain =~ s/runcmd=bmcsetup,//;
$nodechain =~ s/runcmd=bmcsetup//;
}
}
# If the osimage=xxx is specified with nodediscoverstart command,
# append the osimage=xxx at the end of the chain attribute
if ($param{'osimage'}) {
if (!$nodechain) {
$nodechain = "osimage=$param{'osimage'}";
} else {
# do not add multiple osimage=xxx in the chain attribute
# replace the old one with the new one
if ($nodechain !~ /osimage=/) {
$nodechain = $nodechain . ",osimage=$param{'osimage'}";
} else {
$nodechain =~ s/osimage=\w+/osimage=$param{'osimage'}/;
}
}
} # end if $param{'osimage'}
# Update the table only when the chain attribute is changed
if ($nodechain ne $origchain) {
$chaintab->setNodeAttribs($node, { chain => $nodechain });
}
$chaintab->close();
}
# call the discovered command to update the discovery request to a node
xCAT::MsgUtils->message("S", "xcat.discovery.seqdiscovery: ($request->{_xcat_clientmac}->[0]) Found node: $node");
$request->{discoverymethod} = ['sequential'];
my $req = {%$request};
$req->{command} = ['discovered'];
$req->{noderange} = [$node];
$req->{bmc_node} = [$bmc_node];
$req->{updateswitch} = ['yes'];
$subreq->($req);
if (defined($req->{error})) {
$request->{error}->[0] = '1';
$request->{error_msg}->[0] = $req->{error_msg}->[0];
}
%{$req} = (); #Clear req structure, it's done..
undef $mactab;
} else {
nodediscoverstop($callback, undef, "node names");
xCAT::MsgUtils->message("S", "xcat.discovery.seqdiscovery: ($request->{_xcat_clientmac}->[0]) Warning: Could not find any nodes using sequential-based discovery");
return;
}
xCAT::MsgUtils->message("S", "Sequential Discovery: Done");
}
=head3 displayver
Display the version information
=cut
sub displayver {
my $callback = shift;
my $version = xCAT::Utils->Version();
my $rsp;
push @{ $rsp->{data} }, $version;
xCAT::MsgUtils->message("I", $rsp, $callback);
}
=head3 nodediscoverstart
Initiate the sequencial discovery process
=cut
sub nodediscoverstart {
my $callback = shift;
my $args = shift;
my $usage = sub {
my $cb = shift;
my $msg = shift;
my $rsp;
if ($msg) {
push @{ $rsp->{data} }, $msg;
xCAT::MsgUtils->message("E", $rsp, $cb, 1);
}
my $usageinfo = "nodediscoverstart: Start a discovery process: Sequential, Profile or z/VM.
Usage:
Common:
nodediscoverstart [-h|--help|-v|--version|-V|--verbose]
Sequential Discovery:
nodediscoverstart noderange=<noderange> [hostiprange=<hostiprange>] [bmciprange=<bmciprange>] [groups=<groups>] [rack=<rack>] [chassis=<chassis>] [height=<height>] [unit=<unit>] [osimage=<osimagename>] [-n|--dns] [-s|--skipbmcsetup] [-V|--verbose]
Profile Discovery:
nodediscoverstart networkprofile=<networkprofile> imageprofile=<imageprofile> hostnameformat=<hostnameformat> [hardwareprofile=<hardwareprofile>] [groups=<groups>] [rack=<rack>] [chassis=<chassis>] [height=<height>] [unit=<unit>] [rank=rank-num]
z/VM Discovery:
nodediscoverstart zvmhost=<noderange> [defineto=both] [groups=<groups>] [ipfilter=<filter>] [openstackoperands=<operands>] [useridfilter=<filter>]
nodediscoverstart zvmhost=<noderange> defineto=xcatonly [groups=<groups>] [ipfilter=<filter>] [nodenameformat=<nodenameformat>] [useridfilter=<filter>]
nodediscoverstart zvmhost=<noderange> defineto=openstackonly [openstackoperands=<operands>]";
$rsp = ();
push @{ $rsp->{data} }, $usageinfo;
xCAT::MsgUtils->message("I", $rsp, $cb);
};
# valid attributes for seqdiscovery
my %validargs = (
'noderange' => 1,
'hostiprange' => 1,
'bmciprange' => 1,
'groups' => 1,
'rack' => 1,
'chassis' => 1,
'height' => 1,
'unit' => 1,
'osimage' => 1,
);
if ($args) {
@ARGV = @$args;
}
my ($help, $ver);
if (!GetOptions(
'h|help' => \$help,
'V|verbose' => \$::VERBOSE,
'-n|dns' => \$::DNS,
'-s|skipbmcsetup' => \$::SKIPBMCSETUP,
'v|version' => \$ver)) {
$usage->($callback);
return;
}
if ($help) {
$usage->($callback);
return;
}
if ($ver) {
&displayver($callback);
return;
}
my %orgargs;
foreach (@ARGV) {
my ($name, $value) = split('=', $_);
$orgargs{$name} = $value;
}
# Check the zvmhost= has been specified which is the flag that this is for z/VM discovery.
# Otherwise, fall thru to handle either sequential or profile discovery.
if ( defined( $orgargs{zvmhost} ) ) {
return;
}
# Check the noderange= has been specified which is the flag that this is for sequential discovery
# Otherwise try to check the whether the networkprofile || hardwareprofile || imageprofile
# has been passed, if yes, return to profile discovery
unless (defined($orgargs{noderange})) {
if (defined($orgargs{networkprofile}) || defined($orgargs{hostnameformat}) || defined($orgargs{imageprofile})) {
# just return that make profile-based discovery to handle it
return;
} else {
$usage->($callback, "For sequential discovery, the \'noderange\' option must be specified.");
$usage->($callback, "For z/VM discovery, the \'zvmhost\' option must be specified.");
return;
}
}
my %param; # The valid parameters
my $textparam; # The valid parameters in 'name=value,name=value...' format
# Check the validate of parameters
foreach my $name (keys %orgargs) {
unless (defined($validargs{$name})) {
$usage->($callback, "Invalid arguement \"$name\".");
return;
}
unless (defined($orgargs{$name})) {
$usage->($callback, "The parameter \"$name\" need a value.");
return;
}
# keep the valid parameters
$param{$name} = $orgargs{$name};
$textparam .= $name . '=' . $param{$name} . ',';
}
# If the -n flag is specified,
# add setupdns=yes into site.__SEQDiscover
if ($::DNS)
{
$textparam .= "dns=yes,";
}
# If the -s flag is specified,
# add skipbmcsetup=yes into site.__SEQDiscover
if ($::SKIPBMCSETUP)
{
$textparam .= "skipbmcsetup=yes,";
}
$textparam =~ s/,\z//;
# Check the running of profile-based discovery
my @PCMdiscover = xCAT::TableUtils->get_site_attribute("__PCMDiscover");
if ($PCMdiscover[0]) {
my $rsp;
push @{ $rsp->{data} }, "Sequentail Discovery cannot be run together with Profile-based discovery";
xCAT::MsgUtils->message("E", $rsp, $callback, 1);
return;
}
# Check the running of sequential discovery
my @SEQdiscover = xCAT::TableUtils->get_site_attribute("__SEQDiscover");
if ($SEQdiscover[0]) {
my $rsp;
push @{ $rsp->{data} }, "Sequentail Discovery is running. If you want to rerun the discovery, stop the running discovery first.";
xCAT::MsgUtils->message("E", $rsp, $callback, 1);
return;
}
# Check that the dynamic range in the dhcpd.conf has been set correctly
# search all the network in the networks table that make sure the dynamic range for the deployment network has been set
# Set the discovery parameters to the site.__SEQDiscover which will be used by nodediscoverls/status/stop and findme,
my $sitetab = xCAT::Table->new("site");
$sitetab->setAttribs({ "key" => "__SEQDiscover" }, { "value" => "$textparam" });
$sitetab->close();
# Clean the entries which discovery method is 'sequential' from the discoverydata table
my $distab = xCAT::Table->new("discoverydata");
$distab->delEntries({ method => 'sequential' });
$distab->commit();
# Calculate the available node name and IPs
my @freenodes = getfreenodes($param{'noderange'}, "all");
my @freehostips = getfreeips($param{'hostiprange'}, \@freenodes, "host", "all");
my @freebmcips = getfreeips($param{'bmciprange'}, \@freenodes, "bmc", "all");
#xCAT::MsgUtils->message("S", "Sequential Discovery: Start");
my $rsp;
push @{ $rsp->{data} }, "Sequential Discovery: Started:";
push @{ $rsp->{data} }, " Number of free node names: " . ($#freenodes + 1);
if ($param{'hostiprange'}) {
if (@freehostips) {
push @{ $rsp->{data} }, " Number of free host ips: " . ($#freehostips + 1);
} else {
push @{ $rsp->{data} }, " No free host ips.";
}
}
if ($param{'bmciprange'}) {
if (@freebmcips) {
push @{ $rsp->{data} }, " Number of free bmc ips: " . ($#freebmcips + 1);
} else {
push @{ $rsp->{data} }, " No free bmc ips.";
}
}
xCAT::MsgUtils->message("I", $rsp, $callback);
if ($::VERBOSE) {
# dispaly the free nodes
# get predefined host ip
my %prehostips;
my $prehosts = getpredefips(\@freenodes, "host"); #pre{ip} = nodename
foreach (keys %$prehosts) {
$prehostips{ $prehosts->{$_} } = $_; #pre{nodename} = ip
}
# get predefined bmc ip
my %prebmcips;
my $prebmcs = getpredefips(\@freenodes, "bmc"); #pre{ip} = bmcname
foreach (keys %$prebmcs) {
$prebmcips{ $prebmcs->{$_} } = $_; #pre{bmcname} = ip
}
# get the bmc of nodes
my $ipmitab = xCAT::Table->new('ipmi');
my $ipmient;
if ($ipmitab) {
$ipmient = $ipmitab->getNodesAttribs(\@freenodes, ['bmc']);
}
my $vrsp;
push @{ $vrsp->{data} }, "\n====================Free Nodes===================";
push @{ $vrsp->{data} }, sprintf("%-20s%-20s%-20s", "NODE", "HOST IP", "BMC IP");
my $index = 0;
foreach (@freenodes) {
my $hostip;
my $bmcip;
# if predefined, use it; otherwise pop out one from the free ip list
if (defined($prehostips{$_})) {
$hostip = $prehostips{$_};
} else {
while (($hostip = shift @freehostips)) {
if (!defined($prehosts->{$hostip})) { last; }
}
unless ($hostip) {
$hostip = "--no free--";
}
}
# if predefined, use it; otherwise pop out one from the free ip list
my $bmcname;
if (defined($ipmient->{$_}->[0]->{'bmc'})) {
$bmcname = $ipmient->{$_}->[0]->{'bmc'};
if ($bmcname =~ /\d+\.\d+\.\d+\.\d+/) {
$bmcip = $bmcname;
} elsif (defined($prebmcips{$bmcname})) {
$bmcip = $prebmcips{$bmcname};
}
}
unless ($bmcip) {
while (($bmcip = shift @freebmcips)) {
if (!defined($prebmcs->{$bmcip})) { last; }
}
unless ($bmcip) {
$bmcip = "--no free--";
}
}
push @{ $vrsp->{data} }, sprintf("%-20s%-20s%-20s", $_, $hostip, $bmcip);
$index++;
}
xCAT::MsgUtils->message("I", $vrsp, $callback);
}
}
=head3 nodediscoverstop
Stop the sequencial discovery process
=cut
sub nodediscoverstop {
my $callback = shift;
my $args = shift;
my $auto = shift;
my $usage = sub {
my $cb = shift;
my $msg = shift;
my $rsp;
if ($msg) {
push @{ $rsp->{data} }, $msg;
xCAT::MsgUtils->message("E", $rsp, $cb, 1);
}
my $usageinfo = "nodediscoverstop: Stop the running discovery: Profile, Sequential and z/VM.
Usage:
Common:
nodediscoverstop [-h|--help|-v|--version]
z/VM discovery:
nodediscoverstop [-z|--zvmhost <noderange>]";
$rsp = ();
push @{ $rsp->{data} }, $usageinfo;
xCAT::MsgUtils->message("I", $rsp, $cb);
};
if ($args) {
@ARGV = @$args;
}
my ($help, $ver);
if (!GetOptions(
'h|help' => \$help,
'V|verbose' => \$::VERBOSE,
'v|version' => \$ver)) {
$usage->($callback);
return;
}
if ($help) {
$usage->($callback);
return;
}
if ($ver) {
&displayver($callback);
return;
}
# Check the running of sequential discovery
my @SEQDiscover = xCAT::TableUtils->get_site_attribute("__SEQDiscover");
my @PCMDiscover = xCAT::TableUtils->get_site_attribute("__PCMDiscover");
my @ZVMDiscover = xCAT::TableUtils->get_site_attribute("__ZVMDiscover");
if ($PCMDiscover[0] or $ZVMDiscover[0]) {
# return directly that the other discovery will handle the stop function.
return;
} elsif (!$SEQDiscover[0]) {
# Neither of profile nor sequential was running
my $rsp;
push @{ $rsp->{data} }, "Sequential Discovery is stopped.";
push @{ $rsp->{data} }, "Profile Discovery is stopped.";
push @{$rsp->{data}}, "z/VM Discovery is stopped.";
xCAT::MsgUtils->message("E", $rsp, $callback, 1);
return;
}
my $DBname = xCAT::Utils->get_DBName; # support for DB2
# Go through discoverydata table and display the sequential discovery entries
my $distab = xCAT::Table->new('discoverydata');
unless ($distab) {
my $rsp;
push @{ $rsp->{data} }, "Discovery Error: Could not open table: discoverydata.";
xCAT::MsgUtils->message("E", $rsp, $callback);
return;
}
my @disdata;
if ($DBname =~ /^DB2/) {
@disdata = $distab->getAllAttribsWhere("\"method\" = 'sequential'", 'node', 'mtm', 'serial');
} else {
@disdata = $distab->getAllAttribsWhere("method='sequential'", 'node', 'mtm', 'serial');
}
my @discoverednodes;
foreach (@disdata) {
push @discoverednodes, sprintf(" %-20s%-10s%-10s", $_->{'node'}, $_->{'mtm'}, substr($_->{'serial'}, 0, 8),);
}
my $rsp;
push @{ $rsp->{data} }, "Discovered " . ($#discoverednodes + 1) . " nodes.";
if (@discoverednodes) {
push @{ $rsp->{data} }, sprintf(" %-20s%-10s%-10s", 'NODE', 'MTM', 'SERIAL');
foreach (@discoverednodes) {
push @{ $rsp->{data} }, "$_";
}
}
xCAT::MsgUtils->message("I", $rsp, $callback);
if ($auto) {
xCAT::MsgUtils->message("S", "Sequential Discovery: Auto Stopped because all $auto in the specified range have been assigned to discovered nodes. Run \'nodediscoverls -t seq\' to display the discovery result.");
} else {
xCAT::MsgUtils->message("S", "Sequential Discovery: Stoped.");
}
# Remove the site.__SEQDiscover
my $sitetab = xCAT::Table->new("site");
$sitetab->delEntries({ key => '__SEQDiscover' });
$sitetab->commit();
}
=head3 nodediscoverls
Display the discovered nodes. This supports sequential and z/VM and partially Profile discovery.
=cut
sub nodediscoverls {
my $callback = shift;
my $args = shift;
my $usage = sub {
my $cb = shift;
my $msg = shift;
my $rsp;
if ($msg) {
push @{ $rsp->{data} }, $msg;
xCAT::MsgUtils->message("E", $rsp, $cb, 1);
}
my $usageinfo = "nodediscoverls: list the discovered nodes.
Usage:
Common:
nodediscoverls
nodediscoverls [-h|--help|-v|--version]
nodediscoverls [-t seq|profile|switch|blade|manual|mtms|undef|zvm|all] [-l]
nodediscoverls [-u uuid] [-l]
z/VM:
nodediscoverls [-t zvm][-z|--zvmhost <noderange>] [-l]";
$rsp = ();
push @{ $rsp->{data} }, $usageinfo;
xCAT::MsgUtils->message("I", $rsp, $cb);
};
if ($args) {
@ARGV = @$args;
}
my ($type, $uuid, $long, $help, $ver, $zvmHost );
if (!GetOptions(
't=s' => \$type,
'u=s' => \$uuid,
'l' => \$long,
'h|help' => \$help,
'V|verbose' => \$::VERBOSE,
'v|version' => \$ver,
'z|zvmhost=s' => \$zvmHost )) {
$usage->($callback);
return;
}
if ($help) {
$usage->($callback);
return;
}
if ($ver) {
&displayver($callback);
return;
}
# If the type is specified, display the corresponding type of nodes
my ( @SEQDiscover, @ZVMDiscover );
if ($type) {
if ($type !~ /^(seq|profile|switch|blade|manual|mtms|undef|zvm|all)$/) {
$usage->($callback, "The discovery type \'$type\' is not supported.");
return;
}
} elsif ($uuid) {
} else {
# Check the running of sequential discovery
@SEQDiscover = xCAT::TableUtils->get_site_attribute("__SEQDiscover");
@ZVMDiscover = xCAT::TableUtils->get_site_attribute("__ZVMDiscover");
my @PCMDiscover = xCAT::TableUtils->get_site_attribute("__PCMDiscover");
if ($SEQDiscover[0]) {
$type = "seq";
} elsif ($PCMDiscover[0]) {
#return directly if profile discovery is running.
return;
} elsif ( $ZVMDiscover[0] ) {
# zvmdiscovery handles requests for a running z/VM discovery.
return;
} else {
# no type, no seq and no profile, then just display all
$type = "all";
}
}
# If a zvmHost was specified then let zvmdiscovery handle it.
# Specifying '-u' will keep processing within seqdiscovery.
if ( !$uuid && ( $zvmHost || ( $type && $type eq 'zvm' )) ) {
# zvmdiscovery handles request specific to z/VM.
return;
}
my $DBname = xCAT::Utils->get_DBName; # support for DB2
# Go through discoverydata table and display the discovery entries
my $distab = xCAT::Table->new('discoverydata');
unless ($distab) {
my $rsp;
push @{ $rsp->{data} }, "Discovery Error: Could not open table: discoverydata.";
xCAT::MsgUtils->message("E", $rsp, $callback);
return;
}
my @disdata;
my @disattrs;
if ($long) {
@disattrs = ('uuid', 'node', 'method', 'discoverytime', 'arch', 'cpucount', 'cputype', 'memory', 'mtm', 'serial', 'nicdriver', 'nicipv4', 'nichwaddr', 'nicpci', 'nicloc', 'niconboard', 'nicfirm', 'switchname', 'switchaddr', 'switchdesc', 'switchport');
} else {
@disattrs = ('uuid', 'node', 'method', 'mtm', 'serial');
}
if ($type) {
if ($type eq "all") {
@disdata = $distab->getAllAttribs(@disattrs);
} else {
$type = "sequential" if ($type =~ /^seq/);
if ($DBname =~ /^DB2/) {
@disdata = $distab->getAllAttribsWhere("\"method\" = '$type'", @disattrs);
} else {
@disdata = $distab->getAllAttribsWhere("method='$type'", @disattrs);
}
}
} elsif ($uuid) {
if ($DBname =~ /^DB2/) {
@disdata = $distab->getAllAttribsWhere("\"uuid\" = '$uuid'", @disattrs);
} else {
@disdata = $distab->getAllAttribsWhere("uuid='$uuid'", @disattrs);
}
}
my $discoverednum = $#disdata + 1;
my @discoverednodes;
foreach my $ent (@disdata) {
if ($long) {
foreach my $attr (@disattrs) {
if ($attr eq "uuid") {
push @discoverednodes, "Object uuid: $ent->{$attr}";
} elsif (defined($ent->{$attr})) {
push @discoverednodes, " $attr=$ent->{$attr}";
}
}
} else {
$ent->{'node'} = 'undef' unless ($ent->{'node'});
$ent->{'method'} = 'undef' unless ($ent->{'method'});
push @discoverednodes, sprintf(" %-40s%-20s%-15s%-10s %-20s", $ent->{'uuid'}, $ent->{'node'}, $ent->{'method'}, $ent->{'mtm'}, substr($ent->{'serial'}, 0, 19));
}
}
my $rsp;
if (($SEQDiscover[0] && $type eq "sequential" ) or
( $type && $type eq "all" )) {
push @{ $rsp->{data} }, "Discovered $discoverednum node.";
}
if (@discoverednodes) {
unless ($long) {
push @{ $rsp->{data} }, sprintf(" %-40s%-20s%-15s%-10s %-20s", 'UUID', 'NODE', 'METHOD', 'MTM', 'SERIAL');
}
foreach (@discoverednodes) {
push @{ $rsp->{data} }, "$_";
}
}
xCAT::MsgUtils->message("I", $rsp, $callback);
}
=head3 nodediscoverstatus
Display the discovery status
=cut
sub nodediscoverstatus {
my $callback = shift;
my $args = shift;
my $usage = sub {
my $cb = shift;
my $msg = shift;
my $rsp;
if ($msg) {
push @{ $rsp->{data} }, $msg;
xCAT::MsgUtils->message("E", $rsp, $cb, 1);
}
my $usageinfo = "nodediscoverstatus: Display the discovery process status.
Usage:
Common:
nodediscoverstatus [-h|--help|-v|--version]
z/VM
nodediscoverstatus [-z|--zvmhost <noderange>]";
$rsp = ();
push @{ $rsp->{data} }, $usageinfo;
xCAT::MsgUtils->message("I", $rsp, $cb);
};
if ($args) {
@ARGV = @$args;
}
my ($type, $uuid, $long, $help, $ver);
if (!GetOptions(
'h|help' => \$help,
'V|verbose' => \$::VERBOSE,
'v|version' => \$ver)) {
$usage->($callback);
return;
}
if ($help) {
$usage->($callback);
return;
}
if ($ver) {
&displayver($callback);
return;
}
# Check the running of sequential discovery
my @SEQDiscover = xCAT::TableUtils->get_site_attribute("__SEQDiscover");
my @PCMDiscover = xCAT::TableUtils->get_site_attribute("__PCMDiscover");
my @ZVMDiscover = xCAT::TableUtils->get_site_attribute("__ZVMDiscover");
if ($SEQDiscover[0]) {
my $rsp;
push @{ $rsp->{data} }, "Sequential discovery is running.";
push @{ $rsp->{data} }, " The parameters used for discovery: " . $SEQDiscover[0];
xCAT::MsgUtils->message("I", $rsp, $callback);
} elsif ($PCMDiscover[0]) {
my $rsp;
push @{ $rsp->{data} }, "Node discovery for all nodes using profiles is running";
xCAT::MsgUtils->message("I", $rsp, $callback);
} elsif ( $ZVMDiscover[0] ) {
# z/VM discovery is a more complex response so we let its handler return the response.
} else {
my $rsp;
push @{ $rsp->{data} }, "Sequential Discovery is stopped.";
push @{ $rsp->{data} }, "Profile Discovery is stopped.";
push @{$rsp->{data}}, "z/VM Discovery is stopped.";
xCAT::MsgUtils->message("I", $rsp, $callback);
}
}
=head3 nodediscoverdef
Define the undefined entry from the discoverydata table to a specific node
Or clean the discoverydata table
=cut
sub nodediscoverdef {
my $callback = shift;
my $subreq = shift;
my $args = shift;
my @inputZvmHosts; # Input list of z/VM host nodes to stop
my $zvmHost; # Small scope variable to temporarily hold a z/VM host node value
# The subroutine used to display the usage message
my $usage = sub {
my $cb = shift;
my $msg = shift;
my $rsp;
if ($msg) {
push @{ $rsp->{data} }, $msg;
xCAT::MsgUtils->message("E", $rsp, $cb, 1);
}
my $usageinfo = "nodediscoverdef: Define the undefined discovery request, or clean the discovery entries in the discoverydata table (Which can be displayed by nodediscoverls command).
Usage:
Common:
nodediscoverdef -u uuid -n node
nodediscoverdef -r -u uuid
nodediscoverdef -r -t {seq|profile|switch|blade|manual|undef|zvm|all}
nodediscoverdef [-h|--help|-v|--version]
z/VM:
nodediscoverdef -r -t zvm [-z|--zvmhost noderange]";
$rsp = ();
push @{ $rsp->{data} }, $usageinfo;
xCAT::MsgUtils->message("I", $rsp, $cb);
};
# Parse arguments
if ($args) {
@ARGV = @$args;
}
my ($type, $uuid, $node, $remove, $help, $ver);
if (!GetOptions(
'u=s' => \$uuid,
'n=s' => \$node,
't=s' => \$type,
'r' => \$remove,
'h|help' => \$help,
'V|verbose' => \$::VERBOSE,
'v|version' => \$ver,
'z|zvmhost=s' => \$zvmHost )) {
$usage->($callback);
return;
}
if ($help) {
$usage->($callback);
return;
}
if ($ver) {
&displayver($callback);
return;
}
my $DBname = xCAT::Utils->get_DBName; # support for DB2
# Put any specified zvmhosts into an array for later use.
if ( $zvmHost ) {
$type = 'zvm' if ( !$type );
if ( $type ne 'zvm' ) {
xCAT::MsgUtils->message("E", {data=>["Discovery Error: Type must be 'zvm' when '-z' or '--zvmhost' is specified."]}, $callback);
return;
}
if ( index( $zvmHost, ',' ) != -1 ) {
# Must have specified multiple host node names
my @hosts = split( /,/, $zvmHost );
foreach $zvmHost ( @hosts ) {
if ( !$zvmHost ) {
# Tolerate zvmhost value beginning with a comma.
# It is wrong but not worth an error message.
next;
}
push( @inputZvmHosts, $zvmHost );
}
} else {
push( @inputZvmHosts, $zvmHost );
}
}
# open the discoverydata table for the subsequent using
my $distab = xCAT::Table->new("discoverydata");
unless ($distab) {
xCAT::MsgUtils->message("S", "Discovery Error: Could not open table: discoverydata.");
return;
}
if ($remove) {
# handle the -r to remove the entries from discoverydata table
if (!($uuid || $type) || $node) {
$usage->($callback);
return;
}
if ($uuid && $type) {
$usage->($callback);
return;
}
if ($uuid) {
# handle the -r -u <uuid>
my @disdata;
if ($DBname =~ /^DB2/) {
@disdata = $distab->getAllAttribsWhere("\"uuid\" = '$uuid'", 'method');
} else {
@disdata = $distab->getAllAttribsWhere("uuid='$uuid'", 'method');
}
unless (@disdata) {
xCAT::MsgUtils->message("E", { data => ["Cannot find discovery entry with uuid equals [$uuid]."] }, $callback);
return;
}
$distab->delEntries({ uuid => $uuid });
$distab->commit();
} elsif ($type) {
# handle the -r -t <...>
if ($type !~ /^(seq|profile|switch|blade|manual|undef|zvm|all)$/) {
$usage->($callback, "The discovery type \'$type\' is not supported.");
return;
}
if ($type eq "all") {
# remove all the entries from discoverydata table
# there's no subroutine to remove all the entries from a table, so just make code to work around
# get all the entries first
my @disdata = $distab->getAllAttribs('uuid', 'method');
my %methodlist;
foreach my $ent (@disdata) {
if ($ent->{'method'}) {
# if the entry has 'method' att set, classify them and remove at once
$methodlist{ $ent->{'method'} } = 1;
} else {
# if 'method' is not set, remove the entry directly
$distab->delEntries({ uuid => $ent->{'uuid'} });
}
}
# remove entries which have method att been set
foreach (keys %methodlist) {
$distab->delEntries({ method => $_ });
}
$distab->commit();
} else {
# remove the specific type of discovery entries
if ($type =~ /^seq/) {
$type = "sequential";
}
# If a noderange of z/VM hosts was specified then delete
# all z/VM discovered systems for those hosts. 'zvm' type
# must be used when z/VM hosts are specified.
if ( @inputZvmHosts ) {
my %keyhash;
$keyhash{'method'} = 'zvm';
foreach $zvmHost ( @inputZvmHosts ) {
$keyhash{'otherdata'} = "zvmhost." . $zvmHost;
$distab->delEntries( \%keyhash );
}
$distab->commit();
} else {
# Otherwise, Delete all systems discovered using the specified method.
$distab->delEntries({ method => $type });
$distab->commit();
}
}
}
xCAT::MsgUtils->message("I", { data => ["Removing discovery entries finished."] }, $callback);
} elsif ($uuid) {
# define the undefined entry to a node
if (!$node) {
$usage->($callback);
return;
}
# make sure the node is valid.
my @validnode = noderange($node);
if ($#validnode != 0) {
xCAT::MsgUtils->message("E", { data => ["The node [$node] should be a valid xCAT node."] }, $callback);
return;
}
$node = $validnode[0];
# to define the a request to a node, reuse the 'discovered' command to update the node
# so the procedure will be that regenerate the request base on the attributes which stored in the discoverydata table
# get all the attributes for the entry from the discoverydata table
my @disattrs = ('uuid', 'node', 'method', 'discoverytime', 'arch', 'cpucount', 'cputype', 'memory', 'mtm', 'serial', 'nicdriver', 'nicipv4', 'nichwaddr', 'nicpci', 'nicloc', 'niconboard', 'nicfirm', 'switchname', 'switchaddr', 'switchdesc', 'switchport', 'otherdata');
my @disdata;
if ($DBname =~ /^DB2/) {
@disdata = $distab->getAllAttribsWhere("\"uuid\" = '$uuid'", @disattrs);
} else {
@disdata = $distab->getAllAttribsWhere("uuid='$uuid'", @disattrs);
}
unless (@disdata) {
xCAT::MsgUtils->message("E", { data => ["Cannot find discovery entry with uuid equals $uuid"] }, $callback);
return;
}
# generate the request which is used to define the node
my $request;
my $ent = @disdata[0];
my $interfaces;
my $otherdata;
foreach my $key (keys %$ent) {
if ($key =~ /(nicdriver|nicfirm|nicipv4|nichwaddr|nicpci|nicloc|niconboard|switchaddr|switchname|switchport)/) {
# these entries are formatted as: eth0!xxx,eth1!xxx. split it and generate the request as eth0 {...}, eth1 {...}
my @ifs = split(/,/, $ent->{$key});
foreach (@ifs) {
my ($if, $value) = split('!', $_);
my $origname = $key;
if ($key eq "nicdriver") {
$origname = "driver";
} elsif ($key eq "nicfirm") {
$origname = "firmdesc";
} elsif ($key eq "nicipv4") {
$origname = "ip4address";
} elsif ($key eq "nichwaddr") {
$origname = "hwaddr";
} elsif ($key eq "nicpci") {
$origname = "pcidev";
} elsif ($key eq "nicloc") {
$origname = "location";
} elsif ($key eq "niconboard") {
$origname = "onboardeth";
}
push @{ $interfaces->{$if}->{$origname} }, $value;
}
} elsif ($key eq "otherdata") {
# this entry is just keep as is, so translate to hash is enough
$otherdata = eval { XMLin($ent->{$key}, SuppressEmpty => undef, ForceArray => 1) };
} elsif ($key eq "switchdesc") {
# just ingore the switchdesc, since it include ','
} else {
# for general attrs which just have one first level
$request->{$key} = [ $ent->{$key} ];
}
}
# add the interface part to the request hash
if ($interfaces) {
foreach (keys %$interfaces) {
$interfaces->{$_}->{'devname'} = [$_];
push @{ $request->{nic} }, $interfaces->{$_};
}
}
# add the untouched part to the request hash
if ($otherdata) {
foreach (keys %$otherdata) {
$request->{$_} = $otherdata->{$_};
}
}
# call the 'discovered' command to update the request to a node
$request->{command} = ['discovered'];
$request->{node} = [$node];
$request->{discoverymethod} = ['manual'];
$request->{updateswitch} = ['yes'];
my $rsp = $subreq->($request);
if (defined($rsp->{errorcode}->[0])) {
xCAT::MsgUtils->message("E", $rsp, $callback);
} else {
xCAT::MsgUtils->message("I", { data => ["Defined [$uuid] to node $node."] }, $callback);
}
} else {
$usage->($callback);
return;
}
}
sub process_request {
my $request = shift;
my $callback = shift;
my $subreq = shift;
my $command = $request->{command}->[0];
my $args = $request->{arg};
if ($command eq "findme") {
findme($request, $callback, $subreq);
} elsif ($command eq "nodediscoverstart") {
nodediscoverstart($callback, $args);
} elsif ($command eq "nodediscoverstop") {
nodediscoverstop($callback, $args);
} elsif ($command eq "nodediscoverls") {
nodediscoverls($callback, $args);
} elsif ($command eq "nodediscoverstatus") {
nodediscoverstatus($callback, $args);
} elsif ($command eq "nodediscoverdef") {
nodediscoverdef($callback, $subreq, $args);
}
}
=head3 getfreenodes
Get the free nodes base on the user specified noderange and defined nodes
arg1 - the noderange
arg2 - "all': return all the free nodes; otherwise just return one.
=cut
sub getfreenodes () {
my $noderange = shift;
my $all = shift;
my @freenodes;
# get all the nodes from noderange
my @nodes = noderange($noderange, 0);
# get all nodes from nodelist and mac table
my $nltb = xCAT::Table->new('nodelist');
unless ($nltb) {
xCAT::MsgUtils->message("S", "Discovery Error: Could not open table: nodelist.");
return;
}
my $mactb = xCAT::Table->new('mac');
unless ($mactb) {
xCAT::MsgUtils->message("S", "Discovery Error: Could not open table: mac.");
return;
}
# if mac address has been set, the node is not free
my $nlent = $nltb->getNodesAttribs(\@nodes, ['groups']);
my $macent = $mactb->getNodesAttribs(\@nodes, ['mac']);
foreach my $node (@nodes) {
if ($nlent->{$node}->[0]) {
unless ($macent->{$node}->[0] && $macent->{$node}->[0]->{'mac'}) {
push @freenodes, $node;
unless ($all) { last; }
}
} else {
push @freenodes, $node;
unless ($all) { last; }
}
}
unless (@freenodes) {
return;
}
if ($all) {
return @freenodes;
} else {
return $freenodes[0];
}
}
=head3 getpredefips
Get the ips which have been predefined to host or bmc
arg1 - a refenrece to the array of nodes
arg2 - type: host, bmc
return: hash {ip} = node
=cut
sub getpredefips {
my $freenode = shift;
my $type = shift; # type: host, bmc
my $hoststb = xCAT::Table->new('hosts');
unless ($hoststb) {
xCAT::MsgUtils->message("S", "Discovery Error: Could not open table: hosts.");
}
my %predefips; # to have the ip which prefined to the nodes
if ($type eq "bmc") {
# Find the bmc name from the ipmi table.
# if ipmi.bmc is an IP address, that means this is the IP of bmc
my @freebmc;
my %node2bmc; # $node2bmc{$node} = $bmc;
my $ipmitab = xCAT::Table->new('ipmi');
if ($ipmitab) {
my $ipmient = $ipmitab->getNodesAttribs($freenode, ['bmc']);
foreach (@$freenode) {
if (defined($ipmient->{$_}->[0]->{'bmc'})) {
if ($ipmient->{$_}->[0]->{'bmc'} =~ /\d+\.\d+\.\d+\.\d+/) {
$predefips{ $ipmient->{$_}->[0]->{'bmc'} } = $_ . "-bmc";
} else {
push @freebmc, $ipmient->{$_}->[0]->{'bmc'};
$node2bmc{$_} = $ipmient->{$_}->[0]->{'bmc'};
}
}
}
}
# check the system resolution first, then host.ip, then host.otherinterfaces
my $freenodeent = $hoststb->getNodesAttribs(\@freebmc, ['ip']);
foreach (@freebmc) {
my $nodeip = xCAT::NetworkUtils->getipaddr($_);
if ($nodeip) {
# handle the bmc which could be resolved to an IP by system
$predefips{$nodeip} = $_;
} else {
# handle the bmc which IP was defined in the hosts.ip
if (defined($freenodeent->{$_}->[0]) && $freenodeent->{$_}->[0]->{'ip'}) {
$predefips{ $freenodeent->{$_}->[0]->{'ip'} } = $_;
}
}
}
# handle the bmcs which bmc has been set in the hosts.otherinterfaces
$freenodeent = $hoststb->getNodesAttribs($freenode, ['otherinterfaces']);
foreach (@$freenode) {
if (defined($node2bmc{$_})) {
# for bmc node, search the hosts.otherinterface to see whether there's perdefined ip for bmc
my $bmcip = getbmcip_otherinterfaces($_, $node2bmc{$_}, $freenodeent->{$_}->[0]->{'otherinterfaces'});
if ($bmcip) {
$predefips{$bmcip} = $node2bmc{$_};
}
}
}
} elsif ($type eq "host") {
# get the predefined node which ip has been set in the hosts.ip
my $freenodeent = $hoststb->getNodesAttribs($freenode, ['ip']);
foreach (@$freenode) {
my $nodeip = xCAT::NetworkUtils->getipaddr($_);
if ($nodeip) {
$predefips{$nodeip} = $_;
} else {
if (defined($freenodeent->{$_}->[0]) && $freenodeent->{$_}->[0]->{'ip'}) {
$predefips{ $freenodeent->{$_}->[0]->{'ip'} } = $_;
}
}
}
}
return \%predefips;
}
=head3 getfreeips
Get the free ips base on the user specified ip range
arg1 - the ip range. Two format are suported: 192.168.1.1-192.168.2.50; 192.168.[1-2].[10-100]
arg2 - all the free nodes
arg3 - type: host, bmc
arg4 - "all': return free ips for all the free nodes; otherwise just return the first one.
return: array of all free ips or one ip base on the arg4
=cut
sub getfreeips {
my $iprange = shift;
my $freenode = shift;
my $type = shift; # type: host, bmc
my $all = shift;
my @freeips;
my %predefips; # to have the ip which prefined to the nodes
my $hoststb = xCAT::Table->new('hosts');
unless ($hoststb) {
xCAT::MsgUtils->message("S", "Discovery Error: Could not open table: hosts.");
}
my @freebmc;
my %usedips = ();
if ($type eq "bmc") {
# get the host ip for all predefind nodes from $freenode
%predefips = %{ getpredefips($freenode, "bmc") };
# get all the used ips, the predefined ip should be ignored
my @hostsent = $hoststb->getAllNodeAttribs([ 'node', 'ip', 'otherinterfaces' ]);
# Find the bmc name from the ipmi table.
# if ipmi.bmc is an IP address, that means this is the IP of bmc
my %node2bmc; # $node2bmc{$node} = $bmc;
my $ipmitab = xCAT::Table->new('ipmi');
if ($ipmitab) {
my @ipmients = $ipmitab->getAllNodeAttribs([ 'node', 'bmc' ]);
foreach my $ipmient (@ipmients) {
if (defined($ipmient->{'bmc'})) {
if ($ipmient->{'bmc'} =~ /\d+\.\d+\.\d+\.\d+/) {
unless ($predefips{ $ipmient->{'bmc'} }) {
$usedips{ $ipmient->{'bmc'} } = 1;
}
} else {
$node2bmc{ $ipmient->{'node'} } = $ipmient->{'bmc'};
}
}
}
}
foreach my $host (@hostsent) {
# handle the case that bmc has an entry in the hosts table
my $nodeip = xCAT::NetworkUtils->getipaddr($host->{'node'});
if ($nodeip) {
unless ($predefips{$nodeip}) {
$usedips{$nodeip} = 1;
}
} else {
if (defined($host->{'ip'}) && !$predefips{ $host->{'ip'} }) {
$usedips{ $host->{'ip'} } = 1;
}
}
# handle the case that the bmc<->ip mapping is specified in hosts.otherinterfaces
if (defined($node2bmc{ $host->{'node'} })) {
my $bmcip = xCAT::NetworkUtils->getipaddr($node2bmc{ $host->{'node'} });
if ($bmcip) {
unless ($predefips{$bmcip}) {
$usedips{$bmcip} = 1;
}
} else {
if (defined($host->{'otherinterfaces'})) {
my $bmcip = getbmcip_otherinterfaces($host->{'node'}, $node2bmc{ $host->{'node'} }, $host->{'otherinterfaces'});
unless ($predefips{$bmcip}) {
$usedips{$bmcip} = 1;
}
}
}
}
}
} elsif ($type eq "host") {
# get the bmc ip for all predefind nodes from $freenode
%predefips = %{ getpredefips($freenode, "host") };
# get all the used ips, the predefined ip should be ignored
my @hostsent = $hoststb->getAllNodeAttribs([ 'node', 'ip' ]);
foreach my $host (@hostsent) {
my $nodeip = xCAT::NetworkUtils->getipaddr($host->{'node'});
if ($nodeip) {
unless ($predefips{$nodeip}) {
$usedips{$nodeip} = 1;
}
} else {
if (defined($host->{'ip'}) && !$predefips{ $host->{'ip'} }) {
$usedips{ $host->{'ip'} } = 1;
}
}
}
}
# to calculate the free IP. free ip = 'ip in the range' - 'used ip'
if ($iprange =~ /(\d+\.\d+\.\d+\.\d+)-(\d+\.\d+\.\d+\.\d+)/) {
# if ip range format is 192.168.1.0-192.168.1.200
my ($startip, $endip) = ($1, $2);
my $startnum = xCAT::NetworkUtils->ip_to_int($startip);
my $endnum = xCAT::NetworkUtils->ip_to_int($endip);
while ($startnum <= $endnum) {
my $ip = xCAT::NetworkUtils->int_to_ip($startnum);
unless ($usedips{$ip}) {
push @freeips, $ip;
unless ($all) { last; }
}
$startnum++;
}
} elsif ($iprange) {
# use the noderange to expand the range
my @ips = noderange($iprange, 0);
foreach my $ip (@ips) {
unless ($usedips{$ip}) {
push @freeips, $ip;
unless ($all) { last; }
}
}
} else {
# only find the ip which mapping to the node
}
unless (@freeips) {
return;
}
if ($all) {
return @freeips;
} else {
return $freeips[0];
}
}
=head3 getbmcip_otherinterfaces
Parse the value in the hosts.otherinterfaces
arg1 - node
arg2 - bmc name
arg3 - value in the hosts.otherinterfaces
return: the ip of the node <node>-bmc
=cut
sub getbmcip_otherinterfaces
{
my $node = shift;
my $bmc = shift;
my $otherinterfaces = shift;
my @itf_pairs = split(/,/, $otherinterfaces);
foreach (@itf_pairs)
{
my ($itf, $ip);
if ($_ =~ /!/) {
($itf, $ip) = split(/!/, $_);
} else {
($itf, $ip) = split(/:/, $_);
}
if ($itf =~ /^-/)
{
$itf = $node . $itf;
}
if ($itf eq $bmc) {
return xCAT::NetworkUtils->getipaddr($ip);
}
}
return;
}
1;