Skip to content

Commit

Permalink
Add node-set-maintenance command
Browse files Browse the repository at this point in the history
This command accepts an optional reason and calls the new
/nodes/uuid/maintenance endpoint to set and clear maintenance
mode and maintenance reason.

It will reject setting maintenance mode to off with a reason,
as setting maintenance mode to off clears the reason.

Change-Id: I04e14bc239b343d8791747963e4feec1e2cbdda8
Implements: blueprint maintenance-reason
  • Loading branch information
jimrollenhagen committed Oct 28, 2014
1 parent e611ae5 commit dadaf7f
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 0 deletions.
29 changes: 29 additions & 0 deletions ironicclient/tests/v1/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@
{"ports": [PORT]},
),
},
'/v1/nodes/%s/maintenance' % NODE1['uuid']:
{
'PUT': (
{},
None,
),
'DELETE': (
{},
None,
),
},
'/v1/nodes/%s/states/power' % NODE1['uuid']:
{
'PUT': (
Expand Down Expand Up @@ -554,6 +565,24 @@ def test_node_port_list_detail(self):
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(ports))

def test_node_set_maintenance_on(self):
maintenance = self.mgr.set_maintenance(NODE1['uuid'], 'on',
maint_reason='reason')
body = {'reason': 'reason'}
expect = [
('PUT', '/v1/nodes/%s/maintenance' % NODE1['uuid'], {}, body),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(None, maintenance)

def test_node_set_maintenance_off(self):
maintenance = self.mgr.set_maintenance(NODE1['uuid'], 'off')
expect = [
('DELETE', '/v1/nodes/%s/maintenance' % NODE1['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(None, maintenance)

def test_node_set_power_state(self):
power_state = self.mgr.set_power_state(NODE1['uuid'], "on")
body = {'target': 'power on'}
Expand Down
37 changes: 37 additions & 0 deletions ironicclient/tests/v1/test_node_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import mock

from ironicclient.common import utils as commonutils
from ironicclient.openstack.common.apiclient import exceptions
from ironicclient.openstack.common import cliutils
from ironicclient.tests import utils
import ironicclient.v1.node_shell as n_shell
Expand Down Expand Up @@ -161,6 +162,42 @@ def test_do_node_show_by_instance_uuid(self):
# assert get() wasn't called
self.assertFalse(client_mock.node.get.called)

def test_do_node_set_maintenance_on(self):
client_mock = mock.MagicMock()
args = mock.MagicMock()
args.node = 'node_uuid'
args.maintenance_mode = 'on'
args.reason = 'reason'

n_shell.do_node_set_maintenance(client_mock, args)
client_mock.node.set_maintenance.assert_called_once_with('node_uuid',
'on',
maint_reason='reason')

def test_do_node_set_maintenance_off(self):
client_mock = mock.MagicMock()
args = mock.MagicMock()
args.node = 'node_uuid'
args.maintenance_mode = 'off'
# NOTE(jroll) None is the default. <3 mock.
args.reason = None

n_shell.do_node_set_maintenance(client_mock, args)
client_mock.node.set_maintenance.assert_called_once_with('node_uuid',
'off',
maint_reason=None)

def test_do_node_set_maintenance_off_with_reason_fails(self):
client_mock = mock.MagicMock()
args = mock.MagicMock()
args.node = 'node_uuid'
args.maintenance_mode = 'off'
args.reason = 'reason'

self.assertRaises(exceptions.CommandError,
n_shell.do_node_set_maintenance,
client_mock, args)

def _do_node_set_power_state_helper(self, power_state):
client_mock = mock.MagicMock()
args = mock.MagicMock()
Expand Down
8 changes: 8 additions & 0 deletions ironicclient/v1/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,14 @@ def vendor_passthru(self, **kwargs):
path = self._path(node_id) + "/vendor_passthru/%s" % method
return self._update(path, args, method='POST')

def set_maintenance(self, node_id, state, maint_reason=None):
path = "%s/maintenance" % node_id
if state == 'on':
reason = {'reason': maint_reason}
return self._update(self._path(path), reason, method='PUT')
if state == 'off':
return self._delete(self._path(path))

def set_power_state(self, node_id, state):
path = "%s/states/power" % node_id
if state in ['on', 'off']:
Expand Down
22 changes: 22 additions & 0 deletions ironicclient/v1/node_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import six

from ironicclient.common import utils
from ironicclient.openstack.common.apiclient import exceptions
from ironicclient.openstack.common import cliutils
from ironicclient.v1 import resource_fields as res_fields

Expand Down Expand Up @@ -251,6 +252,27 @@ def do_node_port_list(cc, args):
sortby_index=None)


@cliutils.arg('node', metavar='<node id>', help="UUID of node")
@cliutils.arg(
'maintenance_mode',
metavar='<maintenance mode>',
choices=['on', 'off'],
help="Supported states: 'on' or 'off'")
@cliutils.arg(
'--reason',
metavar='<reason>',
default=None,
help=('The reason for setting maintenance mode to "on"; not valid when '
'setting to "off".'))
def do_node_set_maintenance(cc, args):
"""Set maintenance mode on or off."""
if args.reason and args.maintenance_mode == 'off':
raise exceptions.CommandError(_('Cannot set "reason" when turning off '
'maintenance mode.'))
cc.node.set_maintenance(args.node, args.maintenance_mode,
maint_reason=args.reason)


@cliutils.arg('node', metavar='<node id>', help="UUID of node")
@cliutils.arg(
'power_state',
Expand Down

0 comments on commit dadaf7f

Please sign in to comment.