Skip to content

Commit a1ac69c

Browse files
committed
Microversion 2.73: Support adding the reason behind a server lock
This patch adds a new parameter ``--reason`` to ``nova lock`` command and ``--locked`` filtering/sorting parameter to ``nova list`` command. This can help users to provide a reason when locking the server and to filter/sort instances based on their locked or value from 2.73 microversion. Implements blueprint add-locked-reason Depends-On: https://review.opendev.org/#/c/648662/ Change-Id: I438e6db2dd5000ba388d0a0f1c8ab74b96b47a71
1 parent fe4138a commit a1ac69c

8 files changed

Lines changed: 135 additions & 3 deletions

File tree

novaclient/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@
2525
# when client supported the max version, and bumped sequentially, otherwise
2626
# the client may break due to server side new version may include some
2727
# backward incompatible change.
28-
API_MAX_VERSION = api_versions.APIVersion("2.72")
28+
API_MAX_VERSION = api_versions.APIVersion("2.73")

novaclient/tests/unit/fixture_data/servers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,8 @@ def post_servers_1234_action(self, request, context):
454454
pass
455455
elif action == 'migrate':
456456
return None
457+
elif action == 'lock':
458+
return None
457459
elif action == 'rebuild':
458460
body = body[action]
459461
adminPass = body.get('adminPass', 'randompassword')

novaclient/tests/unit/v2/fakes.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ def delete_servers_1234_os_server_password(self, **kw):
774774

775775
none_actions = ['revertResize', 'os-stop', 'os-start',
776776
'forceDelete', 'restore', 'pause', 'unpause', 'unlock',
777-
'unrescue', 'resume', 'suspend', 'lock', 'shelve',
777+
'unrescue', 'resume', 'suspend', 'shelve',
778778
'shelveOffload', 'unshelve', 'resetNetwork']
779779
type_actions = ['os-getVNCConsole', 'os-getSPICEConsole',
780780
'os-getRDPConsole']
@@ -836,6 +836,22 @@ def post_servers_1234_action(self, body, **kw):
836836
# host can be optional
837837
expected.add('host')
838838
assert set(body[action].keys()) == expected
839+
elif action == 'lock':
840+
if self.api_version < api_versions.APIVersion("2.73"):
841+
assert body[action] is None
842+
else:
843+
# In 2.73 and above, we allow body to be one of these:
844+
# a) {'lock': None}
845+
# b) {'lock': {}}
846+
# c) {'lock': {locked_reason': 'blah'}}
847+
if body[action] is not None:
848+
expected = set()
849+
if 'locked_reason' in body[action].keys():
850+
# reason can be optional
851+
expected.add('locked_reason')
852+
assert set(body[action].keys()) == expected
853+
else:
854+
assert body[action] is None
839855
elif action == 'rebuild':
840856
body = body[action]
841857
adminPass = body.get('adminPass', 'randompassword')

novaclient/tests/unit/v2/test_servers.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,3 +1703,26 @@ def test_live_migrate_server(self):
17031703
ex = self.assertRaises(TypeError, self.cs.servers.live_migrate,
17041704
host='hostname', force=True)
17051705
self.assertIn('force', six.text_type(ex))
1706+
1707+
1708+
class ServersV273Test(ServersV268Test):
1709+
1710+
api_version = "2.73"
1711+
1712+
def test_lock_server(self):
1713+
s = self.cs.servers.get(1234)
1714+
ret = s.lock()
1715+
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
1716+
self.assert_called('POST', '/servers/1234/action',
1717+
{'lock': None})
1718+
ret = s.lock(reason='zombie-apocalypse')
1719+
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
1720+
self.assert_called('POST', '/servers/1234/action',
1721+
{'lock': {'locked_reason': 'zombie-apocalypse'}})
1722+
1723+
def test_lock_server_pre_273_fails_with_reason(self):
1724+
self.cs.api_version = api_versions.APIVersion('2.72')
1725+
s = self.cs.servers.get(1234)
1726+
e = self.assertRaises(TypeError,
1727+
s.lock, reason='blah')
1728+
self.assertIn("unexpected keyword argument 'reason'", six.text_type(e))

novaclient/tests/unit/v2/test_shell.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2092,6 +2092,24 @@ def test_lock(self):
20922092
self.run_command('lock sample-server')
20932093
self.assert_called('POST', '/servers/1234/action', {'lock': None})
20942094

2095+
def test_lock_pre_v273(self):
2096+
exp = self.assertRaises(SystemExit,
2097+
self.run_command,
2098+
'lock sample-server --reason zombies',
2099+
api_version='2.72')
2100+
self.assertIn('2', six.text_type(exp))
2101+
2102+
def test_lock_v273(self):
2103+
self.run_command('lock sample-server',
2104+
api_version='2.73')
2105+
self.assert_called('POST', '/servers/1234/action',
2106+
{'lock': None})
2107+
2108+
self.run_command('lock sample-server --reason zombies',
2109+
api_version='2.73')
2110+
self.assert_called('POST', '/servers/1234/action',
2111+
{'lock': {'locked_reason': 'zombies'}})
2112+
20952113
def test_unlock(self):
20962114
self.run_command('unlock sample-server')
20972115
self.assert_called('POST', '/servers/1234/action', {'unlock': None})
@@ -4280,6 +4298,22 @@ def test_show_v269_with_down_cells(self):
42804298
self.assert_called('GET', '/servers/9015', pos=2)
42814299
self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=3)
42824300

