2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-09-04 01:08:18 +00:00
Files
xcat-core/xCAT-server/lib/xcat/plugins/openbmc.pm
yangsong 7b7d9ab675 merge master to 2.14 branch (#5546)
* To fix issue that lose activation success log

* update onie doc to add rspconfig sshcfg command (#5539)

* fix osarch missing from copycds (#5543)

* Modify release information for 2.14.3 release

* update goconserver quickstart document

* Enhancement for using site cache in plugin (#5535)

* site cache when run plugin does not work very well
- using cache from plugin when getNodesAttribs/getNodeAttribs (pass it into DB process from plugin process)
- Site cache is a whole hash, so to use cache when by the hash is there, instead of the specified key is there.
It is because that there might be no key defined in site table.

* with XCATBYPASS, to populate site hash before scan_plugins. Then only 1 query for site table to do whole things.

* cache site when init plugins on service nodes

* missing to comment the old codes query from xCAT DB process
2018-08-21 18:38:08 +08:00

5017 lines
219 KiB
Perl

#!/usr/bin/perl
## IBM(c) 2017 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::openbmc;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
my $async_path = "/usr/local/share/perl5/";
unless (grep { $_ eq $async_path } @INC) {
push @INC, $async_path;
}
}
use lib "$::XCATROOT/lib/perl";
use strict;
use warnings "all";
use JSON;
use HTTP::Async;
use HTTP::Cookies;
use LWP::UserAgent;
use File::Basename;
use File::Spec;
use File::Copy qw/copy cp mv move/;
use File::Path;
use Data::Dumper;
use Getopt::Long;
use xCAT::OPENBMC;
use xCAT::RemoteShellExp;
use xCAT::Utils;
use xCAT::Table;
use xCAT::Usage;
use xCAT::SvrUtils;
use xCAT::GlobalDef;
use xCAT_monitoring::monitorctrl;
use POSIX qw(WNOHANG);
use xCAT::Utils qw/natural_sort_cmp/;
$::VERBOSE = 0;
# String constants for rbeacon states
$::BEACON_STATE_OFF = "off";
$::BEACON_STATE_ON = "on";
# String constants for rpower states
$::POWER_STATE_OFF = "off";
$::POWER_STATE_ON = "on";
$::POWER_STATE_ON_HOSTOFF = "on (Chassis)";
$::POWER_STATE_POWERING_OFF = "powering-off";
$::POWER_STATE_POWERING_ON = "powering-on";
$::POWER_STATE_QUIESCED = "quiesced";
$::POWER_STATE_RESET = "reset";
$::POWER_STATE_REBOOT = "reboot";
$::UPLOAD_FILE = "";
$::UPLOAD_FILE_VERSION = "";
$::UPLOAD_PNOR = "";
$::UPLOAD_PNOR_VERSION = "";
$::UPLOAD_FILE_HASH_ID = "";
$::UPLOAD_PNOR_HASH_ID = "";
$::RSETBOOT_URL_PATH = "boot";
# To improve the output to users, store this value as a global
$::UPLOAD_AND_ACTIVATE = 0;
$::UPLOAD_ACTIVATE_STREAM = 0;
$::RFLASH_STREAM_NO_HOST_REBOOT = 0;
$::NO_ATTRIBUTES_RETURNED = "No attributes returned from the BMC.";
$::FAILED_UPLOAD_MSG = "Failed to upload update file";
$::FAILED_LOGIN_MSG = "BMC did not respond. Validate BMC configuration and retry the command.";
$::UPLOAD_WAIT_ATTEMPT = 6;
$::UPLOAD_WAIT_INTERVAL = 10;
$::UPLOAD_WAIT_TOTALTIME = int($::UPLOAD_WAIT_ATTEMPT*$::UPLOAD_WAIT_INTERVAL);
$::RPOWER_CHECK_INTERVAL = 2;
$::RPOWER_CHECK_ON_INTERVAL = 12;
$::RPOWER_ON_MAX_RETRY = 5;
$::RPOWER_MAX_RETRY = 30;
$::RPOWER_CHECK_ON_TIME = 1;
$::RPOWER_RESET_SLEEP_INTERVAL = 13;
$::BMC_MAX_RETRY = 20;
$::BMC_CHECK_INTERVAL = 15;
$::RSPCONFIG_DUMP_INTERVAL = 15;
$::RSPCONFIG_DUMP_MAX_RETRY = 20;
$::RSPCONFIG_DUMP_WAIT_TOTALTIME = int($::RSPCONFIG_DUMP_INTERVAL*$::RSPCONFIG_DUMP_MAX_RETRY);
$::RSPCONFIG_DUMP_DOWNLOAD_ALL_REQUESTED = 0;
$::RSPCONFIG_WAIT_VLAN_DONE = 15;
$::RSPCONFIG_WAIT_IP_DONE = 3;
$::RSPCONFIG_DUMP_CMD_TIME = 0;
$::RSPCONFIG_CONFIGURED_API_KEY = -1;
$::XCAT_LOG_DIR = "/var/log/xcat";
$::RAS_POLICY_TABLE = "/opt/ibm/ras/lib/policyTable.json";
$::XCAT_LOG_RFLASH_DIR = $::XCAT_LOG_DIR . "/rflash/";
$::XCAT_LOG_DUMP_DIR = $::XCAT_LOG_DIR . "/dump/";
unless (-d $::XCAT_LOG_RFLASH_DIR) {
mkpath($::XCAT_LOG_RFLASH_DIR);
}
unless (-d $::XCAT_LOG_DUMP_DIR) {
mkpath($::XCAT_LOG_DUMP_DIR);
}
# 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'";
sub unsupported {
my $callback = shift;
if (defined($::OPENBMC_DEVEL) && ($::OPENBMC_DEVEL eq "YES")) {
return;
} else {
return ([ 1, "This openbmc related function is not yet supported. Please contact xCAT development team." ]);
}
}
#-------------------------------------------------------
=head3 handled_commands
Return list of commands handled by this plugin
=cut
#-------------------------------------------------------
sub handled_commands {
return {
getopenbmccons => 'nodehm:cons',
rbeacon => 'nodehm:mgt',
renergy => 'nodehm:mgt',
reventlog => 'nodehm:mgt',
rflash => 'nodehm:mgt',
rinv => 'nodehm:mgt',
rpower => 'nodehm:mgt',
rsetboot => 'nodehm:mgt',
rspconfig => 'nodehm:mgt',
rspreset => 'nodehm:mgt',
rvitals => 'nodehm:mgt',
};
}
my $prefix = "xyz.openbmc_project";
my %sensor_units = (
"$prefix.Sensor.Value.Unit.DegreesC" => "C",
"$prefix.Sensor.Value.Unit.RPMS" => "RPMS",
"$prefix.Sensor.Value.Unit.Volts" => "Volts",
"$prefix.Sensor.Value.Unit.Meters" => "Meters",
"$prefix.Sensor.Value.Unit.Amperes" => "Amps",
"$prefix.Sensor.Value.Unit.Watts" => "Watts",
"$prefix.Sensor.Value.Unit.Joules" => "Joules"
);
my %child_node_map; # pid => node
my %fw_tar_files;
my $http_protocol="https";
my $openbmc_url = "/org/openbmc";
my $openbmc_project_url = "/xyz/openbmc_project";
$::SOFTWARE_URL = "$openbmc_project_url/software";
$::LOGGING_URL = "$openbmc_project_url/logging/entry/#ENTRY_ID#/attr/Resolved";
#-------------------------------------------------------
# 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_REQUEST_GENERAL => {
method => "POST",
init_url => "/login",
},
LOGIN_RESPONSE => {
process => \&login_response,
},
LOGIN_RESPONSE_GENERAL => {
process => \&login_response,
},
RBEACON_ON_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/led/groups/enclosure_identify/attr/Asserted",
data => "true",
},
RBEACON_ON_RESPONSE => {
process => \&rbeacon_response,
},
RBEACON_OFF_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/led/groups/enclosure_identify/attr/Asserted",
data => "false",
},
RBEACON_OFF_RESPONSE => {
process => \&rbeacon_response,
},
REVENTLOG_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/logging/enumerate",
},
REVENTLOG_RESPONSE => {
process => \&reventlog_response,
},
REVENTLOG_CLEAR_REQUEST => {
method => "POST",
init_url => "$openbmc_project_url/logging/action/deleteAll",
data => "[]",
},
REVENTLOG_CLEAR_RESPONSE => {
process => \&reventlog_response,
},
REVENTLOG_RESOLVED_REQUEST => {
method => "PUT",
init_url => "$::LOGGING_URL",
data => "1",
},
REVENTLOG_RESOLVED_RESPONSE => {
process => \&reventlog_response,
},
REVENTLOG_RESOLVED_RESPONSE_LED => {
process => \&reventlog_response,
},
RFLASH_LIST_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/software/enumerate",
},
RFLASH_LIST_RESPONSE => {
process => \&rflash_response,
},
RFLASH_FILE_UPLOAD_REQUEST => {
process => \&rflash_response,
},
RFLASH_FILE_UPLOAD_RESPONSE => {
process => \&rflash_response,
},
RFLASH_UPDATE_ACTIVATE_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/software",
data => "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
},
RFLASH_UPDATE_HOST_ACTIVATE_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/software",
data => "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
},
RFLASH_UPDATE_ACTIVATE_RESPONSE => {
process => \&rflash_response,
},
RFLASH_UPDATE_HOST_ACTIVATE_RESPONSE => {
process => \&rflash_response,
},
RFLASH_UPDATE_CHECK_STATE_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/software",
},
RFLASH_UPDATE_CHECK_STATE_RESPONSE => {
process => \&rflash_response,
},
RFLASH_UPDATE_CHECK_ID_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/software/enumerate",
},
RFLASH_UPDATE_CHECK_ID_RESPONSE => {
process => \&rflash_response,
},
RFLASH_SET_PRIORITY_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/software",
data => "false", # Priority state of 0 sets image to active
},
RFLASH_SET_PRIORITY_RESPONSE => {
process => \&rflash_response,
},
RFLASH_DELETE_IMAGE_REQUEST => {
method => "POST",
init_url => "$openbmc_project_url/software",
data => "[]",
},
RFLASH_DELETE_IMAGE_RESPONSE => {
process => \&rflash_response,
},
RFLASH_DELETE_CHECK_STATE_RESPONSE => {
process => \&rflash_response,
},
RINV_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/inventory/enumerate",
},
RINV_RESPONSE => {
process => \&rinv_response,
},
RINV_FIRM_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/software/enumerate",
},
RINV_FIRM_RESPONSE => {
process => \&rinv_response,
},
RPOWER_BMCREBOOT_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/state/bmc0/attr/RequestedBMCTransition",
data => "xyz.openbmc_project.State.BMC.Transition.Reboot",
},
RPOWER_ON_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/state/host0/attr/RequestedHostTransition",
data => "xyz.openbmc_project.State.Host.Transition.On",
},
RPOWER_ON_RESPONSE => {
process => \&rpower_response,
},
RPOWER_CHECK_ON_RESPONSE => {
process => \&rpower_response,
},
RPOWER_OFF_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/state/chassis0/attr/RequestedPowerTransition",
data => "xyz.openbmc_project.State.Chassis.Transition.Off",
},
RPOWER_OFF_RESPONSE => {
process => \&rpower_response,
},
RPOWER_SOFTOFF_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/state/host0/attr/RequestedHostTransition",
data => "xyz.openbmc_project.State.Host.Transition.Off",
},
RPOWER_SOFTOFF_RESPONSE => {
process => \&rpower_response,
},
RPOWER_RESET_RESPONSE => {
process => \&rpower_response,
},
RPOWER_STATUS_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/state/enumerate",
},
RPOWER_STATUS_RESPONSE => {
process => \&rpower_response,
},
RPOWER_CHECK_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/state/enumerate",
},
RPOWER_CHECK_ON_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/state/enumerate",
},
RPOWER_BMC_STATUS_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/state/enumerate",
},
RPOWER_BMC_CHECK_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/state/enumerate",
},
RPOWER_BMC_STATUS_RESPONSE => {
process => \&rpower_response,
},
RPOWER_BMC_CHECK_RESPONSE => {
process => \&rpower_response,
},
RPOWER_CHECK_RESPONSE => {
process => \&rpower_response,
},
RSETBOOT_ENABLE_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/control/host0/boot/one_time/attr/Enabled",
data => '1',
},
RSETBOOT_ENABLE_RESPONSE => {
process => \&rsetboot_response,
},
RSETBOOT_SET_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/control/host0/boot/one_time/attr/BootSource",
data => "xyz.openbmc_project.Control.Boot.Source.Sources.",
},
RSETBOOT_SET_RESPONSE => {
process => \&rsetboot_response,
},
RSETBOOT_STATUS_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/control/host0/enumerate",
},
RSETBOOT_STATUS_RESPONSE => {
process => \&rsetboot_response,
},
RSPCONFIG_GET_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/network/enumerate",
},
RSPCONFIG_GET_RESPONSE => {
process => \&rspconfig_response,
},
RSPCONFIG_GET_NIC_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/network/enumerate",
},
RSPCONFIG_GET_NIC_RESPONSE => {
process => \&rspconfig_response,
},
RSPCONFIG_SET_PASSWD_REQUEST => {
method => "POST",
init_url => "/xyz/openbmc_project/user/root/action/SetPassword",
data => "",
},
"RSPCONFIG_PASSWD_VERIFY" => {
process => \&rspconfig_response,
},
RSPCONFIG_SET_HOSTNAME_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/network/config/attr/HostName",
data => "[]",
},
RSPCONFIG_SET_NTPSERVERS_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/network/#NIC#/attr/NTPServers",
data => "[]",
},
RSPCONFIG_SET_RESPONSE => {
process => \&rspconfig_response,
},
RSPCONFIG_IPOBJECT_REQUEST => {
method => "POST",
init_url => "$openbmc_project_url/network/#NIC#/action/IP",
data => "",
},
RSPCONFIG_IPOBJECT_RESPONSE => {
process => \&rspconfig_response,
},
RSPCONFIG_VLAN_REQUEST => {
method => "POST",
init_url => "$openbmc_project_url/network/action/VLAN",
data => "",
},
RSPCONFIG_VLAN_RESPONSE => {
process => \&rspconfig_response,
},
RSPCONFIG_CHECK_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/network/enumerate",
},
RSPCONFIG_CHECK_RESPONSE => {
process => \&rspconfig_response,
},
RSPCONFIG_DHCP_REQUEST => {
method => "POST",
init_url => "$openbmc_project_url/network/action/Reset",
data => "[]",
},
RSPCONFIG_DHCP_RESPONSE => {
process => \&rspconfig_response,
},
RSPCONFIG_DHCPDIS_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url/network/#NIC#/attr/DHCPEnabled",
data => 0,
},
RSPCONFIG_DHCPDIS_RESPONSE => {
process => \&rspconfig_response,
},
RSPCONFIG_DELETE_REQUEST => {
method => "DELETE",
init_url => "",
},
RSPCONFIG_DELETE_RESPONSE => {
process => \&rspconfig_response,
},
RSPCONFIG_PRINT_BMCINFO => {
process => \&rspconfig_response,
},
RSPCONFIG_SSHCFG_REQUEST => {
process => \&rspconfig_sshcfg_response,
},
RSPCONFIG_SSHCFG_RESPONSE => {
process => \&rspconfig_sshcfg_response,
},
RSPCONFIG_CLEAR_GARD_REQUEST => {
method => "POST",
init_url => "/org/open_power/control/gard/action/Reset",
data => "[]",
},
RSPCONFIG_CLEAR_GARD_RESPONSE => {
process => \&rspconfig_response,
},
RSPCONFIG_DUMP_LIST_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/dump/enumerate",
},
RSPCONFIG_DUMP_LIST_RESPONSE => {
process => \&rspconfig_dump_response,
},
RSPCONFIG_DUMP_CHECK_RESPONSE => {
process => \&rspconfig_dump_response,
},
RSPCONFIG_DUMP_CREATE_REQUEST => {
method => "POST",
init_url => "$openbmc_project_url/dump/action/CreateDump",
data => "[]",
},
RSPCONFIG_DUMP_CREATE_RESPONSE => {
process => \&rspconfig_dump_response,
},
RSPCONFIG_DUMP_CLEAR_REQUEST => {
method => "POST",
init_url => "$openbmc_project_url/dump/entry/#ID#/action/Delete",
data => "[]",
},
RSPCONFIG_DUMP_CLEAR_ALL_REQUEST => {
method => "POST",
init_url => "$openbmc_project_url/dump/action/DeleteAll",
data => "[]",
},
RSPCONFIG_DUMP_CLEAR_RESPONSE => {
process => \&rspconfig_dump_response,
},
RSPCONFIG_DUMP_DOWNLOAD_REQUEST => {
init_url => "download/dump/#ID#",
process => \&rspconfig_dump_response,
},
RSPCONFIG_DUMP_DOWNLOAD_RESPONSE => {
process => \&rspconfig_dump_response,
},
RSPCONFIG_DUMP_DOWNLOAD_ALL_RESPONSE => {
process => \&rspconfig_dump_response,
},
RVITALS_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/sensors/enumerate",
},
RVITALS_RESPONSE => {
process => \&rvitals_response,
},
RVITALS_LEDS_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url/led/physical/enumerate",
},
RVITALS_LEDS_RESPONSE => {
process => \&rvitals_response,
},
RSPCONFIG_API_CONFIG_ON_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url",
data => "true",
},
RSPCONFIG_API_CONFIG_ON_RESPONSE => {
process => \&rspconfig_api_config_response,
},
RSPCONFIG_API_CONFIG_OFF_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url",
data => "false",
},
RSPCONFIG_API_CONFIG_OFF_RESPONSE => {
process => \&rspconfig_api_config_response,
},
RSPCONFIG_API_CONFIG_ATTR_REQUEST => {
method => "PUT",
init_url => "$openbmc_project_url",
data => "false",
},
RSPCONFIG_API_CONFIG_ACTION_ATTR_REQUEST => {
method => "POST",
init_url => "$openbmc_project_url",
data => "false",
},
RSPCONFIG_API_CONFIG_ATTR_RESPONSE => {
process => \&rspconfig_api_config_response,
},
RSPCONFIG_API_CONFIG_ACTION_ATTR_QUERY_REQUEST => {
method => "POST",
init_url => "$openbmc_project_url",
data => "[]",
},
RSPCONFIG_API_CONFIG_QUERY_REQUEST => {
method => "GET",
init_url => "$openbmc_project_url",
},
RSPCONFIG_API_CONFIG_QUERY_RESPONSE => {
process => \&rspconfig_api_config_response,
},
);
# Setup configured subcommand.
# Currently only rspconfig is supported and only for boolean commands or attribute settings.
#
# Usage can also be autogenerated for these commands. However, changes to the xCAT::Usage->usage
# need to be made to split a single string into its components. Look at "rspconfig" usage as an
# example.
#
# For example: rspconfig <subcommand>
# rspconfig <subcommand>=0
# rspconfig <subcommand>=1
# rspconfig <subcommand>=<attr_value>
#
#
my %api_config_info = (
RSPCONFIG_AUTO_REBOOT => {
command => "rspconfig",
url => "/control/host0/auto_reboot",
attr_url => "AutoReboot",
display_name => "BMC AutoReboot",
instruct_msg => "",
type => "boolean",
subcommand => "autoreboot",
},
RSPCONFIG_BOOT_MODE => {
command => "rspconfig",
url => "/control/host0/boot",
attr_url => "BootMode",
display_name => "BMC BootMode",
instruct_msg => "",
type => "attribute",
subcommand => "bootmode",
attr_value => {
regular => "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular",
safe => "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe",
setup => "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup",
},
},
RSPCONFIG_POWERSUPPLY_REDUNDANCY => {
command => "rspconfig",
url => "/sensors/chassis/PowerSupplyRedundancy",
attr_url => "/action/setValue",
query_url => "/action/getValue",
display_name => "BMC PowerSupplyRedundancy",
instruct_msg => "",
type => "action_attribute",
subcommand => "powersupplyredundancy",
attr_value => {
disabled => "Disabled",
enabled => "Enabled",
},
},
RSPCONFIG_POWERRESTORE_POLICY => {
command => "rspconfig",
url => "/control/host0/power_restore_policy",
attr_url => "PowerRestorePolicy",
display_name => "BMC PowerRestorePolicy",
instruct_msg => "",
type => "attribute",
subcommand => "powerrestorepolicy",
attr_value => {
restore => "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore",
always_on => "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn",
always_off => "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff",
},
},
RSPCONFIG_TIME_SYNC_METHOD => {
command => "rspconfig",
url => "/time/sync_method",
attr_url => "TimeSyncMethod",
display_name => "BMC TimeSyncMethod",
instruct_msg => "",
type => "attribute",
subcommand => "timesyncmethod",
attr_value => {
ntp => "xyz.openbmc_project.Time.Synchronization.Method.NTP",
manual => "xyz.openbmc_project.Time.Synchronization.Method.Manual",
},
},
);
$::RESPONSE_OK = "200 OK";
$::RESPONSE_SERVER_ERROR = "500 Internal Server Error";
$::RESPONSE_SERVICE_UNAVAILABLE = "503 Service Unavailable";
$::RESPONSE_FORBIDDEN = "403 Forbidden";
$::RESPONSE_NOT_FOUND = "404 Not Found";
$::RESPONSE_METHOD_NOT_ALLOWED = "405 Method Not Allowed";
$::RESPONSE_SERVICE_TIMEOUT = "504 Gateway Timeout";
#-----------------------------
=head3 %node_info
$node_info = (
$node => {
bmc => "x.x.x.x",
username => "username",
password => "password",
cur_status => "LOGIN_REQUEST",
cur_url => "",
method => "",
},
);
'cur_url', 'method' used for path has a trailing-slash
=cut
#-----------------------------
my %node_info = ();
my %next_status = ();
my %handle_id_node = ();
# Store the value format like '<node> => <time>' to manage the green sleep time, used
# by retry_after and the main loop in process_request only.
my %node_wait = ();
my $wait_node_num;
my $async;
my $cookie_jar;
my $callback;
my %allerrornodes = ();
my $xcatdebugmode = 0;
my $flag_debug = "[openbmc_debug_perl]";
my %login_pid_node; # used in process_request, record login fork pid map
my $event_mapping = "";
#-------------------------------------------------------
=head3 preprocess_request
preprocess the command
=cut
#-------------------------------------------------------
sub preprocess_request {
my $request = shift;
$callback = shift;
if (defined $request->{_xcat_ignore_flag}->[0] and $request->{_xcat_ignore_flag}->[0] eq 'openbmc') {
return [];#workaround the bug 3026, to ignore it for openbmc
}
if (defined $request->{_xcatpreprocessed}->[0] and $request->{_xcatpreprocessed}->[0] == 1) {
return [$request];
}
##############################################
# Delete this when could be released
if (ref($request->{environment}) eq 'ARRAY' and ref($request->{environment}->[0]->{XCAT_OPENBMC_DEVEL}) eq 'ARRAY') {
$::OPENBMC_DEVEL = $request->{environment}->[0]->{XCAT_OPENBMC_DEVEL}->[0];
} elsif (ref($request->{environment}) eq 'ARRAY') {
$::OPENBMC_DEVEL = $request->{environment}->[0]->{XCAT_OPENBMC_DEVEL};
} else {
$::OPENBMC_DEVEL = $request->{environment}->{XCAT_OPENBMC_DEVEL};
}
if (ref($request->{environment}) eq 'ARRAY' and ref($request->{environment}->[0]->{XCAT_OPENBMC_FIRMWARE}) eq 'ARRAY') {
$::OPENBMC_FW = $request->{environment}->[0]->{XCAT_OPENBMC_FIRMWARE}->[0];
} elsif (ref($request->{environment}) eq 'ARRAY') {
$::OPENBMC_FW = $request->{environment}->[0]->{XCAT_OPENBMC_FIRMWARE};
} else {
$::OPENBMC_FW = $request->{environment}->{XCAT_OPENBMC_FIRMWARE};
}
# Provide a way to turn on and off transition state processing, default to off
if (ref($request->{environment}) eq 'ARRAY' and ref($request->{environment}->[0]->{XCAT_OPENBMC_POWER_TRANSITION}) eq 'ARRAY') {
$::OPENBMC_PWR = $request->{environment}->[0]->{XCAT_OPENBMC_POWER_TRANSITION}->[0];
} elsif (ref($request->{environment}) eq 'ARRAY') {
$::OPENBMC_PWR = $request->{environment}->[0]->{XCAT_OPENBMC_POWER_TRANSITION};
} else {
$::OPENBMC_PWR = $request->{environment}->{XCAT_OPENBMC_POWER_TRANSITION};
}
##############################################
my $command = $request->{command}->[0];
if ($request->{command}->[0] eq "getopenbmccons") {
my $nodes = $request->{node};
my $check = parse_node_info($nodes);
foreach my $node (keys %node_info) {
my $donargs = [ $node,$node_info{$node}{bmc},$node_info{$node}{username}, $node_info{$node}{password}];
getopenbmccons($donargs, $callback);
}
return;
}
my ($rc, $msg) = xCAT::OPENBMC->run_cmd_in_perl($command, $request->{environment});
if ($rc == 0) { $request = {}; return;}
if ($rc < 0) {
$request = {};
$callback->({ errorcode => [1], data => [$msg] });
return;
}
if ($::XCATSITEVALS{xcatdebugmode}) { $xcatdebugmode = $::XCATSITEVALS{xcatdebugmode} }
if ($xcatdebugmode) {
process_debug_info("OpenBMC");
}
my $noderange = $request->{node};
my $extrargs = $request->{arg};
my @exargs = ($request->{arg});
my @requests;
my $usage_string;
$::cwd = $request->{cwd}->[0];
if (ref($extrargs)) {
@exargs = @$extrargs;
}
# Request usage for openbmc sections only
$usage_string = xCAT::Usage->parseCommand($command . ".openbmc", @exargs);
if ($usage_string) {
if ($usage_string =~ /cannot be found/) {
# Could not find usage for openbmc section, try getting usage for all sections
$usage_string = xCAT::Usage->parseCommand($command, @exargs);
}
#else {
# Usage for openbmc section was extracted, append autogenerated usage for
# configured commands
# $usage_string .= &build_config_api_usage($callback, $command);
#}
$callback->({ data => [$usage_string] });
$request = {};
return;
}
#pdu commands will be handled in the pdu plugin
if ($command eq "rpower") {
my $subcmd = $exargs[0];
if(($subcmd eq 'pduoff') || ($subcmd eq 'pduon') || ($subcmd eq 'pdustat') || ($subcmd eq 'pdureset')){
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 retry_after
The request will be delayed for the given time and then
send the reqeust based on the status in the main loop.
=cut
#-------------------------------------------------------
sub retry_after {
my ($node, $request_status, $timeout) = @_;
$node_info{$node}{cur_status} = $request_status;
$node_wait{$node} = time() + $timeout;
}
#-------------------------------------------------------
=head3 retry_check_times
The request will be delayed for the given time and then
send the reqeust based on the BMC status after BMCreboot.
=cut
#-------------------------------------------------------
sub retry_check_times {
my ($node, $request_status, $check_type, $wait_time, $response_status) = @_;
if ($node_info{$node}{$check_type} > 0) {
$node_info{$node}{$check_type}--;
if ($node_info{$node}{wait_start}) {
$node_info{$node}{wait_end} = time();
} else {
$node_info{$node}{wait_start} = time();
}
my $retry_msg = "Retry BMC state, wait for $wait_time seconds ...";
xCAT::MsgUtils->message("I", { data => ["$node: $retry_msg"] }, $callback);
my $rflash_log_file = xCAT::Utils->full_path($node.".log", $::XCAT_LOG_RFLASH_DIR);
open (RFLASH_LOG_FILE_HANDLE, ">> $rflash_log_file");
print RFLASH_LOG_FILE_HANDLE "$retry_msg\n";
close(RFLASH_LOG_FILE_HANDLE);
if ($response_status ne $::RESPONSE_SERVICE_UNAVAILABLE) {
my $login_url = "$http_protocol://$node_info{$node}{bmc}/login";
my $content = '[ "' . $node_info{$node}{username} .'", "' . $node_info{$node}{password} . '" ]';
$status_info{LOGIN_REQUEST_GENERAL}{data} = $content;
$node_info{$node}{cur_status} = "LOGIN_REQUEST_GENERAL";
$node_wait{$node} = time() + $wait_time;
return;
}
$node_info{$node}{cur_status} = $request_status;
$node_wait{$node} = time() + $wait_time;
return;
} else {
my $wait_time_X = $node_info{$node}{wait_end} - $node_info{$node}{wait_start};
my $msg="Error: Sent bmcreboot but state did not change to BMC Ready after waiting $wait_time_X seconds. (State=BMC NotReady).";
xCAT::SvrUtils::sendmsg([1, $msg], $callback, $node);
$node_info{$node}{cur_status} = "";
$wait_node_num--;
return;
}
}
#-------------------------------------------------------
=head3 process_request
Process the command
=cut
#-------------------------------------------------------
sub process_request {
my $request = shift;
$callback = shift;
my $command = $request->{command}->[0];
my $noderange = $request->{node};
my $extrargs = $request->{arg};
$::cwd = $request->{cwd}->[0];
my @exargs = ($request->{arg});
if (ref($extrargs)) {
@exargs = @$extrargs;
}
my $check = parse_node_info($noderange);
my $rst = parse_command_status($command, \@exargs);
return if ($rst);
if ($request->{command}->[0] eq "getopenbmccons") {
# This may not be run as "getopenbmccons" is handled in preprocess now
# Leave the code here in case some codes just call `process_request`
foreach my $node (keys %node_info) {
my $donargs = [ $node,$node_info{$node}{bmc},$node_info{$node}{username}, $node_info{$node}{password}];
getopenbmccons($donargs, $callback);
}
return;
}
if ($::VERBOSE) {
xCAT::SvrUtils::sendmsg("Running command in Perl", $callback);
}
$cookie_jar = HTTP::Cookies->new({});
$async = HTTP::Async->new(
slots => 500,
cookie_jar => $cookie_jar,
timeout => 60,
max_request_time => 60,
ssl_options => {
SSL_verify_mode => 0,
},
);
my $login_url;
my $handle_id;
my $content;
my @nodes_login = keys %node_info;
$wait_node_num = @nodes_login;
my $child_num;
my %valid_nodes = ();
my $max_child_num = 64;
my $for_num = ($wait_node_num < $max_child_num) ? $wait_node_num : $max_child_num;
for (my $i = 0; $i < $for_num; $i++) {
my $node = shift @nodes_login;
my $rst_fork = fork_process_login($node);
$child_num++ unless($rst_fork);
}
while (1) {
last if ($child_num == 0 and !@nodes_login);
my $cpid = waitpid(-1, WNOHANG);
if ($cpid > 0) {
if ($login_pid_node{$cpid}) {
$child_num--;
my $node = $login_pid_node{$cpid};
my $rc = $? >> 8;
if ($rc == 0) {
$valid_nodes{$node} = 1;
}
delete $login_pid_node{$cpid};
}
} elsif ($cpid == 0) {
select(undef, undef, undef, 0.01);
} elsif ($cpid < 0 and !@nodes_login) {
last;
}
if (@nodes_login) {
if ($child_num < $max_child_num) {
my $node = shift @nodes_login;
my $rst_fork = fork_process_login($node);
$child_num++ unless($rst_fork);
}
}
}
foreach my $node (keys %node_info) {
if (!$valid_nodes{$node}) {
xCAT::SvrUtils::sendmsg([1, $::FAILED_LOGIN_MSG], $callback, $node);
$node_info{$node}{rst} = $::FAILED_LOGIN_MSG;
$wait_node_num--;
next;
}
if ($next_status{LOGIN_RESPONSE} eq "RSPCONFIG_SET_HOSTNAME_REQUEST" and $status_info{RSPCONFIG_SET_HOSTNAME_REQUEST}{data} =~ /^\*$/) {
if ($node_info{$node}{bmc} =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
my $info_msg = "Invalid OpenBMC Hostname $node_info{$node}{bmc}, can't set to OpenBMC";
xCAT::SvrUtils::sendmsg([1, $info_msg], $callback, $node);
$wait_node_num--;
next;
}
}
# All options parsed and validated. Now lock upload and activate processing, so that only one
# can continue in case multiples are issued for the same node
#
if (($next_status{LOGIN_RESPONSE} eq "RFLASH_FILE_UPLOAD_REQUEST") or
($next_status{LOGIN_RESPONSE} eq "RFLASH_UPDATE_ACTIVATE_REQUEST") or
($next_status{LOGIN_RESPONSE} eq "RFLASH_UPDATE_HOST_ACTIVATE_REQUEST")) {
my $lock = xCAT::Utils->acquire_lock("rflash_$node", 1);
unless ($lock) {
my $lock_msg = "Unable to rflash $node. Another process is already flashing this node.";
xCAT::SvrUtils::sendmsg([ 1, $lock_msg ], $callback, $node);
$node_info{$node}{rst} = $lock_msg;
$wait_node_num--;
next;
}
if ($::VERBOSE) {
xCAT::SvrUtils::sendmsg("Acquired the lock for upload and activate process", $callback, $node);
}
$node_info{$node}{rflash_lock} = $lock;
}
$login_url = "$http_protocol://$node_info{$node}{bmc}/login";
$content = '{ "data": [ "' . $node_info{$node}{username} .'", "' . $node_info{$node}{password} . '" ] }';
if ($xcatdebugmode) {
my $debug_info = "curl -k -c cjar -H \"Content-Type: application/json\" -d '{ \"data\": [\"$node_info{$node}{username}\", \"xxxxxx\"] }' $login_url";
process_debug_info($node, $debug_info);
}
$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} };
}
if ($next_status{LOGIN_RESPONSE} eq "RSPCONFIG_SSHCFG_REQUEST") {
my $home = xCAT::Utils->getHomeDir("root");
open(FILE, ">$home/.ssh/copy.sh")
or die "cannot open file $home/.ssh/copy.sh\n";
print FILE "#!/bin/sh
umask 0077
userid=\$1
home=`egrep \"^\$userid:\" /etc/passwd | cut -f6 -d :`
if [ -n \"\$home\" ]; then
dest_dir=\"\$home/.ssh\"
else
home=`su - root -c pwd`
dest_dir=\"\$home/.ssh\"
fi
mkdir -p \$dest_dir
cat /tmp/\$userid/.ssh/id_rsa.pub >> \$home/.ssh/authorized_keys 2>&1
rm -f /tmp/\$userid/.ssh/* 2>&1
rmdir \"/tmp/\$userid/.ssh\"
rmdir \"/tmp/\$userid\" \n";
close FILE;
chmod 0700, "$home/.ssh/copy.sh";
mkdir "$home/.ssh/tmp";
# create authorized_keys file to be appended to target
if (-f "/etc/xCATMN") { # if on Management Node
copy("$home/.ssh/id_rsa.pub","$home/.ssh/tmp/authorized_keys");
} else {
copy("$home/.ssh/authorized_keys","$home/.ssh/tmp/authorized_keys");
}
}
while (1) {
unless ($wait_node_num) {
if ($event_mapping and (ref($event_mapping) ne "HASH")) {
xCAT::SvrUtils::sendmsg("$event_mapping, install the OpenBMC RAS package to obtain more details logging messages.", $callback);
}
if ($next_status{LOGIN_RESPONSE} eq "RSPCONFIG_SSHCFG_REQUEST") {
my $home = xCAT::Utils->getHomeDir("root");
unlink "$home/.ssh/copy.sh";
File::Path->remove_tree("$home/.ssh/tmp/");
}
if ($::UPLOAD_AND_ACTIVATE or $next_status{LOGIN_RESPONSE} eq "RFLASH_UPDATE_ACTIVATE_REQUEST") {
my %rflash_result = ();
foreach my $node (keys %node_info) {
if ($node_info{$node}{rst} =~ /successful/) {
push @{ $rflash_result{success} }, $node;
} else {
# If there is no error in $node_info{$node}{rst} it is probably because fw file
# upload is done in a forked process and data can not be saved in $node_info{$node}{rst}
# In that case check the rflash log file for this node and extract error from there
unless ($node_info{$node}{rst}) {
my $rflash_log_file = xCAT::Utils->full_path($node.".log", $::XCAT_LOG_RFLASH_DIR);
# Extract the upload error from last line in log file
my $upload_error = `tail $rflash_log_file -n1 | grep "$::FAILED_UPLOAD_MSG"`;
if ($upload_error) {
chomp $upload_error;
$node_info{$node}{rst} = $upload_error;
} else {
$node_info{$node}{rst} = "BMC is not ready";
}
}
push @{ $rflash_result{fail} }, "$node: $node_info{$node}{rst}";
}
}
my $total = keys %node_info;
# Display summary information but only if there were any nodes to process
if ($total > 0) {
xCAT::MsgUtils->message("I", { data => ["-------------------------------------------------------"], host => [1] }, $callback);
my $summary = "Firmware update complete: ";
my $success = 0;
my $fail = 0;
$success = @{ $rflash_result{success} } if (defined $rflash_result{success} and @{ $rflash_result{success} });
$fail = @{ $rflash_result{fail} } if (defined $rflash_result{fail} and @{ $rflash_result{fail} });
$summary .= "Total=$total Success=$success Failed=$fail";
xCAT::MsgUtils->message("I", { data => ["$summary"], host => [1] }, $callback);
if ($rflash_result{fail}) {
foreach (@{ $rflash_result{fail} }) {
xCAT::MsgUtils->message("I", { data => ["$_"], host => [1] }, $callback);
}
}
xCAT::MsgUtils->message("I", { data => ["-------------------------------------------------------"], host => [1] }, $callback);
}
}
last;
}
while (my ($response, $handle_id) = $async->wait_for_next_response) {
deal_with_response($handle_id, $response);
}
if (%child_node_map) {
my $pid_flag = 0;
while ((my $cpid = waitpid(-1, WNOHANG)) > 0) {
if ($child_node_map{$cpid}) {
$pid_flag = 1;
my $node = $child_node_map{$cpid};
my $rc = $? >> 8;
if ($rc != 0) {
$wait_node_num--;
} else {
if ($status_info{ $node_info{$node}{cur_status} }->{process}) {
$status_info{ $node_info{$node}{cur_status} }->{process}->($node, undef);
} else {
xCAT::SvrUtils::sendmsg([1,"Internal error, check the process handler for current status "
.$node_info{$node}{cur_status}."."], $callback, $node);
$wait_node_num--;
}
}
delete $child_node_map{$cpid};
}
}
unless ($pid_flag) {
select(undef, undef, undef, 0.01);
}
}
my @del;
while (my ($k, $v) = each %node_wait) {
if (time() >= $v) {
if ($node_info{$k}{method} || $status_info{ $node_info{$k}{cur_status} }{method}) {
gen_send_request($k);
} else {
xCAT::SvrUtils::sendmsg([1,"Internal error, check the REST handler for current status "
.$node_info{$k}{cur_status}."."], $callback, $k);
$wait_node_num--;
}
push(@del, $k);
}
}
foreach my $d (@del) {
delete $node_wait{$d};
}
}
$callback->({ errorcode => [$check] }) if ($check);
return;
}
#-------------------------------------------------------
=head3 parse_args
Parse the command line options and operands
=cut
#-------------------------------------------------------
sub parse_args {
my $command = shift;
my $extrargs = shift;
my $noderange = shift;
my $check = undef;
my $subcommand = undef;
my $verbose = undef;
unless (GetOptions(
'V|verbose' => \$verbose,
)) {
return ([ 1, "Error parsing arguments." ]);
}
# If command includes '-V', it must be the last one prarmeter. Or print error message.
if ($verbose) {
my $option = $$extrargs[-1];
return ([ 1, "Error parsing arguments." ]) if ($option !~ /V|verbose/);
}
if (scalar(@ARGV) >= 2 and ($command =~ /rpower|rinv|rvitals/)) {
return ([ 1, "Only one option is supported at the same time for $command" ]);
} elsif (scalar(@ARGV) >= 2 and $command eq "reventlog") {
my $option_s;
GetOptions( 's' => \$option_s );
return ([ 1, "The -s option is not supported for OpenBMC." ]) if ($option_s);
if ( "resolved" ~~ @ARGV) {
return ([ 1, "$usage_errormsg $reventlog_no_id_resolved_errormsg" ]);
}
return ([ 1, "Only one option is supported at the same time for $command" ]);
} elsif (scalar(@ARGV) == 0 and $command =~ /rpower|rspconfig|rflash/) {
return ([ 1, "No option specified for $command" ]);
} else {
$subcommand = $ARGV[0];
}
if ($command eq "rbeacon") {
unless ($subcommand =~ /^on$|^off$|^stat$/) {
return ([ 1, "Only 'on', 'off' or 'stat' are supported for OpenBMC managed nodes."]);
}
} 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 "rinv") {
$subcommand = "all" if (!defined($ARGV[0]));
unless ($subcommand =~ /^model$|^serial$|^firm$|^cpu$|^dimm$|^all$/) {
return ([ 1, "Unsupported command: $command $subcommand" ]);
}
} elsif ($command eq "getopenbmccons") {
# command for openbmc rcons
} elsif ($command eq "rsetboot") {
$subcommand = "stat" if (!defined($ARGV[0]));
unless ($subcommand =~ /^net$|^hd$|^cd$|^def$|^default$|^stat$/) {
return ([ 1, "Unsupported command: $command $subcommand" ]);
}
} elsif ($command eq "reventlog") {
$subcommand = "all" if (!defined($ARGV[0]));
if ($subcommand =~ /^(\w+)=(.*)/) {
my $key = $1;
my $value = $2;
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$|^\d+$|^all$|^clear$/) {
if ($subcommand =~ "resolved") {
return ([ 1, "$usage_errormsg $reventlog_no_id_resolved_errormsg" ]);
}
return ([ 1, "Unsupported command: $command $subcommand" ]);
}
} elsif ($command eq "rspconfig") {
my $num_subcommand = @ARGV;
my $setorget;
my $all_subcommand = "";
foreach $subcommand (@ARGV) {
$::RSPCONFIG_CONFIGURED_API_KEY = &is_valid_config_api($subcommand, $callback);
if ($::RSPCONFIG_CONFIGURED_API_KEY ne -1) {
return ([ 1, "Can not query $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{subcommand} information with other options at the same time" ]) if ($#ARGV > 1);
# subcommand defined in the configured API hash, return from here, the RSPCONFIG_CONFIGURED_API_KEY is the key into the hash
return;
}
elsif ($subcommand =~ /^(\w+)=(.*)/) {
return ([ 1, "Can not set and query OpenBMC information at the same time" ]) if ($setorget and $setorget eq "get");
my $key = $1;
my $value = $2;
return ([ 1, "Changing ipsrc value is currently not supported." ]) if ($key eq "ipsrc");
return ([ 1, "Unsupported command: $command $key" ]) unless ($key =~ /^ip$|^netmask$|^gateway$|^hostname$|^vlan$|^admin_passwd$|^ntpservers$/);
return ([ 1, "The option '$key' can not work with other options." ]) if ($key =~ /^hostname$|^admin_passwd$|^ntpservers$/ and $num_subcommand > 1);
if ($key eq "admin_passwd") {
my $comma_num = $value =~ tr/,/,/;
return ([ 1, "Invalid parameter for option $key: $value" ]) if ($comma_num != 1);
if ($subcommand =~ /^admin_passwd=(.*),(.*)/) {
return ([ 1, "Invalid parameter for option $key: $value" ]) if ($1 eq "" or $2 eq "");
}
}
my $nodes_num = @$noderange;
return ([ 1, "Invalid parameter for option $key" ]) if (!$value and $key ne ("ntpservers"));
return ([ 1, "Invalid parameter for option $key: $value" ]) if (($key eq "netmask") and !xCAT::NetworkUtils->isIpaddr($value));
return ([ 1, "Invalid parameter for option $key: $value" ]) if (($key eq "gateway") and ($value !~ "0.0.0.0" and !xCAT::NetworkUtils->isIpaddr($value)));
if ($key eq "ip") {
return ([ 1, "Can not configure more than 1 nodes' ip at the same time" ]) if ($nodes_num >= 2 and $value ne "dhcp");
if ($value ne "dhcp" ) {
if (!xCAT::NetworkUtils->isIpaddr($value)) {
return ([ 1, "Invalid parameter for option $key: $value" ]);
} else {
$all_subcommand .= $key . ",";
}
} else {
return ([ 1, "Setting ip=dhcp must be issued without other options." ]) if ($num_subcommand > 1);
}
} elsif ($key =~ /^netmask$|^gateway$|^vlan$/) {
$all_subcommand .= $key . ",";
}
$setorget = "set";
} elsif ($subcommand =~ /^ip$|^netmask$|^gateway$|^hostname$|^vlan$|^ipsrc$|^ntpservers$/) {
return ([ 1, "Can not set and query OpenBMC information at the same time" ]) if ($setorget and $setorget eq "set");
$setorget = "get";
} elsif ($subcommand =~ /^sshcfg$/) {
return ([ 1, "Configure sshcfg must be issued without other options." ]) if ($num_subcommand > 1);
$setorget = ""; # SSH Keys are copied using a RShellAPI, not REST API
} elsif ($subcommand eq "gard") {
my $option = "";
$option = $ARGV[1] if (defined $ARGV[1]);
return ([ 1, "Clear GARD cannot be issued with other options." ]) if ($num_subcommand > 2);
return ([ 1, "Invalid parameter for $command $subcommand $option" ]) if ($option !~ /^-c$|^--clear$/);
$setorget = "";
return;
} elsif ($subcommand eq "dump") {
my $option = "";
$option = $ARGV[1] if (defined $ARGV[1]);
if ($option =~ /^-d$|^--download$/) {
return ([ 1, "No dump file ID specified" ]) unless ($ARGV[2]);
return ([ 1, "Invalid parameter for $command $option $ARGV[2]" ]) if ($ARGV[2] !~ /^\d*$/ and $ARGV[2] ne "all");
} elsif ($option =~ /^-c$|^--clear$/) {
return ([ 1, "No dump file ID specified. To clear all, specify 'all'." ]) unless ($ARGV[2]);
return ([ 1, "Invalid parameter for $command $option $ARGV[2]" ]) if ($ARGV[2] !~ /^\d*$/ and $ARGV[2] ne "all");
} elsif ($option and $option !~ /^-l$|^--list$|^-g$|^--generate$/) {
return ([ 1, "Invalid parameter for $command $option" ]);
}
return;
} else {
return ([ 1, "Unsupported command: $command $subcommand" ]);
}
}
if ($all_subcommand) {
if ($all_subcommand !~ /ip/ or $all_subcommand !~ /netmask/ or $all_subcommand !~ /gateway/) {
if ($all_subcommand =~ /vlan/) {
return ([ 1, "VLAN must be configured with IP, netmask and gateway" ]);
} else {
return ([ 1, "IP, netmask and gateway must be configured together." ]);
}
}
}
} elsif ($command eq "rvitals") {
$subcommand = "all" if (!defined($ARGV[0]));
unless ($subcommand =~ /^leds$|^temp$|^voltage$|^wattage$|^fanspeed$|^power$|^altitude$|^all$/) {
return ([ 1, "Unsupported command: $command $subcommand" ]);
}
} elsif ($command eq "rflash") {
my $filename_passed = 0;
my $updateid_passed = 0;
my $filepath_passed = 0;
my $option_flag;
my @tarball_path;
my $invalid_options = "";
my @flash_arguments;
foreach my $opt (@$extrargs) {
# Only files ending on .tar are allowed
if ($opt =~ /.*\.tar$/i) {
$filename_passed = 1;
push (@flash_arguments, $opt);
next;
}
# Check if hex number for the updateid is passed
elsif ($opt =~ /^[[:xdigit:]]+$/i) {
$updateid_passed = 1;
push (@flash_arguments, $opt);
next;
}
# check if option starting with - was passed
elsif ($opt =~ /^-/) {
# do not add verbose option to the $option_flag in order to preserve arg checks below
if ($opt !~ /^-V$|^--verbose$/) {
if ($option_flag) {
$option_flag .= " " . $opt;
} else {
$option_flag .= $opt;
}
}
}
elsif ($opt =~ /^\//) {
$filepath_passed = 1;
push (@tarball_path, $opt);
}
else {
my $tmppath = xCAT::Utils->full_path($opt, $::cwd);
if (opendir(TDIR, $tmppath)) {
$filepath_passed = 1;
push (@tarball_path, $tmppath);
close(TDIR);
} else {
push (@flash_arguments, $opt);
$invalid_options .= $opt . " ";
}
}
}
# show options parsed in bypass mode
print "DEBUG filename=$filename_passed, updateid=$updateid_passed, options=$option_flag, tar_file_path=@tarball_path, invalid=$invalid_options rflash_arguments=@flash_arguments\n";
if ($option_flag =~ tr{ }{ } > 0) {
unless ($verbose or $option_flag =~/^-d --no-host-reboot$/) {
return ([ 1, "Multiple options are not supported. Options specified: $option_flag"]);
}
}
if (scalar @flash_arguments > 1) {
if (($option_flag =~ /^-a$|^--activate$|^--delete$/) or ($filename_passed and $option_flag !~ /^-d$/)) {
# Handles:
# - Multiple options not supported to activate/delete at the same time
# - Filename passed in and option is not -d for directory
return ([1, "More than one firmware specified is not supported."]);
} elsif ($option_flag =~ /^-d$/) {
return ([1, "More than one directory specified is not supported."]);
} else {
return ([ 1, "Invalid firmware specified with $option_flag" ]);
}
}
if ($filename_passed) {
# Filename was passed, check flags allowed with file
if ($option_flag !~ /^-c$|^--check$|^-u$|^--upload$|^-a$|^--activate$/) {
return ([ 1, "Invalid option specified when a file is provided: $option_flag" ]);
}
}
else {
if ($updateid_passed) {
# Updateid was passed, check flags allowed with update id
if ($option_flag !~ /^--delete$|^-a$|^--activate$/) {
my $optional_help_msg = "";
if ($option_flag == "-d") {
# For this special case, -d was changed to pass in a directory.
$optional_help_msg = "Did you mean --delete?"
}
return ([ 1, "Invalid option specified when an update id is provided: $option_flag. $optional_help_msg" ]);
}
my $action = "activate";
if ($option_flag =~ /^--delete$/) {
$action = "delete";
}
xCAT::SvrUtils::sendmsg("Attempting to $action ID=$flash_arguments[0], please wait...", $callback);
} elsif ($filepath_passed) {
if ($option_flag =~ /^-d|^-d --no-host-reboot$/) {
if (scalar @tarball_path > 1) {
return ([1, "More than one directory specified is not supported"]);
}
if ($invalid_options) {
return ([ 1, "Invalid option specified $invalid_options"]);
}
} elsif ($option_flag =~ /^-c$|^--check$|^-u$|^--upload$|^-a$|^--activate$/) {
return ([ 1, "Invalid firmware specified with $option_flag" ]);
} else {
return ([ 1, "Invalid option specified" ]);
}
} else {
# Neither Filename nor updateid was not passed, check flags allowed without file or updateid
if ($option_flag !~ /^-c$|^--check$|^-l$|^--list$/) {
return ([ 1, "Invalid option specified with $option_flag: $invalid_options" ]);
}
}
}
} else {
return ([ 1, "Command is not supported." ]);
}
return;
}
#-------------------------------------------------------
=head3 parse_command_status
Parse the command to init status machine
=cut
#-------------------------------------------------------
sub parse_command_status {
my $command = shift;
my $subcommands = shift;
my $subcommand;
return if ($command eq "getopenbmccons");
if ($$subcommands[-1] and $$subcommands[-1] =~ /V|verbose/) {
$::VERBOSE = 1;
pop(@$subcommands);
}
$next_status{LOGIN_REQUEST} = "LOGIN_RESPONSE";
if ($command eq "rbeacon") {
$subcommand = $$subcommands[0];
if ($subcommand eq "on") {
$next_status{LOGIN_RESPONSE} = "RBEACON_ON_REQUEST";
$next_status{RBEACON_ON_REQUEST} = "RBEACON_ON_RESPONSE";
} elsif ($subcommand eq "off") {
$next_status{LOGIN_RESPONSE} = "RBEACON_OFF_REQUEST";
$next_status{RBEACON_OFF_REQUEST} = "RBEACON_OFF_RESPONSE";
} elsif ($subcommand eq "stat") {
$next_status{LOGIN_RESPONSE} = "RVITALS_LEDS_REQUEST";
$next_status{RVITALS_LEDS_REQUEST} = "RVITALS_LEDS_RESPONSE";
$status_info{RVITALS_LEDS_RESPONSE}{argv} = "compact";
}
}
if ($command eq "rpower") {
$subcommand = $$subcommands[0];
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 "softoff") {
$next_status{LOGIN_RESPONSE} = "RPOWER_SOFTOFF_REQUEST";
$next_status{RPOWER_SOFTOFF_REQUEST} = "RPOWER_SOFTOFF_RESPONSE";
} elsif ($subcommand eq "reset") {
$next_status{LOGIN_RESPONSE} = "RPOWER_STATUS_REQUEST";
$next_status{RPOWER_STATUS_REQUEST} = "RPOWER_STATUS_RESPONSE";
$next_status{RPOWER_STATUS_RESPONSE}{ON} = "RPOWER_OFF_REQUEST";
$next_status{RPOWER_OFF_REQUEST} = "RPOWER_OFF_RESPONSE";
$next_status{RPOWER_OFF_RESPONSE} = "RPOWER_CHECK_REQUEST";
$next_status{RPOWER_CHECK_REQUEST} = "RPOWER_CHECK_RESPONSE";
$next_status{RPOWER_CHECK_RESPONSE}{ON} = "RPOWER_CHECK_REQUEST";
$next_status{RPOWER_CHECK_RESPONSE}{OFF} = "RPOWER_ON_REQUEST";
$next_status{RPOWER_ON_REQUEST} = "RPOWER_ON_RESPONSE";
$status_info{RPOWER_ON_RESPONSE}{argv} = "$subcommand";
} elsif ($subcommand =~ /^bmcstate$|^status$|^state$|^stat$/) {
$next_status{LOGIN_RESPONSE} = "RPOWER_STATUS_REQUEST";
$next_status{RPOWER_STATUS_REQUEST} = "RPOWER_STATUS_RESPONSE";
$status_info{RPOWER_STATUS_RESPONSE}{argv} = "$subcommand";
} elsif ($subcommand eq "boot") {
$next_status{LOGIN_RESPONSE} = "RPOWER_OFF_REQUEST";
$next_status{RPOWER_OFF_REQUEST} = "RPOWER_OFF_RESPONSE";
$next_status{RPOWER_OFF_RESPONSE} = "RPOWER_CHECK_REQUEST";
$next_status{RPOWER_CHECK_REQUEST} = "RPOWER_CHECK_RESPONSE";
$next_status{RPOWER_CHECK_RESPONSE}{ON} = "RPOWER_CHECK_REQUEST";
$next_status{RPOWER_CHECK_RESPONSE}{OFF} = "RPOWER_ON_REQUEST";
$next_status{RPOWER_ON_REQUEST} = "RPOWER_ON_RESPONSE";
$status_info{RPOWER_ON_RESPONSE}{argv} = "$subcommand";
} elsif ($subcommand eq "bmcreboot") {
$next_status{LOGIN_RESPONSE} = "RINV_FIRM_REQUEST";
$next_status{RINV_FIRM_REQUEST} = "RINV_FIRM_RESPONSE";
$next_status{RINV_FIRM_RESPONSE}{PENDING} = "RSPCONFIG_DUMP_CLEAR_ALL_REQUEST";
$next_status{RSPCONFIG_DUMP_CLEAR_ALL_REQUEST} = "RSPCONFIG_DUMP_CLEAR_RESPONSE";
$next_status{RSPCONFIG_DUMP_CLEAR_RESPONSE} = "RPOWER_BMCREBOOT_REQUEST";
$next_status{RINV_FIRM_RESPONSE}{NO_PENDING} = "RPOWER_BMCREBOOT_REQUEST";
$next_status{RPOWER_BMCREBOOT_REQUEST} = "RPOWER_RESET_RESPONSE";
$status_info{RPOWER_RESET_RESPONSE}{argv} = "$subcommand";
$status_info{RINV_FIRM_RESPONSE}{check} = 1;
}
}
if ($command eq "rinv") {
if (defined($$subcommands[0])) {
$subcommand = $$subcommands[0];
} else {
$subcommand = "all";
}
if ($subcommand eq "firm") {
$next_status{LOGIN_RESPONSE} = "RINV_FIRM_REQUEST";
$next_status{RINV_FIRM_REQUEST} = "RINV_FIRM_RESPONSE";
} elsif ($subcommand eq "all") {
$next_status{LOGIN_RESPONSE} = "RINV_REQUEST";
$next_status{RINV_REQUEST} = "RINV_RESPONSE";
$status_info{RINV_RESPONSE}{argv} = "$subcommand";
$next_status{RINV_RESPONSE} = "RINV_FIRM_REQUEST";
$next_status{RINV_FIRM_REQUEST} = "RINV_FIRM_RESPONSE";
} else {
$next_status{LOGIN_RESPONSE} = "RINV_REQUEST";
$next_status{RINV_REQUEST} = "RINV_RESPONSE";
$status_info{RINV_RESPONSE}{argv} = "$subcommand";
}
}
if ($command eq "rsetboot") {
if ($$subcommands[-1] and $$subcommands[-1] eq "-p") {
pop(@$subcommands);
$status_info{RSETBOOT_ENABLE_REQUEST}{data} = '0';
$status_info{RSETBOOT_SET_REQUEST}{init_url} = "$openbmc_project_url/control/host0/boot/attr/BootSource";
}
if (defined($$subcommands[0])) {
$subcommand = $$subcommands[0];
} else {
$subcommand = "stat";
}
if ($subcommand =~ /^hd$|^net$|^cd$|^default$|^def$/) {
if (defined($::OPENBMC_FW) && ($::OPENBMC_FW < 1738)) {
#
# In 1738, the endpount URL changed. In order to support the older URL as a work around, allow for a environment
# variable to change this value.
#
$::RSETBOOT_URL_PATH = "boot_source";
$status_info{RSETBOOT_SET_REQUEST}{init_url} = "$openbmc_project_url/control/host0/$::RSETBOOT_URL_PATH/attr/BootSource";
$status_info{RSETBOOT_STATUS_REQUEST}{init_url} = "$openbmc_project_url/control/host0/$::RSETBOOT_URL_PATH";
$next_status{LOGIN_RESPONSE} = "RSETBOOT_SET_REQUEST";
} else {
$next_status{LOGIN_RESPONSE} = "RSETBOOT_ENABLE_REQUEST";
$next_status{RSETBOOT_ENABLE_REQUEST} = "RSETBOOT_ENABLE_RESPONSE";
$next_status{RSETBOOT_ENABLE_RESPONSE} = "RSETBOOT_SET_REQUEST";
}
$next_status{RSETBOOT_SET_REQUEST} = "RSETBOOT_SET_RESPONSE";
if ($subcommand eq "net") {
$status_info{RSETBOOT_SET_REQUEST}{data} .= "Network";
} elsif ($subcommand eq "hd") {
$status_info{RSETBOOT_SET_REQUEST}{data} .= "Disk";
} elsif ($subcommand eq "cd") {
$status_info{RSETBOOT_SET_REQUEST}{data} .= "ExternalMedia";
} elsif ($subcommand eq "def" or $subcommand eq "default") {
$status_info{RSETBOOT_SET_REQUEST}{data} .= "Default";
}
$next_status{RSETBOOT_SET_RESPONSE} = "RSETBOOT_STATUS_REQUEST";
$next_status{RSETBOOT_STATUS_REQUEST} = "RSETBOOT_STATUS_RESPONSE";
} elsif ($subcommand eq "stat") {
$next_status{LOGIN_RESPONSE} = "RSETBOOT_STATUS_REQUEST";
$next_status{RSETBOOT_STATUS_REQUEST} = "RSETBOOT_STATUS_RESPONSE";
}
}
if ($command eq "reventlog") {
if (defined($$subcommands[0])) {
$subcommand = $$subcommands[0];
} else {
$subcommand = "all";
}
if ($subcommand eq "clear") {
$next_status{LOGIN_RESPONSE} = "REVENTLOG_CLEAR_REQUEST";
$next_status{REVENTLOG_CLEAR_REQUEST} = "REVENTLOG_CLEAR_RESPONSE";
} elsif (uc($subcommand) =~ /RESOLVED=LED/) {
$next_status{LOGIN_RESPONSE} = "REVENTLOG_REQUEST";
$next_status{REVENTLOG_REQUEST} = "REVENTLOG_RESOLVED_RESPONSE_LED";
} elsif ($subcommand =~ /resolved=(.+)/) {
$next_status{LOGIN_RESPONSE} = "REVENTLOG_RESOLVED_REQUEST";
$next_status{REVENTLOG_RESOLVED_REQUEST} = "REVENTLOG_RESOLVED_RESPONSE";
my @entries = split(",", $1);
my $init_entry = shift @entries;
$status_info{REVENTLOG_RESOLVED_REQUEST}{init_url} =~ s/#ENTRY_ID#/$init_entry/g;
push @{ $status_info{REVENTLOG_RESOLVED_RESPONSE}{remain_entries} }, @entries;
} else {
$next_status{LOGIN_RESPONSE} = "REVENTLOG_REQUEST";
$next_status{REVENTLOG_REQUEST} = "REVENTLOG_RESPONSE";
$status_info{REVENTLOG_RESPONSE}{argv} = "$subcommand";
if (-e "$::RAS_POLICY_TABLE") {
my $policy_json = `cat $::RAS_POLICY_TABLE`;
if ($policy_json) {
my $policy_hash = decode_json $policy_json;
$event_mapping = $policy_hash->{events};
} else {
$event_mapping = "No data in $::RAS_POLICY_TABLE";
}
} else {
$event_mapping = "Could not find '$::RAS_POLICY_TABLE'";
}
}
}
if ($command eq "rspconfig") {
my @options = ();
my $num_subcommand = @$subcommands;
#Setup chain to process the configured command
$subcommand = $$subcommands[0];
$::RSPCONFIG_CONFIGURED_API_KEY = &is_valid_config_api($subcommand, $callback);
if ($::RSPCONFIG_CONFIGURED_API_KEY ne -1) {
# Check if setting or quering
if ($subcommand =~ /^(\w+)=(.*)/) {
# setting
my $subcommand_key = $1;
my $subcommand_value = lc $2;
if (($subcommand_value eq "1") && ($api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{type} eq "boolean")) {
# Setup chain for subcommand=1
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_API_CONFIG_ON_REQUEST";
$status_info{RSPCONFIG_API_CONFIG_ON_REQUEST}{init_url} = $status_info{RSPCONFIG_API_CONFIG_ON_REQUEST}{init_url} . $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{url} . "/attr/" . $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_url};
$next_status{RSPCONFIG_API_CONFIG_ON_REQUEST} = "RSPCONFIG_API_CONFIG_ON_RESPONSE";
}
elsif (($subcommand_value eq "0") && ($api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{type} eq "boolean")) {
# Setup chain for subcommand=0
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_API_CONFIG_OFF_REQUEST";
$status_info{RSPCONFIG_API_CONFIG_OFF_REQUEST}{init_url} = $status_info{RSPCONFIG_API_CONFIG_OFF_REQUEST}{init_url} . $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{url} . "/attr/" . $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_url};
$next_status{RSPCONFIG_API_CONFIG_OFF_REQUEST} = "RSPCONFIG_API_CONFIG_OFF_RESPONSE";
}
elsif (($api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{type} eq "attribute") && (exists $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_value}{$subcommand_value})) {
# Setup chain for subcommand=<attribute key>
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_API_CONFIG_ATTR_REQUEST";
$status_info{RSPCONFIG_API_CONFIG_ATTR_REQUEST}{init_url} = $status_info{RSPCONFIG_API_CONFIG_ATTR_REQUEST}{init_url} . $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{url} . "/attr/" . $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_url};
$status_info{RSPCONFIG_API_CONFIG_ATTR_REQUEST}{data} = $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_value}{$subcommand_value};
$next_status{RSPCONFIG_API_CONFIG_ATTR_REQUEST} = "RSPCONFIG_API_CONFIG_ATTR_RESPONSE";
}
elsif (($api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{type} eq "action_attribute") && (exists $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_value}{$subcommand_value})) {
# Setup chain for subcommand=<attribute key>
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_API_CONFIG_ACTION_ATTR_REQUEST";
$status_info{RSPCONFIG_API_CONFIG_ACTION_ATTR_REQUEST}{init_url} = $status_info{RSPCONFIG_API_CONFIG_ACTION_ATTR_REQUEST}{init_url} . $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{url} . $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_url};
$status_info{RSPCONFIG_API_CONFIG_ACTION_ATTR_REQUEST}{data} = "[\"" . $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_value}{$subcommand_value} . "\"]";
$next_status{RSPCONFIG_API_CONFIG_ACTION_ATTR_REQUEST} = "RSPCONFIG_API_CONFIG_ATTR_RESPONSE";
}
else {
# Everything else is invalid
xCAT::SvrUtils::sendmsg([1, "Invalid value '$subcommand_value' for '$subcommand_key'"], $callback);
my @valid_values = keys %{ $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_value} };
if (!@valid_values) {
if ($api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{type} eq "boolean") {
xCAT::SvrUtils::sendmsg([1, "Valid values: 0,1"], $callback);
}
} else {
xCAT::SvrUtils::sendmsg([1, "Valid values: " . join(",", @valid_values)], $callback);
}
return 1;
}
}
else {
# Setup chain for query subcommand
if ($api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{type} eq "action_attribute") {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_API_CONFIG_ACTION_ATTR_QUERY_REQUEST";
$status_info{RSPCONFIG_API_CONFIG_ACTION_ATTR_QUERY_REQUEST}{init_url} =
$status_info{RSPCONFIG_API_CONFIG_ACTION_ATTR_QUERY_REQUEST}{init_url} .
$api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{url} .
$api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{query_url};
$next_status{RSPCONFIG_API_CONFIG_ACTION_ATTR_QUERY_REQUEST} = "RSPCONFIG_API_CONFIG_QUERY_RESPONSE";
}
else {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_API_CONFIG_QUERY_REQUEST";
$status_info{RSPCONFIG_API_CONFIG_QUERY_REQUEST}{init_url} = $status_info{RSPCONFIG_API_CONFIG_QUERY_REQUEST}{init_url} . $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{url};
$next_status{RSPCONFIG_API_CONFIG_QUERY_REQUEST} = "RSPCONFIG_API_CONFIG_QUERY_RESPONSE";
}
}
return 0;
}
if ($num_subcommand == 1) {
$subcommand = $$subcommands[0];
if ($subcommand =~ /^sshcfg$/) {
# Special processing to copy ssh keys, currently there is no REST API to do this.
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_SSHCFG_REQUEST";
$next_status{RSPCONFIG_SSHCFG_REQUEST} = "RSPCONFIG_SSHCFG_RESPONSE";
return 0;
}
if ($subcommand eq "ip=dhcp") {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_DHCP_REQUEST";
$next_status{RSPCONFIG_DHCP_REQUEST} = "RSPCONFIG_DHCP_RESPONSE";
$next_status{RSPCONFIG_DHCP_RESPONSE} = "RPOWER_BMCREBOOT_REQUEST";
$next_status{RPOWER_BMCREBOOT_REQUEST} = "RPOWER_RESET_RESPONSE";
$status_info{RPOWER_RESET_RESPONSE}{argv} = "bmcreboot";
return 0;
}
if ($subcommand =~ /^hostname=(.+)/) {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_SET_HOSTNAME_REQUEST";
$next_status{RSPCONFIG_SET_HOSTNAME_REQUEST} = "RSPCONFIG_SET_RESPONSE";
$next_status{RSPCONFIG_SET_RESPONSE} = "RSPCONFIG_GET_REQUEST";
$next_status{RSPCONFIG_GET_REQUEST} = "RSPCONFIG_GET_RESPONSE";
$status_info{RSPCONFIG_SET_HOSTNAME_REQUEST}{data} = $1;
$status_info{RSPCONFIG_SET_RESPONSE}{argv} = "Hostname";
$status_info{RSPCONFIG_GET_RESPONSE}{argv} = "hostname";
return 0;
}
if ($subcommand =~ /^ntpservers=(.*)/) {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_GET_NIC_REQUEST";
$next_status{RSPCONFIG_GET_NIC_REQUEST} = "RSPCONFIG_GET_NIC_RESPONSE";
$next_status{RSPCONFIG_GET_NIC_RESPONSE} = "RSPCONFIG_SET_NTPSERVERS_REQUEST";
$next_status{RSPCONFIG_SET_NTPSERVERS_REQUEST} = "RSPCONFIG_SET_RESPONSE";
$next_status{RSPCONFIG_SET_RESPONSE} = "RSPCONFIG_GET_REQUEST";
$next_status{RSPCONFIG_GET_REQUEST} = "RSPCONFIG_GET_RESPONSE";
$status_info{RSPCONFIG_GET_RESPONSE}{argv} = "ntpservers";
$status_info{RSPCONFIG_SET_RESPONSE}{argv} = "NTPServers";
$status_info{RSPCONFIG_SET_NTPSERVERS_REQUEST}{data} = "[\"$1\"]";
return 0;
}
}
$subcommand = $$subcommands[0];
if ($subcommand eq "dump") {
my $dump_opt = "";
$dump_opt = $$subcommands[1] if (defined $$subcommands[1]);
if ($dump_opt =~ /-l|--list/) {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_DUMP_LIST_REQUEST";
$next_status{RSPCONFIG_DUMP_LIST_REQUEST} = "RSPCONFIG_DUMP_LIST_RESPONSE";
} elsif ($dump_opt =~ /-g|--generate/) {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_DUMP_CREATE_REQUEST";
$next_status{RSPCONFIG_DUMP_CREATE_REQUEST} = "RSPCONFIG_DUMP_CREATE_RESPONSE";
} elsif ($dump_opt =~ /-c|--clear/) {
if ($$subcommands[2] eq "all") {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_DUMP_CLEAR_ALL_REQUEST";
$next_status{RSPCONFIG_DUMP_CLEAR_ALL_REQUEST} = "RSPCONFIG_DUMP_CLEAR_RESPONSE";
} else {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_DUMP_CLEAR_REQUEST";
$next_status{RSPCONFIG_DUMP_CLEAR_REQUEST} = "RSPCONFIG_DUMP_CLEAR_RESPONSE";
$status_info{RSPCONFIG_DUMP_CLEAR_REQUEST}{init_url} =~ s/#ID#/$$subcommands[2]/g;
}
$status_info{RSPCONFIG_DUMP_CLEAR_RESPONSE}{argv} = $$subcommands[2];
} elsif ($dump_opt =~ /-d|--download/) {
# Verify directory for download is there
unless (-d $::XCAT_LOG_DUMP_DIR) {
xCAT::SvrUtils::sendmsg([1, "Unable to create directory " . $::XCAT_LOG_DUMP_DIR . " to download dump file, cannot continue."], $callback);
return 1;
}
$::RSPCONFIG_DUMP_CMD_TIME = time(); #Save time of rspcommand start to use in the dump filename
if ($$subcommands[2] eq "all") {
# if "download all" was passed in
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_DUMP_LIST_REQUEST";
$next_status{RSPCONFIG_DUMP_LIST_REQUEST} = "RSPCONFIG_DUMP_LIST_RESPONSE";
$next_status{RSPCONFIG_DUMP_LIST_RESPONSE} = "RSPCONFIG_DUMP_DOWNLOAD_ALL_RESPONSE";
xCAT::SvrUtils::sendmsg("Downloading all dumps...", $callback);
$::RSPCONFIG_DUMP_DOWNLOAD_ALL_REQUESTED = 1; # Set flag to download all dumps
} else {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_DUMP_DOWNLOAD_REQUEST";
$next_status{RSPCONFIG_DUMP_DOWNLOAD_REQUEST} = "RSPCONFIG_DUMP_DOWNLOAD_RESPONSE";
$status_info{RSPCONFIG_DUMP_DOWNLOAD_REQUEST}{init_url} =~ s/#ID#/$$subcommands[2]/g;
$status_info{RSPCONFIG_DUMP_DOWNLOAD_REQUEST}{argv} = $$subcommands[2];
}
} else {
# this section handles the dump support where no options are given and xCAT will
# # handle the creation, waiting, and download of the dump across a given noderange
# Verify directory for download is there
unless (-d $::XCAT_LOG_DUMP_DIR) {
xCAT::SvrUtils::sendmsg([1, "Unable to find directory " . $::XCAT_LOG_DUMP_DIR . " to download dump file"], $callback);
return 1;
}
$::RSPCONFIG_DUMP_CMD_TIME = time(); #Save time of rspcommand start to use in the dump filename
xCAT::SvrUtils::sendmsg("Capturing BMC Diagnostic information, this will take some time...", $callback);
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_DUMP_CREATE_REQUEST";
$next_status{RSPCONFIG_DUMP_CREATE_REQUEST} = "RSPCONFIG_DUMP_CREATE_RESPONSE";
$next_status{RSPCONFIG_DUMP_CREATE_RESPONSE} = "RSPCONFIG_DUMP_LIST_REQUEST";
$next_status{RSPCONFIG_DUMP_LIST_REQUEST} = "RSPCONFIG_DUMP_CHECK_RESPONSE";
$next_status{RSPCONFIG_DUMP_CHECK_RESPONSE} = "RSPCONFIG_DUMP_DOWNLOAD_REQUEST";
$next_status{RSPCONFIG_DUMP_DOWNLOAD_REQUEST} = "RSPCONFIG_DUMP_DOWNLOAD_RESPONSE";
}
return 0;
} elsif ($subcommand eq "gard") {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_CLEAR_GARD_REQUEST";
$next_status{RSPCONFIG_CLEAR_GARD_REQUEST} = "RSPCONFIG_CLEAR_GARD_RESPONSE";
return 0;
}
if ($subcommand =~ /^admin_passwd=(.+),(.+)/) {
my $currentpasswd = $1;
my $newpasswd = $2;
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_PASSWD_VERIFY";
$next_status{RSPCONFIG_PASSWD_VERIFY} = "RSPCONFIG_SET_PASSWD_REQUEST";
$next_status{RSPCONFIG_SET_PASSWD_REQUEST} = "RSPCONFIG_SET_RESPONSE";
$status_info{RSPCONFIG_PASSWD_VERIFY}{argv} = "$currentpasswd";
$status_info{RSPCONFIG_SET_PASSWD_REQUEST}{data} = "[\"$newpasswd\"]";
$status_info{RSPCONFIG_SET_RESPONSE}{argv} = "Password";
return 0;
}
my $type = "obj";
my %tmp_hash = ();
foreach $subcommand (@$subcommands) {
if ($subcommand =~ /^ip$|^netmask$|^gateway$|^hostname$|^vlan$|^ipsrc$|^ntpservers$/) {
$type = "get";
push @options, $subcommand;
} elsif ($subcommand =~ /^(\w+)=(.+)/) {
my $key = $1;
my $value = $2;
$type = "vlan" if ($key eq "vlan");
$tmp_hash{$key} = $value;
}
}
if ($type eq "get") {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_GET_REQUEST";
$next_status{RSPCONFIG_GET_REQUEST} = "RSPCONFIG_GET_RESPONSE";
$status_info{RSPCONFIG_GET_RESPONSE}{argv} = join(",", @options);
} else {
$next_status{LOGIN_RESPONSE} = "RSPCONFIG_GET_REQUEST";
$next_status{RSPCONFIG_GET_REQUEST} = "RSPCONFIG_GET_RESPONSE";
my $prefix = xCAT::NetworkUtils::formatNetmask($tmp_hash{netmask}, 0, 1);
if ($type eq "obj") {
$next_status{RSPCONFIG_GET_RESPONSE} = "RSPCONFIG_IPOBJECT_REQUEST";
$next_status{RSPCONFIG_IPOBJECT_REQUEST} = "RSPCONFIG_IPOBJECT_RESPONSE";
$status_info{RSPCONFIG_CHECK_RESPONSE}{argv} = "$tmp_hash{ip}-$prefix-$tmp_hash{gateway}";
$status_info{RSPCONFIG_PRINT_BMCINFO}{data} = "BMC IP: $tmp_hash{ip},BMC Netmask: $tmp_hash{netmask},BMC Gateway: $tmp_hash{gateway}";
} elsif ($type eq "vlan") {
$next_status{RSPCONFIG_GET_RESPONSE} = "RSPCONFIG_VLAN_REQUEST";
$next_status{RSPCONFIG_VLAN_REQUEST} = "RSPCONFIG_VLAN_RESPONSE";
$next_status{RSPCONFIG_VLAN_RESPONSE} = "RSPCONFIG_IPOBJECT_REQUEST";
$next_status{RSPCONFIG_IPOBJECT_REQUEST} = "RSPCONFIG_IPOBJECT_RESPONSE";
$status_info{RSPCONFIG_VLAN_REQUEST}{data} = "[\"#NIC#\",\"$tmp_hash{vlan}\"]";
$status_info{RSPCONFIG_IPOBJECT_REQUEST}{init_url} =~ s/#NIC#/#NIC#_$tmp_hash{vlan}/g;
$status_info{RSPCONFIG_CHECK_RESPONSE}{argv} = "$tmp_hash{vlan}-$tmp_hash{ip}-$prefix-$tmp_hash{gateway}";
$status_info{RSPCONFIG_PRINT_BMCINFO}{data} = "BMC IP: $tmp_hash{ip},BMC Netmask: $tmp_hash{netmask},BMC Gateway: $tmp_hash{gateway},BMC VLAN ID: $tmp_hash{vlan}";
}
$next_status{RSPCONFIG_IPOBJECT_RESPONSE} = "RSPCONFIG_CHECK_REQUEST";
$next_status{RSPCONFIG_CHECK_REQUEST} = "RSPCONFIG_CHECK_RESPONSE";
$next_status{RSPCONFIG_CHECK_RESPONSE}{STATIC} = "RSPCONFIG_DELETE_REQUEST";
$next_status{RSPCONFIG_DELETE_REQUEST} = "RSPCONFIG_DELETE_RESPONSE";
$next_status{RSPCONFIG_CHECK_RESPONSE}{DHCP} = "RSPCONFIG_DHCPDIS_REQUEST";
$next_status{RSPCONFIG_DHCPDIS_REQUEST} = "RSPCONFIG_DHCPDIS_RESPONSE";
$next_status{RSPCONFIG_DELETE_RESPONSE} = "RSPCONFIG_PRINT_BMCINFO";
$next_status{RSPCONFIG_DHCPDIS_RESPONSE} = "RSPCONFIG_PRINT_BMCINFO";
$status_info{RSPCONFIG_GET_RESPONSE}{argv} = "all";
$status_info{RSPCONFIG_IPOBJECT_REQUEST}{data} = "[\"xyz.openbmc_project.Network.IP.Protocol.IPv4\",\"$tmp_hash{ip}\",$prefix,\"$tmp_hash{gateway}\"]";
}
}
if ($command eq "rvitals") {
if (defined($$subcommands[0])) {
$subcommand = $$subcommands[0];
} else {
$subcommand = "all";
}
if ($subcommand eq "leds") {
$next_status{LOGIN_RESPONSE} = "RVITALS_LEDS_REQUEST";
$next_status{RVITALS_LEDS_REQUEST} = "RVITALS_LEDS_RESPONSE";
$status_info{RVITALS_LEDS_RESPONSE}{argv} = "$subcommand";
} else {
$next_status{LOGIN_RESPONSE} = "RVITALS_REQUEST";
$next_status{RVITALS_REQUEST} = "RVITALS_RESPONSE";
$status_info{RVITALS_RESPONSE}{argv} = "$subcommand";
if ($subcommand eq "all") {
$next_status{RVITALS_RESPONSE} = "RVITALS_LEDS_REQUEST";
$next_status{RVITALS_LEDS_REQUEST} = "RVITALS_LEDS_RESPONSE";
}
}
}
if ($command eq "rflash") {
my $check_version = 0;
my $list = 0;
my $delete = 0;
my $upload = 0;
my $activate = 0;
my $update_file;
my $streamline = 0;
my $nohost_reboot = 0;
foreach $subcommand (@$subcommands) {
if ($subcommand =~ /^-c|^--check/) {
$check_version = 1;
} elsif ($subcommand =~ /^-l|^--list/) {
$list = 1;
} elsif ($subcommand =~ /^--delete/) {
$delete = 1;
} elsif ($subcommand =~ /^-u|^--upload/) {
$upload = 1;
} elsif ($subcommand =~ /^-a|^--activate/) {
$activate = 1;
} elsif ($subcommand =~ /^-d/) {
$streamline = 1;
} elsif ($subcommand =~ /^--no-host-reboot/) {
$nohost_reboot = 1;
} else {
$update_file = $subcommand;
}
}
my $file_id = undef;
my $grep_cmd = "/usr/bin/grep -a";
my $tr_cmd = "/usr/bin/tr";
my $sha512sum_cmd = "/usr/bin/sha512sum";
my $version_tag = '"^version="';
my $purpose_tag = '"purpose="';
my $purpose_value;
my $version_value;
my $tarfile_path;
if (defined $update_file) {
if ($streamline) {
if ($update_file =~ /^\//){
$tarfile_path = $update_file;
} else {
$tarfile_path =xCAT::Utils->full_path($update_file, $::cwd);
}
}
# Filename or file id was specified
if ($update_file =~ /.*\.tar$/) {
# Filename ending on .tar was specified
if (File::Spec->file_name_is_absolute($update_file)) {
$::UPLOAD_FILE = $update_file;
}
else {
# If relative file path was given, convert it to absolute
$::UPLOAD_FILE = xCAT::Utils->full_path($update_file, $::cwd);
}
# Verify file exists and is readable
unless (-r $::UPLOAD_FILE) {
xCAT::SvrUtils::sendmsg([1,"Cannot access $::UPLOAD_FILE. Check the management node and/or service nodes."], $callback);
return 1;
}
if ($activate) {
# Activate flag was specified together with a update file. We want to
# upload the file and activate it.
$::UPLOAD_AND_ACTIVATE = 1;
$activate = 0;
}
if ($check_version | $::UPLOAD_AND_ACTIVATE) {
# Extract Host version for the update file
my $firmware_version_in_file = `$grep_cmd $version_tag $::UPLOAD_FILE`;
my $purpose_version_in_file = `$grep_cmd $purpose_tag $::UPLOAD_FILE`;
chomp($firmware_version_in_file);
chomp($purpose_version_in_file);
(my $purpose_string,$purpose_value) = split("=", $purpose_version_in_file);
(my $version_string,$version_value) = split("=", $firmware_version_in_file);
if ($purpose_value =~ /host/) {
$purpose_value = "Host";
}
$::UPLOAD_FILE_VERSION = $version_value;
if (-x $sha512sum_cmd && -x $tr_cmd) {
# Save hash id this firmware version should resolve to:
# take version string, get rid of newline, run through sha512sum, take first 8 characters
$::UPLOAD_FILE_HASH_ID = substr(`echo $::UPLOAD_FILE_VERSION | $tr_cmd -d '\n' | $sha512sum_cmd`, 0,8);
}
else {
if ($::VERBOSE) {
xCAT::SvrUtils::sendmsg("WARN: No hashing check being done. ($sha512sum_cmd or $tr_cmd commands not found)
", $callback);
}
}
}
if ($check_version) {
# Display firmware version of the specified .tar file
xCAT::SvrUtils::sendmsg("TAR $purpose_value Firmware Product Version\: $version_value", $callback);
}
} elsif (defined $tarfile_path) {
if (!opendir(DIR, $tarfile_path)) {
xCAT::SvrUtils::sendmsg([1,"Can't open directory : $tarfile_path"], $callback);
closedir(DIR);
return 1;
}
my @tar_files = readdir(DIR);
foreach my $file (@tar_files) {
if ($file !~ /.*\.tar$/) {
next;
} else {
my $full_path_file = $tarfile_path."/".$file;
$full_path_file=~s/\/\//\//g;
my $firmware_version_in_file = `$grep_cmd $version_tag $full_path_file`;
my $purpose_version_in_file = `$grep_cmd $purpose_tag $full_path_file`;
chomp($firmware_version_in_file);
chomp($purpose_version_in_file);
if (defined($firmware_version_in_file) and defined($purpose_version_in_file)) {
(my $purpose_string,$purpose_value) = split("=", $purpose_version_in_file);
(my $version_string,$version_value) = split("=", $firmware_version_in_file);
if ($purpose_value =~ /Purpose.BMC$/ and $version_string =~/version/){
$::UPLOAD_FILE = $full_path_file;
$::UPLOAD_FILE_VERSION = $version_value;
} elsif ($purpose_value =~ /Purpose.Host$/ and $version_value =~ /witherspoon/) {
$::UPLOAD_PNOR = $full_path_file;
$::UPLOAD_PNOR_VERSION = $version_value;
}
}
}
}
my $return_code = 0;
if (!$::UPLOAD_FILE) {
xCAT::SvrUtils::sendmsg([1,"No BMC tar file found in $update_file"], $callback);
$return_code = 1;
}
if (!$::UPLOAD_PNOR) {
xCAT::SvrUtils::sendmsg([1,"No Host tar file found in $update_file"], $callback);
$return_code = 1;
}
if ($return_code) {
return 1;
}
if ($streamline) {
$::UPLOAD_ACTIVATE_STREAM = 1;
if ($nohost_reboot) {
$::RFLASH_STREAM_NO_HOST_REBOOT = 1;
$nohost_reboot = 0;
}
$streamline = 0;
if (-x $sha512sum_cmd && -x $tr_cmd) {
# Save hash id this firmware version should resolve to:
$::UPLOAD_FILE_HASH_ID = substr(`echo $::UPLOAD_FILE_VERSION | $tr_cmd -d '\n' | $sha512sum_cmd`, 0,8);
$::UPLOAD_PNOR_HASH_ID = substr(`echo $::UPLOAD_PNOR_VERSION | $tr_cmd -d '\n' | $sha512sum_cmd`, 0,8);
}
else {
if ($::VERBOSE) {
xCAT::SvrUtils::sendmsg("WARN: No hashing check being done. ($sha512sum_cmd or $tr_cmd commands not found)
", $callback);
}
}
}
}
else {
# Check if hex number for the updateid is passed
if ($update_file =~ /^[[:xdigit:]]+$/i) {
# Update init_url to include the id of the update
$status_info{RFLASH_UPDATE_ACTIVATE_REQUEST}{init_url} .= "/$update_file/attr/RequestedActivation";
$status_info{RFLASH_SET_PRIORITY_REQUEST}{init_url} .= "/$update_file/attr/Priority";
$status_info{RFLASH_UPDATE_CHECK_STATE_REQUEST}{init_url} .= "/$update_file";
$status_info{RFLASH_DELETE_IMAGE_REQUEST}{init_url} .= "/$update_file/action/Delete";
}
}
}
# Check if there are any valid nodes to work on. If none, do not issue these messages
if (keys %node_info > 0) {
if ($upload or $::UPLOAD_AND_ACTIVATE) {
xCAT::SvrUtils::sendmsg("Attempting to upload $::UPLOAD_FILE, please wait...", $callback);
} elsif ($::UPLOAD_ACTIVATE_STREAM) {
xCAT::SvrUtils::sendmsg("Attempting to upload $::UPLOAD_FILE and $::UPLOAD_PNOR, please wait...", $callback);
}
}
if ($check_version) {
# Display firmware version on BMC
$next_status{LOGIN_RESPONSE} = "RINV_FIRM_REQUEST";
$next_status{RINV_FIRM_REQUEST} = "RINV_FIRM_RESPONSE";
}
if ($list) {
# Display firmware update files uploaded to BMC
$next_status{LOGIN_RESPONSE} = "RFLASH_LIST_REQUEST";
$next_status{RFLASH_LIST_REQUEST} = "RFLASH_LIST_RESPONSE";
}
if ($delete) {
# Request to delete uploaded image from BMC or Host
# Firsh check if image is allowed to be deleted
$next_status{LOGIN_RESPONSE} = "RFLASH_LIST_REQUEST";
$next_status{RFLASH_LIST_REQUEST} = "RFLASH_DELETE_CHECK_STATE_RESPONSE";
}
if ($upload) {
# Upload specified update file to BMC
$next_status{LOGIN_RESPONSE} = "RFLASH_FILE_UPLOAD_REQUEST";
$next_status{"RFLASH_FILE_UPLOAD_REQUEST"} = "RFLASH_FILE_UPLOAD_RESPONSE";
}
if ($activate) {
# Activation of an update was requested.
# First we query the update image for its Activation state. If image is in "Ready" we
# need to set "RequestedActivation" attribute to "Active". If image is in "Active" we
# need to set "Priority" to 0.
$next_status{LOGIN_RESPONSE} = "RFLASH_UPDATE_ACTIVATE_REQUEST";
$next_status{"RFLASH_UPDATE_ACTIVATE_REQUEST"} = "RFLASH_UPDATE_ACTIVATE_RESPONSE";
$next_status{"RFLASH_UPDATE_ACTIVATE_RESPONSE"} = "RFLASH_UPDATE_CHECK_STATE_REQUEST";
$next_status{"RFLASH_UPDATE_CHECK_STATE_REQUEST"} = "RFLASH_UPDATE_CHECK_STATE_RESPONSE";
$next_status{"RFLASH_SET_PRIORITY_REQUEST"} = "RFLASH_SET_PRIORITY_RESPONSE";
$next_status{"RFLASH_SET_PRIORITY_RESPONSE"} = "RFLASH_UPDATE_CHECK_STATE_REQUEST";
}
if ($::UPLOAD_AND_ACTIVATE) {
# Upload specified update file to BMC
$next_status{LOGIN_RESPONSE} = "RFLASH_FILE_UPLOAD_REQUEST";
$next_status{"RFLASH_FILE_UPLOAD_REQUEST"} = "RFLASH_FILE_UPLOAD_RESPONSE";
$next_status{"RFLASH_FILE_UPLOAD_RESPONSE"} = "RFLASH_UPDATE_CHECK_ID_REQUEST";
$next_status{"RFLASH_UPDATE_CHECK_ID_REQUEST"} = "RFLASH_UPDATE_CHECK_ID_RESPONSE";
$next_status{"RFLASH_UPDATE_CHECK_ID_RESPONSE"} = "RFLASH_UPDATE_ACTIVATE_REQUEST";
$next_status{"RFLASH_UPDATE_ACTIVATE_REQUEST"} = "RFLASH_UPDATE_ACTIVATE_RESPONSE";
$next_status{"RFLASH_UPDATE_ACTIVATE_RESPONSE"} = "RFLASH_UPDATE_CHECK_STATE_REQUEST";
$next_status{"RFLASH_UPDATE_CHECK_STATE_REQUEST"} = "RFLASH_UPDATE_CHECK_STATE_RESPONSE";
$next_status{"RFLASH_SET_PRIORITY_REQUEST"} = "RFLASH_SET_PRIORITY_RESPONSE";
$next_status{"RFLASH_SET_PRIORITY_RESPONSE"} = "RFLASH_UPDATE_CHECK_STATE_REQUEST";
}
if ($::UPLOAD_ACTIVATE_STREAM) {
$next_status{LOGIN_RESPONSE} = "RFLASH_FILE_UPLOAD_REQUEST";
$next_status{RFLASH_FILE_UPLOAD_REQUEST} = "RFLASH_FILE_UPLOAD_RESPONSE";
$next_status{RFLASH_FILE_UPLOAD_RESPONSE} = "RFLASH_UPDATE_CHECK_ID_REQUEST";
$next_status{RFLASH_UPDATE_CHECK_ID_REQUEST} = "RFLASH_UPDATE_CHECK_ID_RESPONSE";
$next_status{RFLASH_UPDATE_CHECK_ID_RESPONSE} = "RFLASH_UPDATE_ACTIVATE_REQUEST";
$next_status{RFLASH_UPDATE_ACTIVATE_REQUEST} = "RFLASH_UPDATE_ACTIVATE_RESPONSE";
$next_status{RFLASH_UPDATE_ACTIVATE_RESPONSE} = "RFLASH_UPDATE_HOST_ACTIVATE_REQUEST";
$next_status{RFLASH_UPDATE_HOST_ACTIVATE_REQUEST} = "RFLASH_UPDATE_HOST_ACTIVATE_RESPONSE";
$next_status{RFLASH_UPDATE_HOST_ACTIVATE_RESPONSE} = "RFLASH_UPDATE_CHECK_STATE_REQUEST";
$next_status{RFLASH_UPDATE_CHECK_STATE_REQUEST} = "RFLASH_UPDATE_CHECK_STATE_RESPONSE";
$next_status{RFLASH_SET_PRIORITY_REQUEST} = "RFLASH_SET_PRIORITY_RESPONSE";
$next_status{RFLASH_SET_PRIORITY_RESPONSE} = "RFLASH_UPDATE_CHECK_STATE_REQUEST";
$next_status{RFLASH_UPDATE_CHECK_STATE_RESPONSE} = "RPOWER_BMCREBOOT_REQUEST";
$next_status{RPOWER_BMCREBOOT_REQUEST} = "RPOWER_RESET_RESPONSE";
$status_info{RPOWER_RESET_RESPONSE}{argv} = "bmcreboot";
$next_status{RPOWER_RESET_RESPONSE} = "RPOWER_BMC_CHECK_REQUEST";
$next_status{RPOWER_BMC_CHECK_REQUEST} = "RPOWER_BMC_STATUS_RESPONSE";
$next_status{LOGIN_REQUEST_GENERAL} = "LOGIN_RESPONSE_GENERAL";
$next_status{LOGIN_RESPONSE_GENERAL} = "RPOWER_BMC_STATUS_REQUEST";
$next_status{RPOWER_BMC_STATUS_REQUEST} = "RPOWER_BMC_STATUS_RESPONSE";
$status_info{RPOWER_BMC_STATUS_RESPONSE}{argv} = "bmcstate";
if (!$::RFLASH_STREAM_NO_HOST_REBOOT) {
$next_status{RPOWER_BMC_STATUS_RESPONSE} = "RPOWER_OFF_REQUEST";
$next_status{RPOWER_OFF_REQUEST} = "RPOWER_OFF_RESPONSE";
$next_status{RPOWER_OFF_RESPONSE} = "RPOWER_CHECK_REQUEST";
$next_status{RPOWER_CHECK_REQUEST} = "RPOWER_CHECK_RESPONSE";
$next_status{RPOWER_CHECK_RESPONSE}{ON} = "RPOWER_CHECK_REQUEST";
$next_status{RPOWER_CHECK_RESPONSE}{OFF} = "RPOWER_ON_REQUEST";
$next_status{RPOWER_ON_REQUEST} = "RPOWER_ON_RESPONSE";
$status_info{RPOWER_ON_RESPONSE}{argv} = "boot";
$next_status{RPOWER_ON_RESPONSE} = "RPOWER_CHECK_ON_REQUEST";
$next_status{RPOWER_CHECK_ON_REQUEST} = "RPOWER_CHECK_ON_RESPONSE";
$next_status{RPOWER_CHECK_ON_RESPONSE}{OFF} = "RPOWER_ON_REQUEST";
}
}
}
return;
}
#-------------------------------------------------------
=head3 fork_process_login
Fork process to login
Input:
$node: nodename
=cut
#-------------------------------------------------------
sub fork_process_login {
my $node = shift;
my $rst = 0;
my $child = xCAT::Utils->xfork;
if (!defined($child)) {
xCAT::SvrUtils::sendmsg("Failed to fork child process for login request.", $callback, $node);
sleep(1);
$rst = 1;
} elsif ($child == 0) {
exit(login_request($node));
} else {
$login_pid_node{$child} = $node;
}
return $rst;
}
#-------------------------------------------------------
#
#=head3 get_functional_software_ids
#
# Checks if the FW response data contains "functional" which
# indicates the actual software version currently running on
# the Server.
#
# Returns: reference to hash
#
# =cut
#
#-------------------------------------------------------
sub get_functional_software_ids {
my $response = shift;
my %functional;
#
# Get the functional IDs to accurately mark the active running FW
#
if (${ $response->{data} }{'/xyz/openbmc_project/software/functional'} ) {
my %func_data = %{ ${ $response->{data} }{'/xyz/openbmc_project/software/functional'} };
foreach ( @{$func_data{endpoints}} ) {
my $fw_id = (split '/', $_)[-1];
$functional{$fw_id} = 1;
}
}
return \%functional;
}
#-------------------------------------------------------
=head3 parse_node_info
Parse the node information: bmc, 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);
$rst = 1;
next;
}
unless($node_info{$node}{bmcip}) {
xCAT::SvrUtils::sendmsg("Error: Unable to resolved 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;
}
$node_info{$node}{cur_status} = "LOGIN_REQUEST";
$node_info{$node}{rpower_check_times} = $::RPOWER_MAX_RETRY;
$node_info{$node}{rpower_check_on_times} = $::RPOWER_ON_MAX_RETRY;
$node_info{$node}{bmc_conn_check_times} = $::BMC_MAX_RETRY;
$node_info{$node}{bmcstate_check_times} = $::BMC_MAX_RETRY;
} else {
xCAT::SvrUtils::sendmsg("Error: Unable to get information from openbmc table", $callback, $node);
$rst = 1;
next;
}
}
return $rst;
}
#-------------------------------------------------------
=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 (defined($status_info{ $node_info{$node}{cur_status} }{data})) {
# Handle boolean values by create the json objects without wrapping with quotes
if ($status_info{ $node_info{$node}{cur_status} }{data} =~ /^1$|^true$|^True$|^0$|^false$|^False$/) {
$content = '{"data":' . $status_info{ $node_info{$node}{cur_status} }{data} . '}';
} elsif ($status_info{ $node_info{$node}{cur_status} }{data} =~ /^\[\]$/) {
# Special handling of empty data list
$content = '{"data":[]}';
} elsif ($status_info{ $node_info{$node}{cur_status} }{data} =~ /^\[.+\]$/) {
$content = '{"data":' . $status_info{ $node_info{$node}{cur_status} }{data} . '}';
} elsif (($status_info{ $node_info{$node}{cur_status} }{init_url} =~ /config\/attr\/HostName$/) &&
($status_info{ $node_info{$node}{cur_status} }{data} =~ /^\*$/)) {
# Special handling for hostname=*
$content = '{"data":"' . $node_info{$node}{bmc} . '"}';
} else {
$content = '{"data":"' . $status_info{ $node_info{$node}{cur_status} }{data} . '"}';
}
}
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 = "$http_protocol://" . $node_info{$node}{bmc} . $request_url;
if ($xcatdebugmode) {
my $debug_info;
if ($method eq "GET") {
$debug_info = "curl -k -b cjar -X $method -H \"Content-Type: application/json\" $request_url";
} else {
if ($::UPLOAD_FILE and !$::UPLOAD_ACTIVATE_STREAM) {
# Slightly different debug message when doing a file upload
$debug_info = "curl -k -b cjar -X $method -H \"Content-Type: application/json\" -T $::UPLOAD_FILE $request_url";
} else {
if ($node_info{$node}{cur_status} eq "LOGIN_REQUEST_GENERAL") {
$debug_info = "curl -k -c cjar -H \"Content-Type: application/json\" -d '{ \"data\": [\"$node_info{$node}{username}\", \"xxxxxx\"] }' $request_url";
} else {
$debug_info = "curl -k -b cjar -X $method -H \"Content-Type: application/json\" -d '$content' $request_url";
}
}
}
process_debug_info($node, $debug_info);
}
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};
delete $handle_id_node{$handle_id};
if ($::UPLOAD_ACTIVATE_STREAM) {
my $rflash_log_file = xCAT::Utils->full_path($node.".log", $::XCAT_LOG_RFLASH_DIR);
open (RFLASH_LOG_FILE_HANDLE, ">> $rflash_log_file");
}
if ($xcatdebugmode) {
my $debug_info = lc ($node_info{$node}{cur_status}) . " " . $response->status_line;
process_debug_info($node, $debug_info);
}
if ($response->status_line ne $::RESPONSE_OK) {
my $error;
if (defined $status_info{RPOWER_STATUS_RESPONSE}{argv} and $status_info{RPOWER_STATUS_RESPONSE}{argv} =~ /bmcstate$/) {
# Handle the special case to return "NotReady" if the BMC does not return a success response.
# If the REST service is not up, it can't return "NotReady" itself, during reboot.
$error = "BMC NotReady";
xCAT::SvrUtils::sendmsg($error, $callback, $node);
$wait_node_num--;
return;
}
if ($node_info{$node}{cur_status} eq "RPOWER_BMC_STATUS_RESPONSE" and defined $status_info{RPOWER_BMC_STATUS_RESPONSE}{argv} and $status_info{RPOWER_BMC_STATUS_RESPONSE}{argv} =~ /bmcstate$/) {
retry_check_times($node, "RPOWER_BMC_STATUS_REQUEST", "bmc_conn_check_times", $::BMC_CHECK_INTERVAL, $response->status_line);
return;
}
if ($response->status_line eq $::RESPONSE_SERVICE_UNAVAILABLE) {
$error = $::RESPONSE_SERVICE_UNAVAILABLE;
} elsif ($response->status_line eq $::RESPONSE_METHOD_NOT_ALLOWED) {
if ($node_info{$node}{cur_status} eq "REVENTLOG_RESOLVED_RESPONSE") {
$error = "Could not find ID specified.";
} else {
# Special processing for file upload. At this point we do not know how to
# form a proper file upload request. It always fails with "Method not allowed" error.
# If that happens, just assume it worked.
# TODO remove this block when proper request can be generated
$status_info{ $node_info{$node}{cur_status} }->{process}->($node, $response);
return;
}
} elsif ($response->status_line eq $::RESPONSE_SERVICE_TIMEOUT) {
if ($node_info{$node}{cur_status} eq "RPOWER_RESET_RESPONSE" and defined $status_info{RPOWER_RESET_RESPONSE}{argv} and $status_info{RPOWER_RESET_RESPONSE}{argv} =~ /bmcreboot$/) {
my $infomsg = "BMC $::POWER_STATE_REBOOT";
xCAT::SvrUtils::sendmsg($infomsg, $callback, $node);
if ($::UPLOAD_ACTIVATE_STREAM) {
my $timestamp = localtime();
print RFLASH_LOG_FILE_HANDLE "$timestamp ===================Rebooting BMC to apply new BMC firmware===================\n";
print RFLASH_LOG_FILE_HANDLE "BMC $::POWER_STATE_REBOOT\n";
close (RFLASH_LOG_FILE_HANDLE);
retry_after($node, "RPOWER_BMC_CHECK_REQUEST", 15);
return;
}else{
$wait_node_num--;
return;
}
}
$error = $::RESPONSE_SERVICE_TIMEOUT;
} else {
my $response_info = decode_json $response->content;
# Handle 500
if ($response->status_line eq $::RESPONSE_SERVER_ERROR) {
$error = "[" . $response->code . "] " . $response_info->{'data'}->{'exception'};
# Handle 403
} elsif ($response->status_line eq $::RESPONSE_FORBIDDEN) {
#
# For any invalid data that we can detect, provide a better response message
#
if ($node_info{$node}{cur_status} eq "RFLASH_UPDATE_ACTIVATE_RESPONSE" or $node_info{$node}{cur_status} eq "RFLASH_UPDATE_HOST_ACTIVATE_RESPONSE") {
# If 403 is received for an activation, that means the activation ID is incorrect
$error = "Invalid ID provided to activate. Use the -l option to view valid firmware IDs.";
} elsif ($node_info{$node}{cur_status} eq "RSETBOOT_ENABLE_RESPONSE" ) {
# If 403 is received setting boot method, API endpoint changed in 1738 FW, inform the user of work around.
$error = "Invalid endpoint used to set boot method. If running firmware < ibm-v1.99.10-0-r7, 'export XCAT_OPENBMC_FIRMWARE=1736' and retry.";
} elsif ($node_info{$node}{cur_status} eq "REVENTLOG_RESOLVED_RESPONSE") {
my $cur_url;
if ($node_info{$node}{cur_url}) {
$cur_url = $node_info{$node}{cur_url};
} else {
$cur_url = $status_info{REVENTLOG_RESOLVED_REQUEST}{init_url};
}
my $log_id = (split ('/', $cur_url))[5];
$error = "Invalid ID=$log_id provided to be resolved. [$::RESPONSE_FORBIDDEN]";
} else{
$error = "$::RESPONSE_FORBIDDEN - Requested endpoint does not exist or may indicate function is not yet supported by OpenBMC firmware.";
}
# Handle 404
} elsif ($response->status_line eq $::RESPONSE_NOT_FOUND) {
#
# For any invalid data that we can detect, provide a better response message
#
if ($node_info{$node}{cur_status} eq "RFLASH_DELETE_IMAGE_RESPONSE") {
$error = "Invalid ID provided to delete. Use the -l option to view valid firmware IDs.";
} elsif (($node_info{$node}{cur_status} eq "RSPCONFIG_API_CONFIG_QUERY_RESPONSE") ||
($node_info{$node}{cur_status} eq "RSPCONFIG_API_CONFIG_ATTR_RESPONSE")) {
$error = "$::RESPONSE_NOT_FOUND - Requested endpoint does not exist or may indicate function is not supported on this OpenBMC firmware.";
} else {
$error = "[" . $response->code . "] " . $response_info->{'data'}->{'description'};
}
} else {
$error = "[" . $response->code . "] " . $response_info->{'data'}->{'description'};
}
}
if (!($node_info{$node}{cur_status} eq "RSPCONFIG_DUMP_CLEAR_RESPONSE" and $next_status{ $node_info{$node}{cur_status} })) {
xCAT::SvrUtils::sendmsg([1, $error], $callback, $node);
if ($::UPLOAD_AND_ACTIVATE or $next_status{LOGIN_RESPONSE} eq "RFLASH_UPDATE_ACTIVATE_REQUEST") {
$node_info{$node}{rst} = $error;
my $rflash_log_file = xCAT::Utils->full_path($node.".log", $::XCAT_LOG_RFLASH_DIR);
open (RFLASH_LOG_FILE_HANDLE, ">> $rflash_log_file");
print RFLASH_LOG_FILE_HANDLE "$error\n";
close (RFLASH_LOG_FILE_HANDLE);
}
$wait_node_num--;
return;
}
}
if ($status_info{ $node_info{$node}{cur_status} }->{process}) {
$status_info{ $node_info{$node}{cur_status} }->{process}->($node, $response);
} else {
xCAT::SvrUtils::sendmsg([1,"Internal error, check the process handler for current status $node_info{$node}{cur_status}"]);
$wait_node_num--;
}
return;
}
#-------------------------------------------------------
=head3 process_debug_info
print debug info and add to log
Input:
$node: nodename which want to process ingo
$debug_msg: Info for debug
=cut
#-------------------------------------------------------
sub process_debug_info {
my $node = shift;
my $debug_msg = shift;
my $ts_node = localtime() . " " . $node;
if (!$debug_msg) {
$debug_msg = "";
}
xCAT::SvrUtils::sendmsg("$flag_debug $debug_msg", $callback, $ts_node);
xCAT::MsgUtils->trace(0, "D", "$flag_debug $node $debug_msg");
}
#-------------------------------------------------------
=head3 login_request
Send login request using curl command
Input:
$node: nodename
=cut
#-------------------------------------------------------
sub login_request {
my $node = shift;
my $login_url = "$http_protocol://" . $node_info{$node}{bmc} . "/login";
my $data = '{ "data": [ "' . $node_info{$node}{username} .'", "' . $node_info{$node}{password} . '" ] }';
my $brower = LWP::UserAgent->new( ssl_opts => { SSL_verify_mode => 0x00, verify_hostname => 0 }, timeout => 20);
my $cookie_jar = HTTP::Cookies->new();
my $header = HTTP::Headers->new('Content-Type' => 'application/json');
$brower->cookie_jar($cookie_jar);
my $login_request = HTTP::Request->new( 'POST', $login_url, $header, $data );
my $login_response = $brower->request($login_request);
# Check the return code
if ($login_response->code eq 500 or $login_response->code eq 404) {
# handle only 404 and 504 in this code, defer to deal_with_response for the rest
xCAT::SvrUtils::sendmsg([1 ,"[" . $login_response->code . "] Login to BMC failed: " . $login_response->status_line . "."], $callback, $node);
return 1;
}
return 0;
}
#-------------------------------------------------------
=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} };
if ($node_info{$node}{method} || $status_info{ $node_info{$node}{cur_status} }{method}) {
gen_send_request($node);
} elsif ($status_info{ $node_info{$node}{cur_status} }->{process}) {
$status_info{ $node_info{$node}{cur_status} }->{process}->($node, undef);
}
}
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 %new_status = ();
my $response_info = decode_json $response->content;
if ($::UPLOAD_ACTIVATE_STREAM) {
my $rflash_log_file = xCAT::Utils->full_path($node.".log", $::XCAT_LOG_RFLASH_DIR);
open (RFLASH_LOG_FILE_HANDLE, ">> $rflash_log_file");
}
if ($node_info{$node}{cur_status} eq "RPOWER_ON_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
if ($status_info{RPOWER_ON_RESPONSE}{argv}) {
if (defined($node_info{$node}{power_state_rest}) and ($node_info{$node}{power_state_rest} == 1)) {
xCAT::SvrUtils::sendmsg("$::POWER_STATE_ON", $callback, $node);
} else {
$node_info{$node}{power_state_rest} = 1;
xCAT::SvrUtils::sendmsg("$::POWER_STATE_RESET", $callback, $node);
if ($::UPLOAD_ACTIVATE_STREAM) {
print RFLASH_LOG_FILE_HANDLE "Power on host : RPOWER_ON_RESPONSE $::POWER_STATE_RESET\n";
}
}
} else {
if (defined($::OPENBMC_PWR) and ($::OPENBMC_PWR eq "YES")) {
xCAT::SvrUtils::sendmsg("$::STATUS_POWERING_ON", $callback, $node);
} else {
xCAT::SvrUtils::sendmsg("$::POWER_STATE_ON", $callback, $node);
}
}
$new_status{$::STATUS_POWERING_ON} = [$node];
}
}
if ($node_info{$node}{cur_status} =~ /^RPOWER_OFF_RESPONSE$|^RPOWER_SOFTOFF_RESPONSE$/) {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
my $power_state = "$::POWER_STATE_OFF";
if ($node_info{$node}{cur_status} eq "RPOWER_SOFTOFF_RESPONSE") {
$power_state = "$::POWER_STATE_POWERING_OFF";
}
if ($::UPLOAD_ACTIVATE_STREAM) {
my $timestamp = localtime();
print RFLASH_LOG_FILE_HANDLE "$timestamp ===================Start reset host to apply new PNOR===================\n";
print RFLASH_LOG_FILE_HANDLE "$timestamp Power reset host ...\n";
print RFLASH_LOG_FILE_HANDLE "Power off host : RPOWER_OFF_RESPONSE power_state $power_state\n";
print RFLASH_LOG_FILE_HANDLE "Wait for $::RPOWER_RESET_SLEEP_INTERVAL seconds ...\n";
sleep($::RPOWER_RESET_SLEEP_INTERVAL);
}
xCAT::SvrUtils::sendmsg("$power_state", $callback, $node) if (!$next_status{ $node_info{$node}{cur_status} });
$new_status{$::STATUS_POWERING_OFF} = [$node];
}
}
if ($node_info{$node}{cur_status} eq "RPOWER_RESET_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
if (defined $status_info{RPOWER_RESET_RESPONSE}{argv} and $status_info{RPOWER_RESET_RESPONSE}{argv} =~ /bmcreboot$/) {
xCAT::SvrUtils::sendmsg("BMC $::POWER_STATE_REBOOT", $callback, $node);
if ($::UPLOAD_ACTIVATE_STREAM) {
print RFLASH_LOG_FILE_HANDLE "BMC $::POWER_STATE_REBOOT\n";
my $timestamp = localtime();
print RFLASH_LOG_FILE_HANDLE "$timestamp ===================Reboot BMC to apply new BMC===================\n";
retry_after($node, "RPOWER_BMC_CHECK_REQUEST", 15);
return;
}
}
$new_status{$::STATUS_POWERING_ON} = [$node];
}
}
xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%new_status, 1) if (%new_status);
my $all_status;
#get host $all_status for RPOWER_CHECK_ON_RESPONSE
if ($node_info{$node}{cur_status} eq "RPOWER_STATUS_RESPONSE" or $node_info{$node}{cur_status} eq "RPOWER_CHECK_RESPONSE" or $node_info{$node}{cur_status} eq "RPOWER_BMC_STATUS_RESPONSE" or $node_info{$node}{cur_status} eq "RPOWER_CHECK_ON_RESPONSE") {
if ($::UPLOAD_ACTIVATE_STREAM and $node_info{$node}{cur_status} eq "RPOWER_CHECK_ON_RESPONSE" and $::RPOWER_CHECK_ON_TIME == 1 ) {
print RFLASH_LOG_FILE_HANDLE "After power on in reset, wait for $::RPOWER_RESET_SLEEP_INTERVAL seconds ...\n";
sleep($::RPOWER_RESET_SLEEP_INTERVAL);
$::RPOWER_CHECK_ON_TIME = 0;
}
my $bmc_state = "";
my $bmc_transition_state = "";
my $chassis_state = "";
my $chassis_transition_state = "";
my $host_state = "";
my $host_transition_state = "";
foreach my $type (keys %{$response_info->{data}}) {
if ($type =~ /bmc0/) {
$bmc_state = $response_info->{'data'}->{$type}->{CurrentBMCState};
$bmc_transition_state = $response_info->{'data'}->{$type}->{RequestedBMCTransition};
}
if ($type =~ /chassis0/) {
$chassis_state = $response_info->{'data'}->{$type}->{CurrentPowerState};
$chassis_transition_state = $response_info->{'data'}->{$type}->{RequestedPowerTransition};
}
if ($type =~ /host0/) {
$host_state = $response_info->{'data'}->{$type}->{CurrentHostState};
$host_transition_state = $response_info->{'data'}->{$type}->{RequestedHostTransition};
}
}
if (defined($::OPENBMC_PWR) and ($::OPENBMC_PWR eq "YES")) {
# Print this debug only if testing transition states
print "$node: DEBUG State CurrentBMCState=$bmc_state\n";
print "$node: DEBUG State RequestedBMCTransition=$bmc_transition_state\n";
print "$node: DEBUG State CurrentPowerState=$chassis_state\n";
print "$node: DEBUG State RequestedPowerTransition=$chassis_transition_state\n";
print "$node: DEBUG State CurrentHostState=$host_state\n";
print "$node: DEBUG State RequestedHostTransition=$host_transition_state\n";
}
if ($::UPLOAD_ACTIVATE_STREAM and $node_info{$node}{cur_status} eq "RPOWER_CHECK_RESPONSE") {
print RFLASH_LOG_FILE_HANDLE "check power state: RPOWER_CHECK_RESPONSE\n";
print RFLASH_LOG_FILE_HANDLE "DEBUG State CurrentBMCState=$bmc_state\n";
print RFLASH_LOG_FILE_HANDLE "DEBUG State RequestedBMCTransition=$bmc_transition_state\n";
print RFLASH_LOG_FILE_HANDLE "DEBUG State CurrentPowerState=$chassis_state\n";
print RFLASH_LOG_FILE_HANDLE "DEBUG State RequestedPowerTransition=$chassis_transition_state\n";
print RFLASH_LOG_FILE_HANDLE "DEBUG State CurrentHostState=$host_state\n";
print RFLASH_LOG_FILE_HANDLE "DEBUG State RequestedHostTransition=$host_transition_state\n";
}
if (defined $status_info{RPOWER_STATUS_RESPONSE}{argv} and $status_info{RPOWER_STATUS_RESPONSE}{argv} =~ /bmcstate$/) {
my $bmc_short_state = (split(/\./, $bmc_state))[-1];
xCAT::SvrUtils::sendmsg("BMC $bmc_short_state", $callback, $node);
} elsif ($node_info{$node}{cur_status} eq "RPOWER_BMC_STATUS_RESPONSE" and (defined $status_info{RPOWER_BMC_STATUS_RESPONSE}{argv}) and $status_info{RPOWER_BMC_STATUS_RESPONSE}{argv} =~ /bmcstate$/) {
my $bmc_short_state = (split(/\./, $bmc_state))[-1];
if (defined($bmc_state) and $bmc_state !~ /State.BMC.BMCState.Ready$/) {
if ($node_info{$node}{bmcstate_check_times} > 0) {
$node_info{$node}{bmcstate_check_times}--;
if ($node_info{$node}{wait_start}) {
$node_info{$node}{wait_end} = time();
} else {
$node_info{$node}{wait_start} = time();
}
retry_after($node, "RPOWER_BMC_STATUS_REQUEST", $::BMC_CHECK_INTERVAL);
return;
} else {
my $wait_time_X = $node_info{$node}{wait_end} - $node_info{$node}{wait_start};
xCAT::SvrUtils::sendmsg([1, "Error: Sent bmcreboot but state did not change to BMC Ready after waiting $wait_time_X seconds. (State=BMC $bmc_short_state)."], $callback, $node);
$node_info{$node}{cur_status} = "";
$wait_node_num--;
return;
}
}
xCAT::SvrUtils::sendmsg("BMC $bmc_short_state", $callback, $node);
if ($::UPLOAD_ACTIVATE_STREAM) {
print RFLASH_LOG_FILE_HANDLE "BMC $bmc_short_state\n";
my $timestamp = localtime();
print RFLASH_LOG_FILE_HANDLE "$timestamp ===================Finished applying BMC firmware===================\n";
}
} else {
if ($chassis_state =~ /Off$/) {
# Chassis state is Off, but check if we can detect transition states
if ((defined($::OPENBMC_PWR) and ($::OPENBMC_PWR eq "YES")) and
$host_state =~ /Off$/ and $host_transition_state =~ /On$/) {
xCAT::SvrUtils::sendmsg("$::POWER_STATE_POWERING_ON", $callback, $node);
} else {
if (defined $status_info{RPOWER_STATUS_RESPONSE}{argv} and $status_info{RPOWER_STATUS_RESPONSE}{argv} =~ /fw_delete$/) {
# We are here just to check the state of the Host to determine if ok to remove active FW
# The state is Off so FW can be removed
$next_status{"RPOWER_STATUS_RESPONSE"} = "RFLASH_DELETE_IMAGE_REQUEST";
$next_status{"RFLASH_DELETE_IMAGE_REQUEST"} = "RFLASH_DELETE_IMAGE_RESPONSE";
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
gen_send_request($node);
return;
} else {
xCAT::SvrUtils::sendmsg("$::POWER_STATE_OFF", $callback, $node) if (!$next_status{ $node_info{$node}{cur_status} });
}
}
$all_status = $::POWER_STATE_OFF;
} elsif ($chassis_state =~ /On$/) {
if ($host_state =~ /Off$/) {
# This is a debug scenario where the chassis is powered on but hostboot is not
xCAT::SvrUtils::sendmsg("$::POWER_STATE_ON_HOSTOFF", $callback, $node) if (!$next_status{ $node_info{$node}{cur_status} });
$all_status = $::POWER_STATE_OFF;
} elsif ($host_state =~ /Quiesced$/) {
xCAT::SvrUtils::sendmsg("$::POWER_STATE_QUIESCED", $callback, $node) if (!$next_status{ $node_info{$node}{cur_status} });
$all_status = $::POWER_STATE_ON;
} elsif ($host_state =~ /Running$/) {
if ((defined($::OPENBMC_PWR) and ($::OPENBMC_PWR eq "YES")) and
$host_transition_state =~ /Off$/ and $chassis_state =~ /On$/) {
xCAT::SvrUtils::sendmsg("$::POWER_STATE_POWERING_OFF", $callback, $node);
} else {
if (defined $status_info{RPOWER_STATUS_RESPONSE}{argv} and $status_info{RPOWER_STATUS_RESPONSE}{argv} =~ /fw_delete$/) {
xCAT::SvrUtils::sendmsg([1, "Deleting currently active firmware on powered on host is not supported"], $callback, $node);
$wait_node_num--;
return;
}
xCAT::SvrUtils::sendmsg("$::POWER_STATE_ON", $callback, $node) if (!$next_status{ $node_info{$node}{cur_status} });
}
$all_status = $::POWER_STATE_ON;
} else {
xCAT::SvrUtils::sendmsg("Unexpected host state=$host_state", $callback, $node) if (!$next_status{ $node_info{$node}{cur_status} });
$all_status = $::POWER_STATE_ON;
}
} else {
xCAT::SvrUtils::sendmsg("Unexpected chassis state=$chassis_state", $callback, $node) if (!$next_status{ $node_info{$node}{cur_status} });
$all_status = $::POWER_STATE_ON;
}
}
}
if ($next_status{ $node_info{$node}{cur_status} }) {
if ($node_info{$node}{cur_status} eq "RPOWER_CHECK_RESPONSE") {
if ($::UPLOAD_ACTIVATE_STREAM) {
print RFLASH_LOG_FILE_HANDLE "RPOWER_CHECK_RESPONSE,all_status $all_status\n";
}
if ($all_status eq "$::POWER_STATE_OFF") {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} }{OFF};
} else {
if ($node_info{$node}{rpower_check_times} > 0) {
$node_info{$node}{rpower_check_times}--;
if ($node_info{$node}{wait_start}) {
$node_info{$node}{wait_end} = time();
} else {
$node_info{$node}{wait_start} = time();
}
retry_after($node, $next_status{ $node_info{$node}{cur_status} }{ON}, $::RPOWER_CHECK_INTERVAL);
return;
} else {
my $wait_time_X = $node_info{$node}{wait_end} - $node_info{$node}{wait_start};
xCAT::SvrUtils::sendmsg([1, "Error: Sent power-off command but state did not change to $::POWER_STATE_OFF after waiting $wait_time_X seconds. (State=$all_status)."], $callback, $node);
$node_info{$node}{cur_status} = "";
$wait_node_num--;
return;
}
}
} elsif ($node_info{$node}{cur_status} eq "RPOWER_STATUS_RESPONSE") {
if ($all_status eq "$::POWER_STATE_OFF") {
xCAT::SvrUtils::sendmsg("$::POWER_STATE_OFF", $callback, $node);
$node_info{$node}{cur_status} = "";
$wait_node_num--;
return;
} else {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} }{ON};
}
} elsif ($node_info{$node}{cur_status} eq "RPOWER_CHECK_ON_RESPONSE") {
#RPOWER_CHECK_ON_REQUEST and RPOWER_CHECK_ON_RESPONSE are for rflash -d function
#in order to make sure host is reboot successfully
#if rpower reset host and host state is always off, retry to set RPOWER_CHECK_ON_REQUEST to run rpower on the host
#1. if host power state is on, do nothing, and return
print RFLASH_LOG_FILE_HANDLE "Check power state in RPOWER_CHECK_ON_RESPONSE:all_status $all_status.\n";
if ($all_status eq "$::POWER_STATE_ON") {
$node_info{$node}{cur_status} = "";
my $timestamp = localtime();
print RFLASH_LOG_FILE_HANDLE "$timestamp ===================Finished applying Host firmware and resetting Host===================\n";
$wait_node_num--;
return;
}else{
#2. if host state is always off, retry to set RPOWER_CHECK_ON_REQUEST to run rpower on the host
if ($node_info{$node}{rpower_check_on_times} > 0) {
$node_info{$node}{rpower_check_on_times}--;
if ($node_info{$node}{wait_on_start}) {
$node_info{$node}{wait_on_end} = time();
} else {
$node_info{$node}{wait_on_start} = time();
}
#retry to set RPOWER_CHECK_ON_REQUEST after wait for $::RPOWER_CHECK_ON_INTERVAL
print RFLASH_LOG_FILE_HANDLE "Retrying to set RPOWER_CHECK_ON_REQUEST after waiting for $::RPOWER_CHECK_ON_INTERVAL seconds.\n";
retry_after($node, $next_status{ $node_info{$node}{cur_status} }{OFF}, $::RPOWER_CHECK_ON_INTERVAL);
return;
} else {
#after retry 5 times, the host is still off, print error and return
my $wait_time_X = $node_info{$node}{wait_on_end} - $node_info{$node}{wait_on_start};
xCAT::SvrUtils::sendmsg([1, "Sent power-on command but state did not change to $::POWER_STATE_ON after waiting $wait_time_X seconds. (State=$all_status)."], $callback, $node);
$node_info{$node}{cur_status} = "";
$wait_node_num--;
return;
}
}
} else {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
}
gen_send_request($node);
} else {
$wait_node_num--;
}
if ($::UPLOAD_ACTIVATE_STREAM) {
close (RFLASH_LOG_FILE_HANDLE);
}
return;
}
#-------------------------------------------------------
=head3 rinv_response
Deal with response of rinv command
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub rinv_response {
my $node = shift;
my $response = shift;
my $response_info = decode_json $response->content;
my $grep_string;
if ($node_info{$node}{cur_status} eq "RINV_FIRM_RESPONSE") {
$grep_string = "firm";
} else {
$grep_string = $status_info{RINV_RESPONSE}{argv};
}
my $src;
my $content_info;
my @sorted_output;
my $to_clear_dump = 0;
# Get the functional IDs to accurately mark the active running FW
my $functional = get_functional_software_ids($response_info);
foreach my $key_url (keys %{$response_info->{data}}) {
my %content = %{ ${ $response_info->{data} }{$key_url} };
if ($grep_string eq "firm") {
# This handles the data from the /xyz/openbmc_project/Software endpoint.
my $sw_id = (split(/\//, $key_url))[-1];
if (defined($content{Version}) and $content{Version}) {
my $purpose_value = uc ((split(/\./, $content{Purpose}))[-1]);
if ($purpose_value =~ /BMC/) {
$purpose_value = "[B$sw_id]$purpose_value";
} else {
$purpose_value = "[H$sw_id]$purpose_value";
}
my $activation_value = (split(/\./, $content{Activation}))[-1];
my $priority_value = -1;
if (defined($content{Priority})) {
$priority_value = $content{Priority};
}
if ($status_info{RINV_FIRM_RESPONSE}{check}) {
if (($purpose_value =~ /BMC/) and
($priority_value == 0 and %{$functional} and !exists($functional->{$sw_id}))) {
$to_clear_dump = 1;
last;
}
}
#
# For 'rinv firm', only print Active software, unless verbose is specified
#
if ( (%{$functional} and exists($functional->{$sw_id}) ) or
(!%{$functional} and $activation_value =~ "Active" and $priority_value == 0) or
$::VERBOSE ) {
#
# The space below between "Firmware Product Version:" and $content{Version} is intentional
# to cause the sorting of this line before any additional info lines
#
$content_info = "$purpose_value Firmware Product: $content{Version} ($activation_value)";
my $indicator = "";
if ($priority_value == 0 and %{$functional} and !exists($functional->{$sw_id})) {
# indicate that a reboot is needed if priority = 0 and it's not in the functional list
$indicator = "+";
} elsif (%{$functional} and exists($functional->{$sw_id})) {
$indicator = "*";
}
$content_info .= $indicator;
push (@sorted_output, $content_info);
if (defined($content{ExtendedVersion}) and $content{ExtendedVersion} ne "") {
# ExtendedVersion is going to be a comma separated list of additional software
my @versions = split(',', $content{ExtendedVersion});
foreach my $ver (@versions) {
$content_info = "$purpose_value Firmware Product: -- additional info: $ver";
push (@sorted_output, $content_info);
}
}
next;
}
}
} else {
if (! defined $content{Present}) {
# If the Present field is not part of the attribute, then it's most likely a callout
# Do not print as part of the inventory response
next;
}
# SPECIAL CASE: If 'serial' or 'model' is specified, only return the system level information
if ($grep_string eq "serial" or $grep_string eq "model") {
if ($key_url ne "$openbmc_project_url/inventory/system") {
next;
}
}
if ($key_url =~ /\/(cpu\d*)\/(\w+)/) {
$src = "$1 $2";
} else {
$src = basename $key_url;
}
foreach my $key (keys %content) {
# If not all options is specified, check whether the key string contains
# the keyword option. If so, add it to the return data
if ($grep_string ne "all" and ((lc($key) !~ m/$grep_string/i) and ($key_url !~ m/$grep_string/i)) ) {
next;
}
$content_info = uc ($src) . " " . $key . " : " . $content{$key};
push (@sorted_output, $content_info); #Save output in array
}
}
}
@sorted_output = () if ($status_info{RINV_FIRM_RESPONSE}{check});
# If sorted array has any contents, sort it naturally and print it
if (scalar @sorted_output > 0) {
# sort alpha, then numeric
foreach (sort natural_sort_cmp @sorted_output) {
#
# The firmware output requires the ID to be part of the string to sort correctly.
# Remove this ID from the output to the user
#
$_ =~ s/\[.*?\]//;
xCAT::MsgUtils->message("I", { data => ["$node: $_"] }, $callback);
}
} else {
if ($status_info{RINV_FIRM_RESPONSE}{check}) {
if ($to_clear_dump) {
xCAT::MsgUtils->message("I", { data => ["$node: Firmware will be flashed on reboot, deleting all BMC diagnostics..."] }, $callback);
}
} else {
xCAT::MsgUtils->message("I", { data => ["$node: $::NO_ATTRIBUTES_RETURNED"] }, $callback);
}
}
if ($next_status{ $node_info{$node}{cur_status} }) {
if ($status_info{RINV_FIRM_RESPONSE}{check}) {
if ($to_clear_dump) {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} }{PENDING};
} else {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} }{NO_PENDING}
}
} else {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
}
gen_send_request($node);
} else {
$wait_node_num--;
}
return;
}
#-------------------------------------------------------
=head3 getopenbmccons
Process getopenbmccons
=cut
#-------------------------------------------------------
sub getopenbmccons {
my $argr = shift;
#$argr is [$node,$bmcip,$nodeuser,$nodepass];
my $cb = shift;
my $rsp;
#my $node=$argr->[0];
#my $output = "openbmc, getopenbmccons";
#xCAT::SvrUtils::sendmsg($output, $cb, $argr->[0], %allerrornodes);
$rsp = { node => [ { name => [ $argr->[0] ] } ] };
$rsp->{node}->[0]->{bmcip}->[0] = $argr->[1];
$rsp->{node}->[0]->{username}->[0] = $argr->[2];
$rsp->{node}->[0]->{passwd}->[0] = $argr->[3];
$cb->($rsp);
#return $rsp;
}
#-------------------------------------------------------
=head3 rsetboot_response
Deal with response of rsetboot command
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub rsetboot_response {
my $node = shift;
my $response = shift;
my $response_info = decode_json $response->content;
if ($node_info{$node}{cur_status} eq "RSETBOOT_STATUS_RESPONSE") {
my $one_time_enabled;
my $bootsource;
if (defined($::OPENBMC_FW) && ($::OPENBMC_FW < 1738)) {
$bootsource = $response_info->{'data'}->{BootSource};
} else {
foreach my $key_url (keys %{$response_info->{data}}) {
my %content = %{ ${ $response_info->{data} }{$key_url} };
if ($key_url =~ /boot\/one_time/) {
$one_time_enabled = $content{Enabled};
$bootsource = $content{BootSource} if ($one_time_enabled);
} elsif ($key_url =~ /\/boot$/) {
$bootsource = $content{BootSource} unless ($one_time_enabled);
}
}
}
if ($bootsource =~ /Disk$/) {
xCAT::SvrUtils::sendmsg("Hard Drive", $callback, $node);
} elsif ($bootsource =~ /Network$/) {
xCAT::SvrUtils::sendmsg("Network", $callback, $node);
} elsif ($bootsource =~ /ExternalMedia$/) {
xCAT::SvrUtils::sendmsg("CD/DVD", $callback, $node);
} elsif ($bootsource =~ /Default$/) {
xCAT::SvrUtils::sendmsg("Default", $callback, $node);
} else {
my $error_msg = "Can not get valid rsetboot status, the data is " . $response_info->{'data'}->{BootSource};
xCAT::SvrUtils::sendmsg("$error_msg", $callback, $node);
}
}
if ($next_status{ $node_info{$node}{cur_status} }) {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
gen_send_request($node);
} else {
$wait_node_num--;
}
return;
}
#-------------------------------------------------------
=head3 rbeacon_response
Deal with response of rbeacon command
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub rbeacon_response {
my $node = shift;
my $response = shift;
my $response_info = decode_json $response->content;
if ($node_info{$node}{cur_status} eq "RBEACON_ON_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
xCAT::SvrUtils::sendmsg("$::BEACON_STATE_ON", $callback, $node);
}
}
if ($node_info{$node}{cur_status} eq "RBEACON_OFF_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
xCAT::SvrUtils::sendmsg("$::BEACON_STATE_OFF", $callback, $node);
}
}
if ($next_status{ $node_info{$node}{cur_status} }) {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
gen_send_request($node);
} else {
$wait_node_num--;
}
}
#-------------------------------------------------------
=head3 reventlog_response
Deal with response of reventlog command
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub reventlog_response {
my $node = shift;
my $response = shift;
my $response_info = decode_json $response->content;
if ($node_info{$node}{cur_status} eq "REVENTLOG_CLEAR_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
xCAT::SvrUtils::sendmsg("Logs cleared", $callback, $node);
}
} elsif ($node_info{$node}{cur_status} eq "REVENTLOG_RESOLVED_RESPONSE") {
my $cur_url;
if ($node_info{$node}{cur_url}) {
$cur_url = $node_info{$node}{cur_url};
if ($node_info{$node}{bak_url}) {
$node_info{$node}{cur_url} = shift @{ $node_info{$node}{bak_url} };
} else {
$node_info{$node}{cur_url} = "";
}
} else {
$cur_url = $status_info{REVENTLOG_RESOLVED_REQUEST}{init_url};
}
if ($response_info->{'message'} eq $::RESPONSE_OK) {
my $log_id = (split ('/', $cur_url))[5];
xCAT::SvrUtils::sendmsg("Resolved $log_id.", $callback, $node);
}
if ($status_info{REVENTLOG_RESOLVED_RESPONSE}{remain_entries} and !$node_info{$node}{remain_entries}) {
foreach my $entry (@{ $status_info{REVENTLOG_RESOLVED_RESPONSE}{remain_entries} }) {
my $tmp_url = $::LOGGING_URL;
$tmp_url =~ s/#ENTRY_ID#/$entry/g;
push @{ $node_info{$node}{bak_url} }, $tmp_url;
}
$node_info{$node}{cur_url} = shift @{ $node_info{$node}{bak_url} };
$node_info{$node}{remain_entries} = $status_info{REVENTLOG_RESOLVED_RESPONSE}{remain_entries};
}
if ($node_info{$node}{cur_url}) {
$next_status{"REVENTLOG_RESOLVED_RESPONSE"} = "REVENTLOG_RESOLVED_REQUEST";
} else {
# Break out of this loop if there are no more IDs to resolve
$wait_node_num--;
return;
}
} elsif ($node_info{$node}{cur_status} eq "REVENTLOG_RESOLVED_RESPONSE_LED") {
# Scan all event log entries and build an array of all that have callout data
my @entries;
foreach my $key_url (keys %{$response_info->{data}}) {
my %content = %{ ${ $response_info->{data} }{$key_url} };
next unless ($content{Id});
my $event_msg = is_callout_event_data(\%content);
push(@entries, $event_msg) if ($event_msg); # Add array entry of log event id
}
# If some entries with callout data, send them off to be resolved
if (scalar(@entries) > 0) {
$next_status{"REVENTLOG_RESOLVED_RESPONSE_LED"} = "REVENTLOG_RESOLVED_REQUEST";
$next_status{"REVENTLOG_RESOLVED_REQUEST"} = "REVENTLOG_RESOLVED_RESPONSE";
my $init_entry = shift @entries;
$status_info{REVENTLOG_RESOLVED_REQUEST}{init_url} =~ s/#ENTRY_ID#/$init_entry/g;
push @{ $status_info{REVENTLOG_RESOLVED_RESPONSE}{remain_entries} }, @entries;
}
else {
# Return if there are no entries with callout data
xCAT::SvrUtils::sendmsg("There are no event log entries contributing to LED fault", $callback, $node);
$wait_node_num--;
return;
}
} else {
my $entry_string = $status_info{REVENTLOG_RESPONSE}{argv};
my $content_info;
my %output = ();
my $entry_num = 0;
$entry_string = "all" if ($entry_string eq "0");
$entry_num = 0 + $entry_string if ($entry_string ne "all");
my $max_entry = 0;
foreach my $key_url (keys %{$response_info->{data}}) {
my %content = %{ ${ $response_info->{data} }{$key_url} };
next unless ($content{Id});
my $id_num = 0 + $content{Id};
my $event_msg = parse_event_data(\%content);
$output{$id_num} = $event_msg if ($event_msg);
$max_entry = $id_num if ($id_num > $max_entry);
}
xCAT::SvrUtils::sendmsg("$::NO_ATTRIBUTES_RETURNED", $callback, $node) if (!%output);
# If option is "all", print out all sorted msg. If is a num, print out the last <num> msg (sorted)
foreach my $key ( sort { $a <=> $b } keys %output) {
xCAT::MsgUtils->message("I", { data => ["$node: $output{$key}"] }, $callback) if ($entry_string eq "all" or $key > ($max_entry - $entry_num));
}
}
if ($next_status{ $node_info{$node}{cur_status} }) {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
gen_send_request($node);
} else {
$wait_node_num--;
}
}
#-------------------------------------------------------
=head3 is_callout_event_data
Parse reventlog data and return entry ID if it has
CALLOUT data
Input:
$content: data for single entry
=cut
#-------------------------------------------------------
sub is_callout_event_data {
my $content = shift;
my $id_num = $$content{Id};
if ($$content{Message}) {
if (defined $$content{AdditionalData} and $$content{AdditionalData}) {
foreach my $addition (@{ $$content{AdditionalData} }) {
if ($addition =~ /CALLOUT/) {
return $id_num;
}
}
}
}
return "";
}
#-------------------------------------------------------
=head3 parse_event_data
Parse reventlog data
Input:
$content: data for single entry
=cut
#-------------------------------------------------------
sub parse_event_data {
my $content = shift;
my $content_info = "";
my $LED_tag = " [LED]"; # Indicate that the entry contributes to LED fault
my $timestamp = $$content{Timestamp};
my $id_num = $$content{Id};
if ($$content{Message}) {
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($$content{Timestamp}/1000);
$mon += 1;
$year += 1900;
my $UTC_time = sprintf ("%02d/%02d/%04d %02d:%02d:%02d", $mon, $mday, $year, $hour, $min, $sec);
my $message = $$content{Message};
my $callout;
my $msg_pid;
my $i2c_device;
my $esel;
if (defined $$content{AdditionalData} and $$content{AdditionalData}) {
foreach my $addition (@{ $$content{AdditionalData} }) {
if ($addition =~ /CALLOUT_INVENTORY_PATH=(.+)/) {
$callout = $1;
}
if ($addition =~ /CALLOUT_DEVICE_PATH/) {
$callout = "I2C";
my @info = split("=", $addition);
my $tmp = $info[1];
my @tmp_data = split("/", $tmp);
my $data_num = @tmp_data;
$i2c_device = join("/", @tmp_data[($data_num-4)..($data_num-1)])
}
if ($addition =~ /ESEL/) {
my @info = split("=", $addition);
$esel = $info[1];
# maybe useful, so leave it here
}
if ($addition =~ /GPU/) {
my @info = split(" ", $addition);
$callout = "/xyz/openbmc_project/inventory/system/chassis/motherboard/gpu" . $info[-1];
}
if ($addition =~ /PID=(\d*)/) {
$msg_pid = $1;
}
}
}
$message .= "||$callout" if ($callout);
if (ref($event_mapping) eq "HASH") {
if ($event_mapping->{$message}) {
my $event_type = $event_mapping->{$message}{EventType};
my $event_message = $event_mapping->{$message}{Message};
my $severity = $event_mapping->{$message}{Severity};
my $affect = $event_mapping->{$message}{AffectedSubsystem};
$content_info = "$UTC_time [$id_num]: $event_type, ($severity) $event_message (AffectedSubsystem: $affect, PID: $msg_pid), Resolved: $$content{Resolved}";
} else {
$content_info = "$UTC_time [$id_num]: Not found in policy table: $message (PID: $msg_pid), Resolved: $$content{Resolved}";
}
} else {
$content_info = "$UTC_time [$id_num]: $message (PID: $msg_pid), Resolved: $$content{Resolved}";
}
$content_info .= $LED_tag if ($callout);
}
return $content_info;
}
#-------------------------------------------------------
=head3 rspconfig_response
Deal with response of rspconfig command
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub rspconfig_response {
my $node = shift;
my $response = shift;
my $response_info;
$response_info = decode_json $response->content if ($response);
if ($node_info{$node}{cur_status} eq "RSPCONFIG_GET_RESPONSE" or $node_info{$node}{cur_status} eq "RSPCONFIG_GET_NIC_RESPONSE") {
my $hostname = "";
my $default_gateway = "n/a";
my %nicinfo = ();
my $multiple_error = "";
my @output;
my $grep_string = $status_info{RSPCONFIG_GET_RESPONSE}{argv};
foreach my $key_url (keys %{$response_info->{data}}) {
my %content = %{ ${ $response_info->{data} }{$key_url} };
if ($key_url =~ /network\/config/) {
if (defined($content{DefaultGateway}) and $content{DefaultGateway}) {
$default_gateway = $content{DefaultGateway};
}
if (defined($content{HostName}) and $content{HostName}) {
$hostname = $content{HostName};
}
}
my ($path, $adapter_id) = (split(/\/ipv4\//, $key_url));
if ($adapter_id) {
if ( (defined($content{Origin}) and $content{Origin} =~ /LinkLocal/) or
(defined($content{Address}) and $content{Address} =~ /^169.254/) ) {
# OpenBMC driver has a interim bug where ZeroConfigIP comes up as DHCP instead of LinkLocal.
# To protect xCAT while the drivers change, check the 169.254 IP also
if ($xcatdebugmode) {
my $debugmsg = "Found LocalLink " . $content{Address} . " for interface " . $key_url . " Ignoring...";
process_debug_info($node, $debugmsg);
}
next;
}
my $nic = $path;
$nic =~ s/(.*\/)//g;
unless (defined($nicinfo{$nic}{address})) {
$nicinfo{$nic}{address} = ();
$nicinfo{$nic}{gateway} = ();
$nicinfo{$nic}{ipsrc} = ();
$nicinfo{$nic}{netmask} = ();
$nicinfo{$nic}{prefix} = ();
$nicinfo{$nic}{vlan} = "Disable";
}
if (defined($content{Address}) and $content{Address}) {
if ($content{Address} eq $node_info{$node}{bmcip} and $node_info{$node}{cur_status} eq "RSPCONFIG_GET_NIC_RESPONSE") {
$status_info{RSPCONFIG_SET_NTPSERVERS_REQUEST}{init_url} =~ s/#NIC#/$nic/g;
if ($next_status{"RSPCONFIG_GET_NIC_RESPONSE"}) {
$node_info{$node}{cur_status} = $next_status{"RSPCONFIG_GET_NIC_RESPONSE"};
gen_send_request($node);
return;
}
}
if ($nicinfo{$nic}{address}) {
$multiple_error = "Interfaces with multiple IP addresses are not supported";
}
push @{ $nicinfo{$nic}{address} }, $content{Address};
}
if (defined($content{Gateway}) and $content{Gateway}) {
push @{ $nicinfo{$nic}{gateway} }, $content{Gateway};
}
if (defined($content{PrefixLength}) and $content{PrefixLength}) {
push @{ $nicinfo{$nic}{prefix} }, $content{PrefixLength};
}
if (defined($content{Origin})) {
my $ipsrc_tmp = $content{Origin};
$ipsrc_tmp =~ s/^.*\.(\w+)/$1/;
push @{ $nicinfo{$nic}{ipsrc} }, $ipsrc_tmp;
}
if (defined($response_info->{data}->{$path}->{Id})) {
$nicinfo{$nic}{vlan} = $response_info->{data}->{$path}->{Id};
}
if (defined($response_info->{data}->{$path}->{NTPServers})) {
$nicinfo{$nic}{ntpservers} = join(",", @{ $response_info->{data}->{$path}->{NTPServers} });
}
}
}
if (scalar (keys %nicinfo) == 0) {
my $error = "No valid BMC network information";
xCAT::SvrUtils::sendmsg([1, "$error"], $callback, $node);
$node_info{$node}{cur_status} = "";
} else {
my @address = ();
my @ipsrc = ();
my @netmask = ();
my @gateway = ();
my @vlan = ();
my @ntpservers = ();
my $real_ntp_server = 0;
my @nics = keys %nicinfo;
foreach my $nic (@nics) {
my $addon_info = '';
if ($#nics > 1) {
$addon_info = " for $nic";
}
if ($nicinfo{$nic}{ntpservers}) {
push @ntpservers, "BMC NTP Servers$addon_info: $nicinfo{$nic}{ntpservers}";
$real_ntp_server = 1;
} else {
push @ntpservers, "BMC NTP Servers$addon_info: None";
}
next if ($multiple_error);
push @address, "BMC IP$addon_info: ${ $nicinfo{$nic}{address} }[0]";
push @ipsrc, "BMC IP Source$addon_info: ${ $nicinfo{$nic}{ipsrc} }[0]";
if ($nicinfo{$nic}{address}) {
my $mask_shift = 32 - ${ $nicinfo{$nic}{prefix} }[0];
my $decimal_mask = (2 ** ${ $nicinfo{$nic}{prefix} }[0] - 1) << $mask_shift;
push @netmask, "BMC Netmask$addon_info: " . join('.', unpack("C4", pack("N", $decimal_mask)));
}
push @gateway, "BMC Gateway$addon_info: ${ $nicinfo{$nic}{gateway} }[0] (default: $default_gateway)";
push @vlan, "BMC VLAN ID$addon_info: $nicinfo{$nic}{vlan}";
}
my $mul_out = 0;
foreach my $opt (split /,/,$grep_string) {
if ($opt eq "hostname") {
push @output, "BMC Hostname: $hostname";
} elsif ($opt eq "ntpservers") {
push @output, @ntpservers;
if (($real_ntp_server) && ($status_info{RSPCONFIG_SET_RESPONSE}{argv} =~ "NTPServers")) {
# Display a warning if the host in not powered off
# Time on the BMC is not synced while the host is powered on.
push @output, "Warning: time will not be synchronized until the host is powered off.";
}
}
if ($multiple_error and ($opt =~ /^ip$|^ipsrc$|^netmask$|^gateway$|^vlan$/)) {
$mul_out = 1;
next;
}
if ($opt eq "ip") {
push @output, @address;
} elsif ($opt eq "ipsrc") {
push @output, @ipsrc;
} elsif ($opt eq "netmask") {
push @output, @netmask;
} elsif ($opt eq "gateway") {
push @output, @gateway;
} elsif ($opt eq "vlan") {
push @output, @vlan;
}
}
xCAT::SvrUtils::sendmsg("$_", $callback, $node) foreach (@output);
if ($multiple_error and $mul_out) {
xCAT::SvrUtils::sendmsg([1, "$multiple_error"], $callback, $node);
$wait_node_num--;
return;
}
if ($grep_string eq "all") {
# If all current values equal the input, just print out message
my @checks = split("-", $status_info{RSPCONFIG_CHECK_RESPONSE}{argv});
my $check_num = @checks;
my $check_vlan;
if ($check_num == 4) {
$check_vlan = shift @checks;
}
my ($check_ip,$check_netmask,$check_gateway) = @checks;
my $the_nic_to_config = undef;
foreach my $nic (@nics) {
my $address = ${ $nicinfo{$nic}{address} }[0];
my $prefix = ${ $nicinfo{$nic}{prefix} }[0];
my $gateway = ${ $nicinfo{$nic}{gateway} }[0];
if ($check_ip eq $address and $check_netmask eq $prefix and $check_gateway eq $gateway) {
if (($check_vlan and $check_vlan eq $nicinfo{$nic}{vlan}) or !$check_vlan) {
$next_status{ $node_info{$node}{cur_status} } = "RSPCONFIG_PRINT_BMCINFO";
$the_nic_to_config = $nic;
last;
}
}
# Only deal with the nic whose IP matching the BMC IP configured for the node
if ($address eq $node_info{$node}{bmcip}) {
$the_nic_to_config = $nic;
last;
}
}
if (!defined($the_nic_to_config)) {
xCAT::SvrUtils::sendmsg("Can not find the correct device to configure", $callback, $node);
$wait_node_num--;
return;
} else {
my $next_state = $next_status{ $node_info{$node}{cur_status} };
# To create an Object with vlan tag, shall be operated to the eth0
if ($next_state eq "RSPCONFIG_VLAN_REQUEST") {
$the_nic_to_config =~ s/(\_\d*)//g;
$status_info{$next_state}{data} =~ s/#NIC#/$the_nic_to_config/g;
}
$status_info{RSPCONFIG_IPOBJECT_REQUEST}{init_url} =~ s/#NIC#/$the_nic_to_config/g;
$node_info{$node}{nic} = $the_nic_to_config;
}
}
}
}
my $origin_type;
if ($node_info{$node}{cur_status} eq "RSPCONFIG_CHECK_RESPONSE") {
my @checks = split("-", $status_info{RSPCONFIG_CHECK_RESPONSE}{argv});
my $check_num = @checks;
my $check_vlan;
if ($check_num == 4) {
$check_vlan = shift @checks;
}
my ($check_ip,$check_netmask,$check_gateway) = @checks;
my $check_result = 0;
foreach my $key_url (keys %{$response_info->{data}}) {
my %content = %{ ${ $response_info->{data} }{$key_url} };
my ($path, $adapter_id) = (split(/\/ipv4\//, $key_url));
if ($adapter_id) {
if (defined($content{Address}) and $content{Address}) {
if ($content{Address} eq $node_info{$node}{bmcip}) {
if ($content{Origin} =~ /Static/) {
$origin_type = "STATIC";
$status_info{RSPCONFIG_DELETE_REQUEST}{init_url} = "$key_url";
} else {
$origin_type = "DHCP";
my $nic = $path;
$nic =~ s/(.*\/)//g;
$status_info{RSPCONFIG_DHCPDIS_REQUEST}{init_url} =~ s/#NIC#/$nic/g
}
} else {
if (($content{Address} eq $check_ip) and
($content{PrefixLength} eq $check_netmask) and
($content{Gateway} eq $check_gateway)) {
if ($check_vlan) {
if (defined($response_info->{data}->{$path}->{Id}) and $response_info->{data}->{$path}->{Id} eq $check_vlan) {
$check_result = 1;
}
} else {
$check_result = 1;
}
}
}
}
}
}
if (!$check_result or !$origin_type) {
xCAT::SvrUtils::sendmsg("Config IP failed", $callback, $node);
$next_status{ $node_info{$node}{cur_status} } = "";
}
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_PASSWD_VERIFY") {
if ($status_info{RSPCONFIG_PASSWD_VERIFY}{argv} ne $node_info{$node}{password}) {
xCAT::SvrUtils::sendmsg("Current BMC password is incorrect, cannot set the new password.", $callback, $node);
$wait_node_num--;
return;
}
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_SET_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
if (defined $status_info{RSPCONFIG_SET_RESPONSE}{argv}) {
xCAT::SvrUtils::sendmsg("BMC Setting $status_info{RSPCONFIG_SET_RESPONSE}{argv}...", $callback, $node);
}
}
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_DHCP_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
xCAT::SvrUtils::sendmsg("BMC Setting IP to DHCP...", $callback, $node);
}
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_PRINT_BMCINFO") {
if ($status_info{RSPCONFIG_PRINT_BMCINFO}{data}) {
my @output = split(",", $status_info{RSPCONFIG_PRINT_BMCINFO}{data});
xCAT::SvrUtils::sendmsg($_, $callback, $node) foreach (@output);
}
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_VLAN_RESPONSE") {
if ($xcatdebugmode) {
process_debug_info($node, "Wait $::RSPCONFIG_WAIT_VLAN_DONE seconds for interface with VLAN tag be ready");
}
retry_after($node, $next_status{ $node_info{$node}{cur_status} }, $::RSPCONFIG_WAIT_VLAN_DONE);
return;
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_IPOBJECT_RESPONSE") {
if ($xcatdebugmode) {
process_debug_info($node, "Wait $::RSPCONFIG_WAIT_IP_DONE seconds for the configuration done");
}
retry_after($node, $next_status{ $node_info{$node}{cur_status} }, $::RSPCONFIG_WAIT_IP_DONE);
return;
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_CLEAR_GARD_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
xCAT::SvrUtils::sendmsg("GARD cleared", $callback, $node);
}
}
if ($next_status{ $node_info{$node}{cur_status} }) {
if ($node_info{$node}{cur_status} eq "RSPCONFIG_CHECK_RESPONSE") {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} }{$origin_type};
} else {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_PRINT_BMCINFO") {
$status_info{ $node_info{$node}{cur_status} }->{process}->($node, "");
} else {
gen_send_request($node);
}
} else {
$wait_node_num--;
}
}
#-------------------------------------------------------
=head3 rspconfig_api_config_response
Deal with response of rspconfig command for configured subcommand
Currently understands only generic boolean setting and query responses
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub rspconfig_api_config_response {
my $node = shift;
my $response = shift;
my $response_info;
my $value = -1;
$response_info = decode_json $response->content if ($response);
if ($node_info{$node}{cur_status}) {
if ($node_info{$node}{cur_status} eq "RSPCONFIG_API_CONFIG_ON_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
xCAT::SvrUtils::sendmsg("BMC Setting ". $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{display_name} . "...", $callback, $node);
}
else {
xCAT::SvrUtils::sendmsg("Error setting RSPCONFIG_API_CONFIG_ON_RESPONSE", $callback, $node);
}
}
elsif ($node_info{$node}{cur_status} eq "RSPCONFIG_API_CONFIG_OFF_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
xCAT::SvrUtils::sendmsg("BMC Setting ". $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{display_name} . "...", $callback, $node);
}
else {
xCAT::SvrUtils::sendmsg("Error unsetting RSPCONFIG_API_CONFIG_OFF_RESPONSE", $callback, $node);
}
}
elsif ($node_info{$node}{cur_status} eq "RSPCONFIG_API_CONFIG_ATTR_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
xCAT::SvrUtils::sendmsg("BMC Setting ".
$api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{display_name} .
"... " .
$api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{instruct_msg}, $callback, $node);
}
else {
xCAT::SvrUtils::sendmsg("Error unsetting RSPCONFIG_API_CONFIG_OFF_RESPONSE", $callback, $node);
}
}
elsif ($node_info{$node}{cur_status} eq "RSPCONFIG_API_CONFIG_QUERY_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
# Sometimes query will return hash, sometimes just a variable data
if (ref($response_info->{data}) eq 'HASH') {
# Hash returned in "data"
foreach my $key_url (keys %{$response_info->{data}}) {
if ($key_url eq $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_url}) {
# Is this the attribute we are looking for ?
$value = $response_info->{data}{$key_url};
last;
}
}
} else {
# "data" is not a hash, field contains the value
$value = $response_info->{data};
}
if (($value eq "0") || ($value eq "1")) {
# If 0 or 1 display as a boolean value
xCAT::SvrUtils::sendmsg($api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{display_name} . ": $value", $callback, $node);
}
else {
# If not a boolean value, display the last component of the attribute
# For example "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore"
# will be displayed as "Restore"
my @attr_value = split('\.', $value);
my $last_component = $attr_value[-1];
my @valid_values = values %{ $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_value} };
if ($value) {
xCAT::SvrUtils::sendmsg($api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{display_name} . " : $last_component", $callback, $node);
my $found = grep(/$value/, @valid_values);
if ($found eq 0) {
# Received data value not expected
xCAT::SvrUtils::sendmsg("WARNING: Unexpected value set: $value", $callback, $node);
xCAT::SvrUtils::sendmsg("WARNING: Valid values: " . join(",", @valid_values), $callback, $node);
}
}
else {
xCAT::SvrUtils::sendmsg("Unable to query value for " . $api_config_info{$::RSPCONFIG_CONFIGURED_API_KEY}{attr_url}, $callback, $node);
}
}
}
else {
xCAT::SvrUtils::sendmsg("Error query RSPCONFIG_API_CONFIG_QUERY_RESPONSE", $callback, $node);
}
}
}
if ($next_status{ $node_info{$node}{cur_status} }) {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
if ($node_info{$node}{method} || $status_info{ $node_info{$node}{cur_status} }{method}) {
gen_send_request($node);
}
} else {
$wait_node_num--;
}
}
#-------------------------------------------------------
=head3 rspconfig_sshcfg_response
Deal with request and response of rspconfig command for sscfg subcommand.
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub rspconfig_sshcfg_response {
my $node = shift;
my $response = shift;
if ($node_info{$node}{cur_status} eq "RSPCONFIG_SSHCFG_REQUEST") {
my $child = xCAT::Utils->xfork;
if (!defined($child)) {
xCAT::SvrUtils::sendmsg("Failed to fork child process for rspconfig sshcfg.", $callback, $node);
sleep(1)
} elsif ($child == 0) {
$async->remove_all;
exit(sshcfg_process($node))
} else {
$child_node_map{$child} = $node;
}
}
if ($next_status{ $node_info{$node}{cur_status} }) {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
if ($node_info{$node}{method} || $status_info{ $node_info{$node}{cur_status} }{method}) {
gen_send_request($node);
}
} else {
$wait_node_num--;
}
}
#-------------------------------------------------------
=head3 rspconfig_process
Append contents of id_rsa.pub file from management node to
the authorized_keys file on BMC
Input:
$node: nodename of current response
=cut
#-------------------------------------------------------
sub sshcfg_process {
my $node = shift;
my $bmcip = $node_info{$node}{bmc};
my $userid = $node_info{$node}{username};
my $userpw = $node_info{$node}{password};
#backup the previous $ENV{DSH_REMOTE_PASSWORD},$ENV{'DSH_FROM_USERID'}
my $bak_DSH_REMOTE_PASSWORD=$ENV{'DSH_REMOTE_PASSWORD'};
my $bak_DSH_FROM_USERID=$ENV{'DSH_FROM_USERID'};
#xCAT::RemoteShellExp->remoteshellexp dependes on environment
#variables $ENV{DSH_REMOTE_PASSWORD},$ENV{'DSH_FROM_USERID'}
$ENV{'DSH_REMOTE_PASSWORD'}=$userpw;
$ENV{'DSH_FROM_USERID'}=$userid;
#send ssh public key from MN to bmc
my $rc=xCAT::RemoteShellExp->remoteshellexp("s",$callback,"/usr/bin/ssh",$bmcip,10);
if ($rc) {
xCAT::SvrUtils::sendmsg("Error copying ssh keys to $bmcip\n", $callback, $node);
}else{
#check whether the ssh keys has been sent successfully
$rc=xCAT::RemoteShellExp->remoteshellexp("t",$callback,"/usr/bin/ssh",$bmcip,10);
if ($rc) {
xCAT::SvrUtils::sendmsg("Testing the ssh connection to $bmcip failed. Please rerun rspconfig command.", $callback, $node);
}
else {
xCAT::SvrUtils::sendmsg("ssh keys copied to $bmcip", $callback, $node);
}
}
#restore env variables
$ENV{'DSH_REMOTE_PASSWORD'}=$bak_DSH_REMOTE_PASSWORD;
$ENV{'DSH_FROM_USERID'}=$bak_DSH_FROM_USERID;
return $rc;
}
#-------------------------------------------------------
=head3 rspconfig_dump_response
Deal with request and response of rspconfig command for dump subcommand.
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub rspconfig_dump_response {
my $node = shift;
my $response = shift;
my $response_info = decode_json $response->content if (defined($response));
if ($node_info{$node}{cur_status} eq "RSPCONFIG_DUMP_LIST_RESPONSE" or $node_info{$node}{cur_status} eq "RSPCONFIG_DUMP_CHECK_RESPONSE") {
my %dump_info = ();
my $gen_check = 0;
foreach my $key_url (keys %{$response_info->{data}}) {
my %content = %{ ${ $response_info->{data} }{$key_url} };
my $id;
if (defined $content{Elapsed}) {
$id = $key_url;
$id =~ s/.*\///g;
if ($node_info{$node}{cur_status} eq "RSPCONFIG_DUMP_CHECK_RESPONSE") {
if ($id eq $node_info{$node}{dump_id}) {
$gen_check = 1;
last;
}
next;
}
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($content{Elapsed});
$mon += 1;
$year += 1900;
my $UTC_time = sprintf ("%02d/%02d/%04d %02d:%02d:%02d", $mon, $mday, $year, $hour, $min, $sec);
$dump_info{$id} = "[$id] Generated: $UTC_time, Size: $content{Size}";
if ($::RSPCONFIG_DUMP_DOWNLOAD_ALL_REQUESTED) {
# Save dump info for later, when dump download all
$node_info{$node}{dump_info}{$id} = "[$id] Generated: $UTC_time, Size: $content{Size}";
}
}
}
xCAT::SvrUtils::sendmsg("$::NO_ATTRIBUTES_RETURNED", $callback, $node) if (!%dump_info and $node_info{$node}{cur_status} eq "RSPCONFIG_DUMP_LIST_RESPONSE");
# If processing the "download all" request, do not print anything now.
# Download function dump_download_process() will be
# printing the output for each downloaded dump
unless ($::RSPCONFIG_DUMP_DOWNLOAD_ALL_REQUESTED) {
foreach my $key ( sort { $a <=> $b } keys %dump_info) {
xCAT::MsgUtils->message("I", { data => ["$node: $dump_info{$key}"] }, $callback) if ($dump_info{$key});
}
}
if (!$gen_check and $node_info{$node}{cur_status} eq "RSPCONFIG_DUMP_CHECK_RESPONSE") {
if (!exists($node_info{$node}{dump_wait_attemp})) {
$node_info{$node}{dump_wait_attemp} = $::RSPCONFIG_DUMP_MAX_RETRY;
}
if ( $node_info{$node}{dump_wait_attemp} > 0) {
$node_info{$node}{dump_wait_attemp} --;
retry_after($node, "RSPCONFIG_DUMP_LIST_REQUEST", $::RSPCONFIG_DUMP_INTERVAL);
unless ($node_info{$node}{dump_wait_attemp} % int(8)) { # display message every 8 iterations of the interval
xCAT::SvrUtils::sendmsg("Still waiting for dump $node_info{$node}{dump_id} to be generated...", $callback, $node);
}
return;
} else {
xCAT::SvrUtils::sendmsg([1,"Could not find dump $node_info{$node}{dump_id} after waiting $::RSPCONFIG_DUMP_WAIT_TOTALTIME seconds."], $callback, $node);
$wait_node_num--;
return;
}
}
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_DUMP_DOWNLOAD_ALL_RESPONSE") {
&dump_download_all_process($node);
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_DUMP_DOWNLOAD_REQUEST") {
my $child = xCAT::Utils->xfork;
if (!defined($child)) {
xCAT::SvrUtils::sendmsg("Failed to fork child process for rspconfig dump download.", $callback, $node);
sleep(1)
} elsif ($child == 0) {
$async->remove_all;
exit(dump_download_process($node))
} else {
$child_node_map{$child} = $node;
}
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
return;
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_DUMP_CREATE_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
if ($response_info->{'data'}) {
my $dump_id = $response_info->{'data'};
if ($next_status{ $node_info{$node}{cur_status} }) {
$node_info{$node}{dump_id} = $dump_id;
xCAT::SvrUtils::sendmsg("Dump requested. Target ID is $dump_id, waiting for BMC to generate...", $callback, $node);
} else {
xCAT::SvrUtils::sendmsg("[$dump_id] success", $callback, $node);
}
} else {
xCAT::SvrUtils::sendmsg([1, "BMC returned $::RESPONSE_OK but no ID was returned. Verify manually on the BMC."], $callback, $node);
$wait_node_num--;
return;
}
}
}
if ($node_info{$node}{cur_status} eq "RSPCONFIG_DUMP_CLEAR_RESPONSE") {
if ($response_info->{'message'} eq $::RESPONSE_OK) {
my $dump_id = $status_info{RSPCONFIG_DUMP_CLEAR_RESPONSE}{argv};
xCAT::MsgUtils->message("I", { data => ["[$dump_id] clear"] }, $callback) unless ($next_status{ $node_info{$node}{cur_status} });
} else {
my $error_msg = "Could not clear BMC diagnostics successfully (". $response_info->{'message'} . ")";
xCAT::MsgUtils->message("W", { data => ["$node: $error_msg"] }, $callback) if ($next_status{ $node_info{$node}{cur_status} });
}
}
if ($next_status{ $node_info{$node}{cur_status} }) {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
if ($node_info{$node}{method} || $status_info{ $node_info{$node}{cur_status} }{method}) {
gen_send_request($node);
} elsif ($status_info{ $node_info{$node}{cur_status} }->{process}) {
$status_info{ $node_info{$node}{cur_status} }->{process}->($node, undef);
}
} else {
$wait_node_num--;
}
}
#-------------------------------------------------------
=head3 dump_download_process
Process of download dump tar.xz.
Input:
$node: nodename of current response
=cut
#-------------------------------------------------------
sub dump_download_process {
my $node = shift;
my $request_url = "$http_protocol://" . $node_info{$node}{bmc};
my $content_login = '{ "data": [ "' . $node_info{$node}{username} .'", "' . $node_info{$node}{password} . '" ] }';
my $content_logout = '{ "data": [ ] }';
my $cjar_id = "/tmp/_xcat_cjar.$node";
my $dump_id;
$dump_id = $status_info{RSPCONFIG_DUMP_DOWNLOAD_REQUEST}{argv} if ($status_info{RSPCONFIG_DUMP_DOWNLOAD_REQUEST}{argv});
$dump_id = $node_info{$node}{dump_id} if ($node_info{$node}{dump_id});
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($::RSPCONFIG_DUMP_CMD_TIME);
$mon += 1;
$year += 1900;
my $formatted_time = sprintf ("%04d%02d%02d-%02d%02d", $year, $mon, $mday, $hour, $min);
my $file_name = $::XCAT_LOG_DUMP_DIR . $formatted_time . "_$node" . "_dump_$dump_id.tar.xz";
my $down_url;
$down_url = $status_info{RSPCONFIG_DUMP_DOWNLOAD_REQUEST}{init_url};
$down_url =~ s/#ID#/$dump_id/g;
my $curl_login_cmd = "curl -c $cjar_id -k -H 'Content-Type: application/json' -X POST $request_url/login -d '" . $content_login . "'";
my $curl_logout_cmd = "curl -b $cjar_id -k -H 'Content-Type: application/json' -X POST $request_url/logout -d '" . $content_logout . "'";
my $curl_dwld_cmd = "curl -J -b $cjar_id -k -H 'Content-Type: application/octet-stream' -X GET $request_url/$down_url -o $file_name";
my $curl_login_result = `$curl_login_cmd -s`;
my $h;
if (!$curl_login_result) {
xCAT::SvrUtils::sendmsg([1, "Did not receive response from OpenBMC after running command '$curl_login_cmd'"], $callback, $node);
return 1;
}
eval { $h = from_json($curl_login_result) };
if ($@) {
xCAT::SvrUtils::sendmsg([1, "Received wrong format response for command '$curl_login_cmd': $curl_login_result)"], $callback, $node);
return 1;
}
if ($h->{message} eq $::RESPONSE_OK) {
xCAT::SvrUtils::sendmsg("Downloading dump $dump_id to $file_name", $callback, $node);
my $curl_dwld_result = `$curl_dwld_cmd -s`;
if (!$curl_dwld_result) {
if ($xcatdebugmode) {
my $debugmsg = "RSPCONFIG_DUMP_DOWNLOAD_REQUEST: CMD: $curl_dwld_cmd";
process_debug_info($node, $debugmsg);
}
`$curl_logout_cmd -s`;
# Verify the file actually got downloaded
if (-e $file_name) {
# Check inside downloaded file, if there is a "Path not found" -> invalid ID
my $grep_cmd = "/usr/bin/grep -a";
my $path_not_found = "Path not found";
my $grep_for_path = `$grep_cmd $path_not_found $file_name`;
if ($grep_for_path) {
xCAT::SvrUtils::sendmsg([1, "Invalid dump $dump_id was specified. Use -l option to list."], $callback, $node);
# Remove downloaded file, nothing useful inside of it
unlink $file_name;
} else {
xCAT::SvrUtils::sendmsg("Downloaded dump $dump_id to $file_name", $callback, $node) if ($::VERBOSE);
}
}
else {
xCAT::SvrUtils::sendmsg([1, "Failed to download dump $dump_id to $file_name. Verify destination directory exists and has correct access permissions."], $callback, $node);
return 1;
}
} else {
xCAT::SvrUtils::sendmsg([1, "Failed to download dump $dump_id :" . $h->{message} . " - " . $h->{data}->{description}], $callback, $node);
return 1;
}
} else {
xCAT::SvrUtils::sendmsg([1, "Unable to login :" . $h->{message} . " - " . $h->{data}->{description}], $callback, $node);
return 1;
}
return 0;
}
#-------------------------------------------------------
=head3 dump_download_all_process
Process to download all dumps
Input:
$node: nodename of current response
=cut
#-------------------------------------------------------
sub dump_download_all_process {
my $node = shift;
# Call dump_download_process for each dump id in the list
foreach my $dump_id (keys %{$node_info{$node}{dump_info}}) {
$node_info{$node}{dump_id} = $dump_id;
&dump_download_process($node);
}
}
#-------------------------------------------------------
=head3 rvitals_response
Deal with response of rvitals command
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub rvitals_response {
my $node = shift;
my $response = shift;
my $response_info = decode_json $response->content;
my $grep_string;
if ($node_info{$node}{cur_status} =~ "RVITALS_LEDS_RESPONSE") {
$grep_string = $status_info{RVITALS_LEDS_RESPONSE}{argv};
} else {
$grep_string = $status_info{RVITALS_RESPONSE}{argv};
}
my $src;
my $content_info;
my @sorted_output;
my %leds = ();
foreach my $key_url (keys %{$response_info->{data}}) {
my %content = %{ ${ $response_info->{data} }{$key_url} };
my $label = (split(/\//, $key_url))[ -1 ];
# replace underscore with space, uppercase the first letter
$label =~ s/_/ /g;
$label =~ s/\b(\w)/\U$1/g;
my $calc_value = undef;
if ($node_info{$node}{cur_status} =~ "RVITALS_LEDS_RESPONSE") {
# Print out Led info
$calc_value = (split(/\./, $content{State}))[-1];
$content_info = $label . ": " . $calc_value ;
if ($key_url =~ "fan0") { $leds{fan0} = $calc_value; }
if ($key_url =~ "fan1") { $leds{fan1} = $calc_value; }
if ($key_url =~ "fan2") { $leds{fan2} = $calc_value; }
if ($key_url =~ "fan3") { $leds{fan3} = $calc_value; }
if ($key_url =~ "front_id") { $leds{front_id} = $calc_value; }
if ($key_url =~ "front_fault") { $leds{front_fault} = $calc_value; }
if ($key_url =~ "front_power") { $leds{front_power} = $calc_value; }
if ($key_url =~ "rear_id") { $leds{rear_id} = $calc_value; }
if ($key_url =~ "rear_fault") { $leds{rear_fault} = $calc_value; }
if ($key_url =~ "rear_power") { $leds{rear_power} = $calc_value; }
} else {
# print out Sensor info
#
# Skip over attributes that are not asked to be printed
#
if ($grep_string =~ "temp") {
unless ( $content{Unit} =~ "DegreesC") { next; }
}
if ($grep_string =~ "voltage") {
unless ( $content{Unit} =~ "Volts") { next; }
}
if ($grep_string =~ "wattage") {
unless ( $content{Unit} =~ "Watts") { next; }
}
if ($grep_string =~ "fanspeed") {
unless ( $content{Unit} =~ "RPMS") { next; }
}
if ($grep_string =~ "power") {
unless ( $content{Unit} =~ "Amperes" || $content{Unit} =~ "Joules" || $content{Unit} =~ "Watts" ) { next; }
}
if ($grep_string =~ "altitude") {
unless ( $content{Unit} =~ "Meters" ) { next; }
}
#
# Calculate the adjusted value based on the scale attribute
#
$calc_value = $content{Value};
if (!defined($calc_value)) {
# Handle the bug where the keyword in the API is lower case value
$calc_value = $content{value};
}
if (defined $content{Scale} and $content{Scale} != 0) {
$calc_value = ($content{Value} * (10 ** $content{Scale}));
}
$content_info = $label . ": " . $calc_value;
if (defined($content{Unit})) {
$content_info = $content_info . " " . $sensor_units{ $content{Unit} };
}
push (@sorted_output, $content_info); #Save output in array
}
}
if ($node_info{$node}{cur_status} =~ "RVITALS_LEDS_RESPONSE") {
if ($grep_string =~ "compact") {
# Compact output for "rbeacon stat" command
$content_info = "Front:$leds{front_id} Rear:$leds{rear_id}";
push (@sorted_output, $content_info);
} else {
# Full output for "rvitals leds" command
my @front_rear = ("Front", "Rear");
my @led_types = ("Power", "Fault", "Identify");
foreach my $i (@front_rear) {
foreach my $led_type (@led_types) {
my $tmp_type = lc($led_type);
$tmp_type = "id" if ($led_type eq "Identify");
my $key_type = lc($i) . "_" . $tmp_type;
$content_info = "LEDs $i $led_type: $leds{$key_type}";
push (@sorted_output, $content_info);
}
}
# Fans
for (my $i = 0; $i < 4; $i++) {
my $tmp_key = "fan" . $i;
$content_info = "LEDs Fan$i: $leds{$tmp_key}";
push (@sorted_output, $content_info);
}
}
}
# If sorted array has any contents, sort it and print it
if (scalar @sorted_output > 0) {
# Sort the output, alpha, then numeric
xCAT::MsgUtils->message("I", { data => ["$node: $_"] }, $callback) foreach (sort natural_sort_cmp @sorted_output);
} else {
xCAT::SvrUtils::sendmsg("$::NO_ATTRIBUTES_RETURNED", $callback, $node);
}
if ($next_status{ $node_info{$node}{cur_status} }) {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
gen_send_request($node);
} else {
$wait_node_num--;
}
return;
}
#-------------------------------------------------------
=head3 rflash_response
Deal with response of rflash command
Input:
$node: nodename of current response
$response: Async return response
=cut
#-------------------------------------------------------
sub rflash_response {
my $node = shift;
my $response = shift;
my $response_info;
if (defined($response)) {
$response_info = decode_json $response->content;
}
my $update_id;
my $update_activation = "Unknown";
my $update_purpose;
my $update_version;
my $rflash_log_file = xCAT::Utils->full_path($node.".log", $::XCAT_LOG_RFLASH_DIR);
open (RFLASH_LOG_FILE_HANDLE, ">> $rflash_log_file");
if ($node_info{$node}{cur_status} eq "RFLASH_LIST_RESPONSE") {
# Get the functional IDs to accurately mark the active running FW
my $functional = get_functional_software_ids($response_info);
if (!%{$functional}) {
# Inform users that the older firmware levels does not correctly reflect Active version
xCAT::SvrUtils::sendmsg("WARNING, The current firmware is unable to detect running firmware version.", $callback, $node);
}
# Display "list" option header and data
xCAT::SvrUtils::sendmsg("ID Purpose State Version", $callback, $node);
xCAT::SvrUtils::sendmsg("-" x 55, $callback, $node);
foreach my $key_url (keys %{$response_info->{data}}) {
# Initialize values to Unknown for each loop, incase they are not defined in the BMC
$update_activation = "Unknown";
$update_purpose = "Unknown";
$update_version = "Unknown";
my %content = %{ ${ $response_info->{data} }{$key_url} };
$update_id = (split(/\//, $key_url))[ -1 ];
if (defined($content{Version}) and $content{Version}) {
$update_version = $content{Version};
}
else {
# Entry has no Version attribute, skip listing it
next;
}
if ($xcatdebugmode) {
# Only print if xcatdebugmode is set and XCATBYPASS
print "\n\n================================= XCATBYPASS DEBUG START =================================\n";
print "==> KEY_URL=$key_url\n";
print "==> VERSION=$content{Version}\n";
print "==> Dump out JSON data:\n";
print Dumper(%content);
print "================================= XCATBYPASS DEBUG END =================================\n";
}
if (defined($content{Activation}) and $content{Activation}) {
$update_activation = (split(/\./, $content{Activation}))[ -1 ];
}
if (defined($content{Purpose}) and $content{Purpose}) {
$update_purpose = (split(/\./, $content{Purpose}))[ -1 ];
}
my $update_priority = -1;
# Just check defined, because priority=0 is a valid value
if (defined($content{Priority})) {
$update_priority = (split(/\./, $content{Priority}))[ -1 ];
}
# Add indicators to the active firmware
if (exists($functional->{$update_id}) ) {
#
# If the firmware ID exists in the hash, this indicates the really active running FW
#
$update_activation = $update_activation . "(*)";
} elsif ($update_priority == 0) {
# Priority attribute of 0 indicates the firmware to be activated on next boot
my $indicator = "(+)";
if (!%{$functional}) {
# cannot detect, so mark firmware as Active
$indicator = "(*)";
}
$update_activation = $update_activation . $indicator;
}
xCAT::SvrUtils::sendmsg(sprintf("%-8s %-7s %-10s %s", $update_id, $update_purpose, $update_activation, $update_version), $callback, $node);
}
xCAT::SvrUtils::sendmsg("", $callback, $node); #Separate output in case more than 1 endpoint
}
if ($node_info{$node}{cur_status} eq "RFLASH_DELETE_CHECK_STATE_RESPONSE") {
# Verify selected FW ID is not active. If active, display error message,
# If not active, proceed to delete
my $to_delete_id = (split ('/', $status_info{RFLASH_DELETE_IMAGE_REQUEST}{init_url}))[4];
# Get the functional IDs to determint if active running FW can be deleted
my $functional = get_functional_software_ids($response_info);
if ((!%{$functional}) ||
(!exists($functional->{$to_delete_id}))) {
# Can not figure out if FW functional, attempt to delete anyway.
# Worst case, BMC will not allow FW deletion if we are wrong
# OR
# FW is not active, it can be deleted. Send the request to do the deletion
$next_status{"RFLASH_DELETE_CHECK_STATE_RESPONSE"} = "RFLASH_DELETE_IMAGE_REQUEST";
$next_status{"RFLASH_DELETE_IMAGE_REQUEST"} = "RFLASH_DELETE_IMAGE_RESPONSE";
} else {
foreach my $key_url (keys %{$response_info->{data}}) {
$update_id = (split(/\//, $key_url))[ -1 ];
if ($update_id ne $to_delete_id) {
# Not a match on the id, try next one
next;
}
# Initialize values to Unknown for each loop, incase they are not defined in the BMC
$update_activation = "Unknown";
$update_purpose = "Unknown";
$update_version = "Unknown";
my %content = %{ ${ $response_info->{data} }{$key_url} };
if (defined($content{Version}) and $content{Version}) {
$update_version = $content{Version};
} else {
# Entry has no Version attribute, skip listing it
next;
}
if (defined($content{Purpose}) and $content{Purpose}) {
$update_purpose = (split(/\./, $content{Purpose}))[ -1 ];
}
my $update_priority = -1;
# Just check defined, because priority=0 is a valid value
if (defined($content{Priority})) {
$update_priority = (split(/\./, $content{Priority}))[ -1 ];
}
if ($update_purpose eq "BMC") {
# Active BMC firmware can not be deleted
xCAT::SvrUtils::sendmsg([1, "Deleting currently active BMC firmware is not supported"], $callback, $node);
$wait_node_num--;
return;
} elsif ($update_purpose eq "Host") {
# Active Host firmware can NOT be deleted if host is ON
# Active Host firmware can be deleted if host is OFF
# Send the request to check Host state
$next_status{"RFLASH_DELETE_CHECK_STATE_RESPONSE"} = "RPOWER_STATUS_REQUEST";
$next_status{"RPOWER_STATUS_REQUEST"} = "RPOWER_STATUS_RESPONSE";
# Set special argv to fw_delete if Host is off
$status_info{RPOWER_STATUS_RESPONSE}{argv} = "fw_delete";
last;
} else {
xCAT::SvrUtils::sendmsg([1, "Unable to determine the purpose of the firmware to delete"], $callback, $node);
# Can not figure out if Host or BMC, attempt to delete anyway.
# Worst case, BMC will not allow FW deletion if we are wrong
$next_status{"RFLASH_DELETE_CHECK_STATE_RESPONSE"} = "RFLASH_DELETE_IMAGE_REQUEST";
$next_status{"RFLASH_DELETE_IMAGE_REQUEST"} = "RFLASH_DELETE_IMAGE_RESPONSE";
last;
}
}
}
}
if ($node_info{$node}{cur_status} eq "RFLASH_FILE_UPLOAD_REQUEST") {
#
# Special processing for file upload
#
# Unable to form a proper file upload request to the BMC, it fails with: 405 Method Not Allowed
# For now, always upload using curl commands.
#
# TODO: Remove this block when proper request can be generated
#
if ($::UPLOAD_FILE){
$fw_tar_files{$::UPLOAD_FILE}=$::UPLOAD_FILE_VERSION;
}
if ($::UPLOAD_PNOR){
$fw_tar_files{$::UPLOAD_PNOR}=$::UPLOAD_PNOR_VERSION;
}
if (%fw_tar_files) {
my $child = xCAT::Utils->xfork;
if (!defined($child)) {
xCAT::SvrUtils::sendmsg("Failed to fork child process to upload firmware image.", $callback, $node);
sleep(1)
} elsif ($child == 0) {
$async->remove_all;
exit(rflash_upload($node, $callback));
} else {
$child_node_map{$child} = $node;
}
}
}
if ($node_info{$node}{cur_status} eq "RFLASH_UPDATE_ACTIVATE_RESPONSE") {
my $flash_started_msg = "rflash $::UPLOAD_FILE_VERSION started, please wait...";
if ($::VERBOSE) {
xCAT::SvrUtils::sendmsg("$flash_started_msg", $callback, $node);
}
print RFLASH_LOG_FILE_HANDLE "$flash_started_msg\n";
}
if ($node_info{$node}{cur_status} eq "RFLASH_UPDATE_HOST_ACTIVATE_RESPONSE") {
my $flash_started_msg = "rflash $::UPLOAD_PNOR_VERSION started, please wait...";
if ($::VERBOSE) {
xCAT::SvrUtils::sendmsg("$flash_started_msg", $callback, $node);
}
print RFLASH_LOG_FILE_HANDLE "$flash_started_msg\n";
}
if ($node_info{$node}{cur_status} eq "RFLASH_SET_PRIORITY_RESPONSE") {
print "Update priority has been set";
}
if ($node_info{$node}{cur_status} eq "RFLASH_UPDATE_CHECK_STATE_RESPONSE") {
my %activation_state;
my %progress_state;
my %priority_state;
my %update_ids;
my $update_res=0;
my $version;
foreach my $key_url (keys %{$response_info->{data}}) {
if ($::UPLOAD_ACTIVATE_STREAM or $::UPLOAD_AND_ACTIVATE) {
my %content = %{${$response_info->{data}}{$key_url}};
$version = $content{Version};
if (defined($version)) {
if ($version ne $::UPLOAD_FILE_VERSION and $version ne $::UPLOAD_PNOR_VERSION) {
next;
}
# Get values of some attributes to determine activation status
$activation_state{$version} = $content{Activation};
$progress_state{$version} = $content{Progress};
$priority_state{$version} = $content{Priority};
$update_ids{$version} = (split(/\//, $key_url))[ -1 ];
}
} else {
# This is for -a <ID> option
$version = "default";
if ($key_url eq "Activation") {
$activation_state{$version} = ${ $response_info->{data} }{$key_url};
}
if ($key_url eq "Progress") {
$progress_state{$version} = ${ $response_info->{data} }{$key_url};
}
if ($key_url eq "Priority") {
$priority_state{$version} = ${ $response_info->{data} }{$key_url};
}
}
}
my $firm_msg;
my $version_num = 0;
my $rsp;
my $length = keys %activation_state;
foreach my $firm_version (keys %activation_state) {
if ($firm_version eq "default") {
$firm_msg = "Firmware";
} else {
$firm_msg = "Firmware $firm_version";
}
if ($activation_state{$firm_version} =~ /Software.Activation.Activations.Failed/) {
# Activation failed. Report error and exit
my $flash_failed_msg = "$firm_msg activation failed.";
xCAT::SvrUtils::sendmsg([1,"$flash_failed_msg"], $callback, $node);
$update_res = 1;
print RFLASH_LOG_FILE_HANDLE "$flash_failed_msg\n";
$node_info{$node}{rst} = "$flash_failed_msg";
} elsif ($activation_state{$firm_version} =~ /Software.Activation.Activations.Active/) {
if (scalar($priority_state{$firm_version}) == 0) {
$version_num ++;
my $flash_success_msg = "$node: $firm_msg activation successful.";
push @{ $rsp->{data} },$flash_success_msg;
print RFLASH_LOG_FILE_HANDLE "$flash_success_msg\n";
# Activation state of active and priority of 0 indicates the activation has been completed
if ( $length == $version_num ) {
xCAT::MsgUtils->message("I", $rsp, $callback) if ($rsp);
$node_info{$node}{rst} = "$flash_success_msg";
if (!$::UPLOAD_ACTIVATE_STREAM) {
$wait_node_num--;
return;
}
else{
$next_status{ $node_info{$node}{cur_status} } = "RPOWER_BMCREBOOT_REQUEST";
}
}
} else {
# Activation state of active and priority of non 0 - need to just set priority to 0 to activate
print "$firm_version update is already active, just need to set priority to 0\n";
if ($::UPLOAD_ACTIVATE_STREAM) {
$status_info{RFLASH_SET_PRIORITY_REQUEST}{init_url} =
$::SOFTWARE_URL . "/$update_ids{$firm_version}/attr/Priority";
}
$next_status{ $node_info{$node}{cur_status} } = "RFLASH_SET_PRIORITY_REQUEST";
}
} elsif ($activation_state{$firm_version} =~ /Software.Activation.Activations.Activating/) {
my $activating_progress_msg = "Activating $firm_msg... $progress_state{$firm_version}\%";
if ($::VERBOSE) {
xCAT::SvrUtils::sendmsg("$activating_progress_msg", $callback, $node);
}
print RFLASH_LOG_FILE_HANDLE "$activating_progress_msg\n";
# Activation still going, sleep for a bit, then print the progress value
# Set next state to come back here to chect the activation status again.
retry_after($node, "RFLASH_UPDATE_CHECK_STATE_REQUEST", 15);
return;
}
}
if ($update_res) {
close (RFLASH_LOG_FILE_HANDLE);
$wait_node_num--;
return;
}
}
if ($node_info{$node}{cur_status} eq "RFLASH_UPDATE_CHECK_ID_RESPONSE") {
my $activation_state;
my $progress_state;
my $priority_state;
my $found_match = 0;
my $found_pnor_match = 0;
my $debugmsg;
if ($xcatdebugmode) {
$debugmsg = "CHECK_ID_RESPONSE: Looking for software ID: $::UPLOAD_FILE_VERSION $::UPLOAD_PNOR_VERSION...";
process_debug_info($node, $debugmsg);
}
# Look through all the software entries and find the id of the one that matches
# the version of the uploaded file. Once found, set up request/response hash entries
# to activate that image.
foreach my $key_url (keys %{$response_info->{data}}) {
my %content = %{ ${ $response_info->{data} }{$key_url} };
$update_id = (split(/\//, $key_url))[ -1 ];
if (defined($content{Version}) and $content{Version}) {
$update_version = $content{Version};
if ($xcatdebugmode) {
$debugmsg = "CHECK_ID_RESPONSE: key_url=$key_url version=$update_version";
process_debug_info($node, $debugmsg);
}
if ($update_version eq $::UPLOAD_FILE_VERSION) {
$found_match = 1;
# Found a match of uploaded file version with the image in software/enumerate
# If we have a saved expected hash ID, compare it to the one just found
if ($::UPLOAD_FILE_HASH_ID && ($::UPLOAD_FILE_HASH_ID ne $update_id)) {
xCAT::SvrUtils::sendmsg([1,"Firmware uploaded but activation cancelled due to hash ID mismatch. $update_id does not match expected $::UPLOAD_FILE_HASH_ID. Verify BMC firmware is at the latest level."], $callback, $node);
$wait_node_num--;
return; # Stop processing for this node, do not activate. Firmware shold be left in "Ready" state.
}
# Set the image id for the activation request
$status_info{RFLASH_UPDATE_ACTIVATE_REQUEST}{init_url} =
$::SOFTWARE_URL . "/$update_id/attr/RequestedActivation";
$status_info{RFLASH_UPDATE_CHECK_STATE_REQUEST}{init_url} =
$::SOFTWARE_URL . "/enumerate";
$status_info{RFLASH_SET_PRIORITY_REQUEST}{init_url} =
$::SOFTWARE_URL . "/$update_id/attr/Priority";
my $upload_success_msg = "Firmware upload successful. Attempting to activate firmware: $::UPLOAD_FILE_VERSION (ID: $update_id)";
xCAT::SvrUtils::sendmsg("$upload_success_msg", $callback, $node);
print RFLASH_LOG_FILE_HANDLE "$upload_success_msg\n";
my $timestamp = localtime();
print RFLASH_LOG_FILE_HANDLE "$timestamp ===================$upload_success_msg===================\n";
} elsif ($update_version eq $::UPLOAD_PNOR_VERSION) {
$found_pnor_match = 1;
if ($::UPLOAD_PNOR_HASH_ID && ($::UPLOAD_PNOR_HASH_ID ne $update_id)) {
xCAT::SvrUtils::sendmsg([1,"Firmware uploaded but activation cancelled due to hash ID mismatch. $update_id does not match expected $::UPLOAD_PNOR_HASH_ID. Verify BMC firmware is at the latest level."], $callback, $node);
$wait_node_num--;
return;
}
$status_info{RFLASH_UPDATE_HOST_ACTIVATE_REQUEST}{init_url} =
$::SOFTWARE_URL . "/$update_id/attr/RequestedActivation";
$status_info{RFLASH_UPDATE_CHECK_STATE_REQUEST}{init_url} =
$::SOFTWARE_URL . "/enumerate";
$status_info{RFLASH_SET_PRIORITY_REQUEST}{init_url} =
$::SOFTWARE_URL . "/$update_id/attr/Priority";
my $upload_success_msg = "Firmware upload successful. Attempting to activate firmware: $::UPLOAD_PNOR_VERSION (ID: $update_id)";
xCAT::SvrUtils::sendmsg("$upload_success_msg", $callback, $node);
print RFLASH_LOG_FILE_HANDLE "$upload_success_msg\n";
my $timestamp = localtime();
print RFLASH_LOG_FILE_HANDLE "$timestamp ===================$upload_success_msg===================\n";
}
}
}
if ($::UPLOAD_ACTIVATE_STREAM and (!$found_match or !$found_pnor_match) or !$found_match) {
if (!exists($node_info{$node}{upload_wait_attemp})) {
$node_info{$node}{upload_wait_attemp} = $::UPLOAD_WAIT_ATTEMPT;
}
my $upload_file_version = "";
if (!$found_match) {
$upload_file_version = $::UPLOAD_FILE_VERSION;
} else {
$upload_file_version = $::UPLOAD_PNOR_VERSION;
}
if($node_info{$node}{upload_wait_attemp} > 0) {
$node_info{$node}{upload_wait_attemp} --;
my $retry_msg = "Could not find ID for firmware $upload_file_version to activate, waiting $::UPLOAD_WAIT_INTERVAL seconds and retry...";
if ($::VERBOSE) {
xCAT::SvrUtils::sendmsg("$retry_msg", $callback, $node);
}
print RFLASH_LOG_FILE_HANDLE "$retry_msg\n";
close (RFLASH_LOG_FILE_HANDLE);
retry_after($node, "RFLASH_UPDATE_CHECK_ID_REQUEST", $::UPLOAD_WAIT_INTERVAL);
return;
} else {
my $no_firmware_msg = "Could not find firmware $upload_file_version after waiting $::UPLOAD_WAIT_TOTALTIME seconds.";
xCAT::SvrUtils::sendmsg([1,"$no_firmware_msg"], $callback, $node);
print RFLASH_LOG_FILE_HANDLE "$no_firmware_msg\n";
close (RFLASH_LOG_FILE_HANDLE);
$node_info{$node}{rst} = "$no_firmware_msg";
$wait_node_num--;
return;
}
}
}
if ($node_info{$node}{cur_status} eq "RFLASH_DELETE_IMAGE_RESPONSE") {
xCAT::SvrUtils::sendmsg("Firmware removed", $callback, $node);
}
close (RFLASH_LOG_FILE_HANDLE);
if ($next_status{ $node_info{$node}{cur_status} }) {
$node_info{$node}{cur_status} = $next_status{ $node_info{$node}{cur_status} };
if ($node_info{$node}{method} || $status_info{ $node_info{$node}{cur_status} }{method}) {
gen_send_request($node);
}
} else {
$wait_node_num--;
}
return;
}
sub rflash_upload {
my ($node, $callback) = @_;
my $request_url = "$http_protocol://" . $node_info{$node}{bmc};
my $content_login = '{ "data": [ "' . $node_info{$node}{username} .'", "' . $node_info{$node}{password} . '" ] }';
my $content_logout = '{ "data": [ ] }';
my $cjar_id = "/tmp/_xcat_cjar.$node";
my %curl_upload_cmds;
# curl commands
my $curl_login_cmd = "curl -c $cjar_id -k -H 'Content-Type: application/json' -X POST $request_url/login -d '" . $content_login . "'";
my $curl_logout_cmd = "curl -b $cjar_id -k -H 'Content-Type: application/json' -X POST $request_url/logout -d '" . $content_logout . "'";
my $curl_check_cpu_dd_cmd = "curl -b $cjar_id -k -H 'Content-Type: application/json' -X GET $request_url/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu0 | grep Version | cut -d: -f2";
if (%fw_tar_files) {
foreach my $key (keys %fw_tar_files) {
my $curl_upload_cmd = "curl -b $cjar_id -k -H 'Content-Type: application/octet-stream' -X PUT -T " . $key . " $request_url/upload/image/";
$curl_upload_cmds{$key}=$curl_upload_cmd;
}
}
my $rflash_log_file = xCAT::Utils->full_path($node.".log", $::XCAT_LOG_RFLASH_DIR);
open (RFLASH_LOG_FILE_HANDLE, ">> $rflash_log_file");
# Try to login
my $curl_login_result = `$curl_login_cmd -s`;
my $h;
if (!$curl_login_result) {
my $curl_error = "$::FAILED_UPLOAD_MSG. Did not receive response from OpenBMC after running command '$curl_login_cmd'";
xCAT::SvrUtils::sendmsg([1, "$curl_error"], $callback, $node);
print RFLASH_LOG_FILE_HANDLE "$curl_error\n";
$node_info{$node}{rst} = "$curl_error";
return 1;
}
eval { $h = from_json($curl_login_result) }; # convert command output to hash
if ($@) {
my $curl_error = "$::FAILED_UPLOAD_MSG. Received wrong format response for command '$curl_login_cmd': $curl_login_result";
xCAT::SvrUtils::sendmsg([1, "$curl_error"], $callback, $node);
# Before writing error to log, make it a single line
$curl_error =~ tr{\n}{ };
print RFLASH_LOG_FILE_HANDLE "$curl_error\n";
$node_info{$node}{rst} = "$curl_error";
return 1;
}
if ($h->{message} eq $::RESPONSE_OK) {
if(%curl_upload_cmds){
# Before uploading file, check CPU DD version
my $curl_dd_check_result = `$curl_check_cpu_dd_cmd`;
if ($curl_dd_check_result =~ "20") {
# Display warning the only certain firmware versions are supported on DD 2.0
xCAT::SvrUtils::sendmsg("Warning: DD 2.0 processor detected on this node, should not have firmware > ibm-v2.0-0-r13.6 (BMC) and > v1.19_1.94 (Host).", $callback, $node);
}
if ($curl_dd_check_result =~ "21") {
if ($::VERBOSE) {
xCAT::SvrUtils::sendmsg("DD 2.1 processor", $callback, $node);
}
}
while((my $file,my $version)=each(%fw_tar_files)){
my $uploading_msg = "Uploading $file ...";
my $upload_cmd = $curl_upload_cmds{$file};
# Login successfull, upload the file
if ($::VERBOSE) {
xCAT::SvrUtils::sendmsg("$uploading_msg", $callback, $node);
}
print RFLASH_LOG_FILE_HANDLE "$uploading_msg\n";
if ($xcatdebugmode) {
my $debugmsg = "RFLASH_FILE_UPLOAD_RESPONSE: CMD: $upload_cmd";
process_debug_info($node, $debugmsg);
}
my $curl_upload_result = `$upload_cmd`;
if (!$curl_upload_result) {
my $curl_error = "$::FAILED_UPLOAD_MSG. Did not receive response from OpenBMC after running command '$upload_cmd'";
xCAT::SvrUtils::sendmsg([1, "$curl_error"], $callback, $node);
print RFLASH_LOG_FILE_HANDLE "$curl_error\n";
$node_info{$node}{rst} = "$curl_error";
return 1;
}
eval { $h = from_json($curl_upload_result) }; # convert command output to hash
if ($@) {
my $curl_error = "$::FAILED_UPLOAD_MSG. Received wrong format response from command '$upload_cmd': $curl_upload_result";
xCAT::SvrUtils::sendmsg([1, "$curl_error"], $callback, $node);
# Before writing error to log, make it a single line
$curl_error =~ tr{\n}{ };
print RFLASH_LOG_FILE_HANDLE "$curl_error\n";
$node_info{$node}{rst} = "$curl_error";
return 1;
}
if ($h->{message} eq $::RESPONSE_OK) {
# Upload successful, display message
my $upload_success_msg = "Firmware upload successful. Use -l option to list.";
unless ($::UPLOAD_AND_ACTIVATE or $::UPLOAD_ACTIVATE_STREAM) {
xCAT::SvrUtils::sendmsg("$upload_success_msg", $callback, $node);
}
#Put a delay of 3 seconds to allow time for the BMC to untar the file we just uploaded
if (defined($::UPLOAD_ACTIVATE_STREAM)){
sleep 3;
}
print RFLASH_LOG_FILE_HANDLE "$upload_success_msg\n";
# Try to logoff, no need to check result, as there is nothing else to do if failure
} else {
my $upload_fail_msg = $::FAILED_UPLOAD_MSG . " $file :" . $h->{message} . " - " . $h->{data}->{description};
xCAT::SvrUtils::sendmsg("$upload_fail_msg", $callback, $node);
print RFLASH_LOG_FILE_HANDLE "$upload_fail_msg\n";
close (RFLASH_LOG_FILE_HANDLE);
$node_info{$node}{rst} = "$upload_fail_msg";
return 1;
}
}
}
# Try to logoff, no need to check result, as there is nothing else to do if failure
my $curl_logout_result = `$curl_logout_cmd -s`;
}
else {
my $unable_login_msg = "Unable to login :" . $h->{message} . " - " . $h->{data}->{description};
xCAT::SvrUtils::sendmsg("$unable_login_msg", $callback, $node);
print RFLASH_LOG_FILE_HANDLE "$unable_login_msg\n";
close (RFLASH_LOG_FILE_HANDLE);
$node_info{$node}{rst} = "$unable_login_msg";
return 1;
}
close (RFLASH_LOG_FILE_HANDLE);
return 0;
}
#-------------------------------------------------------
=head3 is_valid_config_api
Verify passed in subcommand is defined in the api_config_info
Input:
$subcommand: subcommand to verify
$callback: callback for message display
Output:
returns index into the hash of the $subcommand
returns -1 if no match
=cut
#-------------------------------------------------------
sub is_valid_config_api {
my ($subcommand, $callback) = @_;
my $subcommand_key = $subcommand;
my $subcommand_value;
if ($subcommand =~ /^(\w+)=(.*)/) {
$subcommand_key = $1;
$subcommand_value = $2;
}
foreach my $config_subcommand (keys %api_config_info) {
if ($subcommand_key eq $api_config_info{$config_subcommand}{subcommand}) {
return $config_subcommand;
}
}
return -1;
}
#-------------------------------------------------------
=head3 build_config_api_usage
Build usage string from the api_config_info
Input:
$callback: callback for message display
$requested_command: command for the usage generation
Output:
returns usage string
=cut
#-------------------------------------------------------
sub build_config_api_usage {
my $callback = shift;
my $requested_command = shift;
my $command = "";
my $subcommand = "";
my $type = "";
my $usage_string = "";
my $attr_values = "";
foreach my $config_subcommand (keys %api_config_info) {
$command = "";
$subcommand = "";
$type = "";
$attr_values = "";
if ($api_config_info{$config_subcommand}{command} eq $requested_command) {
$command = $api_config_info{$config_subcommand}{command};
$subcommand = $api_config_info{$config_subcommand}{subcommand};
$type = $api_config_info{$config_subcommand}{type};
$usage_string .= " $command <noderange> $subcommand" . "\n";
if ($type eq "boolean") {
$usage_string .= " $command <noderange> $subcommand={0|1}" . "\n";
}
if (($type eq "attribute") || ($type eq "action_attribute")) {
foreach my $attribute_value (keys %{ $api_config_info{$config_subcommand}{attr_value} }) {
$attr_values .= $attribute_value . "|"
}
chop $attr_values;
$usage_string .= " $command <noderange> $subcommand={" . $attr_values . "}". "\n";
}
}
}
return $usage_string;
}
1;