diff options
author | zhangbailin <zhangbailin@inspur.com> | 2020-03-12 18:44:36 +0800 |
---|---|---|
committer | zhangbailin <zhangbailin@inspur.com> | 2020-04-09 08:52:05 +0800 |
commit | 4d6c70d25df99a4f28f263cd3160c74ccf1343e3 (patch) | |
tree | 64ab8962905dee361f94d2b417081d1e77e5971b /novaclient | |
parent | ea092b29880e71f0ba2d8e1eb93a9cf73edee2a2 (diff) | |
download | python-novaclient-4d6c70d25df99a4f28f263cd3160c74ccf1343e3.tar.gz |
Microversion 2.85: Change volume-update CLI
This commit add a new CLI
``nova volume-update [--[no-]delete-on-termination]
<server> <src_volume> <dest_volume>`` to update
'delete_on_termination' for an attached volume, that the user can
decide whether to delete attached volumes when destroying the server.
Depends-On: https://review.opendev.org/#/c/711194/
Change-Id: I1fc64fb6e6611c92c6b72265e1bf4b32e9c45f0a
Blueprint: destroy-instance-with-datavolume
Diffstat (limited to 'novaclient')
-rw-r--r-- | novaclient/__init__.py | 2 | ||||
-rw-r--r-- | novaclient/tests/unit/v2/test_shell.py | 36 | ||||
-rw-r--r-- | novaclient/tests/unit/v2/test_volumes.py | 23 | ||||
-rw-r--r-- | novaclient/v2/shell.py | 34 | ||||
-rw-r--r-- | novaclient/v2/volumes.py | 30 |
5 files changed, 116 insertions, 9 deletions
diff --git a/novaclient/__init__.py b/novaclient/__init__.py index adcb85dd..5e3e7701 100644 --- a/novaclient/__init__.py +++ b/novaclient/__init__.py @@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1") # when client supported the max version, and bumped sequentially, otherwise # the client may break due to server side new version may include some # backward incompatible change. -API_MAX_VERSION = api_versions.APIVersion("2.84") +API_MAX_VERSION = api_versions.APIVersion("2.85") diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py index 18badc0b..d21252bd 100644 --- a/novaclient/tests/unit/v2/test_shell.py +++ b/novaclient/tests/unit/v2/test_shell.py @@ -3992,11 +3992,43 @@ class ShellTest(utils.TestCase): {'volumeAttachment': {'volumeId': 'Work'}}) - def test_volume_update(self): - self.run_command('volume-update sample-server Work Work') + def test_volume_update_pre_v285(self): + """Before microversion 2.85, we should keep the original behavior""" + self.run_command('volume-update sample-server Work Work', + api_version='2.84') self.assert_called('PUT', '/servers/1234/os-volume_attachments/Work', {'volumeAttachment': {'volumeId': 'Work'}}) + def test_volume_update_swap_v285(self): + """Microversion 2.85, we should also keep the original behavior.""" + self.run_command('volume-update sample-server Work Work', + api_version='2.85') + self.assert_called('PUT', '/servers/1234/os-volume_attachments/Work', + {'volumeAttachment': {'volumeId': 'Work'}}) + + def test_volume_update_v285(self): + self.run_command('volume-update sample-server --delete-on-termination ' + 'Work Work', api_version='2.85') + body = {'volumeAttachment': + {'volumeId': 'Work', 'delete_on_termination': True}} + self.assert_called('PUT', '/servers/1234/os-volume_attachments/Work', + body) + + self.run_command('volume-update sample-server ' + '--no-delete-on-termination ' + 'Work Work', api_version='2.85') + body = {'volumeAttachment': + {'volumeId': 'Work', 'delete_on_termination': False}} + self.assert_called('PUT', '/servers/1234/os-volume_attachments/Work', + body) + + def test_volume_update_v285_conflicting(self): + self.assertRaises( + SystemExit, self.run_command, + 'volume-update sample-server --delete-on-termination ' + '--no-delete-on-termination Work Work', + api_version='2.85') + def test_volume_detach(self): self.run_command('volume-detach sample-server Work') self.assert_called('DELETE', diff --git a/novaclient/tests/unit/v2/test_volumes.py b/novaclient/tests/unit/v2/test_volumes.py index d18f8466..93ea1c96 100644 --- a/novaclient/tests/unit/v2/test_volumes.py +++ b/novaclient/tests/unit/v2/test_volumes.py @@ -156,3 +156,26 @@ class VolumesV279Test(VolumesV249Test): volume_id='15e59938-07d5-11e1-90e3-e3dffe0c5983', delete_on_termination=True) self.assertIn('delete_on_termination', str(ex)) + + +class VolumesV285Test(VolumesV279Test): + api_version = "2.85" + + def test_volume_update_server_volume(self): + v = self.cs.volumes.update_server_volume( + server_id=1234, + src_volid='Work', + dest_volid='Work', + delete_on_termination=True + ) + self.assert_request_id(v, fakes.FAKE_REQUEST_ID_LIST) + self.cs.assert_called('PUT', + '/servers/1234/os-volume_attachments/Work') + self.assertIsInstance(v, volumes.Volume) + + def test_volume_update_server_volume_pre_v285(self): + self.cs.api_version = api_versions.APIVersion('2.84') + ex = self.assertRaises( + TypeError, self.cs.volumes.update_server_volume, "1234", + 'Work', 'Work', delete_on_termination=True) + self.assertIn('delete_on_termination', str(ex)) diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py index ac6c5ea1..e0498df8 100644 --- a/novaclient/v2/shell.py +++ b/novaclient/v2/shell.py @@ -2730,22 +2730,44 @@ def do_volume_attach(cs, args): help=_('Name or ID of server.')) @utils.arg( 'src_volume', - metavar='<src_volid>', + metavar='<src_volume>', help=_('ID of the source (original) volume.')) @utils.arg( 'dest_volume', - metavar='<dest_volid>', + metavar='<dest_volume>', help=_('ID of the destination volume.')) +@utils.arg( + '--delete-on-termination', + default=None, + group='delete_on_termination', + action='store_true', + help=_('Specify that the volume should be deleted ' + 'when the server is destroyed.'), + start_version='2.85') +@utils.arg( + '--no-delete-on-termination', + group='delete_on_termination', + action='store_false', + help=_('Specify that the volume should not be deleted ' + 'when the server is destroyed.'), + start_version='2.85') def do_volume_update(cs, args): """Update the attachment on the server. - Migrates the data from an attached volume to the - specified available volume and swaps out the active - attachment to the new volume. + If dest_volume is the same as the src_volume then the command migrates + the data from the attached volume to the specified available volume + and swaps out the active attachment to the new volume. Otherwise it + only updates the parameters of the existing attachment. """ + kwargs = dict() + if (cs.api_version >= api_versions.APIVersion('2.85') and + args.delete_on_termination is not None): + kwargs['delete_on_termination'] = args.delete_on_termination + cs.volumes.update_server_volume(_find_server(cs, args.server).id, args.src_volume, - args.dest_volume) + args.dest_volume, + **kwargs) @utils.arg( diff --git a/novaclient/v2/volumes.py b/novaclient/v2/volumes.py index 8fc75565..7153c835 100644 --- a/novaclient/v2/volumes.py +++ b/novaclient/v2/volumes.py @@ -103,6 +103,7 @@ class VolumeManager(base.Manager): return self._create("/servers/%s/os-volume_attachments" % server_id, body, "volumeAttachment") + @api_versions.wraps("2.0", "2.84") def update_server_volume(self, server_id, src_volid, dest_volid): """ Swaps the existing volume attachment to point to a new volume. @@ -124,6 +125,35 @@ class VolumeManager(base.Manager): (server_id, src_volid,), body, "volumeAttachment") + @api_versions.wraps("2.85") + def update_server_volume(self, server_id, src_volid, dest_volid, + delete_on_termination=None): + """ + Swaps the existing volume attachment to point to a new volume. + + Takes a server, a source (attached) volume and a destination volume and + performs a hypervisor assisted data migration from src to dest volume, + detaches the original (source) volume and attaches the new destination + volume. Note that not all backing hypervisor drivers support this + operation and it may be disabled via policy. + + + :param server_id: The ID of the server + :param source_volume: The ID of the src volume + :param dest_volume: The ID of the destination volume + :param delete_on_termination: Marked whether to delete the attached + volume when the server is deleted + (optional). + :rtype: :class:`Volume` + """ + body = {'volumeAttachment': {'volumeId': dest_volid}} + if delete_on_termination is not None: + body['volumeAttachment']['delete_on_termination'] = ( + delete_on_termination) + return self._update("/servers/%s/os-volume_attachments/%s" % + (server_id, src_volid), + body, "volumeAttachment") + def get_server_volume(self, server_id, volume_id=None, attachment_id=None): """ Get the volume identified by the volume ID, that is attached to |