diff --git a/cinderclient/tests/v1/fakes.py b/cinderclient/tests/v1/fakes.py index c3c6bf5bd..1bebecb41 100644 --- a/cinderclient/tests/v1/fakes.py +++ b/cinderclient/tests/v1/fakes.py @@ -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': {}}) diff --git a/cinderclient/tests/v1/test_shell.py b/cinderclient/tests/v1/test_shell.py index 0f80062a0..a679e4c7c 100644 --- a/cinderclient/tests/v1/test_shell.py +++ b/cinderclient/tests/v1/test_shell.py @@ -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. diff --git a/cinderclient/tests/v2/fakes.py b/cinderclient/tests/v2/fakes.py index 5804d0f06..01b940462 100644 --- a/cinderclient/tests/v2/fakes.py +++ b/cinderclient/tests/v2/fakes.py @@ -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': {}}) diff --git a/cinderclient/tests/v2/test_shell.py b/cinderclient/tests/v2/test_shell.py index 5ee4e16ad..eccfef97a 100644 --- a/cinderclient/tests/v2/test_shell.py +++ b/cinderclient/tests/v2/test_shell.py @@ -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. diff --git a/cinderclient/v1/shell.py b/cinderclient/v1/shell.py index 0012388b0..acefe26af 100644 --- a/cinderclient/v1/shell.py +++ b/cinderclient/v1/shell.py @@ -311,7 +311,7 @@ def do_force_delete(cs, args): "specified volumes.") -@utils.arg('volume', metavar='', +@utils.arg('volume', metavar='', nargs='+', help='Name or ID of the volume to modify.') @utils.arg('--state', metavar='', 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='', @@ -498,7 +513,7 @@ def do_snapshot_rename(cs, args): _find_volume_snapshot(cs, args.snapshot).update(**kwargs) -@utils.arg('snapshot', metavar='', +@utils.arg('snapshot', metavar='', nargs='+', help='Name or ID of the snapshot to modify.') @utils.arg('--state', metavar='', 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): diff --git a/cinderclient/v2/shell.py b/cinderclient/v2/shell.py index bce7585a0..ddf92c2f1 100644 --- a/cinderclient/v2/shell.py +++ b/cinderclient/v2/shell.py @@ -339,7 +339,7 @@ def do_force_delete(cs, args): "specified volumes.") -@utils.arg('volume', metavar='', +@utils.arg('volume', metavar='', nargs='+', help='Name or ID of the volume to modify.') @utils.arg('--state', metavar='', 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='', +@utils.arg('snapshot', metavar='', nargs='+', help='Name or ID of the snapshot to modify.') @utils.arg('--state', metavar='', 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): diff --git a/doc/source/index.rst b/doc/source/index.rst index 44cc75704..1b9c4f854 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -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