2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-22 03:32:04 +00:00

rbeacon & rvitals for openbmc in python (#4795)

* rbeacon & rvitals for openbmc in python
This commit is contained in:
xuweibj 2018-02-06 18:24:06 +08:00 committed by Bin Xu
parent 09ee65672c
commit ce3a32fdeb
8 changed files with 393 additions and 4 deletions

View File

@ -7,6 +7,7 @@
import struct
import sys
import inspect
import re
import logging
from logging.handlers import SysLogHandler
@ -70,6 +71,17 @@ def update2Ddict(updata_dict, key_a, key_b, value):
else:
updata_dict.update({key_a: {key_b: value}})
def emb_numbers(string):
re_digits = re.compile(r'(\d+)')
pieces = re_digits.split(string)
pieces[1::2] = map(int,pieces[1::2])
return pieces
def sort_string_with_numbers(origin_list):
new_list = [(emb_numbers(string),string) for string in origin_list]
new_list.sort()
return [string for __,string in new_list]
class Messager(object):
def __init__(self, name=None):
self.logger = logging.getLogger(name or 'xcatagent')

View File

@ -0,0 +1,26 @@
#!/usr/bin/env python
###############################################################################
# IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html
###############################################################################
# -*- coding: utf-8 -*-
#
class BeaconInterface(object):
"""Interface for beacon-related actions."""
interface_type = 'beacon'
version = '1.0'
def set_beacon_state(self, task, beacon_state, timeout=None):
"""Set the beacon state of the task's nodes.
:param task: a Task instance containing the nodes to act on.
:param beacon_state: on|off beacon state.
:param timeout: timeout (in seconds) positive integer (> 0) for any
beacon state. ``None`` indicates to use default timeout.
"""
return task.run('set_state', beacon_state, timeout=timeout)
class DefaultBeaconManager(BeaconInterface):
"""Interface for beacon-related actions."""
pass

View File

@ -0,0 +1,35 @@
#!/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
import logging
logger = logging.getLogger('xcatagent')
class OpenBMCBeaconTask(ParallelNodesCommand):
"""Executor for beacon-related actions."""
def set_state(self, state, **kw):
node = kw['node']
obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback,
debugmode=self.debugmode, verbose=self.verbose)
try:
obmc.login()
obmc.set_beacon_state(state)
result = '%s: %s' % (node, state)
except (SelfServerException, SelfClientException) as e:
result = '%s: %s' % (node, e.message)
self.callback.info(result)

View File

@ -0,0 +1,118 @@
#!/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')
SENSOR_TYPE_UNIT = {
"altitude" : "Meters",
"fanspeed" : "RPMS",
"temp" : "DegreesC",
"voltage" : "Volts",
"wattage" : "Watts",
}
SENSOR_POWER_UNITS = ("Amperes", "Joules", "Watts")
class OpenBMCSensorTask(ParallelNodesCommand):
"""Executor for sensor-related actions."""
def _get_beacon_info(self, beacon_dict):
info_list = []
info_list.append('Front . . . . . : Power:%s Fault:%s Identify:%s' %
(beacon_dict.get('front_power', 'N/A'),
beacon_dict.get('front_fault', 'N/A'),
beacon_dict.get('front_id', 'N/A')))
if (beacon_dict.get('fan0', 'N/A') == 'Off' and beacon_dict.get('fan1', 'N/A') == 'Off' and
beacon_dict.get('fan2', 'N/A') == 'Off' and beacon_dict.get('fan3', 'N/A') == 'Off'):
info_list.append('Front Fans . . : No LEDs On')
else:
info_list.append('Front Fans . . : fan0:%s fan1:%s fan2:%s fan3:%s' %
(beacon_dict.get('fan0', 'N/A'), beacon_dict.get('fan1', 'N/A'),
beacon_dict.get('fan2', 'N/A'), beacon_dict.get('fan3', 'N/A')))
info_list.append('Rear . . . . . : Power:%s Fault:%s Identify:%s' %
(beacon_dict.get('rear_power', 'N/A'),
beacon_dict.get('rear_fault', 'N/A'),
beacon_dict.get('rear_id', 'N/A')))
return info_list
def get_sensor_info(self, sensor_type, **kw):
node = kw['node']
obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback,
debugmode=self.debugmode, verbose=self.verbose)
sensor_info = []
try:
obmc.login()
sensor_info_dict = obmc.get_sensor_info()
if sensor_type == 'all' or not sensor_type:
for sensor_key in sensor_info_dict:
sensor_info += sensor_info_dict[sensor_key]
sensor_info = utils.sort_string_with_numbers(sensor_info)
beacon_dict = obmc.get_beacon_info()
sensor_info += self._get_beacon_info(beacon_dict)
elif sensor_type == 'power':
for sensor_key in sensor_info_dict:
if sensor_key in SENSOR_POWER_UNITS:
sensor_info += sensor_info_dict[sensor_key]
sensor_info = utils.sort_string_with_numbers(sensor_info)
else:
sensor_unit = SENSOR_TYPE_UNIT[sensor_type]
if sensor_unit in sensor_info_dict:
sensor_info += sensor_info_dict[sensor_unit]
sensor_info = utils.sort_string_with_numbers(sensor_info)
if not sensor_info:
sensor_info = ['No attributes returned from the BMC.']
for info in sensor_info:
self.callback.info( '%s: %s' % (node, info))
except (SelfServerException, SelfClientException) as e:
self.callback.info('%s: %s' % (node, e.message))
return sensor_info
def get_beacon_info(self, **kw):
node = kw['node']
obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback,
debugmode=self.debugmode, verbose=self.verbose)
beacon_info = []
try:
obmc.login()
beacon_dict = obmc.get_beacon_info()
beacon_info = self._get_beacon_info(beacon_dict)
if not beacon_info:
beacon_info = ['No attributes returned from the BMC.']
for info in beacon_info:
self.callback.info( '%s: %s' % (node, info))
except (SelfServerException, SelfClientException) as e:
self.callback.info('%s: %s' % (node, e.message))
return beacon_info

