mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-05-29 17:23:08 +00:00
Merge pull request #4645 from xuweibj/rflash_python
rflash for openbmc on python, including options a|c|l|delete|u
This commit is contained in:
commit
0982cdf282
@ -1,18 +1,63 @@
|
||||
from xcatagent import base
|
||||
import os
|
||||
import time
|
||||
import sys
|
||||
import gevent
|
||||
|
||||
import utils
|
||||
import xcat_exception
|
||||
import rest
|
||||
import openbmc_rest
|
||||
|
||||
HTTP_PROTOCOL = "https://"
|
||||
PROJECT_URL = "/xyz/openbmc_project"
|
||||
|
||||
RESULT_OK = 'ok'
|
||||
RESULT_FAIL = 'fail'
|
||||
|
||||
DEBUGMODE = False
|
||||
VERBOSE = False
|
||||
|
||||
all_nodes_result = {}
|
||||
|
||||
# global variables of rflash
|
||||
RFLASH_OPTIONS = {
|
||||
"-a" : "activate",
|
||||
"--activate" : "activate",
|
||||
"-c" : "check",
|
||||
"--check" : "check",
|
||||
"-d" : "direcory",
|
||||
"--delete" : "delete",
|
||||
"-l" : "list",
|
||||
"--list" : "list",
|
||||
"-u" : "upload",
|
||||
"--upload" : "upload",
|
||||
}
|
||||
|
||||
RFLASH_URLS = {
|
||||
"activate" : {
|
||||
"url" : PROJECT_URL + "/software/#ACTIVATE_ID#/attr/RequestedActivation",
|
||||
"field" : "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
|
||||
},
|
||||
"delete" : {
|
||||
"url" : PROJECT_URL + "/software/#DELETE_ID#/action/Delete",
|
||||
"field" : [],
|
||||
},
|
||||
"upload" : {
|
||||
"url" : "/upload/image/",
|
||||
},
|
||||
"priority" : {
|
||||
"url" : PROJECT_URL + "/software/#PRIORITY_ID#/attr/Priority",
|
||||
"field" : False,
|
||||
}
|
||||
}
|
||||
|
||||
XCAT_LOG_DIR = "/var/log/xcat"
|
||||
XCAT_LOG_RFLASH_DIR = XCAT_LOG_DIR + "/rflash/"
|
||||
|
||||
# global variable of firmware information
|
||||
FIRM_URL = PROJECT_URL + "/software/enumerate"
|
||||
|
||||
# global variables of rpower
|
||||
POWER_SET_OPTIONS = ('on', 'off', 'bmcreboot', 'softoff')
|
||||
POWER_GET_OPTIONS = ('bmcstate', 'state', 'stat', 'status')
|
||||
|
||||
@ -68,21 +113,383 @@ class OpenBMC(base.BaseDriver):
|
||||
def __init__(self, messager, name, node_info):
|
||||
super(OpenBMC, self).__init__(messager)
|
||||
self.node = name
|
||||
for key, value in node_info.items() :
|
||||
for key, value in node_info.items():
|
||||
setattr(self, key, value)
|
||||
global DEBUGMODE
|
||||
self.client = rest.RestSession(messager, DEBUGMODE)
|
||||
self.client = openbmc_rest.OpenBMCRest(name, messager, DEBUGMODE)
|
||||
|
||||
def _login(self) :
|
||||
def _login(self):
|
||||
""" Login
|
||||
:raise: error message if failed
|
||||
"""
|
||||
url = HTTP_PROTOCOL + self.bmcip + '/login'
|
||||
data = { "data": [ self.username, self.password ] }
|
||||
self.client.request('POST', url, OpenBMC.headers, data, self.node, 'login')
|
||||
self.client.request('POST', url, OpenBMC.headers, data, 'login')
|
||||
return RESULT_OK
|
||||
|
||||
def _set_power_onoff(self, subcommand) :
|
||||
def _msg_process_rflash (self, msg, update_dict, checkv):
|
||||
"""deal with msg during rflash
|
||||
:param msg: the msg want to process
|
||||
"""
|
||||
if not checkv:
|
||||
self.messager.info('%s: %s' % (self.node, msg))
|
||||
elif VERBOSE:
|
||||
self.messager.info('%s: %s' % (self.node, msg))
|
||||
self.rflash_log_handle.writelines(msg + '\n')
|
||||
self.rflash_log_handle.flush()
|
||||
if update_dict:
|
||||
utils.update2Ddict(update_dict, self.node, 'result', [msg])
|
||||
|
||||
def _firm_info(self, status):
|
||||
"""List firmware information including additional
|
||||
called by rflash check and rinv firm
|
||||
:returns: firmware information
|
||||
"""
|
||||
firm_output = []
|
||||
try:
|
||||
(has_functional, firm_info) = self._get_firm_info(status)
|
||||
except (xcat_exception.SelfServerException,
|
||||
xcat_exception.SelfClientException) as e:
|
||||
firm_output.append(e.message)
|
||||
return firm_output
|
||||
|
||||
keys = firm_info.keys()
|
||||
keys.sort()
|
||||
for key in keys:
|
||||
flag = ''
|
||||
if 'is_functional' in firm_info[key]:
|
||||
flag = '*'
|
||||
elif ('Priority' in firm_info[key] and
|
||||
firm_info[key]['Priority'] == '0'):
|
||||
if not has_functional:
|
||||
flag = '*'
|
||||
else:
|
||||
flag = '+'
|
||||
|
||||
if not flag and not VERBOSE:
|
||||
continue
|
||||
|
||||
firm_output.append('%s Firmware Product: %s (%s)%s' %
|
||||
(firm_info[key]['Purpose'],
|
||||
firm_info[key]['Version'],
|
||||
firm_info[key]['Activation'], flag))
|
||||
if 'ExtendedVersion' in firm_info[key]:
|
||||
extendeds = firm_info[key]['ExtendedVersion'].split(',')
|
||||
extendeds.sort()
|
||||
for extended in extendeds:
|
||||
firm_output.append('%s Firmware Product: ' \
|
||||
'-- additional info: %s' % \
|
||||
(firm_info[key]['Purpose'], extended))
|
||||
|
||||
return firm_output
|
||||
|
||||
def _get_firm_info(self, status):
|
||||
"""get firmware information
|
||||
:param status: current status
|
||||
:returns: firmware version information
|
||||
"""
|
||||
firm_info = {}
|
||||
has_functional = False
|
||||
url = HTTP_PROTOCOL + self.bmcip + FIRM_URL
|
||||
response = self.client.request('GET', url, OpenBMC.headers, '', status)
|
||||
functional_url = PROJECT_URL + '/software/functional'
|
||||
|
||||
for key in response['data']:
|
||||
key_id = key.split('/')[-1]
|
||||
if key_id == 'functional':
|
||||
for endpoint in response['data'][key]['endpoints']:
|
||||
purpose = response['data'][endpoint]['Purpose'].split('.')[-1]
|
||||
key_sort = purpose + '-' + endpoint.split('/')[-1]
|
||||
|
||||
utils.update2Ddict(firm_info, key_sort, 'is_functional', True)
|
||||
has_functional = True
|
||||
|
||||
if 'Version' in response['data'][key]:
|
||||
purpose = response['data'][key]['Purpose'].split('.')[-1]
|
||||
key_sort = purpose + '-' + key_id
|
||||
if (functional_url in response['data'] and
|
||||
key in response['data'][functional_url]['endpoints']):
|
||||
utils.update2Ddict(firm_info, key_sort, 'is_functional', True)
|
||||
utils.update2Ddict(firm_info, key_sort, 'Version',
|
||||
response['data'][key]['Version'])
|
||||
utils.update2Ddict(firm_info, key_sort, 'Purpose', purpose)
|
||||
utils.update2Ddict(firm_info, key_sort, 'Activation',
|
||||
response['data'][key]['Activation'].split('.')[-1])
|
||||
if 'Priority' in response['data'][key]:
|
||||
utils.update2Ddict(firm_info, key_sort, 'Priority',
|
||||
str(response['data'][key]['Priority']))
|
||||
if 'ExtendedVersion' in response['data'][key]:
|
||||
utils.update2Ddict(firm_info, key_sort, 'ExtendedVersion',
|
||||
response['data'][key]['ExtendedVersion'])
|
||||
if 'Progress' in response['data'][key]:
|
||||
utils.update2Ddict(firm_info, key_sort, 'Progress',
|
||||
response['data'][key]['Progress'])
|
||||
|
||||
return (has_functional, firm_info)
|
||||
|
||||
def _get_firm_id(self, firm_list):
|
||||
"""get firmware id
|
||||
:param firm_list: the list of firmware versions
|
||||
:return: result and info list
|
||||
"""
|
||||
firm_ids = []
|
||||
url = HTTP_PROTOCOL + self.bmcip + FIRM_URL
|
||||
|
||||
for i in range(6):
|
||||
try:
|
||||
response = self.client.request('GET', url, OpenBMC.headers,
|
||||
'', 'rflash_check_id')
|
||||
except (xcat_exception.SelfServerException,
|
||||
xcat_exception.SelfClientException) as e:
|
||||
self._msg_process_rflash(e.message, all_nodes_result, False)
|
||||
return (RESULT_FAIL, [])
|
||||
|
||||
for key in response['data']:
|
||||
if 'Version' in response['data'][key]:
|
||||
if response['data'][key]['Version'] in firm_list:
|
||||
firm_id = key.split('/')[-1]
|
||||
upload_msg = 'Firmware upload successful. ' \
|
||||
'Attempting to activate firmware: ' \
|
||||
'%s (ID: %s)' % \
|
||||
(response['data'][key]['Version'], firm_id)
|
||||
self._msg_process_rflash(upload_msg, {}, False)
|
||||
firm_ids.append(firm_id)
|
||||
firm_list.remove(response['data'][key]['Version'])
|
||||
|
||||
if firm_list:
|
||||
for firm_ver in firm_list:
|
||||
retry_msg = 'Could not find ID for firmware %s to '\
|
||||
'activate, waiting %d seconds and retry...' \
|
||||
% (firm_ver, 10)
|
||||
self._msg_process_rflash(upload_msg, {}, True)
|
||||
gevent.sleep( 10 )
|
||||
else:
|
||||
break
|
||||
|
||||
if firm_list:
|
||||
for firm_ver in firm_list:
|
||||
error = 'Could not find firmware %s after waiting %d seconds.' \
|
||||
% (firm_ver, 10*6)
|
||||
self._msg_process_rflash(upload_msg, {}, False)
|
||||
error_list.append(error)
|
||||
utils.update2Ddict(all_nodes_result, self.node, 'result', error_list)
|
||||
return (RESULT_FAIL, [])
|
||||
|
||||
return (RESULT_OK, firm_ids)
|
||||
|
||||
def _check_id_status(self, firm_id_list):
|
||||
"""check firm id status
|
||||
:param firm_id_list: list of firm ids want to check
|
||||
:return: result
|
||||
"""
|
||||
result = RESULT_OK
|
||||
set_priority_ids = []
|
||||
process_status = {}
|
||||
failed_msg = []
|
||||
for i in range(80):
|
||||
try:
|
||||
(has_functional, firm_info) = self._get_firm_info('rflash_check_status')
|
||||
except (xcat_exception.SelfServerException,
|
||||
xcat_exception.SelfClientException) as e:
|
||||
self._msg_process_rflash(e.message, all_nodes_result, False)
|
||||
return (RESULT_FAIL, set_priority_ids)
|
||||
|
||||
activation_num = 0
|
||||
for key in firm_info:
|
||||
firm_id = key.split('-')[-1]
|
||||
if firm_id in firm_id_list:
|
||||
activation_state = firm_info[key]['Activation']
|
||||
firm_version = firm_info[key]['Version']
|
||||
if activation_state == 'Failed':
|
||||
activation_msg = 'Firmware %s activation failed.' % (firm_version)
|
||||
self._msg_process_rflash(activation_msg, {}, False)
|
||||
failed_msg.append(activation_msg)
|
||||
result = RESULT_FAIL
|
||||
firm_id_list.rempove(firm_id)
|
||||
if activation_state == 'Active':
|
||||
activation_msg = 'Firmware %s activation successful.' % (firm_version)
|
||||
self._msg_process_rflash(activation_msg, {}, False)
|
||||
firm_id_list.remove(firm_id)
|
||||
priority = firm_info[key]['Priority']
|
||||
if priority != '0':
|
||||
set_priority_ids.append(firm_id)
|
||||
if activation_state == 'Activating':
|
||||
activating_progress_msg = 'Activating %s ... %s%%' \
|
||||
% (firm_version, firm_info[key]['Progress'])
|
||||
self._msg_process_rflash(activating_progress_msg, {}, True)
|
||||
process_status[firm_id] = activating_progress_msg
|
||||
|
||||
if not firm_id_list:
|
||||
break
|
||||
gevent.sleep( 15 )
|
||||
|
||||
if firm_id_list:
|
||||
result = RESULT_FAIL
|
||||
for firm_id in firm_id_list:
|
||||
if firm_id in process_status:
|
||||
failed_msg.append('After %d seconds check the current status is %s' \
|
||||
% (80*15, process_status[firm_id]))
|
||||
|
||||
if failed_msg:
|
||||
utils.update2Ddict(all_nodes_result, self.node, 'result', [failed_msg])
|
||||
|
||||
return (result, set_priority_ids)
|
||||
|
||||
def _set_priority(self, priority_ids):
|
||||
"""set firmware priority to 0
|
||||
:param priority_ids: list of firmware ids
|
||||
:return ok if success
|
||||
:return error msg if failed
|
||||
"""
|
||||
for priority_id in priority_ids:
|
||||
url = (HTTP_PROTOCOL + self.bmcip +
|
||||
RFLASH_URLS['priority']['url'].replace('#PRIORITY_ID#', priority_id))
|
||||
data = { "data": RFLASH_URLS['priority']['field'] }
|
||||
try:
|
||||
response = self.client.request('PUT', url, OpenBMC.headers,
|
||||
data, 'rflash_set_priority')
|
||||
except (xcat_exception.SelfServerException,
|
||||
xcat_exception.SelfClientException) as e:
|
||||
return e.message
|
||||
|
||||
return RESULT_OK
|
||||
|
||||
def _rflash_activate_id(self, activate_id):
|
||||
"""rflash activate id
|
||||
:param activate_id: the id want to activate
|
||||
:raise: error message if failed
|
||||
"""
|
||||
url = (HTTP_PROTOCOL + self.bmcip +
|
||||
RFLASH_URLS['activate']['url'].replace('#ACTIVATE_ID#', activate_id))
|
||||
data = { "data": RFLASH_URLS['activate']['field'] }
|
||||
try:
|
||||
response = self.client.request('PUT', url, OpenBMC.headers,
|
||||
data, 'rflash_activate')
|
||||
except xcat_exception.SelfServerException as e:
|
||||
return e.message
|
||||
except xcat_exception.SelfClientException as e:
|
||||
code = e.code
|
||||
if code == 403:
|
||||
return 'Error: Invalid ID provided to activate. ' \
|
||||
'Use the -l option to view valid firmware IDs.'
|
||||
return e.message
|
||||
|
||||
return RESULT_OK
|
||||
|
||||
def _rflash_activate(self, activate_arg):
|
||||
"""ACTIVATE firmware
|
||||
called by rflash activate
|
||||
:param activate_arg: firmware tar ball or firmware id
|
||||
:return: ok if success
|
||||
:raise: error message if failed
|
||||
"""
|
||||
activate_id = activate_version = ''
|
||||
if 'activate_id' in activate_arg:
|
||||
activate_id = activate_arg['activate_id']
|
||||
if 'update_file' in activate_arg:
|
||||
result = self._rflash_upload(activate_arg['update_file'])
|
||||
if result != RESULT_OK:
|
||||
self._msg_process_rflash(result, all_nodes_result, False)
|
||||
return
|
||||
|
||||
activate_version = activate_arg['activate_version']
|
||||
(result, info) = self._get_firm_id([activate_version])
|
||||
if result == RESULT_OK:
|
||||
activate_id = info.pop(0)
|
||||
else:
|
||||
return
|
||||
|
||||
result = self._rflash_activate_id(activate_id)
|
||||
if result != RESULT_OK:
|
||||
self._msg_process_rflash(result, all_nodes_result, False)
|
||||
return
|
||||
else:
|
||||
flash_started_msg = 'rflash %s started, please wait...' % activate_version
|
||||
self._msg_process_rflash(flash_started_msg, {}, False)
|
||||
|
||||
firm_id_list = [activate_id]
|
||||
(result, priority_ids) = self._check_id_status(firm_id_list)
|
||||
if result == RESULT_OK:
|
||||
utils.update2Ddict(all_nodes_result, self.node, 'result', 'OK')
|
||||
if priority_ids:
|
||||
self._set_priority(priority_ids)
|
||||
|
||||
def _rflash_delete(self, delete_id):
|
||||
"""Delete firmware on OpenBMC
|
||||
called by rflash delete
|
||||
:param delete_id: firmware id want to delete
|
||||
:returns: ok if success
|
||||
:raise: error message if failed
|
||||
"""
|
||||
url = (HTTP_PROTOCOL + self.bmcip +
|
||||
RFLASH_URLS['delete']['url'].replace('#DELETE_ID#', delete_id))
|
||||
data = { "data": RFLASH_URLS['delete']['field'] }
|
||||
try:
|
||||
response = self.client.request('POST', url, OpenBMC.headers,
|
||||
data, 'rflash_delete')
|
||||
except xcat_exception.SelfServerException as e:
|
||||
return e.message
|
||||
except xcat_exception.SelfClientException as e:
|
||||
code = e.code
|
||||
if code == 404:
|
||||
return 'Error: Invalid ID provided to delete. ' \
|
||||
'Use the -l option to view valid firmware IDs.'
|
||||
return e.message
|
||||
|
||||
return RESULT_OK
|
||||
|
||||
|
||||
def _rflash_list(self):
|
||||
"""List firmware information
|
||||
called by rflash list
|
||||
:returns: firmware version if success
|
||||
:raise: error message if failed
|
||||
"""
|
||||
firm_output = []
|
||||
try:
|
||||
(has_functional, firm_info) = self._get_firm_info('rflash_list')
|
||||
except (xcat_exception.SelfServerException,
|
||||
xcat_exception.SelfClientException) as e:
|
||||
firm_output.append(e.message)
|
||||
return firm_output
|
||||
|
||||
firm_output.append('%-8s %-7s %-10s %-s' % ('ID', 'Purpose', 'State', 'Version'))
|
||||
firm_output.append('-' * 55)
|
||||
|
||||
for key in firm_info:
|
||||
status = firm_info[key]['Activation']
|
||||
if 'is_functional' in firm_info[key]:
|
||||
status += '(*)'
|
||||
elif 'Priority' in firm_info[key] and firm_info[key]['Priority'] == '0':
|
||||
if not has_functional:
|
||||
status += '(*)'
|
||||
else:
|
||||
status += '(+)'
|
||||
|
||||
firm_output.append('%-8s %-7s %-10s %-s' % (key.split('-')[-1],
|
||||
firm_info[key]['Purpose'], status, firm_info[key]['Version']))
|
||||
|
||||
return firm_output
|
||||
|
||||
def _rflash_upload(self, upload_file):
|
||||
""" Upload *.tar file to OpenBMC server
|
||||
:param upload_file: file to upload
|
||||
"""
|
||||
url = HTTP_PROTOCOL + self.bmcip + RFLASH_URLS['upload']['url']
|
||||
headers = {'Content-Type': 'application/octet-stream'}
|
||||
uploading_msg = 'Uploading %s ...' % upload_file
|
||||
self._msg_process_rflash(uploading_msg, {}, True)
|
||||
try:
|
||||
self.client.request_upload('PUT', url, headers,
|
||||
upload_file, 'rflash_upload')
|
||||
except (xcat_exception.SelfServerException,
|
||||
xcat_exception.SelfClientException) as e:
|
||||
result = e.message
|
||||
return result
|
||||
|
||||
return RESULT_OK
|
||||
|
||||
def _set_power_onoff(self, subcommand):
|
||||
""" Set power on/off/softoff/bmcreboot
|
||||
:param subcommand: subcommand for rpower
|
||||
:returns: ok if success
|
||||
@ -90,17 +497,19 @@ class OpenBMC(base.BaseDriver):
|
||||
"""
|
||||
url = HTTP_PROTOCOL + self.bmcip + RPOWER_URLS[subcommand]['url']
|
||||
data = { "data": RPOWER_URLS[subcommand]['field'] }
|
||||
try :
|
||||
response = self.client.request('PUT', url, OpenBMC.headers, data, self.node, 'rpower_' + subcommand)
|
||||
try:
|
||||
response = self.client.request('PUT', url, OpenBMC.headers,
|
||||
data, 'rpower_' + subcommand)
|
||||
except (xcat_exception.SelfServerException,
|
||||
xcat_exception.SelfClientException) as e :
|
||||
xcat_exception.SelfClientException) as e:
|
||||
if subcommand != 'bmcreboot':
|
||||
result = e.message
|
||||
return result
|
||||
|
||||
return RESULT_OK
|
||||
|
||||
def _get_power_state(self, subcommand) :
|
||||
|
||||
def _get_power_state(self, subcommand):
|
||||
""" Get power current state
|
||||
:param subcommand: state/stat/status/bmcstate
|
||||
:returns: current state if success
|
||||
@ -109,61 +518,62 @@ class OpenBMC(base.BaseDriver):
|
||||
result = ''
|
||||
bmc_not_ready = 'NotReady'
|
||||
url = HTTP_PROTOCOL + self.bmcip + RPOWER_URLS['state']['url']
|
||||
try :
|
||||
response = self.client.request('GET', url, OpenBMC.headers, '', self.node, 'rpower_' + subcommand)
|
||||
except xcat_exception.SelfServerException, e :
|
||||
try:
|
||||
response = self.client.request('GET', url, OpenBMC.headers,
|
||||
'', 'rpower_' + subcommand)
|
||||
except xcat_exception.SelfServerException, e:
|
||||
if subcommand == 'bmcstate':
|
||||
result = bmc_not_ready
|
||||
else :
|
||||
else:
|
||||
result = e.message
|
||||
except xcat_exception.SelfClientException, e :
|
||||
except xcat_exception.SelfClientException, e:
|
||||
result = e.message
|
||||
|
||||
if result :
|
||||
if result:
|
||||
return result
|
||||
|
||||
for key in response['data'] :
|
||||
for key in response['data']:
|
||||
key_type = key.split('/')[-1]
|
||||
if key_type == 'bmc0' :
|
||||
if key_type == 'bmc0':
|
||||
bmc_current_state = response['data'][key]['CurrentBMCState'].split('.')[-1]
|
||||
if key_type == 'chassis0' :
|
||||
if key_type == 'chassis0':
|
||||
chassis_current_state = response['data'][key]['CurrentPowerState'].split('.')[-1]
|
||||
if key_type == 'host0' :
|
||||
if key_type == 'host0':
|
||||
host_current_state = response['data'][key]['CurrentHostState'].split('.')[-1]
|
||||
|
||||
if subcommand == 'bmcstate' :
|
||||
if bmc_current_state == 'Ready' :
|
||||
if subcommand == 'bmcstate':
|
||||
if bmc_current_state == 'Ready':
|
||||
return bmc_current_state
|
||||
else :
|
||||
else:
|
||||
return bmc_not_ready
|
||||
|
||||
if chassis_current_state == 'Off' :
|
||||
if chassis_current_state == 'Off':
|
||||
return chassis_current_state
|
||||
elif chassis_current_state == 'On' :
|
||||
if host_current_state == 'Off' :
|
||||
elif chassis_current_state == 'On':
|
||||
if host_current_state == 'Off':
|
||||
return 'chassison'
|
||||
elif host_current_state == 'Quiesced' :
|
||||
elif host_current_state == 'Quiesced':
|
||||
return host_current_state
|
||||
elif host_current_state == 'Running' :
|
||||
elif host_current_state == 'Running':
|
||||
return host_current_state
|
||||
else :
|
||||
else:
|
||||
return 'Unexpected chassis state=' + host_current_state
|
||||
else :
|
||||
else:
|
||||
return 'Unexpected chassis state=' + chassis_current_state
|
||||
|
||||
|
||||
def _rpower_boot(self) :
|
||||
def _rpower_boot(self):
|
||||
"""Power boot
|
||||
:returns: 'reset' if success
|
||||
:raise: error message if failed
|
||||
"""
|
||||
result = self._set_power_onoff('off')
|
||||
if result != RESULT_OK :
|
||||
if result != RESULT_OK:
|
||||
return result
|
||||
self.messager.update_node_attributes('status', self.node, POWER_STATE_DB['off'])
|
||||
|
||||
start_timeStamp = int(time.time())
|
||||
for i in range (0,30) :
|
||||
for i in range (0,30):
|
||||
status = self._get_power_state('state')
|
||||
if status in RPOWER_STATE and RPOWER_STATE[status] == 'off':
|
||||
break
|
||||
@ -173,64 +583,128 @@ class OpenBMC(base.BaseDriver):
|
||||
|
||||
if status not in RPOWER_STATE or RPOWER_STATE[status] != 'off':
|
||||
wait_time = str(end_timeStamp - start_timeStamp)
|
||||
result = 'Error: Sent power-off command but state did not change to off after waiting ' + wait_time + ' seconds. (State=' + status + ').'
|
||||
result = 'Error: Sent power-off command but state did not change ' \
|
||||
'to off after waiting %s seconds. (State= %s).' % (wait_time, status)
|
||||
return result
|
||||
|
||||
result = self._set_power_onoff('on')
|
||||
return result
|
||||
|
||||
def rflash(self, args):
|
||||
"""handle rflash command
|
||||
:param args: subcommands and parameters for rflash
|
||||
"""
|
||||
subcommand = args[0]
|
||||
if subcommand == 'activate' or subcommand == 'upload':
|
||||
self.rflash_log_file = XCAT_LOG_RFLASH_DIR + '/' + self.node + '.log'
|
||||
self.rflash_log_handle = open(self.rflash_log_file, 'a')
|
||||
|
||||
def rpower(self, args) :
|
||||
try:
|
||||
result = self._login()
|
||||
except (xcat_exception.SelfServerException,
|
||||
xcat_exception.SelfClientException) as e:
|
||||
result = e.message
|
||||
|
||||
if result != RESULT_OK:
|
||||
self.messager.info('%s: %s'% (self.node,result))
|
||||
if subcommand == 'activate' or subcommand == 'upload':
|
||||
self.rflash_log_handle.writelines(result + '\n')
|
||||
self.rflash_log_handle.flush()
|
||||
if subcommand == 'activate':
|
||||
utils.update2Ddict(all_nodes_result, self.node, 'result', [result])
|
||||
return
|
||||
|
||||
if subcommand == 'activate':
|
||||
activate_arg = args[1]
|
||||
self._rflash_activate(activate_arg)
|
||||
|
||||
if subcommand == 'check':
|
||||
firm_info = self._firm_info('rflash_check')
|
||||
for i in firm_info:
|
||||
result = '%s: %s' % (self.node, i)
|
||||
self.messager.info(result)
|
||||
|
||||
if subcommand == 'delete':
|
||||
firmware_id = args[1]
|
||||
result = self._rflash_delete(firmware_id)
|
||||
if result == RESULT_OK:
|
||||
result = '%s: [%s] Firmware removed' % (self.node, firmware_id)
|
||||
self.messager.info(result)
|
||||
else:
|
||||
result = '%s: %s' % (self.node, result)
|
||||
self.messager.info(result)
|
||||
|
||||
if subcommand == 'list':
|
||||
firm_info = self._rflash_list()
|
||||
for i in firm_info:
|
||||
result = '%s: %s' % (self.node, i)
|
||||
self.messager.info(result)
|
||||
|
||||
if subcommand == 'upload':
|
||||
upload_file = args[1]
|
||||
result = self._rflash_upload(upload_file)
|
||||
if result == RESULT_OK:
|
||||
result = 'Firmware upload successful. Use -l option to list.'
|
||||
self._msg_process_rflash(result, {}, False)
|
||||
else:
|
||||
self._msg_process_rflash(result, {}, False)
|
||||
|
||||
if subcommand == 'activate' or subcommand == 'upload':
|
||||
self.rflash_log_handle.close()
|
||||
|
||||
|
||||
def rpower(self, args):
|
||||
"""handle rpower command
|
||||
:param args: subcommands for rpower
|
||||
"""
|
||||
subcommand = args[0]
|
||||
try :
|
||||
try:
|
||||
result = self._login()
|
||||
except xcat_exception.SelfServerException as e :
|
||||
if subcommand == 'bmcstate' :
|
||||
except xcat_exception.SelfServerException as e:
|
||||
if subcommand == 'bmcstate':
|
||||
result = '%s: %s' % (self.node, RPOWER_STATE['NotReady'])
|
||||
else :
|
||||
else:
|
||||
result = '%s: %s' % (self.node, e.message)
|
||||
except xcat_exception.SelfClientException as e :
|
||||
except xcat_exception.SelfClientException as e:
|
||||
result = '%s: %s' % (self.node, e.message)
|
||||
|
||||
if result != RESULT_OK :
|
||||
if result != RESULT_OK:
|
||||
self.messager.info(result)
|
||||
self._update2Ddict(node_rst, self.node, 'rst', result)
|
||||
return
|
||||
|
||||
new_status = ''
|
||||
if subcommand in POWER_SET_OPTIONS :
|
||||
if subcommand in POWER_SET_OPTIONS:
|
||||
result = self._set_power_onoff(subcommand)
|
||||
if result == RESULT_OK :
|
||||
if result == RESULT_OK:
|
||||
result = RPOWER_STATE[subcommand]
|
||||
new_status = POWER_STATE_DB.get(subcommand, '')
|
||||
|
||||
if subcommand in POWER_GET_OPTIONS :
|
||||
if subcommand in POWER_GET_OPTIONS:
|
||||
tmp_result = self._get_power_state(subcommand)
|
||||
result = RPOWER_STATE.get(tmp_result, tmp_result)
|
||||
|
||||
if subcommand == 'boot' :
|
||||
if subcommand == 'boot':
|
||||
result = self._rpower_boot()
|
||||
if result == RESULT_OK :
|
||||
if result == RESULT_OK:
|
||||
result = RPOWER_STATE[subcommand]
|
||||
new_status = POWER_STATE_DB.get(subcommand, '')
|
||||
|
||||
if subcommand == 'reset' :
|
||||
if subcommand == 'reset':
|
||||
status = self._get_power_state('state')
|
||||
if status == 'Off' or status == 'chassison':
|
||||
result = RPOWER_STATE['Off']
|
||||
else :
|
||||
else:
|
||||
result = self._rpower_boot()
|
||||
if result == RESULT_OK :
|
||||
if result == RESULT_OK:
|
||||
result = RPOWER_STATE[subcommand]
|
||||
new_status = POWER_STATE_DB.get(subcommand, '')
|
||||
|
||||
message = '%s: %s' % (self.node, result)
|
||||
self.messager.info(message)
|
||||
if new_status :
|
||||
if new_status:
|
||||
self.messager.update_node_attributes('status', self.node, new_status)
|
||||
|
||||
|
||||
class OpenBMCManager(base.BaseManager):
|
||||
def __init__(self, messager, cwd, nodes, envs):
|
||||
super(OpenBMCManager, self).__init__(messager, cwd)
|
||||
@ -238,5 +712,101 @@ class OpenBMCManager(base.BaseManager):
|
||||
global DEBUGMODE
|
||||
DEBUGMODE = envs['debugmode']
|
||||
|
||||
def _get_full_path(self,file_path):
|
||||
if type(self.cwd) == 'unicode':
|
||||
dir_path = self.cwd
|
||||
else:
|
||||
dir_path = self.cwd[0]
|
||||
return '%s/%s' % (dir_path,file_path)
|
||||
|
||||
def _check_verbose(self, args):
|
||||
verbose_list = ('-V', '--verbose')
|
||||
for i in verbose_list:
|
||||
if i in args:
|
||||
global VERBOSE
|
||||
VERBOSE = True
|
||||
args.remove(i)
|
||||
|
||||
def _summary(self, nodes_num, title):
|
||||
if all_nodes_result:
|
||||
success_num = failed_num = 0
|
||||
failed_list = []
|
||||
for key in all_nodes_result:
|
||||
if all_nodes_result[key]['result'] == 'OK':
|
||||
success_num += 1
|
||||
else:
|
||||
failed_num += 1
|
||||
for errors in all_nodes_result[key]['result']:
|
||||
for error in errors:
|
||||
failed_list.append('%s: %s' % (key, error))
|
||||
self.messager.info('-' * 55)
|
||||
self.messager.info('%s complete: Total=%d Success=%d Failed=%d' % \
|
||||
(title, nodes_num, success_num, failed_num))
|
||||
if failed_list:
|
||||
for i in failed_list:
|
||||
self.messager.info(i)
|
||||
self.messager.info('-' * 55)
|
||||
|
||||
def rflash(self, nodeinfo, args):
|
||||
if not os.path.exists(XCAT_LOG_RFLASH_DIR):
|
||||
os.makedirs(XCAT_LOG_RFLASH_DIR)
|
||||
nodes_num = len(self.nodes)
|
||||
self._check_verbose(args)
|
||||
|
||||
for key,value in RFLASH_OPTIONS.items():
|
||||
if key in args:
|
||||
args.remove(key)
|
||||
args.insert(0, value)
|
||||
break
|
||||
|
||||
upload_file = None
|
||||
activate_arg = {}
|
||||
args_num = len(args)
|
||||
subcommand = args[0]
|
||||
if (subcommand == 'upload' or subcommand == 'activate' or
|
||||
(subcommand == 'check' and args_num > 1)):
|
||||
arg_type = args[1].split('.')[-1]
|
||||
if arg_type == 'tar':
|
||||
upload_file = args[1]
|
||||
if not os.path.isabs(upload_file):
|
||||
upload_file = self._get_full_path(upload_file)
|
||||
|
||||
if (not os.access(upload_file, os.F_OK) or
|
||||
not os.access(upload_file, os.R_OK)):
|
||||
error = 'Error: Cannot access %s. Check the management ' \
|
||||
'node and/or service nodes.' % upload_file
|
||||
self.messager.error(error)
|
||||
return
|
||||
activate_arg['update_file'] = upload_file
|
||||
else:
|
||||
activate_arg['activate_id'] = args[1]
|
||||
|
||||
if (subcommand == 'check' or subcommand == 'activate') and upload_file:
|
||||
grep_cmd = '/usr/bin/grep -a'
|
||||
version_cmd = grep_cmd + ' ^version= ' + upload_file
|
||||
purpose_cmd = grep_cmd + ' purpose= ' + upload_file
|
||||
firmware_ver = os.popen(version_cmd).readlines()[0].split('=')[-1].strip()
|
||||
purpose_ver = os.popen(purpose_cmd).readlines()[0].split('=')[-1].strip()
|
||||
if subcommand == 'check':
|
||||
self.messager.info('TAR %s Firmware Product Version: %s' \
|
||||
% (purpose_ver,firmware_ver))
|
||||
else:
|
||||
activate_arg['activate_version'] = firmware_ver
|
||||
activate_arg['purpose'] = purpose_ver.split('.')[-1]
|
||||
|
||||
if subcommand == 'activate':
|
||||
args[1] = activate_arg
|
||||
|
||||
if subcommand == 'upload':
|
||||
args[1] = upload_file
|
||||
|
||||
if subcommand == 'upload' or subcommand == 'activate' and upload_file:
|
||||
self.messager.info('Attempting to upload %s, please wait...' % upload_file)
|
||||
|
||||
super(OpenBMCManager, self).process_nodes_worker('openbmc', 'OpenBMC',
|
||||
self.nodes, nodeinfo, 'rflash', args)
|
||||
self._summary(nodes_num, 'Firmware update')
|
||||
|
||||
def rpower(self, nodeinfo, args):
|
||||
super(OpenBMCManager, self).process_nodes_worker('openbmc', 'OpenBMC', self.nodes, nodeinfo, 'rpower', args)
|
||||
super(OpenBMCManager, self).process_nodes_worker('openbmc', 'OpenBMC',
|
||||
self.nodes, nodeinfo, 'rpower', args)
|
||||
|
113
xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc_rest.py
Normal file
113
xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc_rest.py
Normal file
@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
|
||||
import rest
|
||||
import xcat_exception
|
||||
|
||||
class OpenBMCRest:
|
||||
|
||||
def __init__(self, name, messager, debugmode):
|
||||
self.session = rest.RestSession()
|
||||
self.name = name
|
||||
self.messager = messager
|
||||
self.debugmode = debugmode
|
||||
|
||||
def _print_record_log (self, log_string, status):
|
||||
if self.debugmode :
|
||||
localtime = time.asctime( time.localtime(time.time()) )
|
||||
log = self.name + ': [openbmc_debug] ' + status + ' ' + log_string
|
||||
self.messager.info(localtime + ' ' + log)
|
||||
self.messager.syslog(log)
|
||||
|
||||
def _request_log (self, method, url, headers, data, files):
|
||||
log_string = 'curl -k -c cjar -b cjar'
|
||||
log_string += ' -X %s' % method
|
||||
for key,value in headers.items():
|
||||
header_data = key + ": " + value
|
||||
log_string += ' -H "' + header_data + '"'
|
||||
log_string += ' %s' % url
|
||||
|
||||
if data:
|
||||
log_string += ' -d \'%s\'' % data
|
||||
if files:
|
||||
log_string += ' -T \'%s\'' % files
|
||||
|
||||
return log_string
|
||||
|
||||
|
||||
def _response_check (self, response, response_dict, status):
|
||||
if response.status_code != requests.codes.ok:
|
||||
description = ''.join(response_dict['data']['description'])
|
||||
error = 'Error: [%d] %s' % (response.status_code, description)
|
||||
self._print_record_log(error, status)
|
||||
code = response.status_code
|
||||
raise xcat_exception.SelfClientException(error, code)
|
||||
else:
|
||||
self._print_record_log(response_dict['message'], status)
|
||||
|
||||
def request (self, method, url, headers, in_data, status):
|
||||
data = log_data = ''
|
||||
|
||||
if in_data:
|
||||
data = json.dumps(in_data)
|
||||
log_data = data
|
||||
if status == 'login':
|
||||
in_data['data'][1] = 'xxxxxx'
|
||||
log_data = json.dumps(in_data)
|
||||
|
||||
log_string = self._request_log(method, url, headers, log_data, '')
|
||||
self._print_record_log(log_string, status)
|
||||
|
||||
try:
|
||||
response = self.session.request(method, url, headers, data)
|
||||
except xcat_exception.SelfServerException as e:
|
||||
self._print_record_log(e.message, status)
|
||||
raise xcat_exception.SelfServerException(e.message)
|
||||
|
||||
try:
|
||||
response_dict = response.json()
|
||||
except ValueError:
|
||||
error = 'Error: Received wrong format response: %s' % response
|
||||
self._print_record_log(error, status)
|
||||
raise xcat_exception.SelfServerException(error)
|
||||
|
||||
self._response_check(response, response_dict, status)
|
||||
|
||||
return response_dict
|
||||
|
||||
|
||||
def request_upload (self, method, url, headers, files, status):
|
||||
for key,value in headers.items():
|
||||
header_data = key + ': ' + value
|
||||
request_cmd_log = 'curl -k -c cjar -b cjar -H "%s" -X %s -T %s %s -s' \
|
||||
% (header_data, method, files, url)
|
||||
log_string = self._request_log(method, url, headers, '', files)
|
||||
self._print_record_log(log_string, status)
|
||||
|
||||
response = self.session.request_upload(method, url, header_data, files)
|
||||
|
||||
if not response:
|
||||
error = 'Error: Did not receive response from OpenBMC after ' \
|
||||
'running command form \'%s\'' % request_cmd_log
|
||||
raise xcat_exception.SelfServerException(error)
|
||||
|
||||
try:
|
||||
response_dict = json.loads(response)
|
||||
except ValueError:
|
||||
error = 'Error: Received wrong format response: %s: %s' % \
|
||||
(request_cmd_log, response)
|
||||
self._print_record_log(error, status)
|
||||
raise xcat_exception.SelfServerException(error)
|
||||
|
||||
if response_dict['message'] != '200 OK':
|
||||
error = 'Error: Failed to upload update file %s : %s-%s' % \
|
||||
(files, response_dict['message'], \
|
||||
''.join(response_dict['data']['description']))
|
||||
self._print_record_log(error, status)
|
||||
raise xcat_exception.SelfClientException(error, code)
|
||||
|
||||
self._print_record_log(response_dict['message'], status)
|
||||
|
||||
return
|
@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
from gevent.subprocess import Popen, PIPE
|
||||
import urllib3
|
||||
urllib3.disable_warnings()
|
||||
|
||||
@ -9,76 +8,34 @@ import xcat_exception
|
||||
|
||||
class RestSession :
|
||||
|
||||
def __init__(self, messager, debugmode) :
|
||||
def __init__(self):
|
||||
self.session = requests.Session()
|
||||
self.messager = messager
|
||||
self.debugmode = debugmode
|
||||
self.cookies = None
|
||||
|
||||
def _print_record_log (self, node, log_string, status) :
|
||||
if self.debugmode :
|
||||
localtime = time.asctime( time.localtime(time.time()) )
|
||||
log = node + ': [openbmc_debug] ' + status + ' ' + log_string
|
||||
self.messager.info(localtime + ' ' + log)
|
||||
self.messager.syslog(log)
|
||||
|
||||
def _request_log (self, method, url, headers, data):
|
||||
log_string = 'curl -k -c cjar'
|
||||
log_string += ' -X %s' % method
|
||||
for key,value in headers.items() :
|
||||
header_data = key + ": " + value
|
||||
log_string += ' -H "' + header_data + '"'
|
||||
log_string += ' %s' % url
|
||||
|
||||
if data :
|
||||
log_string += ' -d \'%s\'' % data
|
||||
|
||||
return log_string
|
||||
|
||||
def request (self, method, url, headers, in_data, node, status) :
|
||||
if in_data :
|
||||
data = json.dumps(in_data)
|
||||
else :
|
||||
data = ''
|
||||
|
||||
if status == 'login' :
|
||||
in_data['data'][1] = 'xxxxxx'
|
||||
log_data = json.dumps(in_data)
|
||||
else :
|
||||
log_data = data
|
||||
|
||||
log_string = self._request_log(method, url, headers, log_data)
|
||||
self._print_record_log(node, log_string, status)
|
||||
|
||||
response = ''
|
||||
error = ''
|
||||
try :
|
||||
def request (self, method, url, headers, data):
|
||||
try:
|
||||
response = self.session.request(method, url,
|
||||
data=data,
|
||||
headers=headers,
|
||||
verify=False,
|
||||
timeout=30)
|
||||
except requests.exceptions.ConnectionError :
|
||||
error = 'Error: BMC did not respond. Validate BMC configuration and retry the command.'
|
||||
except requests.exceptions.Timeout :
|
||||
error = 'Error: Timeout to connect to server'
|
||||
data=data,
|
||||
headers=headers,
|
||||
verify=False,
|
||||
timeout=30)
|
||||
except requests.exceptions.ConnectionError:
|
||||
raise xcat_exception.SelfServerException(
|
||||
'Error: BMC did not respond. ' \
|
||||
'Validate BMC configuration and retry the command.')
|
||||
except requests.exceptions.Timeout:
|
||||
raise xcat_exception.SelfServerException('Error: Timeout to connect to server')
|
||||
|
||||
if error :
|
||||
self._print_record_log(node, error, status)
|
||||
raise xcat_exception.SelfServerException(error)
|
||||
if not self.cookies:
|
||||
self.cookies = requests.utils.dict_from_cookiejar(self.session.cookies)
|
||||
|
||||
try :
|
||||
response_dict = response.json()
|
||||
except ValueError :
|
||||
error = 'Error: Received wrong format response:' + response_dict
|
||||
self._print_record_log(node, error, status)
|
||||
raise xcat_exception.SelfServerException(error)
|
||||
return response
|
||||
|
||||
if response.status_code != requests.codes.ok :
|
||||
description = ''.join(response_dict['data']['description'])
|
||||
error = 'Error: [%d] %s' % (response.status_code, description)
|
||||
self._print_record_log(node, error, status)
|
||||
raise xcat_exception.SelfClientException(error)
|
||||
else :
|
||||
self._print_record_log(node, response_dict['message'], status)
|
||||
|
||||
return response_dict
|
||||
def request_upload (self, method, url, headers, files):
|
||||
request_cmd = 'curl -k -b sid=%s -H "%s" -X %s -T %s %s -s' % \
|
||||
(self.cookies['sid'], headers, method, files, url)
|
||||
|
||||
sub = Popen(request_cmd, stdout=PIPE, shell=True)
|
||||
response, err = sub.communicate()
|
||||
|
||||
return response
|
||||
|
@ -39,3 +39,11 @@ def recv_all(sock, size):
|
||||
buf_size += len(buf_part)
|
||||
buf = ''.join(buf_parts)
|
||||
return buf
|
||||
|
||||
|
||||
def update2Ddict(updata_dict, key_a, key_b, value):
|
||||
if key_a in updata_dict:
|
||||
updata_dict[key_a].update({key_b: value})
|
||||
else:
|
||||
updata_dict.update({key_a: {key_b: value}})
|
||||
|
||||
|
@ -4,4 +4,6 @@ class SelfServerException(Exception) :
|
||||
pass
|
||||
|
||||
class SelfClientException(Exception) :
|
||||
pass
|
||||
def __init__(self, message, code) :
|
||||
super(Exception, self).__init__(message)
|
||||
self.code = code
|
||||
|
@ -30,6 +30,7 @@ use xCAT::OPENBMC;
|
||||
|
||||
sub handled_commands {
|
||||
return {
|
||||
rflash => 'nodehm:mgt=openbmc',
|
||||
rpower => 'nodehm:mgt=openbmc',
|
||||
};
|
||||
}
|
||||
@ -136,13 +137,61 @@ sub parse_args {
|
||||
my $noderange = shift;
|
||||
my $subcommand = undef;
|
||||
|
||||
if (scalar(@ARGV) != 1 and ($command =~ /rpower/)) {
|
||||
if (scalar(@ARGV) >= 2 and ($command =~ /rpower/)) {
|
||||
return ([ 1, "Only one option is supported at the same time for $command" ]);
|
||||
} elsif (scalar(@ARGV) == 0 and $command =~ /rpower|rflash/) {
|
||||
return ([ 1, "No option specified for $command" ]);
|
||||
} else {
|
||||
$subcommand = $ARGV[0];
|
||||
}
|
||||
|
||||
if ($command eq "rpower") {
|
||||
if ($command eq "rflash") {
|
||||
my $verbose;
|
||||
my ($activate, $check, $delete, $directory, $list, $upload) = (0) x 6;
|
||||
my $no_host_reboot;
|
||||
GetOptions(
|
||||
'a|activate' => \$activate,
|
||||
'c|check' => \$check,
|
||||
'delete' => \$delete,
|
||||
'd' => \$directory,
|
||||
'l|list' => \$list,
|
||||
'u|upload' => \$upload,
|
||||
'V|verbose' => \$verbose,
|
||||
'no-host-reboot' => \$no_host_reboot,
|
||||
);
|
||||
my $option_num = $activate+$check+$delete+$directory+$list+$upload;
|
||||
if ($option_num >= 2) {
|
||||
return ([ 1, "Multiple options are not supported."]);
|
||||
} elsif ($option_num == 0) {
|
||||
return ([ 1, "No options specified."]);
|
||||
}
|
||||
if ($activate or $check or $delete or $upload) {
|
||||
return ([ 1, "More than one firmware specified is not supported."]) if ($#ARGV >= 1);
|
||||
if ($check) {
|
||||
return ([ 1, "Invalid firmware specified with '-c|--check'."]) if (@ARGV and ($ARGV[0] !~ /.*\.tar$/i or $#ARGV >= 1));
|
||||
}
|
||||
if ($activate or $delete or $upload) {
|
||||
my $option = "-a|--activate";
|
||||
if ($upload) {
|
||||
$option = "-u|--upload";
|
||||
} elsif ($delete) {
|
||||
$option = "--delete"
|
||||
}
|
||||
return ([ 1, "Invalid firmware specified with '$option'"]) if (!@ARGV);
|
||||
my $param = $ARGV[0];
|
||||
return ([ 1, "Invalid firmware specified with '$option': $param"]) if (($delete and $param !~ /^[[:xdigit:]]+$/i)
|
||||
or ($activate and $param !~ /^[[:xdigit:]]+$/i and $param !~ /.*\.tar$/i) or ($upload and $param !~ /.*\.tar$/i));
|
||||
}
|
||||
}
|
||||
if ($directory) {
|
||||
return ([ 1, "Unsupported command: $command '-d'" ]);
|
||||
return ([ 1, "More than one directory specified is not supported."]) if ($#ARGV >= 1);
|
||||
return ([ 1, "Invalid option specified with '-d'."]) if (!@ARGV);
|
||||
}
|
||||
if ($list) {
|
||||
return ([ 1, "Invalid option specified with '-l|--list'."]) if (@ARGV);
|
||||
}
|
||||
} elsif ($command eq "rpower") {
|
||||
unless ($subcommand =~ /^on$|^off$|^softoff$|^reset$|^boot$|^bmcreboot$|^bmcstate$|^status$|^stat$|^state$/) {
|
||||
return ([ 1, "Unsupported command: $command $subcommand" ]);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user