From 4d6c70d25df99a4f28f263cd3160c74ccf1343e3 Mon Sep 17 00:00:00 2001 From: zhangbailin Date: Thu, 12 Mar 2020 18:44:36 +0800 Subject: [PATCH] Microversion 2.85: Change volume-update CLI This commit add a new CLI ``nova volume-update [--[no-]delete-on-termination] `` 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 --- doc/source/cli/nova.rst | 29 ++++++++++----- novaclient/__init__.py | 2 +- novaclient/tests/unit/v2/test_shell.py | 36 +++++++++++++++++-- novaclient/tests/unit/v2/test_volumes.py | 23 ++++++++++++ novaclient/v2/shell.py | 34 ++++++++++++++---- novaclient/v2/volumes.py | 30 ++++++++++++++++ .../microversion-v2_85-230931f88c4f1d52.yaml | 16 +++++++++ 7 files changed, 153 insertions(+), 17 deletions(-) create mode 100644 releasenotes/notes/microversion-v2_85-230931f88c4f1d52.yaml diff --git a/doc/source/cli/nova.rst b/doc/source/cli/nova.rst index 26c2ce2a8..6db1cd0cb 100644 --- a/doc/source/cli/nova.rst +++ b/doc/source/cli/nova.rst @@ -558,10 +558,12 @@ nova usage Detach a volume from a server. ``volume-update`` - 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. + 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. + Since microversion 2.85, support for updating the + ``delete_on_termination`` delete flag, which allows changing the + behavior of volume deletion on instance deletion. ``x509-create-cert`` **DEPRECATED** Create x509 cert for a user in @@ -3896,7 +3898,7 @@ Attach a volume to a server. Tag for the attached volume. (Supported by API versions '2.49' - '2.latest') ``--delete-on-termination`` - Specify if the attached volume sholud be deleted when the server is + Specify if the attached volume should be deleted when the server is destroyed. By default the attached volume is not deleted when the server is destroyed. (Supported by API versions '2.79' - '2.latest') @@ -3942,7 +3944,8 @@ nova volume-update .. code-block:: console - usage: nova volume-update + usage: nova volume-update [--[no-]delete-on-termination] + 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 @@ -3953,12 +3956,22 @@ new volume. ```` Name or ID of server. -```` +```` ID of the source (original) volume. -```` +```` ID of the destination volume. +**Optional arguments:** + +``--delete-on-termination`` + Specify that the volume should be deleted when the server is destroyed. + (Supported by API versions '2.85' - '2.latest') + +``--no-delete-on-termination`` + Specify that the attached volume should not be deleted when + the server is destroyed. (Supported by API versions '2.85' - '2.latest') + .. _nova_bash-completion: nova bash-completion diff --git a/novaclient/__init__.py b/novaclient/__init__.py index adcb85dd5..5e3e77016 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 18badc0b7..d21252bd3 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 d18f84664..93ea1c961 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 ac6c5ea1f..e0498df81 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='', + metavar='', help=_('ID of the source (original) volume.')) @utils.arg( 'dest_volume', - metavar='', + metavar='', 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 8fc755658..7153c835d 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 diff --git a/releasenotes/notes/microversion-v2_85-230931f88c4f1d52.yaml b/releasenotes/notes/microversion-v2_85-230931f88c4f1d52.yaml new file mode 100644 index 000000000..859534c4b --- /dev/null +++ b/releasenotes/notes/microversion-v2_85-230931f88c4f1d52.yaml @@ -0,0 +1,16 @@ +--- +features: + - | + Support is added for compute API `microversion 2.85`_. This adds the + ability to update an attached volume with a ``delete_on_termination``, + which specify if the attached volume should be deleted when the server + is destroyed. + + - The ``--delete-on-termination`` and ``--no-delete-on-termination`` + options are added to the ``nova volume-update`` CLI. + - New kwarg called ``delete_on_termination`` added to the python API + binding: + + - ``novaclient.v2.volumes.VolumeManager.update_server_volume()`` + + .. _microversion 2.85: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id78