mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-05-22 03:32:04 +00:00
400 lines
14 KiB
Perl
400 lines
14 KiB
Perl
#!/usr/bin/perl
|
|
### IBM(c) 2017 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
package xCAT_plugin::openbmc2;
|
|
|
|
BEGIN
|
|
{
|
|
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
|
|
}
|
|
use lib "$::XCATROOT/lib/perl";
|
|
use strict;
|
|
use warnings "all";
|
|
|
|
use JSON;
|
|
use Getopt::Long;
|
|
use xCAT::Utils;
|
|
use xCAT::Usage;
|
|
use xCAT::SvrUtils;
|
|
use xCAT::OPENBMC;
|
|
use xCAT_plugin::openbmc;
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 handled_commands
|
|
|
|
Return list of commands handled by this plugin
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
|
|
sub handled_commands {
|
|
return {
|
|
rbeacon => 'nodehm:mgt=openbmc',
|
|
rflash => 'nodehm:mgt=openbmc',
|
|
rinv => 'nodehm:mgt=openbmc',
|
|
rpower => 'nodehm:mgt=openbmc',
|
|
rsetboot => 'nodehm:mgt=openbmc',
|
|
rvitals => 'nodehm:mgt=openbmc',
|
|
rspconfig => 'nodehm:mgt=openbmc',
|
|
reventlog => 'nodehm:mgt=openbmc',
|
|
};
|
|
}
|
|
|
|
# Common logging messages:
|
|
my $usage_errormsg = "Usage error.";
|
|
my $reventlog_no_id_resolved_errormsg = "Provide a comma separated list of IDs to be resolved. Example: 'resolved=x,y,z'";
|
|
|
|
my %node_info = ();
|
|
my $callback;
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 preprocess_request
|
|
|
|
preprocess the command
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub preprocess_request {
|
|
my $request = shift;
|
|
$callback = shift;
|
|
|
|
my $command = $request->{command}->[0];
|
|
my $python_env = xCAT::OPENBMC->is_openbmc_python($request->{environment});
|
|
# Process command in this module only if PYTHON env is not NO or
|
|
# command is listed in the PYTHON env list (without EXCEPT: prefix).
|
|
# All other cases => return
|
|
|
|
SWITCH: {
|
|
if ($python_env eq "NO") {$request = {}; return;}
|
|
if ($python_env eq "ALL") {last SWITCH;}
|
|
if ($python_env !~ $command) {
|
|
if ($python_env =~ /^EXCEPT:/) {last SWITCH}
|
|
else {$request = {}; return;}
|
|
}
|
|
if ($python_env =~ $command) {
|
|
if ($python_env =~ /^EXCEPT:/) {$request = {}; return;}
|
|
else {last SWITCH;}
|
|
}
|
|
}
|
|
|
|
my $noderange = $request->{node};
|
|
my $extrargs = $request->{arg};
|
|
my @exargs = ($request->{arg});
|
|
my @requests;
|
|
|
|
if (ref($extrargs)) {
|
|
@exargs = @$extrargs;
|
|
}
|
|
my $usage_string = xCAT::Usage->parseCommand($command, @exargs);
|
|
if ($usage_string) {
|
|
$callback->({ data => [$usage_string] });
|
|
$request = {};
|
|
return;
|
|
}
|
|
|
|
my $parse_result = parse_args($command, $extrargs, $noderange);
|
|
if (ref($parse_result) eq 'ARRAY') {
|
|
my $error_data;
|
|
foreach my $node (@$noderange) {
|
|
$error_data .= "\n" if ($error_data);
|
|
$error_data .= "$node: Error: " . "$parse_result->[1]";
|
|
}
|
|
$callback->({ errorcode => [$parse_result->[0]], data => [$error_data] });
|
|
$request = {};
|
|
return;
|
|
}
|
|
|
|
my $sn = xCAT::ServiceNodeUtils->get_ServiceNode($noderange, "xcat", "MN");
|
|
foreach my $snkey (keys %$sn) {
|
|
my $reqcopy = {%$request};
|
|
$reqcopy->{node} = $sn->{$snkey};
|
|
$reqcopy->{'_xcatdest'} = $snkey;
|
|
$reqcopy->{_xcatpreprocessed}->[0] = 1;
|
|
push @requests, $reqcopy;
|
|
}
|
|
|
|
return \@requests;
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 process_request
|
|
|
|
Process the command
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub process_request {
|
|
my $request = shift;
|
|
$callback = shift;
|
|
my $noderange = $request->{node};
|
|
my $check = parse_node_info($noderange);
|
|
if (&refactor_args($request)) {
|
|
xCAT::MsgUtils->message("E", { data => ["Failed to refactor arguments"] }, $callback);
|
|
return;
|
|
}
|
|
$callback->({ errorcode => [$check] }) if ($check);
|
|
return unless(%node_info);
|
|
my $pid = xCAT::OPENBMC::start_python_agent();
|
|
if (!defined($pid)) {
|
|
xCAT::MsgUtils->message("E", { data => ["Failed to start python agent"] }, $callback);
|
|
return;
|
|
}
|
|
|
|
xCAT::OPENBMC::submit_agent_request($pid, $request, \%node_info, $callback);
|
|
xCAT::OPENBMC::wait_agent($pid, $callback);
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 parse_args
|
|
|
|
Parse the command line options and operands
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub parse_args {
|
|
my $command = shift;
|
|
my $extrargs = shift;
|
|
my $noderange = shift;
|
|
my $subcommand = undef;
|
|
|
|
my $verbose;
|
|
unless (GetOptions(
|
|
'V|verbose' => \$verbose,
|
|
)) {
|
|
return ([ 1, "Error parsing arguments." ]);
|
|
}
|
|
|
|
if (scalar(@ARGV) >= 2 and ($command =~ /rbeacon|rinv|rpower|rvitals/)) {
|
|
return ([ 1, "Only one option is supported at the same time for $command" ]);
|
|
} elsif (scalar(@ARGV) == 0 and $command =~ /rbeacon|rpower|rflash/) {
|
|
return ([ 1, "No option specified for $command" ]);
|
|
} else {
|
|
$subcommand = $ARGV[0];
|
|
}
|
|
|
|
if ($command eq "rbeacon") {
|
|
unless ($subcommand =~ /^on$|^off$/) {
|
|
return ([ 1, "Only 'on' or 'off' is supported for OpenBMC managed nodes."]);
|
|
}
|
|
} elsif ($command eq "rflash") {
|
|
my ($activate, $check, $delete, $directory, $list, $upload) = (0) x 6;
|
|
my $no_host_reboot;
|
|
GetOptions(
|
|
'a|activate' => \$activate,
|
|
'c|check' => \$check,
|
|
'delete' => \$delete,
|
|
'd' => \$directory,
|
|
'l|list' => \$list,
|
|
'u|upload' => \$upload,
|
|
'no-host-reboot' => \$no_host_reboot,
|
|
);
|
|
my $option_num = $activate+$check+$delete+$directory+$list+$upload;
|
|
if ($option_num >= 2) {
|
|
return ([ 1, "Multiple options are not supported."]);
|
|
} elsif ($option_num == 0) {
|
|
for my $arg (@ARGV) {
|
|
if ($arg =~ /^-/) {
|
|
return ([ 1, "Unsupported command: $command $arg" ]);
|
|
}
|
|
}
|
|
return ([ 1, "No options specified." ]);
|
|
}
|
|
if ($activate or $check or $delete or $upload) {
|
|
return ([ 1, "More than one firmware specified is not supported."]) if ($#ARGV >= 1);
|
|
if ($check) {
|
|
return ([ 1, "Invalid firmware specified with '-c|--check'."]) if (@ARGV and ($ARGV[0] !~ /.*\.tar$/i or $#ARGV >= 1));
|
|
}
|
|
if ($activate or $delete or $upload) {
|
|
my $option = "-a|--activate";
|
|
if ($upload) {
|
|
$option = "-u|--upload";
|
|
} elsif ($delete) {
|
|
$option = "--delete"
|
|
}
|
|
return ([ 1, "Invalid firmware specified with '$option'"]) if (!@ARGV);
|
|
my $param = $ARGV[0];
|
|
return ([ 1, "Invalid firmware specified with '$option': $param"]) if (($delete and $param !~ /^[[:xdigit:]]+$/i)
|
|
or ($activate and $param !~ /^[[:xdigit:]]+$/i and $param !~ /.*\.tar$/i) or ($upload and $param !~ /.*\.tar$/i));
|
|
}
|
|
}
|
|
if ($directory) {
|
|
return ([ 1, "Unsupported command: $command '-d'" ]);
|
|
return ([ 1, "More than one directory specified is not supported."]) if ($#ARGV >= 1);
|
|
return ([ 1, "Invalid option specified with '-d'."]) if (!@ARGV);
|
|
}
|
|
if ($list) {
|
|
return ([ 1, "Invalid option specified with '-l|--list'."]) if (@ARGV);
|
|
}
|
|
} elsif ($command eq "rinv") {
|
|
$subcommand = "all" if (!defined($ARGV[0]));
|
|
unless ($subcommand =~ /^all$|^cpu$|^dimm$|^firm$|^model$|^serial$/) {
|
|
return ([ 1, "Unsupported command: $command $subcommand" ]);
|
|
}
|
|
} elsif ($command eq "rpower") {
|
|
unless ($subcommand =~ /^on$|^off$|^softoff$|^reset$|^boot$|^bmcreboot$|^bmcstate$|^status$|^stat$|^state$/) {
|
|
return ([ 1, "Unsupported command: $command $subcommand" ]);
|
|
}
|
|
} elsif ($command eq "rsetboot") {
|
|
my $persistant;
|
|
GetOptions('p' => \$persistant);
|
|
return ([ 1, "Only one option is supported at the same time for $command" ]) if (@ARGV > 1);
|
|
$subcommand = "stat" if (!defined($ARGV[0]));
|
|
unless ($subcommand =~ /^net$|^hd$|^cd$|^def$|^default$|^stat$/) {
|
|
return ([ 1, "Unsupported command: $command $subcommand" ]);
|
|
}
|
|
} elsif ($command eq "rvitals") {
|
|
$subcommand = "all" if (!defined($ARGV[0]));
|
|
unless ($subcommand =~ /^all$|^altitude$|^fanspeed$|^leds$|^power$|^temp$|^voltage$|^wattage$/) {
|
|
return ([ 1, "Unsupported command: $command $subcommand" ]);
|
|
}
|
|
} elsif ($command eq 'rspconfig') {
|
|
xCAT_plugin::openbmc::parse_args('rspconfig', $extrargs, $noderange);
|
|
} elsif ($command eq "reventlog") {
|
|
$subcommand = "all" if (!defined($ARGV[0]));
|
|
if ($subcommand =~ /^resolved=(.*)/) {
|
|
my $value = $1;
|
|
if (not $value) {
|
|
return ([ 1, "$usage_errormsg $reventlog_no_id_resolved_errormsg" ]);
|
|
}
|
|
|
|
my $nodes_num = @$noderange;
|
|
if (@$noderange > 1) {
|
|
return ([ 1, "Resolving faults over a xCAT noderange is not recommended." ]);
|
|
}
|
|
|
|
xCAT::SvrUtils::sendmsg("Attempting to resolve the following log entries: $value...", $callback);
|
|
} elsif ($subcommand !~ /^\d+$|^all$|^clear$/) {
|
|
if ($subcommand =~ "resolved") {
|
|
return ([ 1, "$usage_errormsg $reventlog_no_id_resolved_errormsg" ]);
|
|
}
|
|
return ([ 1, "Unsupported command: $command $subcommand" ]);
|
|
}
|
|
} else {
|
|
return ([ 1, "Unsupported command: $command" ]);
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 parse_node_info
|
|
|
|
Parse the node information: bmc, bmcip, username, password
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub parse_node_info {
|
|
my $noderange = shift;
|
|
my $rst = 0;
|
|
|
|
my $passwd_table = xCAT::Table->new('passwd');
|
|
my $passwd_hash = $passwd_table->getAttribs({ 'key' => 'openbmc' }, qw(username password));
|
|
|
|
my $openbmc_table = xCAT::Table->new('openbmc');
|
|
my $openbmc_hash = $openbmc_table->getNodesAttribs(\@$noderange, ['bmc', 'username', 'password']);
|
|
|
|
foreach my $node (@$noderange) {
|
|
if (defined($openbmc_hash->{$node}->[0])) {
|
|
if ($openbmc_hash->{$node}->[0]->{'bmc'}) {
|
|
$node_info{$node}{bmc} = $openbmc_hash->{$node}->[0]->{'bmc'};
|
|
$node_info{$node}{bmcip} = xCAT::NetworkUtils::getNodeIPaddress($openbmc_hash->{$node}->[0]->{'bmc'});
|
|
}
|
|
unless($node_info{$node}{bmc}) {
|
|
xCAT::SvrUtils::sendmsg("Error: Unable to get attribute bmc", $callback, $node);
|
|
delete $node_info{$node};
|
|
$rst = 1;
|
|
next;
|
|
}
|
|
unless($node_info{$node}{bmcip}) {
|
|
xCAT::SvrUtils::sendmsg("Error: Unable to resolve ip address for bmc: $node_info{$node}{bmc}", $callback, $node);
|
|
delete $node_info{$node};
|
|
$rst = 1;
|
|
next;
|
|
}
|
|
if ($openbmc_hash->{$node}->[0]->{'username'}) {
|
|
$node_info{$node}{username} = $openbmc_hash->{$node}->[0]->{'username'};
|
|
} elsif ($passwd_hash and $passwd_hash->{username}) {
|
|
$node_info{$node}{username} = $passwd_hash->{username};
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg("Error: Unable to get attribute username", $callback, $node);
|
|
delete $node_info{$node};
|
|
$rst = 1;
|
|
next;
|
|
}
|
|
|
|
if ($openbmc_hash->{$node}->[0]->{'password'}) {
|
|
$node_info{$node}{password} = $openbmc_hash->{$node}->[0]->{'password'};
|
|
} elsif ($passwd_hash and $passwd_hash->{password}) {
|
|
$node_info{$node}{password} = $passwd_hash->{password};
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg("Error: Unable to get attribute password", $callback, $node);
|
|
delete $node_info{$node};
|
|
$rst = 1;
|
|
next;
|
|
}
|
|
} else {
|
|
xCAT::SvrUtils::sendmsg("Error: Unable to get information from openbmc table", $callback, $node);
|
|
$rst = 1;
|
|
next;
|
|
}
|
|
}
|
|
|
|
return $rst;
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 refactor_args
|
|
|
|
refractor args to be easily dealt by python client
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
|
|
sub refactor_args {
|
|
my $request = shift;
|
|
my $command = $request->{command}->[0];
|
|
my $extrargs = $request->{arg};
|
|
my $subcommand;
|
|
if ($command eq "rspconfig") {
|
|
$subcommand = $extrargs->[0];
|
|
if ($subcommand !~ /^dump$|^sshcfg$|^ip=dhcp$/) {
|
|
if (grep /=/, @$extrargs) {
|
|
unshift @$extrargs, "set";
|
|
} else {
|
|
unshift @$extrargs, "get";
|
|
}
|
|
}
|
|
}
|
|
if ($command eq "reventlog") {
|
|
if (!defined($extrargs->[0])) {
|
|
# If no parameters are passed, default to list all records
|
|
$request->{arg} = ["list","all"];
|
|
}
|
|
else {
|
|
$subcommand = $extrargs->[0];
|
|
}
|
|
if ($subcommand =~ /^\d+$/) {
|
|
unshift @$extrargs, "list";
|
|
}
|
|
elsif ($subcommand =~/^resolved=(.*)/) {
|
|
unshift @$extrargs, "resolved";
|
|
}
|
|
elsif ($subcommand =~/^all$/) {
|
|
unshift @$extrargs, "list";
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
1;
|