Add soft reboot/poweroff power states.
Soft reboot/poweroff is now being implemented in Ironic. And the Ironic driver in Nova should support this new feature. This patch adds soft reboot and soft poweroff states. Co-Authored-By: Naohiro Tamura <naohirot@jp.fujitsu.com> Partial-Bug: #1526226 Change-Id: Icd2859784f3df85c08162c14464bce58067aab1d Depends-On: I1c9bbd1f11f6a8565607c874b3c99aa10eeb62a5
This commit is contained in:
parent
2fd05f3cf3
commit
249215e3d5
@ -268,9 +268,10 @@ class FunctionalTestBase(base.ClientTestBase):
|
||||
'node-set-maintenance',
|
||||
params='{0} {1} {2}'.format(node_id, maintenance_mode, params))
|
||||
|
||||
def set_node_power_state(self, node_id, power_state):
|
||||
def set_node_power_state(self, node_id, power_state, params=''):
|
||||
self.ironic('node-set-power-state',
|
||||
params='{0} {1}'.format(node_id, power_state))
|
||||
params='{0} {1} {2}'
|
||||
.format(node_id, power_state, params))
|
||||
|
||||
def set_node_provision_state(self, node_id, provision_state, params=''):
|
||||
self.ironic('node-set-provision-state',
|
||||
|
@ -59,8 +59,8 @@ PORTGROUP = {'uuid': '11111111-2222-3333-4444-555555555555',
|
||||
'address': 'AA:BB:CC:DD:EE:FF',
|
||||
'extra': {}}
|
||||
|
||||
POWER_STATE = {'power_state': 'power off',
|
||||
'target_power_state': 'power on'}
|
||||
POWER_STATE = {'power_state': 'power on',
|
||||
'target_power_state': 'power off'}
|
||||
|
||||
DRIVER_IFACES = {'deploy': {'result': True},
|
||||
'power': {'result': False, 'reason': 'Invalid IPMI username'},
|
||||
@ -891,13 +891,56 @@ class NodeManagerTest(testtools.TestCase):
|
||||
self.assertIsNone(maintenance)
|
||||
|
||||
def test_node_set_power_state(self):
|
||||
power_state = self.mgr.set_power_state(NODE1['uuid'], "on")
|
||||
body = {'target': 'power on'}
|
||||
power_state = self.mgr.set_power_state(NODE1['uuid'], "off")
|
||||
body = {'target': 'power off'}
|
||||
expect = [
|
||||
('PUT', '/v1/nodes/%s/states/power' % NODE1['uuid'], {}, body),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual('power on', power_state.target_power_state)
|
||||
self.assertEqual('power off', power_state.target_power_state)
|
||||
|
||||
def test_node_set_power_timeout(self):
|
||||
power_state = self.mgr.set_power_state(NODE1['uuid'], "off", timeout=2)
|
||||
body = {'target': 'power off', 'timeout': 2}
|
||||
expect = [
|
||||
('PUT', '/v1/nodes/%s/states/power' % NODE1['uuid'], {}, body),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual('power off', power_state.target_power_state)
|
||||
|
||||
def test_node_set_power_timeout_str(self):
|
||||
power_state = self.mgr.set_power_state(NODE1['uuid'], "off",
|
||||
timeout="2")
|
||||
body = {'target': 'power off', 'timeout': 2}
|
||||
expect = [
|
||||
('PUT', '/v1/nodes/%s/states/power' % NODE1['uuid'], {}, body),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual('power off', power_state.target_power_state)
|
||||
|
||||
def test_node_set_power_state_soft(self):
|
||||
power_state = self.mgr.set_power_state(NODE1['uuid'], "off", soft=True)
|
||||
body = {'target': 'soft power off'}
|
||||
expect = [
|
||||
('PUT', '/v1/nodes/%s/states/power' % NODE1['uuid'], {}, body),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual('power off', power_state.target_power_state)
|
||||
|
||||
def test_node_set_power_state_soft_fail(self):
|
||||
self.assertRaises(ValueError,
|
||||
self.mgr.set_power_state,
|
||||
NODE1['uuid'], 'on', soft=True)
|
||||
|
||||
def test_node_set_power_state_on_timeout_fail(self):
|
||||
self.assertRaises(ValueError,
|
||||
self.mgr.set_power_state,
|
||||
NODE1['uuid'], 'off', soft=False, timeout=0)
|
||||
|
||||
def test_node_set_power_state_on_timeout_type_error(self):
|
||||
self.assertRaises(ValueError,
|
||||
self.mgr.set_power_state,
|
||||
NODE1['uuid'], 'off', soft=False, timeout='a')
|
||||
|
||||
def test_set_target_raid_config(self):
|
||||
self.mgr.set_target_raid_config(
|
||||
|
@ -376,15 +376,26 @@ class NodeShellTest(utils.BaseTestCase):
|
||||
n_shell.do_node_set_maintenance,
|
||||
client_mock, args)
|
||||
|
||||
def _do_node_set_power_state_helper(self, power_state):
|
||||
def _do_node_set_power_state_helper(self, power_state,
|
||||
soft=False, timeout=None, error=False):
|
||||
client_mock = mock.MagicMock()
|
||||
args = mock.MagicMock()
|
||||
args.node = 'node_uuid'
|
||||
args.power_state = power_state
|
||||
args.soft = soft
|
||||
args.power_timeout = timeout
|
||||
|
||||
n_shell.do_node_set_power_state(client_mock, args)
|
||||
client_mock.node.set_power_state.assert_called_once_with('node_uuid',
|
||||
power_state)
|
||||
if error:
|
||||
client_mock.node = mock.MagicMock()
|
||||
client_mock.node.set_power_state = mock.MagicMock()
|
||||
client_mock.node.set_power_state.side_effect = ValueError("fake")
|
||||
self.assertRaises(exc.CommandError,
|
||||
n_shell.do_node_set_power_state,
|
||||
client_mock, args)
|
||||
else:
|
||||
n_shell.do_node_set_power_state(client_mock, args)
|
||||
client_mock.node.set_power_state.assert_called_once_with(
|
||||
'node_uuid', power_state, soft, timeout=timeout)
|
||||
|
||||
def test_do_node_set_power_state_on(self):
|
||||
self._do_node_set_power_state_helper('on')
|
||||
@ -395,6 +406,37 @@ class NodeShellTest(utils.BaseTestCase):
|
||||
def test_do_node_set_power_state_reboot(self):
|
||||
self._do_node_set_power_state_helper('reboot')
|
||||
|
||||
def test_do_node_set_power_state_on_timeout(self):
|
||||
self._do_node_set_power_state_helper('on', timeout=10)
|
||||
|
||||
def test_do_node_set_power_state_on_timeout_fail(self):
|
||||
self._do_node_set_power_state_helper('on', timeout=0, error=True)
|
||||
|
||||
def test_do_node_set_power_state_off_timeout(self):
|
||||
self._do_node_set_power_state_helper('off', timeout=10)
|
||||
|
||||
def test_do_node_set_power_state_reboot_timeout(self):
|
||||
self._do_node_set_power_state_helper('reboot', timeout=10)
|
||||
|
||||
def test_do_node_set_power_state_soft_on_fail(self):
|
||||
self._do_node_set_power_state_helper('on', soft=True, error=True)
|
||||
|
||||
def test_do_node_set_power_state_soft_off(self):
|
||||
self._do_node_set_power_state_helper('off', soft=True)
|
||||
|
||||
def test_do_node_set_power_state_soft_reboot(self):
|
||||
self._do_node_set_power_state_helper('reboot', soft=True)
|
||||
|
||||
def test_do_node_set_power_state_soft_on_timeout_fail(self):
|
||||
self._do_node_set_power_state_helper('on', soft=True, timeout=10,
|
||||
error=True)
|
||||
|
||||
def test_do_node_set_power_state_soft_off_timeout(self):
|
||||
self._do_node_set_power_state_helper('off', soft=True, timeout=10)
|
||||
|
||||
def test_do_node_set_power_state_soft_reboot_timeout(self):
|
||||
self._do_node_set_power_state_helper('reboot', soft=True, timeout=10)
|
||||
|
||||
def test_do_node_set_target_raid_config_file(self):
|
||||
contents = '{"raid": "config"}'
|
||||
|
||||
|
@ -28,6 +28,8 @@ _power_states = {
|
||||
'on': 'power on',
|
||||
'off': 'power off',
|
||||
'reboot': 'rebooting',
|
||||
'soft off': 'soft power off',
|
||||
'soft reboot': 'soft rebooting',
|
||||
}
|
||||
|
||||
|
||||
@ -273,10 +275,39 @@ class NodeManager(base.CreateManager):
|
||||
else:
|
||||
return self.delete(path)
|
||||
|
||||
def set_power_state(self, node_id, state):
|
||||
def set_power_state(self, node_id, state, soft=False, timeout=None):
|
||||
"""Sets power state for a node.
|
||||
|
||||
:param node_id: Node identifier
|
||||
:param state: One of target power state, 'on', 'off', or 'reboot'
|
||||
:param soft: The flag for graceful power 'off' or 'reboot'
|
||||
:param timeout: The timeout (in seconds) positive integer value (> 0)
|
||||
:raises: ValueError if 'soft' or 'timeout' option is invalid
|
||||
:returns: The status of the request
|
||||
"""
|
||||
if state == 'on' and soft:
|
||||
raise ValueError(
|
||||
_("'soft' option is invalid for the power-state 'on'"))
|
||||
|
||||
path = "%s/states/power" % node_id
|
||||
target = {'target': _power_states.get(state, state)}
|
||||
return self.update(path, target, http_method='PUT')
|
||||
|
||||
requested_state = 'soft ' + state if soft else state
|
||||
target = _power_states.get(requested_state, state)
|
||||
|
||||
body = {'target': target}
|
||||
if timeout is not None:
|
||||
msg = _("'timeout' option for setting power state must have "
|
||||
"positive integer value (> 0)")
|
||||
try:
|
||||
timeout = int(timeout)
|
||||
except (ValueError, TypeError):
|
||||
raise ValueError(msg)
|
||||
|
||||
if timeout <= 0:
|
||||
raise ValueError(msg)
|
||||
body = {'target': target, 'timeout': timeout}
|
||||
|
||||
return self.update(path, body, http_method='PUT')
|
||||
|
||||
def set_target_raid_config(self, node_ident, target_raid_config):
|
||||
"""Sets target_raid_config for a node.
|
||||
|
@ -14,6 +14,7 @@
|
||||
# under the License.
|
||||
|
||||
import argparse
|
||||
import six
|
||||
|
||||
from ironicclient.common.apiclient import exceptions
|
||||
from ironicclient.common import cliutils
|
||||
@ -409,9 +410,27 @@ def do_node_set_maintenance(cc, args):
|
||||
metavar='<power-state>',
|
||||
choices=['on', 'off', 'reboot'],
|
||||
help="'on', 'off', or 'reboot'.")
|
||||
@cliutils.arg(
|
||||
'--soft',
|
||||
dest='soft',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=("Gracefully change the power state. Only valid for 'off' and "
|
||||
"'reboot' power states."))
|
||||
@cliutils.arg(
|
||||
'--power-timeout',
|
||||
metavar='<power-timeout>',
|
||||
type=int,
|
||||
default=None,
|
||||
help=("Timeout (in seconds, positive integer) to wait for the target "
|
||||
"power state before erroring out."))
|
||||
def do_node_set_power_state(cc, args):
|
||||
"""Power a node on or off or reboot."""
|
||||
cc.node.set_power_state(args.node, args.power_state)
|
||||
try:
|
||||
cc.node.set_power_state(args.node, args.power_state, args.soft,
|
||||
timeout=args.power_timeout)
|
||||
except ValueError as e:
|
||||
raise exc.CommandError(six.text_type(e))
|
||||
|
||||
|
||||
@cliutils.arg('node', metavar='<node>', help="Name or UUID of the node.")
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Add optional arguments '--soft' and '--power-timeout' to the command
|
||||
"node-set-power-state".
|
Loading…
Reference in New Issue
Block a user