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
ironicclient
releasenotes/notes
@ -268,9 +268,10 @@ class FunctionalTestBase(base.ClientTestBase):
|
|||||||
'node-set-maintenance',
|
'node-set-maintenance',
|
||||||
params='{0} {1} {2}'.format(node_id, maintenance_mode, params))
|
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',
|
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=''):
|
def set_node_provision_state(self, node_id, provision_state, params=''):
|
||||||
self.ironic('node-set-provision-state',
|
self.ironic('node-set-provision-state',
|
||||||
|
@ -59,8 +59,8 @@ PORTGROUP = {'uuid': '11111111-2222-3333-4444-555555555555',
|
|||||||
'address': 'AA:BB:CC:DD:EE:FF',
|
'address': 'AA:BB:CC:DD:EE:FF',
|
||||||
'extra': {}}
|
'extra': {}}
|
||||||
|
|
||||||
POWER_STATE = {'power_state': 'power off',
|
POWER_STATE = {'power_state': 'power on',
|
||||||
'target_power_state': 'power on'}
|
'target_power_state': 'power off'}
|
||||||
|
|
||||||
DRIVER_IFACES = {'deploy': {'result': True},
|
DRIVER_IFACES = {'deploy': {'result': True},
|
||||||
'power': {'result': False, 'reason': 'Invalid IPMI username'},
|
'power': {'result': False, 'reason': 'Invalid IPMI username'},
|
||||||
@ -891,13 +891,56 @@ class NodeManagerTest(testtools.TestCase):
|
|||||||
self.assertIsNone(maintenance)
|
self.assertIsNone(maintenance)
|
||||||
|
|
||||||
def test_node_set_power_state(self):
|
def test_node_set_power_state(self):
|
||||||
power_state = self.mgr.set_power_state(NODE1['uuid'], "on")
|
power_state = self.mgr.set_power_state(NODE1['uuid'], "off")
|
||||||
body = {'target': 'power on'}
|
body = {'target': 'power off'}
|
||||||
expect = [
|
expect = [
|
||||||
('PUT', '/v1/nodes/%s/states/power' % NODE1['uuid'], {}, body),
|
('PUT', '/v1/nodes/%s/states/power' % NODE1['uuid'], {}, body),
|
||||||
]
|
]
|
||||||
self.assertEqual(expect, self.api.calls)
|
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):
|
def test_set_target_raid_config(self):
|
||||||
self.mgr.set_target_raid_config(
|
self.mgr.set_target_raid_config(
|
||||||
|
@ -376,15 +376,26 @@ class NodeShellTest(utils.BaseTestCase):
|
|||||||
n_shell.do_node_set_maintenance,
|
n_shell.do_node_set_maintenance,
|
||||||
client_mock, args)
|
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()
|
client_mock = mock.MagicMock()
|
||||||
args = mock.MagicMock()
|
args = mock.MagicMock()
|
||||||
args.node = 'node_uuid'
|
args.node = 'node_uuid'
|
||||||
args.power_state = power_state
|
args.power_state = power_state
|
||||||
|
args.soft = soft
|
||||||
|
args.power_timeout = timeout
|
||||||
|
|
||||||
n_shell.do_node_set_power_state(client_mock, args)
|
if error:
|
||||||
client_mock.node.set_power_state.assert_called_once_with('node_uuid',
|
client_mock.node = mock.MagicMock()
|
||||||
power_state)
|
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):
|
def test_do_node_set_power_state_on(self):
|
||||||
self._do_node_set_power_state_helper('on')
|
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):
|
def test_do_node_set_power_state_reboot(self):
|
||||||
self._do_node_set_power_state_helper('reboot')
|
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):
|
def test_do_node_set_target_raid_config_file(self):
|
||||||
contents = '{"raid": "config"}'
|
contents = '{"raid": "config"}'
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ _power_states = {
|
|||||||
'on': 'power on',
|
'on': 'power on',
|
||||||
'off': 'power off',
|
'off': 'power off',
|
||||||
'reboot': 'rebooting',
|
'reboot': 'rebooting',
|
||||||
|
'soft off': 'soft power off',
|
||||||
|
'soft reboot': 'soft rebooting',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -273,10 +275,39 @@ class NodeManager(base.CreateManager):
|
|||||||
else:
|
else:
|
||||||
return self.delete(path)
|
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
|
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):
|
def set_target_raid_config(self, node_ident, target_raid_config):
|
||||||
"""Sets target_raid_config for a node.
|
"""Sets target_raid_config for a node.
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import six
|
||||||
|
|
||||||
from ironicclient.common.apiclient import exceptions
|
from ironicclient.common.apiclient import exceptions
|
||||||
from ironicclient.common import cliutils
|
from ironicclient.common import cliutils
|
||||||
@ -409,9 +410,27 @@ def do_node_set_maintenance(cc, args):
|
|||||||
metavar='<power-state>',
|
metavar='<power-state>',
|
||||||
choices=['on', 'off', 'reboot'],
|
choices=['on', 'off', 'reboot'],
|
||||||
help="'on', 'off', or '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):
|
def do_node_set_power_state(cc, args):
|
||||||
"""Power a node on or off or reboot."""
|
"""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.")
|
@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…
x
Reference in New Issue
Block a user