4301+
def test_list_pre_v273(self):
4302+
exp = self.assertRaises(SystemExit,
4303+
self.run_command,
4304+
'list --locked t',
4305+
api_version='2.72')
4306+
self.assertEqual(2, exp.code)
4307+
4308+
def test_list_v273(self):
4309+
self.run_command('list --locked t', api_version='2.73')
4310+
self.assert_called('GET', '/servers/detail?locked=t')
4311+
4312+
def test_list_v273_with_sort_key_dir(self):
4313+
self.run_command('list --sort locked:asc', api_version='2.73')
4314+
self.assert_called(
4315+
'GET', '/servers/detail?sort_dir=asc&sort_key=locked')
4316+
42834317

42844318
class PollForStatusTestCase(utils.TestCase):
42854319
@mock.patch("novaclient.v2.shell.time")

novaclient/v2/servers.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ def unpause(self):
214214
"""
215215
return self.manager.unpause(self)
216216

217+
@api_versions.wraps("2.0", "2.72")
217218
def lock(self):
218219
"""
219220
Lock -- Lock the instance from certain operations.
@@ -222,6 +223,16 @@ def lock(self):
222223
"""
223224
return self.manager.lock(self)
224225

226+
@api_versions.wraps("2.73")
227+
def lock(self, reason=None):
228+
"""
229+
Lock -- Lock the instance from certain operations.
230+
231+
:param reason: (Optional) The lock reason.
232+
:returns: An instance of novaclient.base.TupleWithMeta
233+
"""
234+
return self.manager.lock(self, reason=reason)
235+
225236
def unlock(self):
226237
"""
227238
Unlock -- Remove instance lock.
@@ -1097,6 +1108,7 @@ def unpause(self, server):
10971108
"""
10981109
return self._action('unpause', server, None)
10991110

1111+
@api_versions.wraps("2.0", "2.72")
11001112
def lock(self, server):
11011113
"""
11021114
Lock the server.
@@ -1106,6 +1118,22 @@ def lock(self, server):
11061118
"""
11071119
return self._action('lock', server, None)
11081120

1121+
@api_versions.wraps("2.73")
1122+
def lock(self, server, reason=None):
1123+
"""
1124+
Lock the server.
1125+
1126+
:param server: The :class:`Server` (or its ID) to lock
1127+
:param reason: (Optional) The lock reason.
1128+
:returns: An instance of novaclient.base.TupleWithMeta
1129+
"""
1130+
info = None
1131+
1132+
if reason:
1133+
info = {'locked_reason': reason}
1134+
1135+
return self._action('lock', server, info)
1136+
11091137
def unlock(self, server):
11101138
"""
11111139
Unlock the server.

novaclient/v2/shell.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1563,6 +1563,13 @@ def _print_flavor(flavor):
15631563
"case is 'NOT(t1 OR t2)'. Tags must be separated by commas: "
15641564
"--not-tags-any <tag1,tag2>"),
15651565
start_version="2.26")
1566+
@utils.arg(
1567+
'--locked',
1568+
dest='locked',
1569+
metavar='<locked>',
1570+
default=None,
1571+
help=_('Display servers based on their locked value'),
1572+
start_version="2.73")
15661573
def do_list(cs, args):
15671574
"""List servers."""
15681575
imageid = None
@@ -1639,6 +1646,11 @@ def do_list(cs, args):
16391646
raise exceptions.CommandError(_('Invalid changes-before value: %s')
16401647
% search_opts['changes-before'])
16411648

1649+
# In microversion 2.73 we added ``locked`` option in server details.
1650+
have_added_locked = cs.api_version >= api_versions.APIVersion('2.73')
1651+
if have_added_locked and args.locked:
1652+
search_opts['locked'] = args.locked
1653+
16421654
servers = cs.servers.list(detailed=detailed,
16431655
search_opts=search_opts,
16441656
sort_keys=sort_keys,
@@ -2155,12 +2167,23 @@ def do_start(cs, args):
21552167
_("Unable to start the specified server(s)."))
21562168

21572169

2170+
# From microversion 2.73, we can specify a reason for locking the server.
21582171
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2172+
@utils.arg(
2173+
'--reason',
2174+
metavar='<reason>',
2175+
help=_('Reason for locking the server.'),
2176+
start_version='2.73')
21592177
def do_lock(cs, args):
21602178
"""Lock a server. A normal (non-admin) user will not be able to execute
21612179
actions on a locked server.
21622180
"""
2163-
_find_server(cs, args.server).lock()
2181+
update_kwargs = {}
2182+
if 'reason' in args and args.reason is not None:
2183+
update_kwargs['reason'] = args.reason
2184+
2185+
server = _find_server(cs, args.server)
2186+
server.lock(**update_kwargs)
21642187

21652188

21662189
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
features:
3+
- Added a ``--reason`` option to ``nova lock`` command that enables users
4+
to specify a reason when locking a server and a ``locked``
5+
filtering/sorting option to ``nova list`` command which enables users to
6+
filter/sort servers based on their ``locked`` value in microversion 2.73.

0 commit comments

Comments
 (0)