diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_inventory.py b/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_inventory.py new file mode 100644 index 000000000..58e3fc906 --- /dev/null +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_inventory.py @@ -0,0 +1,122 @@ +#!/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 + +from common.task import ParallelNodesCommand +from common.exceptions import SelfClientException, SelfServerException +from hwctl import openbmc_client as openbmc +from common import utils + +import logging +logger = logging.getLogger('xcatagent') + +class OpenBMCInventoryTask(ParallelNodesCommand): + """Executor for inventory-related actions.""" + + def _get_firm_info(self, firm_info_list): + (has_functional, firm_obj_dict) = firm_info_list + firm_info = [] + keys = firm_obj_dict.keys() + keys.sort() + for key in keys: + flag = '' + if firm_obj_dict[key].functional: + flag = '*' + elif firm_obj_dict[key].priority == 0: + if not has_functional: + flag = '*' + else: + flag = '+' + + if flag != '*' and not self.verbose: + continue + + firm_info.append('%s Firmware Product: %s (%s)%s' % + (firm_obj_dict[key].purpose, + firm_obj_dict[key].version, + firm_obj_dict[key].active, + flag)) + if firm_obj_dict[key].extver: + extendeds = firm_obj_dict[key].extver.split(',') + extendeds.sort() + for extended in extendeds: + firm_info.append('%s Firmware Product: ' \ + '-- additional info: %s' % \ + (firm_obj_dict[key].purpose, extended)) + + return firm_info + + def get_info(self, inventory_type, **kw): + + node = kw['node'] + obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, + debugmode=self.debugmode, verbose=self.verbose) + + inventory_info = [] + try: + obmc.login() + inventory_info_dict = obmc.get_inventory_info() + + if inventory_type == 'all' or not inventory_type: + keys = inventory_info_dict.keys() + keys.sort() + for key in keys: + inventory_info += utils.sort_string_with_numbers(inventory_info_dict[key]) + + firm_dict_list = obmc.list_firmware() + firm_info = self._get_firm_info(firm_dict_list) + + inventory_info += firm_info + elif inventory_type == 'model' or inventory_type == 'serial': + key = 'Model' if inventory_type == 'model' else 'SerialNumber' + if 'SYSTEM' in inventory_info_dict: + for system_info in inventory_info_dict['SYSTEM']: + if key in system_info: + inventory_info = [system_info] + break + else: + key = inventory_type.upper() + if key in inventory_info_dict: + inventory_info = utils.sort_string_with_numbers(inventory_info_dict[key]) + + if not inventory_info: + inventory_info = ['No attributes returned from the BMC.'] + + for info in inventory_info: + self.callback.info( '%s: %s' % (node, info)) + + except (SelfServerException, SelfClientException) as e: + self.callback.info('%s: %s' % (node, e.message)) + + return inventory_info + + def get_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() + firm_dict_list = obmc.list_firmware() + firm_info = self._get_firm_info(firm_dict_list) + + for info in firm_info: + self.callback.info( '%s: %s' % (node, info)) + + except (SelfServerException, SelfClientException) as e: + self.callback.info('%s: %s' % (node, e.message)) + + return firm_info + + + + diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/inventory.py b/xCAT-openbmc-py/lib/python/agent/hwctl/inventory.py new file mode 100644 index 000000000..9a85daa09 --- /dev/null +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/inventory.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +############################################################################### +# IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html +############################################################################### +# -*- coding: utf-8 -*- +# + +class InventoryInterface(object): + """Interface for inventory-related actions.""" + interface_type = 'inventory' + version = '1.0' + + def get_inventory_info(self, task, inventory_type=None): + """Return the inventory info of the task's nodes. + + :param inventory_type: type of inventory info want to get. + :param task: a Task instance containing the nodes to act on. + :return inventory info list + """ + return task.run('get_info', inventory_type) + + def get_firm_info(self, task): + """Return the firm info of the task's nodes. + + :param task: a Task instance containing the nodes to act on. + :return firm info list + """ + return task.run('get_firm_info') + +class DefaultInventoryManager(InventoryInterface): + """Interface for inventory-related actions.""" + pass diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py b/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py index caa8c3cda..94ba4ea60 100644 --- a/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py @@ -29,6 +29,8 @@ RBEACON_URLS = { }, } +INVENTORY_URL = "/inventory/enumerate" + LEDS_URL = "/led/physical/enumerate" LEDS_KEY_LIST = ("fan0", "fan1", "fan2", "fan3", @@ -382,6 +384,43 @@ class OpenBMCRest(object): error = 'Error: Received wrong format response: %s' % sensor_data raise SelfServerException(error) + def get_inventory_info(self): + + inventory_data = self.request('GET', INVENTORY_URL, cmd='get_inventory_info') + try: + inverntory_dict = {} + for key, value in inventory_data.items(): + if 'Present' not in value: + logger.debug('Not "Present" for %s' % key) + continue + + key_list = key.split('/') + try: + key_id = key_list[-1] + key_tmp = key_list[-2] + except IndexError: + logger.debug('IndexError (-2) for %s' % key) + continue + + key_type = filter(lambda x:x not in '0123456789', key_id).upper() + + if key_type == 'CORE': + key_type = 'CPU' + source = '%s %s' % (key_tmp, key_id) + else: + source = key_id + + if key_type not in inverntory_dict: + inverntory_dict[key_type] = [] + + for (sub_key, v) in value.items(): + inverntory_dict[key_type].append('%s %s : %s' % (source.upper(), sub_key, v)) + + return inverntory_dict + except KeyError: + error = 'Error: Received wrong format response: %s' % inventory_data + raise SelfServerException(error) + def list_firmware(self): data = self.request('GET', FIRM_URLS['list']['path'], cmd='list_firmware') @@ -407,8 +446,12 @@ class OpenBMCRest(object): class OpenBMCImage(object): def __init__(self, rawid, data=None): self.id = rawid.split('/')[-1] + self.extver = None self.functional = False + self.priority = None + self.progress = None self.purpose = 'Unknown' + if data: self.version = data.get('Version') self.purpose = data.get('Purpose', self.purpose).split('.')[-1] diff --git a/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py b/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py index 82ec20b8f..0591a217d 100644 --- a/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py +++ b/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py @@ -14,12 +14,14 @@ from docopt import docopt from common import utils from common import exceptions as xcat_exception from hwctl.executor.openbmc_beacon import OpenBMCBeaconTask -from hwctl.executor.openbmc_power import OpenBMCPowerTask from hwctl.executor.openbmc_setboot import OpenBMCBootTask +from hwctl.executor.openbmc_inventory import OpenBMCInventoryTask +from hwctl.executor.openbmc_power import OpenBMCPowerTask from hwctl.executor.openbmc_sensor import OpenBMCSensorTask from hwctl.beacon import DefaultBeaconManager -from hwctl.power import DefaultPowerManager from hwctl.setboot import DefaultBootManager +from hwctl.inventory import DefaultInventoryManager +from hwctl.power import DefaultPowerManager from hwctl.sensor import DefaultSensorManager from xcatagent import base @@ -81,6 +83,9 @@ 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') + # global variables of rpower POWER_REBOOT_OPTIONS = ('boot', 'reset') POWER_SET_OPTIONS = ('on', 'off', 'bmcreboot', 'softoff') @@ -585,6 +590,41 @@ 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 rinv(self, nodesinfo, args): + + # 1, parse agrs + if not args: + args = ['all'] + + rinv_usage = """ + Usage: + rinv [-V|--verbose] [all|cpu|dimm|firm|model|serial] + + Options: + -V --verbose rinv verbose mode. + """ + + try: + opts = docopt(rinv_usage, argv=args) + + self.verbose = opts.pop('--verbose') + action = [k for k,v in opts.items() if v][0] + except Exception as e: + self.messager.error("Failed to parse arguments for rinv: %s" % args) + return + + # 2, validate the args + if action not in INVENTORY_OPTIONS: + self.messager.error("Not supported subcommand for rinv: %s" % action) + return + + # 3, run the subcommands + runner = OpenBMCInventoryTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose) + if action == 'firm': + DefaultInventoryManager().get_firm_info(runner) + else: + DefaultInventoryManager().get_inventory_info(runner, action) + def rpower(self, nodesinfo, args): # 1, parse args diff --git a/xCAT-server/lib/xcat/plugins/openbmc2.pm b/xCAT-server/lib/xcat/plugins/openbmc2.pm index 584e801e9..98be371f6 100644 --- a/xCAT-server/lib/xcat/plugins/openbmc2.pm +++ b/xCAT-server/lib/xcat/plugins/openbmc2.pm @@ -32,6 +32,7 @@ sub handled_commands { return { rbeacon => 'nodehm:mgt=openbmc', rflash => 'nodehm:mgt=openbmc', + rinv => 'nodehm:mgt=openbmc', rpower => 'nodehm:mgt=openbmc', rsetboot => 'nodehm:mgt=openbmc', rvitals => 'nodehm:mgt=openbmc', @@ -147,7 +148,7 @@ sub parse_args { return ([ 1, "Error parsing arguments." ]); } - if (scalar(@ARGV) >= 2 and ($command =~ /rbeacon|rpower|rvitals/)) { + if (scalar(@ARGV) >= 2 and ($command =~ /rbeacon|rinv|rpower|rvitals/)) { return ([ 1, "Only one option is supported at the same time for $command" ]); } elsif (scalar(@ARGV) == 0 and $command =~ /rbeacon|rpower|rflash/) { return ([ 1, "No option specified for $command" ]); @@ -208,6 +209,11 @@ sub parse_args { if ($list) { return ([ 1, "Invalid option specified with '-l|--list'."]) if (@ARGV); } + } elsif ($command eq "rinv") { + $subcommand = "all" if (!defined($ARGV[0])); + unless ($subcommand =~ /^all$|^cpu$|^dimm$|^firm$|^model$|^serial$/) { + return ([ 1, "Unsupported command: $command $subcommand" ]); + } } elsif ($command eq "rpower") { unless ($subcommand =~ /^on$|^off$|^softoff$|^reset$|^boot$|^bmcreboot$|^bmcstate$|^status$|^stat$|^state$/) { return ([ 1, "Unsupported command: $command $subcommand" ]);