2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-21 19:22:05 +00:00
2019-10-18 12:00:26 -04:00

648 lines
23 KiB
Perl

# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
#-------------------------------------------------------
=head1
xCAT plugin package to handle rinstall and winstall
Supported command:
rinstall - runs nodeset, rsetboot, rpower commands
winstall - also opens the console
=cut
#-------------------------------------------------------
package xCAT_plugin::rinstall;
use strict;
require xCAT::Utils;
require xCAT::MsgUtils;
use xCAT::NodeRange;
use xCAT::Table;
use xCAT::Usage;
use Data::Dumper;
use Getopt::Long;
#-------------------------------------------------------
=head3 handled_commands
Return list of commands handled by this plugin
=cut
#-------------------------------------------------------
sub handled_commands {
return {
rinstall => "rinstall",
winstall => "rinstall",
};
}
#-------------------------------------------------------
=head3 Process the command
=cut
#-------------------------------------------------------
sub process_request {
my $request = shift;
my $callback = shift;
my $subreq = shift;
rinstall($request, $callback, $subreq);
}
#-------------------------------------------------------
=head3 rinstall
Wrapper around nodeset, rsetboot, rpower for the admin convenience
=cut
#-------------------------------------------------------
sub rinstall {
my ($req, $callback, $subreq) = @_;
$::CALLBACK = $callback;
my $CONSOLE;
my $OSIMAGE;
my $STATES;
my $ignorekernelchk;
my $noupdateinitrd;
my $VERBOSE;
my $HELP;
my $VERSION;
my $UEFIMODE;
# Could be rinstall or winstall
my $command = $req->{command}->[0];
my $nodes;
my @nodes;
my %nodes;
my $rsp = {};
# There are nodes
if (defined($req->{node})) {
$nodes = $req->{node};
@nodes = @$nodes;
}
my $args;
# There are arguments
if (defined($req->{arg})) {
$args = $req->{arg};
@ARGV = @{$args};
}
if (($command =~ /rinstall/) or ($command =~ /winstall/)) {
my $ret=xCAT::Usage->validateArgs($command,@ARGV);
if ($ret->[0]!=0) {
$rsp->{error}->[0] = $ret->[1];
$rsp->{errorcode}->[0] = $ret->[0];
xCAT::MsgUtils->message("E", $rsp, $callback);
&usage($command,$callback);
return;
}
my $state = $ARGV[0];
chomp($state);
if ($state =~ /^osimage=(\S+)/) {
$OSIMAGE = $1; # osimage was specified
xCAT::MsgUtils->message("I", $rsp, $callback);
}
elsif ($state =~ /^boot$|^shell$|^osimage$|^runcmd=|^runimage=/) {
# the rest are valid actions, just pass to nodeset
$STATES=$state;
}
elsif ($state =~ /^-/) {
# if starts with dash, let GetOptions below to process
}
else {
if ($state) {
$rsp->{errorcode}->[0]=1;
$rsp->{error}->[0]="Invalid option $state";
xCAT::MsgUtils->message("E",$rsp,$callback);
&usage($command, $callback);
return 1;
}
}
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
unless (
GetOptions(
'ignorekernelchk' => \$ignorekernelchk,
'noupdateinitrd' => \$noupdateinitrd,
'V|verbose' => \$VERBOSE,
'h|help' => \$HELP,
'v|version' => \$VERSION,
'u|uefimode' => \$UEFIMODE,
'c|console' => \$CONSOLE)
) {
&usage($command, $callback);
return 1;
}
}
if ($HELP) {
&usage($command, $callback);
return 0;
}
if ($VERSION) {
my $version = xCAT::Utils->Version();
$rsp->{data}->[0] = "$version";
xCAT::MsgUtils->message("I", $rsp, $callback);
return 0;
}
if (scalar(@nodes) == 0) {
&usage($command, $callback);
return 1;
}
if($command eq "rinstall" and scalar(@nodes) > 1 and $CONSOLE){
$rsp->{errorcode}->[0]=1;
$rsp->{error}->[0]="rinstall -c/--console can only be run against one node! Please use winstall -c/--console for multiple nodes.";
xCAT::MsgUtils->message("E",$rsp,$callback);
return 1;
}
my $rc = 0;
my @parameter;
my $nodehmtable = xCAT::Table->new("nodehm");
my $nodehmcache = $nodehmtable->getNodesAttribs(\@nodes, ['mgt']);
$nodehmtable->close();
if ($OSIMAGE) {
# if osimage=<imagename> is specified,
# call "nodeset ... osimage= ..." to set the boot state of the noderange to the specified osimage,
# "nodeset" will handle the updating of node attributes such as os,arch,profile,provmethod.
my $noderestable = xCAT::Table->new("noderes");
my $noderescache = $noderestable->getNodesAttribs(\@nodes, ['netboot']);
$noderestable->close();
my $nodetypetable = xCAT::Table->new("nodetype");
my $nodetypecache = $nodetypetable->getNodesAttribs(\@nodes, ['arch']);
$nodetypetable->close();
my $osimagetable = xCAT::Table->new("osimage");
(my $ref) = $osimagetable->getAttribs({ imagename => $OSIMAGE }, 'osvers', 'osarch', 'imagetype');
$osimagetable->close();
unless ($ref) {
# Nothing was returned from getAttrbs for the specified image
$rsp->{data}->[0] = "Cannot find the OS image $OSIMAGE in the osimage table.";
xCAT::MsgUtils->message("E", $rsp, $callback);
return 1;
}
unless (defined($ref->{osarch})) {
$rsp->{error}->[0] = "$OSIMAGE 'osarch' attribute not defined in 'osimage' table.";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
return 1;
}
my $osimagearch = $ref->{osarch};
my $netbootval = xCAT::Utils->lookupNetboot($ref->{osvers}, $ref->{osarch}, $ref->{imagetype});
my @validnodes;
foreach my $node (@nodes) {
unless ($noderescache) { next; }
unless ($nodetypecache) { next; }
unless ($nodehmcache) { next; }
my $noderesattribs = $noderescache->{$node}->[0];
my $nodetypeattribs = $nodetypecache->{$node}->[0];
my $nodehmattribs = $nodehmcache->{$node}->[0];
unless (defined($noderesattribs) and defined($noderesattribs->{'netboot'})) {
$rsp->{error}->[0] = "$node: Missing the 'netboot' attribute.";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
next;
}
else {
unless ($netbootval =~ /$noderesattribs->{'netboot'}/i) {
$callback->({ warning => [ $node . ": $noderesattribs->{'netboot'} might be invalid when provisioning $OSIMAGE,valid options: \"$netbootval\". For more details see the 'netboot' description in the output of \"tabdump -d noderes\"." ] });
next;
}
}
unless (defined($nodetypeattribs) and defined($nodetypeattribs->{'arch'})) {
$rsp->{error}->[0] = "$node: 'arch' attribute not defined in 'nodetype' table.";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
next;
}
my $nodetypearch = $nodetypeattribs->{'arch'};
if ($nodetypearch ne $osimagearch) {
unless(($nodetypearch =~ /^ppc64(le|el)?$/i) and ($osimagearch =~ /^ppc64(le|el)?$/i)){
$rsp->{error}->[0] = "$node: The value of 'arch' attribute of node does not match the 'osarch' attribute of osimage.";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
next;
}
}
unless (defined($nodehmattribs) and defined($nodehmattribs->{'mgt'})) {
$rsp->{error}->[0] = "$node: 'mgt' attribute not defined in 'nodehm' table.";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
next;
}
push @validnodes, $node;
}
#only provision the normal nodes
@nodes = @validnodes;
push @parameter, "osimage=$OSIMAGE";
}
elsif ($STATES) {
push @parameter, "$STATES";
}
else {
# No osimage specified, set the boot state of each node based on the nodetype.provmethod:
# 1) if nodetype.provmethod = [install/netboot/statelite],
# then output error message.
# 2) if nodetype.provmethod = <osimage>,
# then call "nodeset ... osimage"
# Group the nodes according to the nodetype.provmethod
my %tphash;
my $nodetypetable = xCAT::Table->new("nodetype");
my $nodetypecache = $nodetypetable->getNodesAttribs(\@nodes, ['provmethod']);
$nodetypetable->close();
foreach my $node (@nodes) {
unless ($nodetypecache) { next; }
my $nodetypeattribs = $nodetypecache->{$node}->[0];
unless (defined($nodetypeattribs) and defined($nodetypeattribs->{'provmethod'})) {
$rsp->{error}->[0] = "$node: 'provmethod' attribute not defined in 'nodetype' table.";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
next;
}
else {
push(@{ $tphash{ $nodetypeattribs->{'provmethod'} } }, $node);
}
}
# Now for each group based on provmethod
my @validnodes;
foreach my $key (keys %tphash) {
$::RUNCMD_RC = 0;
my @pnnodes = @{ $tphash{$key} };
# If nodetype.provmethod = [install|netboot|statelite]
if ($key =~ /^(install|netboot|statelite)$/) {
my $rsp = {};
$rsp->{error}->[0] = "@pnnodes: The options 'install', 'netboot', and 'statelite' have been deprecated, use 'nodeset <noderange> osimage=<imagename>' instead.";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
next;
}
# If nodetype.provmethod != [install|netboot|statelite]
else {
push @validnodes, @pnnodes;
}
}
#only provision the normal nodes
@nodes = @validnodes;
push @parameter, "osimage";
}
if ( grep( /osimage/, @parameter ) ) {
# Task is osimage, check --ignorekernelchk and --noupdateinitrd flags and set if needed
if ($ignorekernelchk) {
push @parameter, "--ignorekernelchk";
}
if ($noupdateinitrd) {
push @parameter, "--noupdateinitrd";
}
}
if (scalar(@nodes) == 0) {
$rsp->{error}->[0] = "No available nodes for provision.";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
return 1;
}
else {
$rsp->{data}->[0] = "Provision node(s): @nodes";
xCAT::MsgUtils->message("I", $rsp, $callback);
}
%nodes = map { $_, 1 } @nodes;
# Run nodeset $noderange $parameter
my $res =
xCAT::Utils->runxcmd(
{
command => ["nodeset"],
node => \@nodes,
arg => \@parameter
},
$subreq, -1, 1);
$rc = $::RUNCMD_RC;
my $rsp = {};
if ($VERBOSE) {
my @cmd = "Run command: nodeset @nodes @parameter";
push @{ $rsp->{data} }, @cmd;
push @{ $rsp->{data} }, @$res;
xCAT::MsgUtils->message("D", $rsp, $callback);
}
unless ($rc == 0) {
# We got an error with the nodeset
my @successnodes;
my @failurenodes;
my @failuresns;
my $snfailure;
# copy into a temporary variable to avoid of circular reference
my @lines = @$res;
foreach my $line (@lines) {
$rsp->{data}->[0] = $line;
if($line =~ /The (\S+) can not be resolved/){
push @failurenodes,$1;
}
if ($line =~ /dhcp server is not running/) {
my $rsp = {};
$rsp->{error}->[0] = "Fatal error: dhcp server is not running";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
return 1;
}
if ($line =~ /Cannot wget/) {
# If nodeset returns error that runimage can not be downloaded by wget,
# display the error from nodeset (if not alredy displayed by VERBOSE above), stop processing and return.
unless ($VERBOSE) {
xCAT::MsgUtils->message("I", $rsp, $callback);
}
return 1;
}
if ($line =~ /Unable to dispatch hierarchical sub-command to (\S+):3001/) {
$snfailure=1;
push @failuresns,$1;
}
xCAT::MsgUtils->message("I", $rsp, $callback);
}
# if only provision one node and failed nodeset, will exit the command
# instead of continue with rnetboot/rsetboot, rpower.
if ( (scalar(@nodes) == 1) ) {
#exit the command if it's service node failure
if ($snfailure) {
my $node = $nodes[0];
my $nrtab = xCAT::Table->new('noderes');
my $nrents = $nrtab->getNodeAttribs($node, [qw(servicenode)]);
my $nodesn = $nrents->{servicenode};
foreach my $tmpsn (@failuresns) {
if ($nodesn eq $tmpsn) {
$rsp->{error}->[0] = "Unable connect to Service node $nodesn, failed to run 'nodeset' against the node: @nodes";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
return 1;
}
}
} else {
$rsp->{error}->[0] = "Failed to run 'nodeset' against the node: @nodes";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
return 1;
}
}
foreach my $node (@failurenodes) {
delete $nodes{$node};
}
if (0+@failurenodes > 0) {
$rsp->{error}->[0] = "Failed to run 'nodeset' against the following nodes: @failurenodes";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
}
@nodes = keys %nodes;
}
# Group the nodes according to the nodehm.mgt
my %hmhash;
foreach my $node (@nodes) {
unless ($nodehmcache) { next; }
my $nodehmattribs = $nodehmcache->{$node}->[0];
push(@{ $hmhash{ $nodehmattribs->{'mgt'} } }, $node);
}
# Now for each group based on mgt
foreach my $hmkey (keys %hmhash) {
$::RUNCMD_RC = 0;
my @nodes = @{ $hmhash{$hmkey} };
unless ($hmkey =~ /^(ipmi|blade|hmc|ivm|fsp|kvm|esx|rhevm|openbmc)$/) {
$rsp->{error}->[0] = "@nodes: rinstall only support nodehm.mgt type 'ipmi', 'blade', 'hmc', 'ivm', 'fsp', 'kvm', 'esx', 'rhevm', 'openbmc'.";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
next;
}
if (($hmkey =~ /^ivm$/) or ($hmkey =~ /^fsp$/) or ($hmkey =~ /^hmc$/)) {
%nodes = map { $_, 1 } @nodes;
# Run rnetboot $noderange
my $res =
xCAT::Utils->runxcmd(
{
command => ["rnetboot"],
node => \@nodes
},
$subreq, -1, 1);
$rc = $::RUNCMD_RC;
if ($VERBOSE) {
my @cmd = "Run command: rnetboot @nodes";
push @{ $rsp->{data} }, @cmd;
push @{ $rsp->{data} }, @$res;
xCAT::MsgUtils->message("D", $rsp, $callback);
}
unless ($rc == 0) {
# We got an error with the rnetboot
my @failurenodes;
# copy into a temporary variable to avoid of circular reference
my @lines = @$res;
foreach my $line (@lines) {
$rsp->{data}->[0] = $line;
if ($line =~ /: Success/) {
my $successnode;
my $restline;
($successnode, $restline) = split(/:/, $line, 2);
$nodes{$successnode} = 0;
}
xCAT::MsgUtils->message("I", $rsp, $callback);
}
foreach my $node (@nodes) {
if ($nodes{$node} == 1) {
push @failurenodes, $node;
}
}
if (0+@failurenodes > 0) {
$rsp->{error}->[0] = "Failed to run 'rnetboot' against the following nodes: @failurenodes";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
}
}
}
else {
# Call "rsetboot" to set the boot order of the nodehm.mgt=ipmi/openbmc nodes
if ($hmkey =~ /^(ipmi|openbmc)$/) {
%nodes = map { $_, 1 } @nodes;
# Run rsetboot $noderange net
my @rsetbootarg;
push @rsetbootarg, "net";
if ($UEFIMODE) {
push @rsetbootarg, "-u";
}
my %req=(
command => ["rsetboot"],
node => \@nodes,
arg => \@rsetbootarg
);
#TODO: When OPENBMC support is finished, this line should be removed
if($hmkey =~ /^openbmc$/){
$req{environment}{XCAT_OPENBMC_DEVEL}= "YES";
}
my $res =
xCAT::Utils->runxcmd(
\%req,
$subreq, -1, 1);
$rc = $::RUNCMD_RC;
my $rsp = {};
if ($VERBOSE) {
my @cmd = "Run command: rsetboot @nodes @rsetbootarg";
push @{ $rsp->{data} }, @cmd;
push @{ $rsp->{data} }, @$res;
xCAT::MsgUtils->message("D", $rsp, $callback);
}
unless ($rc == 0) {
# We got an error with the rsetboot
my @successnodes;
my @failurenodes;
# copy into a temporary variable to avoid of circular reference
my @lines = @$res;
foreach my $line (@lines) {
$rsp->{data}->[0] = $line;
if ($line =~ /: Network/) {
my $successnode;
my $restline;
($successnode, $restline) = split(/:/, $line, 2);
$nodes{$successnode} = 0;
push @successnodes, $successnode;
}
xCAT::MsgUtils->message("I", $rsp, $callback);
}
foreach my $node (@nodes) {
if ($nodes{$node} == 1) {
push @failurenodes, $node;
}
}
my $rsp = {};
if (0+@failurenodes > 0) {
$rsp->{error}->[0] = "Failed to run 'rsetboot' against the following nodes: @failurenodes";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
}
@nodes = @successnodes;
}
}
# Call "rpower" to start the node provision process
%nodes = map { $_, 1 } @nodes;
# Run rpower $noderange boot
my @rpowerarg;
push @rpowerarg, "boot";
my %req=(
command => ["rpower"],
node => \@nodes,
arg => \@rpowerarg
);
my $res =
xCAT::Utils->runxcmd(
\%req,
$subreq, -1, 1);
$rc = $::RUNCMD_RC;
if ($VERBOSE) {
my @cmd = "Run command: rpower @nodes @rpowerarg";
push @{ $rsp->{data} }, @cmd;
push @{ $rsp->{data} }, @$res;
xCAT::MsgUtils->message("D", $rsp, $callback);
}
unless ($rc == 0) {
# We got an error with the rpower
my @failurenodes;
# copy into a temporary variable to avoid of circular reference
my @lines = @$res;
foreach my $line (@lines) {
$rsp->{data}->[0] = $line;
if (($line =~ /: on reset/) or ($line =~ /: off on/)) {
my $successnode;
my $restline;
($successnode, $restline) = split(/:/, $line, 2);
$nodes{$successnode} = 0;
}
xCAT::MsgUtils->message("I", $rsp, $callback);
}
foreach my $node (@nodes) {
if ($nodes{$node} == 1) {
push @failurenodes, $node;
}
}
my $rsp = {};
if (0+@failurenodes > 0) {
$rsp->{error}->[0] = "Failed to run 'rpower' against the following nodes: @failurenodes";
$rsp->{errorcode}->[0] = 1;
xCAT::MsgUtils->message("E", $rsp, $callback);
}
}
}
}
return 0;
}
#-------------------------------------------------------
=head3 Usage
=cut
#-------------------------------------------------------
sub usage {
my $command = shift;
my $callback = shift;
my $rsp = {};
$rsp->{data}->[0] = "Usage:";
$rsp->{data}->[1] = " $command <noderange> [boot | shell | runcmd=<command>] [-c|--console] [-u|--uefimode] [-V|--verbose]";
$rsp->{data}->[2] = " $command <noderange> [osimage[=<imagename>]] [--noupdateinitrd] [--ignorekernelchk] [-c|--console] [-u|--uefimode] [-V|--verbose]";
$rsp->{data}->[3] = " $command <noderange> runimage=<task>";
$rsp->{data}->[4] = " $command [-h|--help|-v|--version]";
xCAT::MsgUtils->message("I", $rsp, $callback);
}
1;