Reset-state and snapshot-reset-state for multiple objects

This allows a user to call reset-state or snapshot-reset-state on a
list of objects.  The behavior is modeled after a similar change to
delete multiple volumes.

$ cinder reset-state good_volume good_volume_2
<no output>

Error behavior is as follows:

One success/one failure:
$ cinder reset-state good_volume asdf
Reset state for volume asdf failed: No volume with a name or ID of 'asdf' exists.

One failure:
$ cinder reset-state asdf
ERROR: Reset state for volume asdf failed: No volume with a name or ID of 'asdf' exists.

Two failures:
$ cinder reset-state asdf qwert
Reset state for volume asdf failed: No volume with a name or ID of 'asdf' exists.
Reset state for volume qwert failed: No volume with a name or ID of 'qwert' exists.
ERROR: Unable to reset the state for any of the specified volumes.

Related-Bug: 1241941
Closes-Bug: 1256069
Change-Id: Id0a36fb7de0d69be0dac98ea04e4708775250b7a
This commit is contained in:
Eric Harney 2013-12-02 18:05:54 -05:00
parent e64973f9a7
commit 0e2bd33265
7 changed files with 126 additions and 12 deletions

View File

@ -258,6 +258,9 @@ class FakeHTTPClient(base_client.HTTPClient):
def get_snapshots_1234(self, **kw):
return (200, {}, {'snapshot': _stub_snapshot(id='1234')})
def get_snapshots_5678(self, **kw):
return (200, {}, {'snapshot': _stub_snapshot(id='5678')})
def put_snapshots_1234(self, **kw):
snapshot = _stub_snapshot(id='1234')
snapshot.update(kw['body']['snapshot'])
@ -276,6 +279,9 @@ class FakeHTTPClient(base_client.HTTPClient):
raise AssertionError("Unexpected action: %s" % action)
return (resp, {}, _body)
def post_snapshots_5678_action(self, body, **kw):
return self.post_snapshots_1234_action(body, **kw)
#
# Volumes
#
@ -349,6 +355,9 @@ class FakeHTTPClient(base_client.HTTPClient):
raise AssertionError("Unexpected action: %s" % action)
return (resp, {}, _body)
def post_volumes_5678_action(self, body, **kw):
return self.post_volumes_1234_action(body, **kw)
def post_volumes(self, **kw):
return (202, {}, {'volume': {}})

View File

@ -212,6 +212,14 @@ class ShellTest(utils.TestCase):
expected = {'os-reset_status': {'status': 'error'}}
self.assert_called('POST', '/volumes/1234/action', body=expected)
def test_reset_state_multiple(self):
self.run_command('reset-state 1234 5678 --state error')
expected = {'os-reset_status': {'status': 'error'}}
self.assert_called_anytime('POST', '/volumes/1234/action',
body=expected)
self.assert_called_anytime('POST', '/volumes/5678/action',
body=expected)
def test_snapshot_reset_state(self):
self.run_command('snapshot-reset-state 1234')
expected = {'os-reset_status': {'status': 'available'}}
@ -222,6 +230,14 @@ class ShellTest(utils.TestCase):
expected = {'os-reset_status': {'status': 'error'}}
self.assert_called('POST', '/snapshots/1234/action', body=expected)
def test_snapshot_reset_state_multiple(self):
self.run_command('snapshot-reset-state 1234 5678')
expected = {'os-reset_status': {'status': 'available'}}
self.assert_called_anytime('POST', '/snapshots/1234/action',
body=expected)
self.assert_called_anytime('POST', '/snapshots/5678/action',
body=expected)
def test_encryption_type_list(self):
"""
Test encryption-type-list shell command.

View File

@ -267,6 +267,9 @@ class FakeHTTPClient(base_client.HTTPClient):
def get_snapshots_1234(self, **kw):
return (200, {}, {'snapshot': _stub_snapshot(id='1234')})
def get_snapshots_5678(self, **kw):
return (200, {}, {'snapshot': _stub_snapshot(id='5678')})
def put_snapshots_1234(self, **kw):
snapshot = _stub_snapshot(id='1234')
snapshot.update(kw['body']['snapshot'])
@ -285,6 +288,9 @@ class FakeHTTPClient(base_client.HTTPClient):
raise AssertionError('Unexpected action: %s' % action)
return (resp, {}, _body)
def post_snapshots_5678_action(self, body, **kw):
return self.post_snapshots_1234_action(body, **kw)
#
# Volumes
#
@ -358,6 +364,9 @@ class FakeHTTPClient(base_client.HTTPClient):
raise AssertionError("Unexpected action: %s" % action)
return (resp, {}, _body)
def post_volumes_5678_action(self, body, **kw):
return self.post_volumes_1234_action(body, **kw)
def post_volumes(self, **kw):
return (202, {}, {'volume': {}})

View File

@ -190,6 +190,14 @@ class ShellTest(utils.TestCase):
expected = {'os-reset_status': {'status': 'error'}}
self.assert_called('POST', '/volumes/1234/action', body=expected)
def test_reset_state_multiple(self):
self.run_command('reset-state 1234 5678 --state error')
expected = {'os-reset_status': {'status': 'error'}}
self.assert_called_anytime('POST', '/volumes/1234/action',
body=expected)
self.assert_called_anytime('POST', '/volumes/5678/action',
body=expected)
def test_snapshot_reset_state(self):
self.run_command('snapshot-reset-state 1234')
expected = {'os-reset_status': {'status': 'available'}}
@ -200,6 +208,14 @@ class ShellTest(utils.TestCase):
expected = {'os-reset_status': {'status': 'error'}}
self.assert_called('POST', '/snapshots/1234/action', body=expected)
def test_snapshot_reset_state_multiple(self):
self.run_command('snapshot-reset-state 1234 5678')
expected = {'os-reset_status': {'status': 'available'}}
self.assert_called_anytime('POST', '/snapshots/1234/action',
body=expected)
self.assert_called_anytime('POST', '/snapshots/5678/action',
body=expected)
def test_encryption_type_list(self):
"""
Test encryption-type-list shell command.

