diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_setboot.py b/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_setboot.py new file mode 100644 index 000000000..eb8ff65dc --- /dev/null +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_setboot.py @@ -0,0 +1,64 @@ +#!/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 OpenBMCBootTask(ParallelNodesCommand): + """Executor for setboot-related actions.""" + + def get_state(self, **kw): + + node = kw['node'] + obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, + debugmode=self.debugmode, verbose=self.verbose) + + state = 'Unknown' + try: + obmc.login() + state = obmc.get_boot_state() + + result = '%s: %s' % (node, state) + + except (SelfServerException, SelfClientException) as e: + result = '%s: %s' % (node, e.message) + + self.callback.info(result) + return state + + def set_state(self, state, persistant, **kw): + + node = kw['node'] + obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, + debugmode=self.debugmode, verbose=self.verbose) + + try: + obmc.login() + if persistant: + obmc.set_one_time_boot_enable(0) + obmc.set_boot_state(state) + else: + obmc.set_one_time_boot_enable(1) + obmc.set_one_time_boot_state(state) + + state = obmc.get_boot_state() + + result = '%s: %s' % (node, state) + + except (SelfServerException, SelfClientException) as e: + result = '%s: %s' % (node, e.message) + + self.callback.info(result) + 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 d9f4922d3..52dacb7cd 100644 --- a/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py @@ -62,6 +62,37 @@ BMC_URLS = { }, } +BOOTSOURCE_URLS = { + "enable" : { + "path" : "/control/host0/boot/one_time/attr/Enabled", + }, + "get" : { + "path" : "/control/host0/enumerate", + }, + "set_one_time" : { + "path" : "/control/host0/boot/one_time/attr/BootSource", + }, + "set" : { + "path" : "/control/host0/boot/attr/BootSource", + }, + "field" : "xyz.openbmc_project.Control.Boot.Source.Sources.", +} + +BOOTSOURCE_GET_STATE = { + "Default" : "Default", + "Disk" : "Hard Drive", + "ExternalMedia" : "CD/DVD", + "Network" : "Network", +} + +BOOTSOURCE_SET_STATE = { + "cd" : "ExternalMedia", + "def" : "Default", + "default" : "Default", + "hd" : "Disk", + "net" : "Network", +} + RESULT_OK = 'ok' RESULT_FAIL = 'fail' @@ -225,3 +256,37 @@ class OpenBMCRest(object): except SelfServerException,SelfClientException: # TODO: Need special handling for bmc reset, as it is normal bmc may return error pass + + def set_one_time_boot_enable(self, enabled): + + payload = { "data": enabled } + self.request('PUT', BOOTSOURCE_URLS['enable']['path'], payload=payload, cmd='set_one_time_boot_enable') + + def set_boot_state(self, state): + + payload = { "data": BOOTSOURCE_URLS['field'] + BOOTSOURCE_SET_STATE[state] } + self.request('PUT', BOOTSOURCE_URLS['set']['path'], payload=payload, cmd='set_boot_state') + + def set_one_time_boot_state(self, state): + + payload = { "data": BOOTSOURCE_URLS['field'] + BOOTSOURCE_SET_STATE[state] } + self.request('PUT', BOOTSOURCE_URLS['set_one_time']['path'], payload=payload, cmd='set_one_time_boot_state') + + def get_boot_state(self): + + state = self.request('GET', BOOTSOURCE_URLS['get']['path'], cmd='get_boot_state') + try: + one_time_path = PROJECT_URL + '/control/host0/boot/one_time' + one_time_enabled = state[one_time_path]['Enabled'] + if one_time_enabled: + boot_source = state[one_time_path]['BootSource'].split('.')[-1] + else: + boot_source = state[PROJECT_URL + '/control/host0/boot']['BootSource'].split('.')[-1] + + error = 'Can not get valid rsetboot status, the data is %s' % boot_source + boot_state = BOOTSOURCE_GET_STATE.get(boot_source.split('.')[-1], error) + return boot_state + except KeyError: + error = 'Error: Received wrong format response: %s' % states + raise SelfServerException(error) + diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/setboot.py b/xCAT-openbmc-py/lib/python/agent/hwctl/setboot.py new file mode 100644 index 000000000..d45ae34e7 --- /dev/null +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/setboot.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +############################################################################### +# IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html +############################################################################### +# -*- coding: utf-8 -*- +# + +class BootInterface(object): + """Interface for setboot-related actions.""" + interface_type = 'setboot' + version = '1.0' + + def get_boot_state(self, task): + """Return the setboot state of the task's nodes. + + :param task: a Task instance containing the nodes to act on. + :returns: a setboot state. + """ + return task.run('get_state') + + def set_boot_state(self, task, setboot_state, persistant=False, timeout=None): + """Set the setboot state of the task's nodes. + + :param task: a Task instance containing the nodes to act on. + :param setboot_state: Any boot state allowed. + :param persistant: whether set boot state persistant + :param timeout: timeout (in seconds) positive integer (> 0) for any + setboot state. ``None`` indicates to use default timeout. + """ + return task.run('set_state', setboot_state, persistant, timeout=timeout) + +class DefaultBootManager(BootInterface): + """Interface for setboot-related actions.""" + pass diff --git a/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py b/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py index 004a392bc..7fe009949 100644 --- a/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py +++ b/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py @@ -10,11 +10,14 @@ import os import time import sys import gevent +from docopt import docopt from common import utils from common import exceptions as xcat_exception from hwctl.executor.openbmc_power import OpenBMCPowerTask +from hwctl.executor.openbmc_setboot import OpenBMCBootTask from hwctl.power import DefaultPowerManager +from hwctl.setboot import DefaultBootManager from xcatagent import base import openbmc_rest @@ -73,6 +76,10 @@ POWER_REBOOT_OPTIONS = ('boot', 'reset') POWER_SET_OPTIONS = ('on', 'off', 'bmcreboot', 'softoff') POWER_GET_OPTIONS = ('bmcstate', 'state', 'stat', 'status') +# global variables of rsetboot +SETBOOT_GET_OPTIONS = ('stat', '') +SETBOOT_SET_OPTIONS = ('cd', 'def', 'default', 'hd', 'net') + class OpenBMC(base.BaseDriver): headers = {'Content-Type': 'application/json'} @@ -562,6 +569,44 @@ class OpenBMCManager(base.BaseManager): else: DefaultPowerManager().set_power_state(runner, power_state=opts.action) + def rsetboot(self, nodesinfo, args): + + # 1, parse args + if not args: + args = ['stat'] + + rsetboot_usage = """ + Usage: + rsetboot [-V|--verbose] [cd|def|default|hd|net|stat] [-p] + + Options: + -V --verbose rsetboot verbose mode. + -p persistant boot source. + """ + + try: + opts = docopt(rsetboot_usage, argv=args) + + self.verbose = opts.pop('--verbose') + action_type = opts.pop('-p') + action = [k for k,v in opts.items() if v][0] + except Exception as e: + self.messager.error("Failed to parse arguments for rsetboot: %s" % args) + return + + # 2, validate the args + if action not in (SETBOOT_GET_OPTIONS + SETBOOT_SET_OPTIONS): + self.messager.error("Not supported subcommand for rsetboot: %s" % action) + return + + # 3, run the subcommands + runner = OpenBMCBootTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose) + if action in SETBOOT_GET_OPTIONS: + DefaultBootManager().get_boot_state(runner) + else: + DefaultBootManager().set_boot_state(runner, setboot_state=action, persistant=action_type) + + def _get_full_path(self,file_path): if type(self.cwd) == 'unicode': dir_path = self.cwd diff --git a/xCAT-server/lib/xcat/plugins/openbmc2.pm b/xCAT-server/lib/xcat/plugins/openbmc2.pm index 3f8855527..bc3161e2d 100644 --- a/xCAT-server/lib/xcat/plugins/openbmc2.pm +++ b/xCAT-server/lib/xcat/plugins/openbmc2.pm @@ -137,6 +137,13 @@ sub parse_args { my $noderange = shift; my $subcommand = undef; + my $verbose; + unless (GetOptions( + 'V|verbose' => \$verbose, + )) { + return ([ 1, "Error parsing arguments." ]); + } + if (scalar(@ARGV) >= 2 and ($command =~ /rpower/)) { return ([ 1, "Only one option is supported at the same time for $command" ]); } elsif (scalar(@ARGV) == 0 and $command =~ /rpower|rflash/) { @@ -146,7 +153,6 @@ sub parse_args { } if ($command eq "rflash") { - my $verbose; my ($activate, $check, $delete, $directory, $list, $upload) = (0) x 6; my $no_host_reboot; GetOptions( @@ -156,7 +162,6 @@ sub parse_args { 'd' => \$directory, 'l|list' => \$list, 'u|upload' => \$upload, - 'V|verbose' => \$verbose, 'no-host-reboot' => \$no_host_reboot, ); my $option_num = $activate+$check+$delete+$directory+$list+$upload; @@ -200,6 +205,14 @@ sub parse_args { unless ($subcommand =~ /^on$|^off$|^softoff$|^reset$|^boot$|^bmcreboot$|^bmcstate$|^status$|^stat$|^state$/) { return ([ 1, "Unsupported command: $command $subcommand" ]); } + } elsif ($command eq "rsetboot") { + my $persistant; + GetOptions('p' => \$persistant); + return ([ 1, "Only one option is supported at the same time for $command" ]) if (@ARGV > 1); + $subcommand = "stat" if (!defined($ARGV[0])); + unless ($subcommand =~ /^net$|^hd$|^cd$|^def$|^default$|^stat$/) { + return ([ 1, "Unsupported command: $command $subcommand" ]); + } } else { return ([ 1, "Unsupported command: $command" ]); }