View File

@ -19,6 +19,34 @@ HTTP_PROTOCOL = "https://"
PROJECT_URL = "/xyz/openbmc_project"
PROJECT_PAYLOAD = "xyz.openbmc_project."
RBEACON_URLS = {
"path" : "/led/groups/enclosure_identify/attr/Asserted",
"on" : {
"field" : True,
},
"off" : {
"field" : False,
},
}
LEDS_URL = "/led/physical/enumerate"
LEDS_KEY_LIST = ("fan0", "fan1", "fan2", "fan3",
"front_id", "front_fault", "front_power",
"rear_id", "rear_fault", "rear_power")
SENSOR_URL = "/sensors/enumerate"
SENSOR_UNITS = {
"Amperes" : "Amps",
"DegreesC" : "C",
"Joules" : "Joules",
"Meters" : "Meters",
"RPMS" : "RPMS",
"Volts" : "Volts",
"Watts" : "Watts",
}
RPOWER_STATES = {
"on" : "on",
"off" : "off",
@ -308,6 +336,52 @@ class OpenBMCRest(object):
error = 'Error: Received wrong format response: %s' % states
raise SelfServerException(error)
def get_beacon_info(self):
beacon_data = self.request('GET', LEDS_URL, cmd='get_beacon_info')
try:
beacon_dict = {}
for key, value in beacon_data.items():
key_id = key.split('/')[-1]
if key_id in LEDS_KEY_LIST:
beacon_dict[key_id] = value['State'].split('.')[-1]
return beacon_dict
except KeyError:
error = 'Error: Received wrong format response: %s' % beacon_data
raise SelfServerException(error)
def set_beacon_state(self, state):
payload = { "data": RBEACON_URLS[state]['field'] }
self.request('PUT', RBEACON_URLS['path'], payload=payload, cmd='set_beacon_state')
def get_sensor_info(self):
sensor_data = self.request('GET', SENSOR_URL, cmd='get_sensor_info')
try:
sensor_dict = {}
for k, v in sensor_data.items():
if 'Unit' in v:
unit = v['Unit'].split('.')[-1]
if unit in SENSOR_UNITS:
label = k.split('/')[-1].replace('_', ' ').title()
value = v['Value']
scale = v['Scale']
value = value * pow(10, scale)
value = '{:g}'.format(value)
if unit not in sensor_dict:
sensor_dict[unit] = []
sensor_dict[unit].append('%s: %s %s' % (label, value, SENSOR_UNITS[unit]))
elif 'units' in v and 'value' in v:
label = k.split('/')[-1]
value = v['value']
sensor_dict[label] = ['%s: %s' % (label, value)]
return sensor_dict
except KeyError:
error = 'Error: Received wrong format response: %s' % sensor_data
raise SelfServerException(error)
def list_firmware(self):
data = self.request('GET', FIRM_URLS['list']['path'], cmd='list_firmware')
@ -347,3 +421,4 @@ class OpenBMCImage(object):
def __str__(self):
return '%s-%s' % (self.purpose, self.id)

View File

@ -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 SensorInterface(object):
"""Interface for sensor-related actions."""
interface_type = 'sensor'
version = '1.0'
def get_sensor_info(self, task, sensor_type=None):
"""Return the sensor info of the task's nodes.
:param sensor_type: type of sensor info want to get.
:param task: a Task instance containing the nodes to act on.
:return: sensor info list
"""
return task.run('get_sensor_info', sensor_type)
def get_beacon_info(self, task):
"""Return the beacon info of the task's nodes.
:param task: a Task instance containing the nodes to act on.
:return: beacon info list
"""
return task.run('get_beacon_info')
class DefaultSensorManager(SensorInterface):
"""Interface for sensor-related actions."""
pass

View File

@ -13,10 +13,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_sensor import OpenBMCSensorTask
from hwctl.beacon import DefaultBeaconManager
from hwctl.power import DefaultPowerManager
from hwctl.setboot import DefaultBootManager
from hwctl.sensor import DefaultSensorManager
from xcatagent import base
import openbmc_rest
@ -36,6 +40,9 @@ VERBOSE = False
all_nodes_result = {}
# global variables of rbeacon
BEACON_SET_OPTIONS = ('on', 'off')
# global variables of rflash
RFLASH_OPTIONS = {
"-a" : "activate",
@ -83,6 +90,10 @@ POWER_GET_OPTIONS = ('bmcstate', 'state', 'stat', 'status')
SETBOOT_GET_OPTIONS = ('stat', '')
SETBOOT_SET_OPTIONS = ('cd', 'def', 'default', 'hd', 'net')
# global variables of rvitals
VITALS_OPTIONS = ('all', 'altitude', 'fanspeed', 'leds', 'power',
'temp', 'voltage', 'wattage')
class OpenBMC(base.BaseDriver):
headers = {'Content-Type': 'application/json'}
@ -541,6 +552,39 @@ class OpenBMCManager(base.BaseManager):
if self.debugmode:
logger.setLevel(logging.DEBUG)
def rbeacon(self, nodesinfo, args):
# 1, parse args
rbeacon_usage = """
Usage:
rbeacon [-V|--verbose] [on|off]
Options:
-V --verbose rbeacon verbose mode.
"""
try:
opts = docopt(rbeacon_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 rbeacon: %s" % args)
return
# 2, validate the args
if action is None:
self.messager.error("Not specify the subcommand for rbeacon")
return
if action not in BEACON_SET_OPTIONS:
self.messager.error("Not supported subcommand for rbeacon: %s" % action)
return
# 3, run the subcommands
runner = OpenBMCBeaconTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose)
DefaultBeaconManager().set_beacon_state(runner, beacon_state=action)
def rpower(self, nodesinfo, args):
# 1, parse args
@ -617,6 +661,41 @@ class OpenBMCManager(base.BaseManager):
else:
DefaultBootManager().set_boot_state(runner, setboot_state=action, persistant=action_type)
def rvitals(self, nodesinfo, args):
# 1, parse agrs
if not args:
args = ['all']
rvitals_usage = """
Usage:
rvitals [-V|--verbose] [all|altitude|fanspeed|leds|power|temp|voltage|wattage]
Options:
-V --verbose rvitals verbose mode.
"""
try:
opts = docopt(rvitals_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 rvitals: %s" % args)
return
# 2, validate the args
if action not in VITALS_OPTIONS:
self.messager.error("Not supported subcommand for rvitals: %s" % action)
return
# 3, run the subcommands
runner = OpenBMCSensorTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose)
if action == 'leds':
DefaultSensorManager().get_beacon_info(runner)
else:
DefaultSensorManager().get_sensor_info(runner, action)
def _get_full_path(self,file_path):
if type(self.cwd) == 'unicode':

View File

@ -30,8 +30,11 @@ use xCAT::OPENBMC;
sub handled_commands {
return {
rbeacon => 'nodehm:mgt=openbmc',
rflash => 'nodehm:mgt=openbmc',
rpower => 'nodehm:mgt=openbmc',
rsetboot => 'nodehm:mgt=openbmc',
rvitals => 'nodehm:mgt=openbmc',
};
}
@ -144,15 +147,19 @@ sub parse_args {
return ([ 1, "Error parsing arguments." ]);
}
if (scalar(@ARGV) >= 2 and ($command =~ /rpower/)) {
if (scalar(@ARGV) >= 2 and ($command =~ /rbeacon|rpower|rvitals/)) {
return ([ 1, "Only one option is supported at the same time for $command" ]);
} elsif (scalar(@ARGV) == 0 and $command =~ /rpower|rflash/) {
} elsif (scalar(@ARGV) == 0 and $command =~ /rbeacon|rpower|rflash/) {
return ([ 1, "No option specified for $command" ]);
} else {
$subcommand = $ARGV[0];
}
if ($command eq "rflash") {
if ($command eq "rbeacon") {
unless ($subcommand =~ /^on$|^off$/) {
return ([ 1, "Only 'on' or 'off' is supported for OpenBMC managed nodes."]);
}
} elsif ($command eq "rflash") {
my ($activate, $check, $delete, $directory, $list, $upload) = (0) x 6;
my $no_host_reboot;
GetOptions(
@ -173,7 +180,7 @@ sub parse_args {
return ([ 1, "Unsupported command: $command $arg" ]);
}
}
return ([ 1, "No options specified."]);
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);
@ -213,6 +220,11 @@ sub parse_args {
unless ($subcommand =~ /^net$|^hd$|^cd$|^def$|^default$|^stat$/) {
return ([ 1, "Unsupported command: $command $subcommand" ]);
}
} elsif ($command eq "rvitals") {
$subcommand = "all" if (!defined($ARGV[0]));
unless ($subcommand =~ /^all$|^altitude$|^fanspeed$|^leds$|^power$|^temp$|^voltage$|^wattage$/) {
return ([ 1, "Unsupported command: $command $subcommand" ]);
}
} else {
return ([ 1, "Unsupported command: $command" ]);
}