2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-06-15 19:01:44 +00:00

[DO NOT MERGER] framwork for openbmc support

This commit is contained in:
XuWei
2017-03-01 08:23:31 -05:00
parent 83ebef0425
commit 72a0a2b5a9
3 changed files with 679 additions and 1 deletions

View File

@ -443,6 +443,19 @@ passed as argument rather than by table value',
disable => "Set to 'yes' or '1' to comment out this row.",
},
},
openbmc => {
cols => [qw(node bmc username password comments disable)],
keys => [qw(node)],
table_desc => 'Setting for nodes that are controlled by an on-board OpenBmc.',
descriptions => {
node => 'The node name or group name.',
bmc => 'The hostname of the BMC adapter.',
username => 'The BMC userid.',
password => 'The BMC password.',
comments => 'Any user-written notes.',
disable => "Set to 'yes' or '1' to comment out this row.",
},
},
iscsi => {
cols => [qw(node server target lun iname file userid passwd kernel kcmdline initrd comments disable)],
keys => [qw(node)],
@ -586,7 +599,7 @@ passed as argument rather than by table value',
descriptions => {
node => 'The node name or group name.',
power => 'The method to use to control the power of the node. If not set, the mgt attribute will be used. Valid values: ipmi, blade, hmc, ivm, fsp, kvm, esx, rhevm. If "ipmi", xCAT will search for this node in the ipmi table for more info. If "blade", xCAT will search for this node in the mp table. If "hmc", "ivm", or "fsp", xCAT will search for this node in the ppc table.',
mgt => 'The method to use to do general hardware management of the node. This attribute is used as the default if power or getmac is not set. Valid values: ipmi, blade, hmc, ivm, fsp, bpa, kvm, esx, rhevm. See the power attribute for more details.',
mgt => 'The method to use to do general hardware management of the node. This attribute is used as the default if power or getmac is not set. Valid values: openbmc, ipmi, blade, hmc, ivm, fsp, bpa, kvm, esx, rhevm. See the power attribute for more details.',
cons => 'The console method. If nodehm.serialport is set, this will default to the nodehm.mgt setting, otherwise it defaults to unused. Valid values: cyclades, mrv, or the values valid for the mgt attribute.',
termserver => 'The hostname of the terminal server.',
termport => 'The port number on the terminal server that this node is connected to.',
@ -2447,6 +2460,26 @@ my @nodeattrs = (
tabentry => 'mpa.urlpath',
access_tabentry => 'mpa.mpa=attr:node',
},
#########################
# openbmc table #
##########################
{ attr_name => 'bmc',
only_if => 'mgt=openbmc',
tabentry => 'openbmc.bmc',
access_tabentry => 'openbmc.node=attr:node',
},
{ attr_name => 'bmcusername',
only_if => 'mgt=openbmc',
tabentry => 'openbmc.username',
access_tabentry => 'openbmc.node=attr:node',
},
{ attr_name => 'bmcpassword',
only_if => 'mgt=openbmc',
tabentry => 'openbmc.password',
access_tabentry => 'openbmc.node=attr:node',
},
######################
# nodepos table #
######################

View File

@ -0,0 +1,46 @@
#!/usr/bin/perl
## IBM(c) 2107 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT::OPENBMC;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use strict;
use warnings "all";
use HTTP::Async;
use HTTP::Request;
use HTTP::Headers;
use HTTP::Cookies;
use Data::Dumper;
my $header = HTTP::Headers->new('Content-Type' => 'application/json');
sub new {
my $async = shift;
$async = shift if (($async) && ($async =~ /OPENBMC/));
my $url = shift;
my $content = shift;
my $method = 'POST';
my $id = send_request( $async, $method, $url, $content );
return $id;
}
sub send_request {
my $async = shift;
$async = shift if (($async) && ($async =~ /OPENBMC/));
my $method = shift;
my $url = shift;
my $content = shift;
my $request = HTTP::Request->new( $method, $url, $header, $content );
my $id = $async->add_with_opts($request, {});
return $id;
}
1;

View File

@ -0,0 +1,599 @@
#!/usr/bin/perl
## IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::openbmc;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use strict;
use warnings "all";
use HTTP::Async;
use HTTP::Cookies;
use xCAT::OPENBMC;
use xCAT::Utils;
use xCAT::Table;
use xCAT::Usage;
use xCAT::SvrUtils;
use File::Basename;
use Data::Dumper;
use JSON;
#-------------------------------------------------------
=head3 handled_commands
Return list of commands handled by this plugin
=cut
#-------------------------------------------------------
sub handled_commands {
return {
rpower => 'nodehm:mgt',
rinv => 'nodehm:mgt',
};
}
#-------------------------------------------------------
# The hash table to store method and url for request,
# process function for response
#-------------------------------------------------------
my %status_info = (
LOGIN_REQUEST => {
method => "POST",
init_url => "/login",
},
LOGIN_RESPONSE => {
process => \&login_response,
},
RPOWER_ON_REQUEST => {
method => "POST",
init_url => "/power/on",
},
RPOWER_ON_RESPONSE => {
process => \&rpower_response,
},
RPOWER_OFF_REQUEST => {
method => "POST",
init_url => "/power/off",
},
RPOWER_OFF_RESPONSE => {
process => \&rpower_response,
},
RPOWER_RESET_REQUEST => {
method => "POST",
init_url => "/power/reset",
},
RPOWER_RESET_RESPONSE => {
process => \&rpower_response,
},
RPOWER_STATUS_REQUEST => {
method => "GET",
init_url => "/org/openbmc/settings/host0",
},
RPOWER_STATUS_RESPONSE => {
process => \&rpower_response,
},
RINV_MTHBRD_REQUEST => {
method => "GET",
init_url => "/org/openbmc/inventory/system/chassis/motherboard",
},
RINV_MTHBRD_RESPONSE => {
process => \&rinv_response,
},
RINV_CPU_REQUEST => {
method => "GET",
init_url => "/org/openbmc/inventory/system/chassis/motherboard/",
},
RINV_CPU_RESPONSE => {
process => \&rinv_response,
},
RINV_DIMM_REQUEST => {
method => "GET",
init_url => "/org/openbmc/inventory/system/chassis/motherboard/",
},
RINV_DIMM_RESPONSE => {
process => \&rinv_response,
},
);
#-----------------------------
=head3 %node_info
$node_info = (
$node => {
bmc => "x.x.x.x",
username => "username",
password => "password",
cur_status => "LOGIN_REQUEST",
cur_url => "",
method => "",
back_urls => (),
$src => "",
},
);
'cur_url', 'method', 'back_urls' used for path has a trailing-slash
=cut
#-----------------------------
my %node_info = ();
my %next_status = ();
my %handle_id_node = ();
my $wait_node_num;
my $async;
my $cookie_jar;
my $callback;
#-------------------------------------------------------
=head3 preprocess_request
preprocess the command
=cut
#-------------------------------------------------------
sub preprocess_request {
my $request = shift;
if (defined $request->{_xcatpreprocessed}->[0] and $request->{_xcatpreprocessed}->[0] == 1) {
return [$request];
}
$callback = shift;
my $command = $request->{command}->[0];
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);
if (ref($parse_result) eq 'ARRAY') {
$callback->({ error => $parse_result->[1], errorcode => $parse_result->[0] });
$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;
my $noderange = $request->{node};
parse_node_info($noderange);
$cookie_jar = HTTP::Cookies->new({});
$async = HTTP::Async->new(
cookie_jar => $cookie_jar,
ssl_options => {
SSL_verify_mode => 0,
},
);
my $bmcip;
my $login_url;
my $handle_id;
my $content;
$wait_node_num = keys %node_info;
foreach my $node (keys %node_info) {
$bmcip = $node_info{$node}{bmc};
$login_url = "https://$bmcip/login";
$content = '{"data": [ "' . $node_info{$node}{username} .'", "' . $node_info{$node}{password} . '" ] }';
$handle_id = xCAT::OPENBMC->new($async, $login_url, $content);
$handle_id_node{$handle_id} = $node;
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
}
while (1) {
last unless ($wait_node_num);
while (my ($response, $handle_id) = $async->wait_for_next_response) {
deal_with_response($handle_id, $response);
}
}
return;
}
#-------------------------------------------------------
=head3 parse_args
Parse the command line options and operands
=cut
#-------------------------------------------------------
sub parse_args {
my $command = shift;
my $extrargs = shift;
$next_status{LOGIN_REQUEST} = "LOGIN_RESPONSE";
if ($command eq "rpower") {
if (!defined($extrargs)) {
return ([ 1, "No option specified for rpower" ]);
}
if (scalar(@ARGV) > 1) {
return ([ 1, "Only one option is supportted at the same time" ]);
}
my $subcommand = $ARGV[0];
# now, only support status, delete when other command supported
if ($subcommand ne "status" and $subcommand ne "state" and $subcommand ne "stat") {
return ([ 1, "Only support status check currently" ])
}
#----------------------------------------------------------------
if ($subcommand eq "on") {
$next_status{LOGIN_RESPONSE} = "RPOWER_ON_REQUEST";
$next_status{RPOWER_ON_REQUEST} = "RPOWER_ON_RESPONSE";
} elsif ($subcommand eq "off") {
$next_status{LOGIN_RESPONSE} = "RPOWER_OFF_REQUEST";
$next_status{RPOWER_OFF_REQUEST} = "RPOWER_OFF_RESPONSE";
} elsif ($subcommand eq "status" or $subcommand eq "state" or $subcommand eq "stat") {
$next_status{LOGIN_RESPONSE} = "RPOWER_STATUS_REQUEST";
$next_status{RPOWER_STATUS_REQUEST} = "RPOWER_STATUS_RESPONSE";
} elsif ($subcommand eq "boot") {
$next_status{LOGIN_RESPONSE} = "RPOWER_STATUS_REQUEST";
$next_status{RPOWER_STATUS_REQUEST} = "RPOWER_STATUS_RESPONSE";
$next_status{RPOWER_STATUS_RESPONSE}{OFF} = "RPOWER_ON_REQUEST";
$next_status{RPOWER_ON_REQUEST} = "RPOWER_ON_RESPONSE";
$next_status{RPOWER_STATUS_RESPONSE}{ON} = "RPOWER_RESET_REQUEST";
$next_status{RPOWER_RESET_REQUEST} = "RPOWER_RESET_RESPONSE";
} else {
return ([ 1, "$subcommand is not supported for rpower" ]);
}
}
if ($command eq "rinv") {
if (!defined($extrargs)) {
return ([ 1, "No option specified for rpower" ]);
}
if (scalar(@ARGV) > 1) {
return ([ 1, "Only one option is supportted at the same time" ]);
}
my $subcommand = $ARGV[0];
if ($subcommand eq "cpu") {
$next_status{LOGIN_RESPONSE} = "RINV_CPU_REQUEST";
$next_status{RINV_CPU_REQUEST} = "RINV_CPU_RESPONSE";
} elsif ($subcommand eq "dimm") {
$next_status{LOGIN_RESPONSE} = "RINV_DIMM_REQUEST";
$next_status{RINV_DIMM_REQUEST} = "RINV_DIMM_RESPONSE";
} elsif ($subcommand eq "all") {
$next_status{LOGIN_RESPONSE} = "RINV_MTHBRD_REQUEST";
$next_status{RINV_MTHBRD_REQUEST} = "RINV_MTHBRD_RESPONSE";
$next_status{RINV_MTHBRD_RESPONSE} = "RINV_CPU_REQUEST";
$next_status{RINV_CPU_REQUEST} = "RINV_CPU_RESPONSE";
$next_status{RINV_CPU_RESPONSE} = "RINV_DIMM_REQUEST";
$next_status{RINV_DIMM_REQUEST} = "RINV_DIMM_RESPONSE";
} else {
return ([ 1, "Only 'cpu','dimm','all' are supportted at the same time" ]);
}
}
print Dumper(%next_status) . "\n";
return;
}
#-------------------------------------------------------
=head3 parse_node_info
Parse the node information: bmc, username, password
=cut
#-------------------------------------------------------
sub parse_node_info {
my $noderange = shift;
my $table = xCAT::Table->new('openbmc');
my $tablehash = $table->getNodesAttribs(\@$noderange, ['bmc', 'username', 'password']);
foreach my $node (@$noderange) {
if (defined($tablehash->{$node}->[0])) {
if ($tablehash->{$node}->[0]->{'bmc'}) {
$node_info{$node}{bmc} = $tablehash->{$node}->[0]->{'bmc'};
} else {
xCAT::SvrUtils::sendmsg("Unable to get attribute bmc", $callback, $node);
next;
}
if ($tablehash->{$node}->[0]->{'username'}) {
$node_info{$node}{username} = $tablehash->{$node}->[0]->{'username'};
} else {
xCAT::SvrUtils::sendmsg("Unable to get attribute username", $callback, $node);
delete $node_info{$node};
next;
}
if ($tablehash->{$node}->[0]->{'password'}) {
$node_info{$node}{password} = $tablehash->{$node}->[0]->{'password'};
} else {
xCAT::SvrUtils::sendmsg("Unable to get attribute password", $callback, $node);
delete $node_info{$node};
next;
}
$node_info{$node}{cur_status} = "LOGIN_REQUEST";
} else {
xCAT::SvrUtils::sendmsg("Unable to get information from openbmc table", $callback, $node);
next;
}
}
print Dumper(%node_info) ."\n";
return;
}
#-------------------------------------------------------
=head3 gen_send_request
Generate request's information
If the node has method itself, use it as request's method.
If not, use method %status_info defined.
If the node has cur_url, check whether also has sub_urls.
If has, request's url is join cur_url and one in sub_urls(use one at once to check which is needed).
If not, use method %status_info defined.
use xCAT::OPENBMC->send_request send request
store handle_id and mapping node
Input:
$node: nodename of current node
=cut
#-------------------------------------------------------
sub gen_send_request {
my $node = shift;
my $method;
my $request_url;
my $content;
if ($node_info{$node}{method}) {
$method = $node_info{$node}{method};
} else {
$method = $status_info{ $node_info{$node}{cur_status} }{method};
}
if ($node_info{$node}{cur_url}) {
$request_url = $node_info{$node}{cur_url};
} else {
$request_url = $status_info{ $node_info{$node}{cur_status} }{init_url};
}
$request_url = "https://" . $node_info{$node}{bmc} . $request_url;
my $handle_id = xCAT::OPENBMC->send_request($async, $method, $request_url, $content);
$handle_id_node{$handle_id} = $node;
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
return;
}
#-------------------------------------------------------
=head3 deal_with_response
Check response's status_line and
Input:
$handle_id: Async return ID with response
$response: Async return response
=cut
#-------------------------------------------------------
sub deal_with_response {
my $handle_id = shift;
my $response = shift;
my $node = $handle_id_node{$handle_id};
if ($response->status_line ne "200 OK") {
my $response_info = decode_json $response->content;
xCAT::SvrUtils::sendmsg($response_info->{'data'}->{'description'}, $callback, $node);
$wait_node_num--;
return;
}
delete $handle_id_node{$handle_id};
$status_info{ $node_info{$node}{cur_status} }->{process}->($node, $response);
return;
}
#-------------------------------------------------------
=head3 login_response
Deal with response of login
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub login_response {
my $node = shift;
my $response = shift;
if ($next_status{ $node_info{$node}{cur_status} }) {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
gen_send_request($node);
}
return;
}
#-------------------------------------------------------
=head3 rpower_response
Deal with response of rpower command
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub rpower_response {
my $node = shift;
my $response = shift;
my $response_info = decode_json $response->content;
if ($node_info{$node}{cur_status} eq "RPOWER_ON_RESPONSE") {
xCAT::SvrUtils::sendmsg("on", $callback, $node);
}
if ($node_info{$node}{cur_status} eq "RPOWER_OFF_RESPONSE") {
xCAT::SvrUtils::sendmsg("off", $callback, $node);
}
if ($node_info{$node}{cur_status} eq "RPOWER_STATUS_RESPONSE") {
xCAT::SvrUtils::sendmsg($response_info->{'data'}->{system_state}, $callback, $node);
}
if ($next_status{ $node_info{$node}{cur_status} }) {
if ($node_info{$node}{cur_status} eq "RPOWER_STATUS_RESPONSE") {
if ($response_info->{'data'}->{system_state} =~ /HOST_POWERED_ON/) {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} }{ON};
} else {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} }{OFF};
}
} else {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
}
gen_send_request($node);
} else {
$wait_node_num--;
}
return;
}
sub rinv_response {
my $node = shift;
my $response = shift;
my $response_info = decode_json $response->content;
my %rinv_response = (
RINV_CPU_RESPONSE => {
map_str => "cpu",
repeat => "RINV_CPU_REQUEST",
},
RINV_DIMM_RESPONSE => {
map_str => "dimm",
repeat => "RINV_DIMM_REQUEST",
},
);
my $grep_string = $rinv_response{$node_info{$node}{cur_status}}{map_str};
my $repeat_status = $rinv_response{$node_info{$node}{cur_status}}{repeat};
if ($node_info{$node}{cur_status} ne "RINV_MTHBRD_RESPONSE") {
if (ref($response_info->{data}) eq "ARRAY") {
foreach my $rsp_url (@{$response_info->{data}}) {
if ($rsp_url =~ /\/$grep_string/) {
push @{ $node_info{$node}{back_urls} }, $rsp_url;
}
}
$node_info{$node}{cur_url} = shift @{ $node_info{$node}{back_urls} };
$node_info{$node}{cur_status} = $repeat_status;
$node_info{$node}{src} = basename $node_info{$node}{cur_url};
gen_send_request($node);
} elsif (ref($response_info->{data}) eq "HASH") {
my $cpu_info;
foreach my $key (keys %{$response_info->{data}}) {
$cpu_info = uc ($node_info{$node}{src}) . " " . $key . " : " . ${$response_info->{data}}{$key};
xCAT::SvrUtils::sendmsg("$cpu_info", $callback, $node);
}
if (@{ $node_info{$node}{back_urls} }) {
$node_info{$node}{cur_url} = shift @{ $node_info{$node}{back_urls} };
$node_info{$node}{cur_status} = $repeat_status;
$node_info{$node}{src} = basename $node_info{$node}{cur_url};
gen_send_request($node);
} else {
if ($next_status{ $node_info{$node}{cur_status} }) {
delete $node_info{$node}{cur_url};
delete $node_info{$node}{src};
gen_send_request($node);
} else {
$wait_node_num--;
}
}
}
}
#print Dumper(%node_info) ."\n";
return;
}
1;