summaryrefslogtreecommitdiff
path: root/novaclient
diff options
context:
space:
mode:
authorzhangbailin <zhangbailin@inspur.com>2020-03-12 18:44:36 +0800
committerzhangbailin <zhangbailin@inspur.com>2020-04-09 08:52:05 +0800
commit4d6c70d25df99a4f28f263cd3160c74ccf1343e3 (patch)
tree64ab8962905dee361f94d2b417081d1e77e5971b /novaclient
parentea092b29880e71f0ba2d8e1eb93a9cf73edee2a2 (diff)
downloadpython-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__.py2
-rw-r--r--novaclient/tests/unit/v2/test_shell.py36
-rw-r--r--novaclient/tests/unit/v2/test_volumes.py23
-rw-r--r--novaclient/v2/shell.py34
-rw-r--r--novaclient/v2/volumes.py30
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