diff --git a/novaclient/exceptions.py b/novaclient/exceptions.py index 371197581..ede19bd8c 100644 --- a/novaclient/exceptions.py +++ b/novaclient/exceptions.py @@ -93,6 +93,11 @@ class VersionNotFoundForAPIMethod(Exception): return self.msg_fmt % {"vers": self.version, "method": self.method} +class InstanceInDeletedState(Exception): + """Instance is in the deleted state.""" + pass + + class ClientException(Exception): """ The base exception class for all exceptions this library raises. diff --git a/novaclient/tests/unit/v2/fakes.py b/novaclient/tests/unit/v2/fakes.py index b6656a2f4..add037a9c 100644 --- a/novaclient/tests/unit/v2/fakes.py +++ b/novaclient/tests/unit/v2/fakes.py @@ -665,6 +665,8 @@ class FakeHTTPClient(base_client.HTTPClient): elif action == 'createImage': assert set(body[action].keys()) == set(['name', 'metadata']) _headers = dict(location="http://blah/images/456") + if body[action]['name'] == 'mysnapshot_deleted': + _headers = dict(location="http://blah/images/457") elif action == 'os-getConsoleOutput': assert list(body[action]) == ['length'] return (202, {}, {'output': 'foo'}) @@ -1035,6 +1037,16 @@ class FakeHTTPClient(base_client.HTTPClient): "status": "SAVING", "progress": 80, "links": {}, + }, + { + "id": 3, + "name": "My Server Backup Deleted", + "serverId": 1234, + "updated": "2010-10-10T12:00:00Z", + "created": "2010-08-10T12:00:00Z", + "status": "DELETED", + "fault": {'message': 'Image has been deleted.'}, + "links": {}, } ]}) @@ -1047,6 +1059,9 @@ class FakeHTTPClient(base_client.HTTPClient): def get_images_456(self, **kw): return (200, {}, {'image': self.get_images_detail()[2]['images'][1]}) + def get_images_457(self, **kw): + return (200, {}, {'image': self.get_images_detail()[2]['images'][2]}) + def get_images_3e861307_73a6_4d1f_8d68_f68b03223032(self): raise exceptions.NotFound('404') diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py index 8abfd1b81..3e1c4d700 100644 --- a/novaclient/tests/unit/v2/test_shell.py +++ b/novaclient/tests/unit/v2/test_shell.py @@ -813,6 +813,24 @@ class ShellTest(utils.TestCase): self.assertIn('My Server Backup', output) self.assertIn('SAVING', output) + @mock.patch('novaclient.v2.shell._poll_for_status') + def test_create_image_with_poll(self, poll_method): + self.run_command( + 'image-create sample-server mysnapshot --poll') + self.assert_called_anytime( + 'POST', '/servers/1234/action', + {'createImage': {'name': 'mysnapshot', 'metadata': {}}}, + ) + self.assertEqual(1, poll_method.call_count) + poll_method.assert_has_calls( + [mock.call(self.shell.cs.images.get, '456', 'snapshotting', + ['active'])]) + + def test_create_image_with_poll_to_check_image_state_deleted(self): + self.assertRaises( + exceptions.InstanceInDeletedState, self.run_command, + 'image-create sample-server mysnapshot_deleted --poll') + def test_image_delete(self): self.run_command('image-delete 1') self.assert_called('DELETE', '/images/1') diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py index dbab11c09..1e74ff83b 100644 --- a/novaclient/v2/shell.py +++ b/novaclient/v2/shell.py @@ -583,6 +583,10 @@ def _poll_for_status(poll_fn, obj_id, action, final_ok_states, if not silent: print(_("\nError %s server") % action) raise exceptions.InstanceInErrorState(obj.fault['message']) + elif status == "deleted": + if not silent: + print(_("\nDeleted %s server") % action) + raise exceptions.InstanceInDeletedState(obj.fault['message']) if not silent: print_progress(progress)