View File

@ -311,7 +311,7 @@ def do_force_delete(cs, args):
"specified volumes.")
@utils.arg('volume', metavar='<volume>',
@utils.arg('volume', metavar='<volume>', nargs='+',
help='Name or ID of the volume to modify.')
@utils.arg('--state', metavar='<state>', default='available',
help=('Indicate which state to assign the volume. Options include '
@ -320,8 +320,23 @@ def do_force_delete(cs, args):
@utils.service_type('volume')
def do_reset_state(cs, args):
"""Explicitly update the state of a volume."""
volume = utils.find_volume(cs, args.volume)
volume.reset_state(args.state)
failure_count = 0
single = (len(args.volume) == 1)
for volume in args.volume:
try:
utils.find_volume(cs, volume).reset_state(args.state)
except Exception as e:
failure_count += 1
msg = "Reset state for volume %s failed: %s" % (volume, e)
if not single:
print(msg)
if failure_count == len(args.volume):
if not single:
msg = "Unable to reset the state for any of the specified volumes."
raise exceptions.CommandError(msg)
@utils.arg('volume', metavar='<volume>',
@ -498,7 +513,7 @@ def do_snapshot_rename(cs, args):
_find_volume_snapshot(cs, args.snapshot).update(**kwargs)
@utils.arg('snapshot', metavar='<snapshot>',
@utils.arg('snapshot', metavar='<snapshot>', nargs='+',
help='Name or ID of the snapshot to modify.')
@utils.arg('--state', metavar='<state>',
default='available',
@ -509,8 +524,24 @@ def do_snapshot_rename(cs, args):
@utils.service_type('volume')
def do_snapshot_reset_state(cs, args):
"""Explicitly update the state of a snapshot."""
snapshot = _find_volume_snapshot(cs, args.snapshot)
snapshot.reset_state(args.state)
failure_count = 0
single = (len(args.snapshot) == 1)
for snapshot in args.snapshot:
try:
_find_volume_snapshot(cs, snapshot).reset_state(args.state)
except Exception as e:
failure_count += 1
msg = "Reset state for snapshot %s failed: %s" % (snapshot, e)
if not single:
print(msg)
if failure_count == len(args.snapshot):
if not single:
msg = ("Unable to reset the state for any of the the specified "
"snapshots.")
raise exceptions.CommandError(msg)
def _print_volume_type_list(vtypes):

View File

@ -339,7 +339,7 @@ def do_force_delete(cs, args):
"specified volumes.")
@utils.arg('volume', metavar='<volume>',
@utils.arg('volume', metavar='<volume>', nargs='+',
help='Name or ID of the volume to modify.')
@utils.arg('--state', metavar='<state>', default='available',
help=('Indicate which state to assign the volume. Options include '
@ -348,8 +348,23 @@ def do_force_delete(cs, args):
@utils.service_type('volumev2')
def do_reset_state(cs, args):
"""Explicitly update the state of a volume."""
volume = utils.find_volume(cs, args.volume)
volume.reset_state(args.state)
failure_count = 0
single = (len(args.volume) == 1)
for volume in args.volume:
try:
utils.find_volume(cs, volume).reset_state(args.state)
except Exception as e:
failure_count += 1
msg = "Reset state for volume %s failed: %s" % (volume, e)
if not single:
print(msg)
if failure_count == len(args.volume):
if not single:
msg = "Unable to reset the state for any of the specified volumes."
raise exceptions.CommandError(msg)
@utils.arg('volume',
@ -556,7 +571,7 @@ def do_snapshot_rename(cs, args):
_find_volume_snapshot(cs, args.snapshot).update(**kwargs)
@utils.arg('snapshot', metavar='<snapshot>',
@utils.arg('snapshot', metavar='<snapshot>', nargs='+',
help='Name or ID of the snapshot to modify.')
@utils.arg('--state', metavar='<state>',
default='available',
@ -567,8 +582,24 @@ def do_snapshot_rename(cs, args):
@utils.service_type('volumev2')
def do_snapshot_reset_state(cs, args):
"""Explicitly update the state of a snapshot."""
snapshot = _find_volume_snapshot(cs, args.snapshot)
snapshot.reset_state(args.state)
failure_count = 0
single = (len(args.snapshot) == 1)
for snapshot in args.snapshot:
try:
_find_volume_snapshot(cs, snapshot).reset_state(args.state)
except Exception as e:
failure_count += 1
msg = "Reset state for snapshot %s failed: %s" % (snapshot, e)
if not single:
print(msg)
if failure_count == len(args.snapshot):
if not single:
msg = ("Unable to reset the state for any of the the specified "
"snapshots.")
raise exceptions.CommandError(msg)
def _print_volume_type_list(vtypes):

View File

@ -31,6 +31,8 @@ Release Notes
=============
MASTER
------
* Add support for reset-state on multiple volumes or snapshots at once
.. _1254951: http://bugs.launchpad.net/python-cinderclient/+bug/1254951
.. _1254587: http://bugs.launchpad.net/python-cinderclient/+bug/1254587
.. _1253142: http://bugs.launchpad.net/python-cinderclient/+bug/1253142