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
This commit is contained in:
zhangbailin 2020-03-12 18:44:36 +08:00
parent ea092b2988
commit 4d6c70d25d
7 changed files with 153 additions and 17 deletions

View File

@ -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 <server> <src_volid> <dest_volid>
usage: nova volume-update [--[no-]delete-on-termination]
<server> <src_volume> <dest_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
@ -3953,12 +3956,22 @@ new volume.
``<server>``
Name or ID of server.
``<src_volid>``
``<src_volume>``
ID of the source (original) volume.
``<dest_volid>``
``<dest_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

View File

@ -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")

View File

@ -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',

View File

@ -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))

View File

@ -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(

View File

@ -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

View File

@ -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