mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-05-21 19:22:05 +00:00
rflash in python for OpenBMC
This commit is contained in:
parent
f047cc73e3
commit
b8a489899b
@ -122,7 +122,7 @@ class RestSession(object):
|
||||
response, err = sub.communicate()
|
||||
|
||||
if not response:
|
||||
error = 'Error: Did not receive response from server after ' \
|
||||
error = 'Did not receive response from server after ' \
|
||||
'running command \'%s\'' % request_cmd
|
||||
raise SelfServerException(error)
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
import struct
|
||||
import sys
|
||||
import inspect
|
||||
import re
|
||||
import re, os
|
||||
import logging
|
||||
from logging.handlers import SysLogHandler
|
||||
|
||||
@ -92,6 +92,11 @@ def mask_int2str(mask_int):
|
||||
mask_num = (0x1 << 32) - (0x1 << (32 - mask_int))
|
||||
return "%s.%s.%s.%s" % (str((mask_num >> 24) & 0xff), str((mask_num >>16)&0xff), str((mask_num >> 8) & 0xff), str(mask_num & 0xff))
|
||||
|
||||
def get_full_path(cwd, directory):
|
||||
if not os.path.isabs(directory):
|
||||
directory = '%s/%s' % (cwd, directory)
|
||||
return directory
|
||||
|
||||
class Messager(object):
|
||||
def __init__(self, name=None):
|
||||
self.logger = logging.getLogger(name or 'xcatagent')
|
||||
|
562
xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_flash.py
Normal file
562
xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_flash.py
Normal file
@ -0,0 +1,562 @@
|
||||
#!/usr/bin/env python
|
||||
###############################################################################
|
||||
# IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html
|
||||
###############################################################################
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from __future__ import print_function
|
||||
import gevent
|
||||
import time
|
||||
import os, re
|
||||
|
||||
from common import utils
|
||||
from common.task import ParallelNodesCommand
|
||||
from common.exceptions import SelfClientException, SelfServerException
|
||||
from hwctl import openbmc_client as openbmc
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger('xcatagent')
|
||||
|
||||
XCAT_LOG_DIR = "/var/log/xcat"
|
||||
XCAT_LOG_RFLASH_DIR = XCAT_LOG_DIR + "/rflash/"
|
||||
|
||||
class OpenBMCFlashTask(ParallelNodesCommand):
|
||||
"""Executor for flash-related actions."""
|
||||
activate_result = {}
|
||||
firmware = {}
|
||||
firmware_file = None
|
||||
log_handle = {}
|
||||
nodes_num = 0
|
||||
|
||||
def _msg_process(self, node, msg, msg_type='I', update_rc=False, checkv=False):
|
||||
|
||||
if msg_type == 'E':
|
||||
self.callback.error(msg, node)
|
||||
elif not checkv:
|
||||
self.callback.info('%s: %s' % (node, msg))
|
||||
elif self.verbose:
|
||||
self.callback.info('%s: %s' % (node, msg))
|
||||
|
||||
if update_rc:
|
||||
self.activate_result.update({node: msg})
|
||||
|
||||
if node not in self.log_handle:
|
||||
log_file = XCAT_LOG_RFLASH_DIR + '/' + node + '.log'
|
||||
self.log_handle.update({node: open(log_file, 'a')})
|
||||
try:
|
||||
self.log_handle[node].writelines(msg + '\n')
|
||||
self.log_handle[node].flush()
|
||||
except Exception as e:
|
||||
self.callback.error('Failed to record rflash log for node %s' % node)
|
||||
|
||||
def _firmware_file_check(self, firmware_file, **kw):
|
||||
|
||||
target_file = utils.get_full_path(self.cwd, firmware_file)
|
||||
self.firmware_file = target_file
|
||||
|
||||
if (not os.access(target_file, os.F_OK) or
|
||||
not os.access(target_file, os.R_OK)):
|
||||
error = 'Cannot access %s. Check the management ' \
|
||||
'node and/or service nodes.' % target_file
|
||||
self.callback.error(error)
|
||||
raise Exception('Invalid firmware file %s' % target_file)
|
||||
|
||||
def validate_activate_firm(self, task, activate_arg, **kw):
|
||||
|
||||
if activate_arg.endswith('.tar'):
|
||||
self._firmware_file_check(activate_arg)
|
||||
else:
|
||||
if not re.match('\A[0-9a-fA-F]+\Z', activate_arg):
|
||||
self.callback.error('Invalid firmware ID %s' % activate_arg)
|
||||
|
||||
def validate_delete_firm(self, task, delete_id, **kw):
|
||||
|
||||
if not re.match('\A[0-9a-fA-F]+\Z', delete_id):
|
||||
self.callback.error('Invalid firmware ID %s' % activate_arg)
|
||||
|
||||
def validate_upload_firm(self, task, upload_file, **kw):
|
||||
|
||||
self._firmware_file_check(upload_file)
|
||||
|
||||
def _get_firmware_version(self, target_file):
|
||||
|
||||
grep_cmd = '/usr/bin/grep -a'
|
||||
version_cmd = grep_cmd + ' ^version= ' + target_file
|
||||
purpose_cmd = grep_cmd + ' purpose= ' + target_file
|
||||
firmware = os.popen(version_cmd).readlines()[0].split('=')[-1].strip()
|
||||
purpose = os.popen(purpose_cmd).readlines()[0].split('=')[-1].strip().split('.')[-1]
|
||||
return { firmware: {'purpose': purpose} }
|
||||
|
||||
def pre_activate_firm(self, task, activate_arg, **kw):
|
||||
|
||||
if not os.path.exists(XCAT_LOG_RFLASH_DIR):
|
||||
os.makedirs(XCAT_LOG_RFLASH_DIR)
|
||||
|
||||
if activate_arg.endswith('.tar'):
|
||||
version = self._get_firmware_version()
|
||||
self.firmware.update(version)
|
||||
self.callback.info('Attempting to upload %s, please wait...' % self.firmware_file)
|
||||
else:
|
||||
self.callback.info('Attempting to activate ID=%s, please wait..' % activate_arg)
|
||||
self.nodes_num = len(self.inventory)
|
||||
|
||||
def pre_delete_firm(self, task, delete_id, **kw):
|
||||
|
||||
self.callback.info('Attempting to delete ID=%s, please wait..' % delete_id)
|
||||
|
||||
def pre_flash_process(self, task, directory, no_host_reboot, **kw):
|
||||
|
||||
if not os.path.exists(XCAT_LOG_RFLASH_DIR):
|
||||
os.makedirs(XCAT_LOG_RFLASH_DIR)
|
||||
|
||||
directory = utils.get_full_path(self.cwd, directory)
|
||||
tmp_dict = {'BMC': [], 'Host': []}
|
||||
for filename in os.listdir(directory):
|
||||
if filename.endswith('.tar'):
|
||||
filename = os.path.join(directory, filename)
|
||||
try:
|
||||
version = self._get_firmware_version(filename)
|
||||
except Exception as e:
|
||||
continue
|
||||
self.firmware.update(version)
|
||||
for key, value in version.items():
|
||||
tmp_dict[ value['purpose'] ].append(filename)
|
||||
self.firmware[key].update({'file': filename})
|
||||
|
||||
bmc_file_num = len(tmp_dict['BMC'])
|
||||
host_file_num = len(tmp_dict['Host'])
|
||||
error = None
|
||||
if not bmc_file_num:
|
||||
error = 'No BMC tar file found in %s' % directory
|
||||
elif not host_file_num:
|
||||
error = 'No HOST tar file found in %s' % directory
|
||||
elif bmc_file_num > 1:
|
||||
error = 'More than 1 BMC tar file %s found in %s' \
|
||||
% (' '.join(tmp_dict['BMC']), directory)
|
||||
elif host_file_num > 1:
|
||||
error = 'More than 1 HOST tar file %s found in %s' \
|
||||
% (' '.join(tmp_dict['Host']), directory)
|
||||
if error:
|
||||
self.callback.error(error)
|
||||
raise Exception('No or More tar file found')
|
||||
|
||||
self.callback.info('Attempting to upload %s and %s, please wait..' \
|
||||
% (tmp_dict['BMC'][0], tmp_dict['Host'][0]))
|
||||
self.nodes_num = len(self.inventory)
|
||||
|
||||
def pre_upload_firm(self, task, upload_arg, **kw):
|
||||
|
||||
if not os.path.exists(XCAT_LOG_RFLASH_DIR):
|
||||
os.makedirs(XCAT_LOG_RFLASH_DIR)
|
||||
self.callback.info('Attempting to upload %s, please wait...' % self.firmware_file)
|
||||
|
||||
def _get_firm_id(self, obmc, node):
|
||||
|
||||
mapping_ids = []
|
||||
if self.firmware:
|
||||
version_list = self.firmware.keys()
|
||||
else:
|
||||
return []
|
||||
|
||||
for i in range(6):
|
||||
try:
|
||||
has_functional, firm_obj_dict = obmc.list_firmware()
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
self._msg_process(node, e.message, msg_type='E', update_rc=True)
|
||||
return []
|
||||
|
||||
for key, value in firm_obj_dict.items():
|
||||
if value.version and value.version in version_list:
|
||||
firm_id = key.split('-')[-1]
|
||||
mapping_ids.append(firm_id)
|
||||
msg = 'Firmware upload successful. ' \
|
||||
'Attempting to activate firmware: %s (ID: %s)' \
|
||||
% (value.version, firm_id)
|
||||
self._msg_process(node, msg, update_rc=True)
|
||||
version_list.remove(value.version)
|
||||
|
||||
if not version_list:
|
||||
return mapping_ids
|
||||
for i in version_list:
|
||||
msg = 'Could not find ID for firmware %s to '\
|
||||
'activate, waiting %d seconds and retry...' \
|
||||
% (i, 10)
|
||||
self._msg_process(node, msg, update_rc=True, checkv=True)
|
||||
gevent.sleep ( 10 )
|
||||
|
||||
error = []
|
||||
for i in version_list:
|
||||
msg = 'Could not find firmware %s after waiting %d seconds.' % (i, 10*6)
|
||||
error.qppend(msg)
|
||||
self._msg_process(node, msg, msg_type='E')
|
||||
|
||||
if error:
|
||||
msg = ' '.join(error)
|
||||
self.activate_result.update({node: msg})
|
||||
return []
|
||||
|
||||
def _check_id_status(self, obmc, check_ids, node, only_act=True):
|
||||
|
||||
firm_ids = check_ids
|
||||
priority_ids = []
|
||||
process_status = {}
|
||||
for i in range(80):
|
||||
try:
|
||||
has_functional, firm_obj_dict = obmc.list_firmware()
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self._msg_process(node, e.message, msg_type='E', update_rc=True)
|
||||
|
||||
for key, value in firm_obj_dict.items():
|
||||
key_id = key.split('-')[-1]
|
||||
if key_id in firm_ids:
|
||||
activation_state = value.active
|
||||
firm_version = value.version
|
||||
if activation_state == 'Failed':
|
||||
activation_msg = 'Firmware %s activation failed.' % (firm_version)
|
||||
self._msg_process(node, activation_msg, msg_type='E', update_rc=True)
|
||||
firm_ids.remove(key_id)
|
||||
if activation_state == 'Active':
|
||||
activation_msg = 'Firmware %s activation successful.' % (firm_version)
|
||||
self._msg_process(node, activation_msg, update_rc=True)
|
||||
firm_ids.remove(key_id)
|
||||
if value.priority != 0:
|
||||
priority_ids.append(key_id)
|
||||
if activation_state == 'Activating':
|
||||
activating_progress_msg = 'Activating %s ... %s%%' \
|
||||
% (firm_version, value.progress)
|
||||
process_status[key_id] = activating_progress_msg
|
||||
self._msg_process(node, activating_progress_msg, checkv=True)
|
||||
|
||||
if not firm_ids:
|
||||
break
|
||||
gevent.sleep( 15 )
|
||||
|
||||
error = []
|
||||
for i in firm_ids:
|
||||
msg = 'After %d seconds check the firmware id %s current status is "%s"' \
|
||||
% (80*15, process_status[i], i)
|
||||
error.append(msg)
|
||||
self._msg_process(node, msg, msg_type='E')
|
||||
|
||||
if error:
|
||||
msg = ' '.join(error)
|
||||
self.activate_result.update({node: msg})
|
||||
return
|
||||
|
||||
for i in priority_ids:
|
||||
try:
|
||||
obmc.set_priority(i)
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
msg = e.message
|
||||
error.append(msg)
|
||||
self._msg_process(node, msg, msg_type='E')
|
||||
|
||||
if error:
|
||||
msg = ' '.join(error)
|
||||
self.activate_result.update({node: msg})
|
||||
return
|
||||
|
||||
self.activate_result.update({node: 'SUCCESS'})
|
||||
|
||||
def _reboot_to_effect(self, obmc, no_host_reboot, node):
|
||||
|
||||
self._msg_process(node, 'Firmware will be flashed on reboot, deleting all BMC diagnostics...')
|
||||
try:
|
||||
obmc.clear_dump('all')
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
self.callback.warn('%s: Could not clear BMC diagnostics successfully %s, ignoring...' % (node, e.message))
|
||||
|
||||
try:
|
||||
obmc.reboot_bmc()
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self._msg_process(node, e.message, msg_type='E', update_rc=True)
|
||||
|
||||
self._msg_process(node, openbmc.RPOWER_STATES['bmcreboot'], update_rc=True)
|
||||
|
||||
gevent.sleep( 10 )
|
||||
|
||||
bmc_state = None
|
||||
for i in range(20):
|
||||
try:
|
||||
obmc.login()
|
||||
state = obmc.get_bmc_state()
|
||||
bmc_state = state.get('bmc')
|
||||
|
||||
if bmc_state == 'Ready':
|
||||
break
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
self._msg_process(node, e.message, checkv=True)
|
||||
|
||||
self._msg_process(node, 'Retry BMC state, wait for 15 seconds ...', update_rc=True)
|
||||
gevent.sleep( 15 )
|
||||
|
||||
if bmc_state != 'Ready':
|
||||
error = 'Sent bmcreboot but state did not change to BMC Ready after ' \
|
||||
'waiting %s seconds. (State=BMC %s).' % (20*15, bmc_state)
|
||||
return self._msg_process(node, error, msg_type='E', update_rc=True)
|
||||
|
||||
self._msg_process(node, 'BMC %s' % bmc_state, update_rc=True)
|
||||
|
||||
if no_host_reboot:
|
||||
self.activate_result.update({node: 'SUCCESS'})
|
||||
return
|
||||
|
||||
try:
|
||||
obmc.set_power_state('off')
|
||||
self.callback.update_node_attributes('status', node, 'powering-off')
|
||||
|
||||
off_flag = False
|
||||
start_timeStamp = int(time.time())
|
||||
for i in range (0, 30):
|
||||
states = obmc.list_power_states()
|
||||
state = obmc.get_host_state(states)
|
||||
if openbmc.RPOWER_STATES.get(state) == 'off':
|
||||
off_flag = True
|
||||
break
|
||||
gevent.sleep( 2 )
|
||||
|
||||
end_timeStamp = int(time.time())
|
||||
|
||||
if not off_flag:
|
||||
error = 'Error: Sent power-off command but state did not change ' \
|
||||
'to off after waiting %s seconds. (State= %s).' % (end_timeStamp - start_timeStamp, status)
|
||||
return self._msg_process(node, error, update_rc=True)
|
||||
|
||||
ret = obmc.set_power_state('on')
|
||||
self.callback.update_node_attributes('status', node, 'powering-on')
|
||||
|
||||
self._msg_process(node, 'reset')
|
||||
self.activate_result.update({node: 'SUCCESS'})
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
self._msg_process(node, e.message, update_rc=True)
|
||||
|
||||
def activate_firm(self, activate_arg, **kw):
|
||||
|
||||
node = kw['node']
|
||||
obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback,
|
||||
debugmode=self.debugmode, verbose=self.verbose)
|
||||
|
||||
try:
|
||||
obmc.login()
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self._msg_process(node, e.message, msg_type='E', update_rc=True)
|
||||
|
||||
firmware_version = ''
|
||||
if self.firmware_file:
|
||||
firmware_version = self.firmware.keys()[0]
|
||||
try:
|
||||
obmc.upload_firmware(self.firmware_file)
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self._msg_process(node, e.message, msg_type='E', update_rc=True)
|
||||
|
||||
activate_ids = self._get_firm_id(obmc, node)
|
||||
if not activate_ids:
|
||||
return
|
||||
activate_id = activate_ids[0]
|
||||
else:
|
||||
activate_id = activate_arg
|
||||
|
||||
error = ''
|
||||
try:
|
||||
obmc.activate_firmware(activate_id)
|
||||
except SelfServerException as e:
|
||||
error = e.message
|
||||
except SelfClientException as e:
|
||||
if e.code == 403:
|
||||
error = 'Invalid ID provided to activate. ' \
|
||||
'Use the -l option to view valid firmware IDs.'
|
||||
else:
|
||||
error = e.message
|
||||
if error:
|
||||
return self._msg_process(node, error, msg_type='E', update_rc=True)
|
||||
|
||||
msg = 'rflash %s started, please wait...' % firmware_version
|
||||
self._msg_process(node, msg, checkv=True)
|
||||
|
||||
check_ids = [activate_id]
|
||||
self._check_id_status(obmc, check_ids, node)
|
||||
|
||||
def delete_firm(self, delete_id, **kw):
|
||||
|
||||
node = kw['node']
|
||||
obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback,
|
||||
debugmode=self.debugmode, verbose=self.verbose)
|
||||
|
||||
error = ''
|
||||
try:
|
||||
obmc.login()
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self.callback.error(e.message, node)
|
||||
|
||||
try:
|
||||
has_functional, firm_obj_dict = obmc.list_firmware()
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self.callback.error(e.message, node)
|
||||
|
||||
host_flag = False
|
||||
for key, value in firm_obj_dict.items():
|
||||
key_id = key.split('-')[-1]
|
||||
if key_id != delete_id:
|
||||
continue
|
||||
if value.functional or (value.priority == 0 and not has_functional):
|
||||
if value.purpose == 'BMC':
|
||||
return self.callback.error('Deleting currently active BMC firmware' \
|
||||
' is not supported', node)
|
||||
elif value.purpose == 'Host':
|
||||
host_flag = True
|
||||
break
|
||||
else:
|
||||
self.callback.error('Unable to determine the purpose of the ' \
|
||||
'firmware to delete', node)
|
||||
|
||||
if host_flag:
|
||||
try:
|
||||
states = obmc.list_power_states()
|
||||
state = obmc.get_host_state(states)
|
||||
if openbmc.RPOWER_STATES.get(state) == 'on':
|
||||
return self.callback.error('Deleting currently active firmware on' \
|
||||
' powered on host is not supported', node)
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self.callback.error(e.message, node)
|
||||
|
||||
try:
|
||||
obmc.delete_firmware(delete_id)
|
||||
except SelfServerException as e:
|
||||
error = e.message
|
||||
except SelfClientException as e:
|
||||
if e.code == 404:
|
||||
error = 'Invalid ID provided to delete. ' \
|
||||
'Use the -l option to view valid firmware IDs.'
|
||||
else:
|
||||
error = e.message
|
||||
|
||||
if error:
|
||||
self.callback.error(error, node)
|
||||
else:
|
||||
self.callback.info('%s: [%s] Firmware removed' % (node, delete_id))
|
||||
|
||||
def flash_process(self, directory, no_host_reboot, **kw):
|
||||
|
||||
node = kw['node']
|
||||
obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback,
|
||||
debugmode=self.debugmode, verbose=self.verbose)
|
||||
|
||||
try:
|
||||
obmc.login()
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self._msg_process(node, e.message, msg_type='E', update_rc=True)
|
||||
|
||||
try:
|
||||
for key, value in self.firmware.items():
|
||||
obmc.upload_firmware(value['file'])
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self._msg_process(node, e.message, msg_type='E', update_rc=True)
|
||||
|
||||
activate_ids = self._get_firm_id(obmc, node)
|
||||
if not activate_ids:
|
||||
return
|
||||
|
||||
for i in activate_ids:
|
||||
error = ''
|
||||
try:
|
||||
obmc.activate_firmware(i)
|
||||
except SelfServerException as e:
|
||||
error = e.message
|
||||
except SelfClientException as e:
|
||||
if e.code == 403:
|
||||
error = 'Invalid ID %s provided to activate. Use the -l option ' \
|
||||
'to view valid firmware IDs.' % i
|
||||
else:
|
||||
error = e.message
|
||||
if error:
|
||||
return self._msg_process(node, error, msg_type='E', update_rc=True)
|
||||
|
||||
for key in self.firmware:
|
||||
msg = 'rflash %s started, please wait...' % key
|
||||
self._msg_process(node, msg, checkv=True)
|
||||
|
||||
self._check_id_status(obmc, activate_ids, node, only_act=False)
|
||||
|
||||
self._reboot_to_effect(obmc, no_host_reboot, node)
|
||||
|
||||
def list_firm_info(self, **kw):
|
||||
|
||||
node = kw['node']
|
||||
obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback,
|
||||
debugmode=self.debugmode, verbose=self.verbose)
|
||||
|
||||
firm_info = []
|
||||
try:
|
||||
obmc.login()
|
||||
has_functional, firm_obj_dict = obmc.list_firmware()
|
||||
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self.callback.error(e.message, node)
|
||||
|
||||
firm_info.append('%-8s %-7s %-10s %-s' % ('ID', 'Purpose', 'State', 'Version'))
|
||||
firm_info.append('-' * 55)
|
||||
|
||||
for key, value in firm_obj_dict.items():
|
||||
status = value.active
|
||||
if value.functional:
|
||||
status += '(*)'
|
||||
elif value.priority == 0:
|
||||
if not has_functional:
|
||||
status += '(*)'
|
||||
else:
|
||||
status += '(+)'
|
||||
|
||||
firm_info.append('%-8s %-7s %-10s %-s' % (key.split('-')[-1],
|
||||
value.purpose, status, value.version))
|
||||
|
||||
for info in firm_info:
|
||||
self.callback.info('%s: %s' % (node, info))
|
||||
|
||||
return firm_info
|
||||
|
||||
def upload_firm(self, upload_file, **kw):
|
||||
|
||||
node = kw['node']
|
||||
obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback,
|
||||
debugmode=self.debugmode, verbose=self.verbose)
|
||||
|
||||
try:
|
||||
obmc.login()
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self._msg_process(node, e.message, msg_type='E')
|
||||
|
||||
try:
|
||||
obmc.upload_firmware(self.firmware_file)
|
||||
self._msg_process(node, 'Firmware upload successful. Use -l option to list.')
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
return self._msg_process(node, e.message, msg_type='E')
|
||||
|
||||
def _flash_summary(self):
|
||||
|
||||
if not self.activate_result:
|
||||
return self.callback.error('No summary infomation')
|
||||
|
||||
success_num = failed_num = 0
|
||||
failed_list = []
|
||||
for key, value in self.activate_result.items():
|
||||
if value == 'SUCCESS':
|
||||
success_num += 1
|
||||
else:
|
||||
failed_num += 1
|
||||
failed_list.append('%s: %s' % (key, value))
|
||||
|
||||
self.callback.info('-' * 55)
|
||||
self.callback.info('%s complete: Total=%d Success=%d Failed=%d' % \
|
||||
('Firmware update', self.nodes_num, success_num, failed_num))
|
||||
|
||||
for i in failed_list:
|
||||
self.callback.info(i)
|
||||
self.callback.info('-' * 55)
|
||||
|
||||
def post_activate_firm(self, task, activate_arg, **kw):
|
||||
|
||||
self._flash_summary()
|
||||
|
||||
def post_flash_process(self, task, directory, no_host_reboot, **kw):
|
||||
|
||||
self._flash_summary()
|
@ -8,6 +8,7 @@
|
||||
from __future__ import print_function
|
||||
import gevent
|
||||
import time
|
||||
import os
|
||||
|
||||
from common.task import ParallelNodesCommand
|
||||
from common.exceptions import SelfClientException, SelfServerException
|
||||
@ -20,6 +21,30 @@ logger = logging.getLogger('xcatagent')
|
||||
class OpenBMCInventoryTask(ParallelNodesCommand):
|
||||
"""Executor for inventory-related actions."""
|
||||
|
||||
def pre_get_firm_info(self, task, target_file=None, **kw):
|
||||
|
||||
if not target_file:
|
||||
return
|
||||
|
||||
target_file = utils.get_full_path(self.cwd, target_file)
|
||||
|
||||
grep_cmd = '/usr/bin/grep -a'
|
||||
version_cmd = grep_cmd + ' ^version= ' + target_file
|
||||
purpose_cmd = grep_cmd + ' purpose= ' + target_file
|
||||
purpose_ver = os.popen(purpose_cmd).readlines()
|
||||
firmware_ver = os.popen(version_cmd).readlines()
|
||||
if purpose_ver:
|
||||
purpose_ver = purpose_ver[0].split('=')[-1].strip()
|
||||
else:
|
||||
purpose_ver = ''
|
||||
if firmware_ver:
|
||||
firmware_ver = firmware_ver[0].split('=')[-1].strip()
|
||||
else:
|
||||
firmware_ver = ''
|
||||
|
||||
self.callback.info('TAR %s Firmware Product Version: %s' \
|
||||
% (purpose_ver,firmware_ver))
|
||||
|
||||
def _get_firm_info(self, firm_info_list):
|
||||
(has_functional, firm_obj_dict) = firm_info_list
|
||||
firm_info = []
|
||||
@ -97,7 +122,7 @@ class OpenBMCInventoryTask(ParallelNodesCommand):
|
||||
|
||||
return inventory_info
|
||||
|
||||
def get_firm_info(self, **kw):
|
||||
def get_firm_info(self, target_file=None, **kw):
|
||||
|
||||
node = kw['node']
|
||||
obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback,
|
||||
|
@ -26,25 +26,6 @@ POWER_STATE_DB = {
|
||||
class OpenBMCPowerTask(ParallelNodesCommand):
|
||||
"""Executor for power-related actions."""
|
||||
|
||||
def _determine_state(self, states):
|
||||
|
||||
chassis_state = states.get('chassis')
|
||||
host_state = states.get('host')
|
||||
state = 'Unknown'
|
||||
if chassis_state == 'Off':
|
||||
state = chassis_state
|
||||
|
||||
elif chassis_state == 'On':
|
||||
if host_state == 'Off':
|
||||
state = 'chassison'
|
||||
elif host_state in ['Quiesced', 'Running']:
|
||||
state = host_state
|
||||
else:
|
||||
state = 'Unexpected host state=%s' % host_state
|
||||
else:
|
||||
state = 'Unexpected chassis state=%s' % chassis_state
|
||||
return state
|
||||
|
||||
def get_state(self, **kw):
|
||||
|
||||
node = kw['node']
|
||||
@ -54,7 +35,7 @@ class OpenBMCPowerTask(ParallelNodesCommand):
|
||||
try:
|
||||
obmc.login()
|
||||
states = obmc.list_power_states()
|
||||
state = self._determine_state(states)
|
||||
state = obmc.get_host_state(states)
|
||||
self.callback.info('%s: %s' % (node, openbmc.RPOWER_STATES.get(state, state)))
|
||||
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
@ -115,7 +96,7 @@ class OpenBMCPowerTask(ParallelNodesCommand):
|
||||
try:
|
||||
obmc.login()
|
||||
states = obmc.list_power_states()
|
||||
status = self._determine_state(states)
|
||||
status = obmc.get_host_state(states)
|
||||
|
||||
new_status =''
|
||||
if optype == 'reset' and status in ['Off', 'chassison']:
|
||||
@ -130,7 +111,7 @@ class OpenBMCPowerTask(ParallelNodesCommand):
|
||||
start_timeStamp = int(time.time())
|
||||
for i in range (0, 30):
|
||||
states = obmc.list_power_states()
|
||||
status = self._determine_state(states)
|
||||
status = obmc.get_host_state(states)
|
||||
if openbmc.RPOWER_STATES.get(status) == 'off':
|
||||
off_flag = True
|
||||
break
|
||||
|
54
xCAT-openbmc-py/lib/python/agent/hwctl/flash.py
Normal file
54
xCAT-openbmc-py/lib/python/agent/hwctl/flash.py
Normal file
@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python
|
||||
###############################################################################
|
||||
# IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html
|
||||
###############################################################################
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
class FlashInterface(object):
|
||||
"""Interface for flash-related actions."""
|
||||
interface_type = 'flash'
|
||||
version = '1.0'
|
||||
|
||||
def activate_firm(self, task, activate_arg):
|
||||
"""Activate firmware.
|
||||
|
||||
:param task: a Task instance containing the nodes to act on.
|
||||
:activate_arg: arg for activate
|
||||
"""
|
||||
return task.run('activate_firm', activate_arg)
|
||||
|
||||
def delete_firm(self, task, delete_id):
|
||||
"""Delete firmware.
|
||||
|
||||
:param task: a Task instance containing the nodes to act on.
|
||||
:param delete_id: firmware id want to delete
|
||||
"""
|
||||
return task.run('delete_firm', delete_id)
|
||||
|
||||
def flash_process(self, task, directory, no_host_reboot):
|
||||
"""Upload and activate firmware
|
||||
|
||||
:param task: a Task instance containing the nodes to act on.
|
||||
:directory: firmware directory
|
||||
"""
|
||||
return task.run('flash_process', directory, no_host_reboot)
|
||||
|
||||
def list_firm_info(self, task):
|
||||
"""List firmware
|
||||
|
||||
:param task: a Task instance containing the nodes to act on.
|
||||
"""
|
||||
return task.run('list_firm_info')
|
||||
|
||||
def upload_firm(self, task, upload_file):
|
||||
"""Upload firmware file.
|
||||
|
||||
:param task: a Task instance containing the nodes to act on.
|
||||
:param upload_file: the file want to upload
|
||||
"""
|
||||
return task.run('upload_firm', upload_file)
|
||||
|
||||
class DefaultFlashManager(FlashInterface):
|
||||
"""Interface for flash-related actions."""
|
||||
pass
|
@ -19,13 +19,14 @@ class InventoryInterface(object):
|
||||
"""
|
||||
return task.run('get_info', inventory_type)
|
||||
|
||||
def get_firm_info(self, task):
|
||||
def get_firm_info(self, task, target_arg=None):
|
||||
"""Return the firm info of the task's nodes.
|
||||
|
||||
:param task: a Task instance containing the nodes to act on.
|
||||
:param check_arg: firmware file to check, for rflash check
|
||||
:return firm info list
|
||||
"""
|
||||
return task.run('get_firm_info')
|
||||
return task.run('get_firm_info', target_arg)
|
||||
|
||||
class DefaultInventoryManager(InventoryInterface):
|
||||
"""Interface for inventory-related actions."""
|
||||
|
@ -145,15 +145,15 @@ BOOTSOURCE_SET_STATE = {
|
||||
|
||||
FIRM_URLS = {
|
||||
"activate" : {
|
||||
"path" : "/software/%(id)s/attr/RequestedActivation",
|
||||
"field" : "Software.Activation.RequestedActivations.Active",
|
||||
"path" : "/software/%s/attr/RequestedActivation",
|
||||
"field" : "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
|
||||
},
|
||||
"delete" : {
|
||||
"path" : "/software/%(id)s/action/Delete",
|
||||
"path" : "/software/%s/action/Delete",
|
||||
"field" : [],
|
||||
},
|
||||
"priority" : {
|
||||
"path" : "/software/%(id)s/attr/Priority",
|
||||
"path" : "/software/%s/attr/Priority",
|
||||
"field" : False,
|
||||
},
|
||||
"list" : {
|
||||
@ -426,6 +426,25 @@ class OpenBMCRest(object):
|
||||
# TODO: Need special handling for bmc reset, as it is normal bmc may return error
|
||||
pass
|
||||
|
||||
def get_host_state(self, states):
|
||||
|
||||
chassis_state = states.get('chassis')
|
||||
host_state = states.get('host')
|
||||
state = 'Unknown'
|
||||
if chassis_state == 'Off':
|
||||
state = chassis_state
|
||||
|
||||
elif chassis_state == 'On':
|
||||
if host_state == 'Off':
|
||||
state = 'chassison'
|
||||
elif host_state in ['Quiesced', 'Running']:
|
||||
state = host_state
|
||||
else:
|
||||
state = 'Unexpected host state=%s' % host_state
|
||||
else:
|
||||
state = 'Unexpected chassis state=%s' % chassis_state
|
||||
return state
|
||||
|
||||
def set_one_time_boot_enable(self, enabled):
|
||||
|
||||
payload = { "data": enabled }
|
||||
@ -542,6 +561,18 @@ class OpenBMCRest(object):
|
||||
error = 'Received wrong format response: %s' % inventory_data
|
||||
raise SelfServerException(error)
|
||||
|
||||
def activate_firmware(self, activate_id):
|
||||
|
||||
payload = { "data": FIRM_URLS['activate']['field'] }
|
||||
url = FIRM_URLS['activate']['path'] % activate_id
|
||||
return self.request('PUT', url, payload=payload, cmd='activate_firmware')
|
||||
|
||||
def delete_firmware(self, delete_id):
|
||||
|
||||
payload = { "data": FIRM_URLS['delete']['field'] }
|
||||
url = FIRM_URLS['delete']['path'] % delete_id
|
||||
return self.request('POST', url, payload=payload, cmd='delete_firmware')
|
||||
|
||||
def list_firmware(self):
|
||||
|
||||
data = self.request('GET', FIRM_URLS['list']['path'], cmd='list_firmware')
|
||||
@ -564,6 +595,18 @@ class OpenBMCRest(object):
|
||||
|
||||
return bool(func_list), fw_dict
|
||||
|
||||
def upload_firmware(self, upload_file):
|
||||
|
||||
headers = {'Content-Type': 'application/octet-stream'}
|
||||
path = HTTP_PROTOCOL + self.bmcip + '/upload/image/'
|
||||
self.upload('PUT', path, upload_file, headers=headers, cmd='upload_firmware')
|
||||
|
||||
def set_priority(self, firm_id):
|
||||
|
||||
payload = { "data": FIRM_URLS['priority']['field'] }
|
||||
url = FIRM_URLS['priority']['path'] % firm_id
|
||||
return self.request('PUT', url, payload=payload, cmd='set_priority')
|
||||
|
||||
# Extract all eventlog info and parse it
|
||||
def get_eventlog_info(self):
|
||||
|
||||
|
@ -16,6 +16,7 @@ from common import utils
|
||||
from common import exceptions as xcat_exception
|
||||
from hwctl.executor.openbmc_beacon import OpenBMCBeaconTask
|
||||
from hwctl.executor.openbmc_setboot import OpenBMCBootTask
|
||||
from hwctl.executor.openbmc_flash import OpenBMCFlashTask
|
||||
from hwctl.executor.openbmc_inventory import OpenBMCInventoryTask
|
||||
from hwctl.executor.openbmc_power import OpenBMCPowerTask
|
||||
from hwctl.executor.openbmc_sensor import OpenBMCSensorTask
|
||||
@ -23,6 +24,7 @@ from hwctl.executor.openbmc_bmcconfig import OpenBMCBmcConfigTask
|
||||
from hwctl.executor.openbmc_eventlog import OpenBMCEventlogTask
|
||||
from hwctl.beacon import DefaultBeaconManager
|
||||
from hwctl.setboot import DefaultBootManager
|
||||
from hwctl.flash import DefaultFlashManager
|
||||
from hwctl.inventory import DefaultInventoryManager
|
||||
from hwctl.power import DefaultPowerManager
|
||||
from hwctl.sensor import DefaultSensorManager
|
||||
@ -30,7 +32,6 @@ from hwctl.bmcconfig import DefaultBmcConfigManager
|
||||
from hwctl.eventlog import DefaultEventlogManager
|
||||
|
||||
from xcatagent import base
|
||||
import openbmc_rest
|
||||
import logging
|
||||
logger = logging.getLogger('xcatagent')
|
||||
if not logger.handlers:
|
||||
@ -50,38 +51,6 @@ all_nodes_result = {}
|
||||
# global variables of rbeacon
|
||||
BEACON_SET_OPTIONS = ('on', 'off')
|
||||
|
||||
# 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,
|
||||
}
|
||||
}
|
||||
|
||||
RSPCONFIG_GET_OPTIONS = ['ip','ipsrc','netmask','gateway','vlan','ntpservers','hostname','bootmode','autoreboot','powersupplyredundancy','powerrestorepolicy']
|
||||
RSPCONFIG_SET_OPTIONS = {
|
||||
'ip':'.*',
|
||||
@ -126,12 +95,6 @@ The supported attributes and its values to set are:
|
||||
powerrestorepolicy={always_on|always_off|restore}
|
||||
""" % RSPCONFIG_GET_OPTIONS
|
||||
|
||||
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 rinv
|
||||
INVENTORY_OPTIONS = ('all', 'cpu', 'dimm', 'firm', 'model', 'serial')
|
||||
|
||||
@ -151,452 +114,6 @@ VITALS_OPTIONS = ('all', 'altitude', 'fanspeed', 'leds', 'power',
|
||||
# global variables of reventlog
|
||||
EVENTLOG_OPTIONS = ('list', 'clear', 'resolved')
|
||||
|
||||
class OpenBMC(base.BaseDriver):
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
def __init__(self, messager, name, node_info):
|
||||
super(OpenBMC, self).__init__(messager)
|
||||
self.node = name
|
||||
for key, value in node_info.items():
|
||||
setattr(self, key, value)
|
||||
global DEBUGMODE
|
||||
self.client = openbmc_rest.OpenBMCRest(name, messager, DEBUGMODE)
|
||||
|
||||
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, 'login')
|
||||
return RESULT_OK
|
||||
|
||||
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 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')
|
||||
|
||||
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()
|
||||
|
||||
|
||||
class OpenBMCManager(base.BaseManager):
|
||||
def __init__(self, messager, cwd, nodes=None, envs=None):
|
||||
super(OpenBMCManager, self).__init__(messager, cwd)
|
||||
@ -642,6 +159,51 @@ class OpenBMCManager(base.BaseManager):
|
||||
runner = OpenBMCBeaconTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose)
|
||||
DefaultBeaconManager().set_beacon_state(runner, beacon_state=action)
|
||||
|
||||
def rflash(self, nodesinfo, args):
|
||||
|
||||
# 1, parse agrs
|
||||
rflash_usage = """
|
||||
Usage:
|
||||
rflash [[-a|--activate <arg>] | [-c|--check <arg>] | [-d <arg> [--no-host-reboot]] | [--delete <arg>] | [-l|--list] | [-u|--upload <arg>]] [-V|--verbose]
|
||||
|
||||
Options:
|
||||
-V,--verbose Show verbose message
|
||||
-a,--activate <arg> Activate firmware
|
||||
-c,--check Check firmware info
|
||||
-d <arg> Upload and activate all firmware files under directory
|
||||
-l,--list List firmware info
|
||||
-u,--upload <arg> Upload firmware file
|
||||
--delete <arg> Delete firmware
|
||||
--no-host-reboot Not reboot host after activate
|
||||
"""
|
||||
|
||||
try:
|
||||
opts = docopt(rflash_usage, argv=args)
|
||||
self.verbose = opts.pop('--verbose')
|
||||
except Exception as e:
|
||||
self.messager.error("Failed to parse arguments for rflash: %s" % args)
|
||||
return
|
||||
|
||||
if opts['--check']:
|
||||
check_arg = None
|
||||
if opts['<arg>']:
|
||||
check_arg = opts['<arg>']
|
||||
runner = runner = OpenBMCInventoryTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose, cwd=self.cwd[0])
|
||||
DefaultInventoryManager().get_firm_info(runner, check_arg)
|
||||
return
|
||||
|
||||
runner = OpenBMCFlashTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose, cwd=self.cwd[0])
|
||||
if opts['--activate']:
|
||||
DefaultFlashManager().activate_firm(runner, opts['--activate'][0])
|
||||
elif opts['--list']:
|
||||
DefaultFlashManager().list_firm_info(runner)
|
||||
elif opts['-d']:
|
||||
DefaultFlashManager().flash_process(runner, opts['-d'], opts['--no-host-reboot'])
|
||||
elif opts['--delete']:
|
||||
DefaultFlashManager().delete_firm(runner, opts['--delete'])
|
||||
elif opts['--upload']:
|
||||
DefaultFlashManager().upload_firm(runner, opts['--upload'][0])
|
||||
|
||||
def rinv(self, nodesinfo, args):
|
||||
|
||||
# 1, parse agrs
|
||||
@ -715,6 +277,7 @@ class OpenBMCManager(base.BaseManager):
|
||||
DefaultPowerManager().reboot(runner, optype=action)
|
||||
else:
|
||||
DefaultPowerManager().set_power_state(runner, power_state=action)
|
||||
|
||||
def rspconfig(self, nodesinfo, args):
|
||||
try:
|
||||
opts=docopt(RSPCONFIG_USAGE, argv=args)
|
||||
@ -768,6 +331,7 @@ class OpenBMCManager(base.BaseManager):
|
||||
DefaultBmcConfigManager().set_attributes(runner, opts['<args>'])
|
||||
else:
|
||||
self.messager.error("Failed to deal with rspconfig: %s" % args)
|
||||
|
||||
def rsetboot(self, nodesinfo, args):
|
||||
|
||||
# 1, parse args
|
||||
@ -884,98 +448,3 @@ class OpenBMCManager(base.BaseManager):
|
||||
else:
|
||||
DefaultEventlogManager().get_eventlog_info(runner, "all")
|
||||
|
||||
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')
|
||||
|
||||
|
@ -1,113 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
|
||||
from common import rest
|
||||
from common import exceptions as 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
|
@ -231,7 +231,6 @@ sub parse_args {
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user