diff --git a/ironicclient/common/base.py b/ironicclient/common/base.py index 0b11d3480..81305410d 100644 --- a/ironicclient/common/base.py +++ b/ironicclient/common/base.py @@ -71,9 +71,9 @@ class Manager(object): return [obj_class(self, res, loaded=True) for res in data if res] - def _update(self, url, body, response_key=None): - resp, body = self.api.json_request('PATCH', url, body=body) - # PATCH requests may not return a body + def _update(self, url, body, method='PATCH', response_key=None): + resp, body = self.api.json_request(method, url, body=body) + # PATCH/PUT requests may not return a body if body: return self.resource_class(self, body) diff --git a/ironicclient/shell.py b/ironicclient/shell.py index 3ff4c0fdc..5d253392e 100644 --- a/ironicclient/shell.py +++ b/ironicclient/shell.py @@ -23,6 +23,9 @@ import ironicclient from ironicclient import client as iroclient from ironicclient.common import utils from ironicclient import exc +from ironicclient.openstack.common import gettextutils + +gettextutils.install('ironicclient') class IronicShell(object): diff --git a/ironicclient/tests/v1/test_node.py b/ironicclient/tests/v1/test_node.py index 926ca7006..d0037ca5c 100644 --- a/ironicclient/tests/v1/test_node.py +++ b/ironicclient/tests/v1/test_node.py @@ -36,6 +36,9 @@ PORT = {'id': 456, 'address': 'AA:AA:AA:AA:AA:AA', 'extra': {}} +POWER_STATE = {'current': 'power off', + 'target': 'power on'} + CREATE_NODE = copy.deepcopy(NODE) del CREATE_NODE['id'] del CREATE_NODE['uuid'] @@ -78,6 +81,13 @@ fixtures = { {"ports": [PORT]}, ), }, + '/v1/nodes/%s/state/power' % NODE['uuid']: + { + 'PUT': ( + {}, + POWER_STATE, + ), + }, } @@ -140,3 +150,12 @@ class NodeManagerTest(testtools.TestCase): self.assertEqual(len(ports), 1) self.assertEqual(ports[0].uuid, PORT['uuid']) self.assertEqual(ports[0].address, PORT['address']) + + def test_node_set_power_state(self): + power_state = self.mgr.set_power_state(NODE['uuid'], "on") + body = {'target': 'power on'} + expect = [ + ('PUT', '/v1/nodes/%s/state/power' % NODE['uuid'], {}, body), + ] + self.assertEqual(expect, self.api.calls) + self.assertEqual('power on', power_state.target) diff --git a/ironicclient/v1/node.py b/ironicclient/v1/node.py index eab7e2248..310ff6126 100644 --- a/ironicclient/v1/node.py +++ b/ironicclient/v1/node.py @@ -61,3 +61,8 @@ class NodeManager(base.Manager): def update(self, node_id, patch): return self._update(self._path(node_id), patch) + + def set_power_state(self, node_id, state): + path = "%s/state/power" % node_id + target = {'target': "power %s" % state} + return self._update(self._path(path), target, method='PUT') diff --git a/ironicclient/v1/node_shell.py b/ironicclient/v1/node_shell.py index 90b7e7c93..d3e4e774a 100644 --- a/ironicclient/v1/node_shell.py +++ b/ironicclient/v1/node_shell.py @@ -134,3 +134,22 @@ def do_node_port_list(cc, args): field_labels = ['UUID', 'Address'] fields = ['uuid', 'address'] utils.print_list(ports, fields, field_labels, sortby=1) + + +@utils.arg('node', + metavar='<node id>', + help="ID of node") +@utils.arg('power_state', + metavar='<power state>', + choices=['on', 'off'], + help="Supported states: 'on' or 'off'") +def do_node_set_power_state(cc, args): + """Power the node on or off.""" + try: + state = cc.node.set_power_state(args.node, args.power_state) + except exc.HTTPNotFound: + raise exc.CommandError(_('Node not found: %s') % args.node) + + field_list = ['current', 'target'] + data = dict([(f, getattr(state, f, '')) for f in field_list]) + utils.print_dict(data, wrap=72)