summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2017-01-18 16:22:13 +0000
committerGerrit Code Review <review@openstack.org>2017-01-18 16:22:13 +0000
commit625f45d699f360c0e98420b1999e8d881ddfeb0d (patch)
treeeb9826a1626c85171ef653bc20dda8c3f1a07193
parent301c85e0e6b2d96d7fa711b592c763774377534e (diff)
parent249215e3d5607830ed6e6c7fe6f2dda33782f211 (diff)
downloadpython-ironicclient-625f45d699f360c0e98420b1999e8d881ddfeb0d.tar.gz
Merge "Add soft reboot/poweroff power states."
-rw-r--r--ironicclient/tests/functional/base.py5
-rw-r--r--ironicclient/tests/unit/v1/test_node.py53
-rw-r--r--ironicclient/tests/unit/v1/test_node_shell.py52
-rw-r--r--ironicclient/v1/node.py37
-rw-r--r--ironicclient/v1/node_shell.py21
-rw-r--r--releasenotes/notes/soft-reboot-poweroff-e33d078a05db3894.yaml4
6 files changed, 156 insertions, 16 deletions
diff --git a/ironicclient/tests/functional/base.py b/ironicclient/tests/functional/base.py
index f4dd7b0..1110787 100644
--- a/ironicclient/tests/functional/base.py
+++ b/ironicclient/tests/functional/base.py
@@ -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',
diff --git a/ironicclient/tests/unit/v1/test_node.py b/ironicclient/tests/unit/v1/test_node.py
index 0a410d6..936445a 100644
--- a/ironicclient/tests/unit/v1/test_node.py
+++ b/ironicclient/tests/unit/v1/test_node.py
@@ -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'},
@@ -900,13 +900,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(
diff --git a/ironicclient/tests/unit/v1/test_node_shell.py b/ironicclient/tests/unit/v1/test_node_shell.py
index 55a6496..8cd572c 100644
--- a/ironicclient/tests/unit/v1/test_node_shell.py
+++ b/ironicclient/tests/unit/v1/test_node_shell.py
@@ -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
-
- n_shell.do_node_set_power_state(client_mock, args)
- client_mock.node.set_power_state.assert_called_once_with('node_uuid',
- power_state)
+ args.soft = soft
+ args.power_timeout = timeout
+
+ 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"}'
diff --git a/ironicclient/v1/node.py b/ironicclient/v1/node.py
index c6af933..ac806f3 100644
--- a/ironicclient/v1/node.py
+++ b/ironicclient/v1/node.py
@@ -28,6 +28,8 @@ _power_states = {
'on': 'power on',
'off': 'power off',
'reboot': 'rebooting',
+ 'soft off': 'soft power off',
+ 'soft reboot': 'soft rebooting',
}
@@ -308,10 +310,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.
diff --git a/ironicclient/v1/node_shell.py b/ironicclient/v1/node_shell.py
index b43fe9e..6b22b3c 100644
--- a/ironicclient/v1/node_shell.py
+++ b/ironicclient/v1/node_shell.py
@@ -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.")
diff --git a/releasenotes/notes/soft-reboot-poweroff-e33d078a05db3894.yaml b/releasenotes/notes/soft-reboot-poweroff-e33d078a05db3894.yaml
new file mode 100644
index 0000000..cbbd530
--- /dev/null
+++ b/releasenotes/notes/soft-reboot-poweroff-e33d078a05db3894.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - Add optional arguments '--soft' and '--power-timeout' to the command
+ "node-set-power-state".