mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-05-22 03:32:04 +00:00
Merge pull request #4786 from zet809/rspconfig_sshcfg
Rspconfig framework for openbmc in python
This commit is contained in:
commit
acae60a30a
42
xCAT-openbmc-py/lib/python/agent/hwctl/bmcconfig.py
Normal file
42
xCAT-openbmc-py/lib/python/agent/hwctl/bmcconfig.py
Normal file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python
|
||||
###############################################################################
|
||||
# IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html
|
||||
###############################################################################
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
class BmcConfigInterface(object):
|
||||
"""Interface for bmc configuration related actions."""
|
||||
interface_type = 'bmcconfig'
|
||||
version = '1.0'
|
||||
|
||||
def dump_list(self, task):
|
||||
return task.run('dump_list')
|
||||
|
||||
def dump_generate(self, task):
|
||||
return task.run("dump_generate")
|
||||
|
||||
def dump_clear(self, task, id):
|
||||
return task.run("dump_clear", id)
|
||||
|
||||
def dump_download(self, task, id):
|
||||
return task.run("dump_download", id)
|
||||
|
||||
def dump_process(self, task):
|
||||
return task.run("dump_process")
|
||||
|
||||
def set_sshcfg(self, task):
|
||||
return task.run("set_sshcfg")
|
||||
|
||||
def set_ipdhcp(self, task):
|
||||
return task.run("set_ipdhcp")
|
||||
|
||||
def get_attributes(self, task, attributes):
|
||||
return task.run("get_attributes", attributes)
|
||||
|
||||
def set_attributes(self, task, attributes):
|
||||
return task.run("set_attributes", attributes)
|
||||
|
||||
class DefaultBmcConfigManager(BmcConfigInterface):
|
||||
"""Interface for BmcConfig actions."""
|
||||
pass
|
@ -0,0 +1,132 @@
|
||||
#!/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')
|
||||
|
||||
RSPCONFIG_GET_NETINFO=['ip', 'netmask', 'gateway', 'vlan', 'ipsrc', 'hostname']
|
||||
RSPCONFIG_SET_NETINFO=['ip', 'netmask', 'gateway', 'vlan']
|
||||
|
||||
class OpenBMCBmcConfigTask(ParallelNodesCommand):
|
||||
|
||||
def dump_list(self, **kw):
|
||||
return self.callback.info('dump_list')
|
||||
|
||||
def dump_generate(self, **kw):
|
||||
return self.callback.info("dump_generate")
|
||||
|
||||
def dump_clear(self, id, **kw):
|
||||
return self.callback.info("dump_clear id: %s" % id)
|
||||
|
||||
def dump_download(self, id, **kw):
|
||||
return self.callback.info("dump_download id: %s" % id)
|
||||
|
||||
def dump_process(self, **kw):
|
||||
return self.callback.info("dump_process: trigger, list and download")
|
||||
|
||||
def set_sshcfg(self, **kw):
|
||||
return self.callback.info("set_sshcfg")
|
||||
|
||||
def set_ipdhcp(self, **kw):
|
||||
return self.callback.info("set_ipdhcp")
|
||||
|
||||
def get_attributes(self, attributes, **kw):
|
||||
netinfo_dict={}
|
||||
for attr in attributes:
|
||||
if attr in RSPCONFIG_GET_NETINFO:
|
||||
netinfo_dict[attr]=True
|
||||
getnet=1
|
||||
elif openbmc.RSPCONFIG_APIS.has_key(attr):
|
||||
self._get_apis_values(attr, **kw)
|
||||
else:
|
||||
self.callback.error("get_attributes can not deal with attr %s" % attr)
|
||||
if len(netinfo_dict):
|
||||
self._get_netinfo(netinfo_dict.has_key('ip'), netinfo_dict.has_key('ipsrc'), netinfo_dict.has_key('netmask'),
|
||||
netinfo_dict.has_key('gateway'), netinfo_dict.has_key('vlan'), netinfo_dict.has_key('hostname'), **kw)
|
||||
|
||||
def set_attributes(self, attributes, **kw):
|
||||
netinfo_dict={'vlan':False}
|
||||
for attr in attributes:
|
||||
k,v = attr.split('=')
|
||||
if k in RSPCONFIG_SET_NETINFO:
|
||||
netinfo_dict[k] = v
|
||||
elif k == 'hostname':
|
||||
self._set_hostname(v, **kw)
|
||||
elif openbmc.RSPCONFIG_APIS.has_key(k):
|
||||
self._set_apis_values(k, v, **kw)
|
||||
else:
|
||||
return self.callback.error("set_attributes unsupported attribute:%s" % k)
|
||||
if len(netinfo_dict) > 1 and (not netinfo_dict.has_key('ip') or not netinfo_dict.has_key('netmask') or not netinfo_dict.has_key('gateway')):
|
||||
self.callback.info("set_attributes miss either ip, netmask or gateway to set network information")
|
||||
else:
|
||||
self._set_netinfo(netinfo_dict['ip'], netinfo_dict['netmask'],
|
||||
netinfo_dict['gateway'], netinfo_dict['vlan'])
|
||||
|
||||
def _set_hostname(self, hostname, **kw):
|
||||
if hostname == '*':
|
||||
if kw['nodeinfo']['bmc'] != kw['nodeinfo']['bmcip']:
|
||||
hostname = kw['nodeinfo']['bmc']
|
||||
else:
|
||||
hostname = '%s-bmc' % kw['node']
|
||||
return self.callback.info("set_hostname: %s" % hostname)
|
||||
|
||||
def _set_apis_values(self, key, value, **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_apis_values(key, value)
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
self.callback.info("%s: %s" % (node, e.message))
|
||||
|
||||
self.callback.info("%s: BMC Setting %s..." % (node, openbmc.RSPCONFIG_APIS[key]['display_name']))
|
||||
|
||||
def _get_apis_values(self, key, **kw):
|
||||
node = kw['node']
|
||||
obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback,
|
||||
debugmode=self.debugmode, verbose=self.verbose)
|
||||
try:
|
||||
obmc.login()
|
||||
value = obmc.get_apis_values(key)
|
||||
|
||||
except (SelfServerException, SelfClientException) as e:
|
||||
self.callback.info('%s: %s' % (node, e.message))
|
||||
|
||||
str_value = '0.'+str(value)
|
||||
result = '%s: %s: %s' % (node, openbmc.RSPCONFIG_APIS[key]['display_name'], str_value.split('.')[-1])
|
||||
self.callback.info(result)
|
||||
|
||||
def _set_netinfo(self, ip, netmask, gateway, vlan=False, **kw):
|
||||
if vlan:
|
||||
result = "set net(%s, %s, %s) for vlan %s" % (ip, netmask, gateway, vlan)
|
||||
else:
|
||||
result = "set net(%s, %s, %s) for eth0" % (ip, netmask, gateway)
|
||||
return self.callback.info("set_netinfo %s" % result)
|
||||
|
||||
def _get_netinfo(self, ip=False, ipsrc=False, netmask=False, gateway=False, vlan=False, hostname=False, **kw):
|
||||
result = ''
|
||||
if ip:
|
||||
result += "Get IP, "
|
||||
if netmask:
|
||||
result += "Get Mask, "
|
||||
if gateway:
|
||||
result += "Get Gateway, "
|
||||
if ipsrc:
|
||||
result += "Get IP source, "
|
||||
if hostname:
|
||||
result += "Get BMC hostname, "
|
||||
if vlan:
|
||||
result += "Get BMC vlan."
|
||||
return self.callback.info("get_netinfo: %s" % result)
|
@ -141,6 +141,49 @@ FIRM_URLS = {
|
||||
}
|
||||
}
|
||||
|
||||
RSPCONFIG_APIS = {
|
||||
'autoreboot' : {
|
||||
'baseurl': "/control/host0/auto_reboot/",
|
||||
'set_url': "attr/AutoReboot",
|
||||
'get_url': "attr/AutoReboot",
|
||||
'display_name': "BMC AutoReboot",
|
||||
},
|
||||
'powersupplyredundancy':{
|
||||
'baseurl': "/sensors/chassis/PowerSupplyRedundancy/",
|
||||
'set_url': "/action/setValue",
|
||||
'get_url': "/action/getValue",
|
||||
'get_method': 'POST',
|
||||
'get_data': '[]',
|
||||
'display_name': "BMC PowerSupplyRedundancy",
|
||||
'attr_values': {
|
||||
'disabled': "Disables",
|
||||
'enabled': "Enabled",
|
||||
},
|
||||
},
|
||||
'powerrestorepolicy': {
|
||||
'baseurl': "/control/host0/power_restore_policy/",
|
||||
'set_url': "attr/PowerRestorePolicy",
|
||||
'get_url': "attr/PowerRestorePolicy",
|
||||
'display_name': "BMC PowerRestorePolicy",
|
||||
'attr_values': {
|
||||
'restore': "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore",
|
||||
'always_on': "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn",
|
||||
'always_off': "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff",
|
||||
},
|
||||
},
|
||||
'bootmode': {
|
||||
'baseurl': "/control/host0/boot/",
|
||||
'set_url': "attr/BootMode",
|
||||
'get_url': "attr/BootMode",
|
||||
'display_name':"BMC BootMode",
|
||||
'attr_values': {
|
||||
'regular': "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular",
|
||||
'safe': "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe",
|
||||
'setup': "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
RESULT_OK = 'ok'
|
||||
RESULT_FAIL = 'fail'
|
||||
|
||||
@ -443,6 +486,31 @@ class OpenBMCRest(object):
|
||||
|
||||
return bool(func_list), fw_dict
|
||||
|
||||
def set_apis_values(self, key, value):
|
||||
attr_info = RSPCONFIG_APIS[key]
|
||||
if not attr_info.has_key('set_url'):
|
||||
raise SelfServerException("config %s failed, not url available" % key)
|
||||
set_url = attr_info['baseurl']+attr_info['set_url']
|
||||
if attr_info.has_key('attr_values') and attr_info['attr_values'].has_key(value):
|
||||
data = attr_info['attr_values'][value]
|
||||
else:
|
||||
data = value
|
||||
self.request('PUT', set_url, payload={"data": data}, cmd="set_%s" % key)
|
||||
|
||||
def get_apis_values(self, key):
|
||||
attr_info = RSPCONFIG_APIS[key]
|
||||
if not attr_info.has_key('get_url'):
|
||||
raise SelfServerException("Reading %s failed, not url available" % key)
|
||||
get_url = attr_info['baseurl']+attr_info['get_url']
|
||||
|
||||
method = 'GET'
|
||||
if attr_info.has_key('get_method'):
|
||||
method = attr_info['get_method']
|
||||
data = None
|
||||
if attr_info.has_key('get_data'):
|
||||
data={"data": attr_info['get_data']}
|
||||
return self.request(method, get_url, payload=data, cmd="get_%s" % key)
|
||||
|
||||
class OpenBMCImage(object):
|
||||
def __init__(self, rawid, data=None):
|
||||
self.id = rawid.split('/')[-1]
|
||||
|
@ -9,7 +9,8 @@ import os
|
||||
import time
|
||||
import sys
|
||||
import gevent
|
||||
from docopt import docopt
|
||||
import re
|
||||
from docopt import docopt,DocoptExit
|
||||
|
||||
from common import utils
|
||||
from common import exceptions as xcat_exception
|
||||
@ -18,11 +19,13 @@ 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.executor.openbmc_bmcconfig import OpenBMCBmcConfigTask
|
||||
from hwctl.beacon import DefaultBeaconManager
|
||||
from hwctl.setboot import DefaultBootManager
|
||||
from hwctl.inventory import DefaultInventoryManager
|
||||
from hwctl.power import DefaultPowerManager
|
||||
from hwctl.sensor import DefaultSensorManager
|
||||
from hwctl.bmcconfig import DefaultBmcConfigManager
|
||||
|
||||
from xcatagent import base
|
||||
import openbmc_rest
|
||||
@ -77,6 +80,46 @@ RFLASH_URLS = {
|
||||
}
|
||||
}
|
||||
|
||||
RSPCONFIG_GET_OPTIONS = ['ip','ipsrc','netmask','gateway','vlan','hostname','bootmode','autoreboot','powersupplyredundancy','powerrestorepolicy']
|
||||
RSPCONFIG_SET_OPTIONS = {
|
||||
'ip':'.*',
|
||||
'netmask':'.*',
|
||||
'gateway':'.*',
|
||||
'vlan':'\d+',
|
||||
'hostname':"\*|.*",
|
||||
'autoreboot':"^0|1$",
|
||||
'powersupplyredundancy':"^enabled$|^disabled$",
|
||||
'powerrestorepolicy':"^always_on$|^always_off$|^restore$",
|
||||
'bootmode':"^regular$|^safe$|^setup$",
|
||||
}
|
||||
RSPCONFIG_USAGE = """
|
||||
Handle rspconfig operations.
|
||||
|
||||
Usage:
|
||||
rspconfig -h|--help
|
||||
rspconfig dump [[-l|--list] | [-g|--generate] | [-c|--clear <arg>] | [-d|--download <arg>]] [-V|--verbose]
|
||||
rspconfig sshcfg [-V|--verbose]
|
||||
rspconfig ip=dhcp [-V|--verbose]
|
||||
rspconfig get [<args>...] [-V|--verbose]
|
||||
rspconfig set [<args>...] [-V|--verbose]
|
||||
|
||||
Options:
|
||||
-V,--verbose Show verbose message
|
||||
-l,--list List are dump files
|
||||
-g,--generate Trigger a new dump file
|
||||
-c,--clear <arg> The id of file to clear or all if specify 'all'
|
||||
-d,--download <arg> The id of file to download or all if specify 'all'
|
||||
|
||||
The supported attributes to get are: %s
|
||||
|
||||
The supported attributes and its values to set are:
|
||||
ip=<ip address> netmask=<mask> gateway=<gateway> [vlan=<vlanid>]
|
||||
hostname=*|<string>
|
||||
autoreboot={0|1}
|
||||
powersupplyredundancy={enabled|disabled}
|
||||
powerrestorepolicy={always_on|always_off|restore}
|
||||
""" % RSPCONFIG_GET_OPTIONS
|
||||
|
||||
XCAT_LOG_DIR = "/var/log/xcat"
|
||||
XCAT_LOG_RFLASH_DIR = XCAT_LOG_DIR + "/rflash/"
|
||||
|
||||
@ -663,7 +706,56 @@ 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)
|
||||
except DocoptExit as e:
|
||||
self.messager.error("Failed to parse args by docopt: %s" % e)
|
||||
return
|
||||
except Exception as e:
|
||||
self.messager.error("Failed to parse arguments for rspconfig: %s" % args)
|
||||
return
|
||||
self.verbose=opts.pop('--verbose')
|
||||
runner = OpenBMCBmcConfigTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose)
|
||||
|
||||
if opts['dump']:
|
||||
if opts['--list']:
|
||||
DefaultBmcConfigManager().dump_list(runner)
|
||||
elif opts['--generate']:
|
||||
DefaultBmcConfigManager().dump_generate(runner)
|
||||
elif opts['--clear']:
|
||||
DefaultBmcConfigManager().dump_clear(runner, opts['--clear'])
|
||||
elif opts['--download']:
|
||||
DefaultBmcConfigManager().dump_download(runner, opts['--download'])
|
||||
else:
|
||||
DefaultBmcConfigManager().dump_process(runner)
|
||||
elif opts['sshcfg']:
|
||||
DefaultBmcConfigManager().set_sshcfg(runner)
|
||||
elif opts['ip=dhcp']:
|
||||
DefaultBmcConfigManager().set_ipdhcp(runner)
|
||||
elif opts['get']:
|
||||
unsupport_list=list(set(opts['<args>']) - set(RSPCONFIG_GET_OPTIONS))
|
||||
if len(unsupport_list) > 0:
|
||||
self.messager.error("Have unsupported option: %s" % unsupport_args)
|
||||
return
|
||||
else:
|
||||
DefaultBmcConfigManager().get_attributes(runner, opts['<args>'])
|
||||
elif opts['set']:
|
||||
rc=0
|
||||
for attr in opts['<args>']:
|
||||
k,v = attr.split('=')
|
||||
if not RSPCONFIG_SET_OPTIONS.has_key(k):
|
||||
self.messager.error("The attribute %s is not support to set" % k)
|
||||
rc=1
|
||||
elif not re.match(RSPCONFIG_SET_OPTIONS[k], v):
|
||||
self.messager.error("The value %s is invalid for %s" %(v, k))
|
||||
rc=1
|
||||
if rc:
|
||||
return
|
||||
else:
|
||||
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
|
||||
@ -736,7 +828,6 @@ class OpenBMCManager(base.BaseManager):
|
||||
else:
|
||||
DefaultSensorManager().get_sensor_info(runner, action)
|
||||
|
||||
|
||||
def _get_full_path(self,file_path):
|
||||
if type(self.cwd) == 'unicode':
|
||||
dir_path = self.cwd
|
||||
|
@ -92,8 +92,12 @@ class Server(object):
|
||||
if not hasattr(manager, req['command']):
|
||||
messager.error("command %s is not supported" % req['command'])
|
||||
func = getattr(manager, req['command'])
|
||||
# translate unicode string to normal string to avoid docopt error
|
||||
new_args=[]
|
||||
for a in req['args']:
|
||||
new_args.append(a.encode('utf-8'))
|
||||
# call the function in the specified manager
|
||||
func(req['nodeinfo'], req['args'])
|
||||
func(req['nodeinfo'], new_args)
|
||||
# after the method returns, the request should be handled
|
||||
# completely, close the socket for client
|
||||
if not self.standalone:
|
||||
|
@ -17,6 +17,7 @@ use xCAT::Utils;
|
||||
use xCAT::Usage;
|
||||
use xCAT::SvrUtils;
|
||||
use xCAT::OPENBMC;
|
||||
use xCAT_plugin::openbmc;
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
@ -36,6 +37,7 @@ sub handled_commands {
|
||||
rpower => 'nodehm:mgt=openbmc',
|
||||
rsetboot => 'nodehm:mgt=openbmc',
|
||||
rvitals => 'nodehm:mgt=openbmc',
|
||||
rspconfig => 'nodehm:mgt=openbmc',
|
||||
};
|
||||
}
|
||||
|
||||
@ -114,6 +116,10 @@ sub process_request {
|
||||
$callback = shift;
|
||||
my $noderange = $request->{node};
|
||||
my $check = parse_node_info($noderange);
|
||||
if (&refactor_args($request)) {
|
||||
xCAT::MsgUtils->message("E", { data => ["Failed to refactor arguments"] }, $callback);
|
||||
return;
|
||||
}
|
||||
$callback->({ errorcode => [$check] }) if ($check);
|
||||
return unless(%node_info);
|
||||
my $pid = xCAT::OPENBMC::start_python_agent();
|
||||
@ -231,6 +237,8 @@ sub parse_args {
|
||||
unless ($subcommand =~ /^all$|^altitude$|^fanspeed$|^leds$|^power$|^temp$|^voltage$|^wattage$/) {
|
||||
return ([ 1, "Unsupported command: $command $subcommand" ]);
|
||||
}
|
||||
} elsif ($command eq 'rspconfig') {
|
||||
xCAT_plugin::openbmc::parse_args('rspconfig', $extrargs, $noderange);
|
||||
} else {
|
||||
return ([ 1, "Unsupported command: $command" ]);
|
||||
}
|
||||
@ -304,4 +312,31 @@ sub parse_node_info {
|
||||
return $rst;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 refactor_args
|
||||
|
||||
refractor args to be easily dealt by python client
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
sub refactor_args {
|
||||
my $request = shift;
|
||||
my $command = $request->{command}->[0];
|
||||
my $extrargs = $request->{arg};
|
||||
if ($command eq "rspconfig") {
|
||||
my $subcommand = $extrargs->[0];
|
||||
if ($subcommand !~ /^dump$|^sshcfg$|^ip=dhcp$/) {
|
||||
if (grep /=/, @$extrargs) {
|
||||
unshift @$extrargs, "set";
|
||||
} else {
|
||||
unshift @$extrargs, "get";
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user