Microversion 2.79: Add delete_on_termination to volume-attach API
Support add 'delete_on_termination' field to the voume attach API to support configuring whether to delete the data volume when the server is destroyed. * Updating the ``nova volume-attachments`` command to show the ``delete_on_termination`` value if 2.79 or greater is used. * The '--delete-on-termination' option is added to the `nova volume-attach` CLI. Depends-On: https://review.opendev.org/#/c/673133/ Part of blueprint support-delete-on-termination-in-server-attach-volume Change-Id: I8dcf2fd21a2fd99ca4e05bd953fbbe026be3a619
This commit is contained in:
parent
aae95dcc7a
commit
cd396b8b61
@ -3849,7 +3849,8 @@ nova volume-attach
|
|||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
usage: nova volume-attach [--tag <tag>] <server> <volume> [<device>]
|
usage: nova volume-attach [--delete-on-termination] [--tag <tag>]
|
||||||
|
<server> <volume> [<device>]
|
||||||
|
|
||||||
Attach a volume to a server.
|
Attach a volume to a server.
|
||||||
|
|
||||||
@ -3870,6 +3871,11 @@ Attach a volume to a server.
|
|||||||
``--tag <tag>``
|
``--tag <tag>``
|
||||||
Tag for the attached volume. (Supported by API versions '2.49' - '2.latest')
|
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
|
||||||
|
destroyed. By default the attached volume is not deleted when the server is
|
||||||
|
destroyed. (Supported by API versions '2.79' - '2.latest')
|
||||||
|
|
||||||
.. _nova_volume-attachments:
|
.. _nova_volume-attachments:
|
||||||
|
|
||||||
nova volume-attachments
|
nova volume-attachments
|
||||||
|
@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1")
|
|||||||
# when client supported the max version, and bumped sequentially, otherwise
|
# when client supported the max version, and bumped sequentially, otherwise
|
||||||
# the client may break due to server side new version may include some
|
# the client may break due to server side new version may include some
|
||||||
# backward incompatible change.
|
# backward incompatible change.
|
||||||
API_MAX_VERSION = api_versions.APIVersion("2.78")
|
API_MAX_VERSION = api_versions.APIVersion("2.79")
|
||||||
|
@ -2115,6 +2115,11 @@ class FakeSessionClient(base_client.SessionClient):
|
|||||||
if self.api_version >= api_versions.APIVersion('2.70'):
|
if self.api_version >= api_versions.APIVersion('2.70'):
|
||||||
# Include the "tag" field in the response.
|
# Include the "tag" field in the response.
|
||||||
attachment['tag'] = 'test-tag'
|
attachment['tag'] = 'test-tag'
|
||||||
|
|
||||||
|
if self.api_version >= api_versions.APIVersion('2.79'):
|
||||||
|
# Include the "delete_on_termination" field in the
|
||||||
|
# response.
|
||||||
|
attachment['delete_on_termination'] = True
|
||||||
return (200, FAKE_RESPONSE_HEADERS, {"volumeAttachment": attachment})
|
return (200, FAKE_RESPONSE_HEADERS, {"volumeAttachment": attachment})
|
||||||
|
|
||||||
def put_servers_1234_os_volume_attachments_Work(self, **kw):
|
def put_servers_1234_os_volume_attachments_Work(self, **kw):
|
||||||
@ -2139,6 +2144,12 @@ class FakeSessionClient(base_client.SessionClient):
|
|||||||
# Include the "tag" field in each attachment.
|
# Include the "tag" field in each attachment.
|
||||||
for attachment in attachments['volumeAttachments']:
|
for attachment in attachments['volumeAttachments']:
|
||||||
attachment['tag'] = 'test-tag'
|
attachment['tag'] = 'test-tag'
|
||||||
|
|
||||||
|
if self.api_version >= api_versions.APIVersion('2.79'):
|
||||||
|
# Include the "delete_on_termination" field in each
|
||||||
|
# attachment.
|
||||||
|
for attachment in attachments['volumeAttachments']:
|
||||||
|
attachment['delete_on_termination'] = True
|
||||||
return (200, FAKE_RESPONSE_HEADERS, attachments)
|
return (200, FAKE_RESPONSE_HEADERS, attachments)
|
||||||
|
|
||||||
def get_servers_1234_os_volume_attachments_Work(self, **kw):
|
def get_servers_1234_os_volume_attachments_Work(self, **kw):
|
||||||
|
@ -3825,6 +3825,42 @@ class ShellTest(utils.TestCase):
|
|||||||
'tag': 'test-tag'}})
|
'tag': 'test-tag'}})
|
||||||
self.assertIn('test-tag', out)
|
self.assertIn('test-tag', out)
|
||||||
|
|
||||||
|
def test_volume_attachments_pre_v2_79(self):
|
||||||
|
out = self.run_command(
|
||||||
|
'volume-attachments 1234', api_version='2.78')[0]
|
||||||
|
self.assert_called('GET', '/servers/1234/os-volume_attachments')
|
||||||
|
self.assertNotIn('DELETE ON TERMINATION', out)
|
||||||
|
|
||||||
|
def test_volume_attachments_v2_79(self):
|
||||||
|
out = self.run_command(
|
||||||
|
'volume-attachments 1234', api_version='2.79')[0]
|
||||||
|
self.assert_called('GET', '/servers/1234/os-volume_attachments')
|
||||||
|
self.assertIn('DELETE ON TERMINATION', out)
|
||||||
|
|
||||||
|
def test_volume_attach_with_delete_on_termination_pre_v2_79(self):
|
||||||
|
self.assertRaises(
|
||||||
|
SystemExit, self.run_command,
|
||||||
|
'volume-attach --delete-on-termination sample-server '
|
||||||
|
'Work /dev/vdb', api_version='2.78')
|
||||||
|
|
||||||
|
def test_volume_attach_with_delete_on_termination_v2_79(self):
|
||||||
|
out = self.run_command(
|
||||||
|
'volume-attach --delete-on-termination sample-server '
|
||||||
|
'2 /dev/vdb', api_version='2.79')[0]
|
||||||
|
self.assert_called('POST', '/servers/1234/os-volume_attachments',
|
||||||
|
{'volumeAttachment':
|
||||||
|
{'device': '/dev/vdb',
|
||||||
|
'volumeId': '2',
|
||||||
|
'delete_on_termination': True}})
|
||||||
|
self.assertIn('delete_on_termination', out)
|
||||||
|
|
||||||
|
def test_volume_attach_without_delete_on_termination(self):
|
||||||
|
self.run_command('volume-attach sample-server Work',
|
||||||
|
api_version='2.79')
|
||||||
|
self.assert_called('POST', '/servers/1234/os-volume_attachments',
|
||||||
|
{'volumeAttachment':
|
||||||
|
{'volumeId': 'Work'}})
|
||||||
|
|
||||||
def test_volume_update(self):
|
def test_volume_update(self):
|
||||||
self.run_command('volume-update sample-server Work Work')
|
self.run_command('volume-update sample-server Work Work')
|
||||||
self.assert_called('PUT', '/servers/1234/os-volume_attachments/Work',
|
self.assert_called('PUT', '/servers/1234/os-volume_attachments/Work',
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
import six
|
||||||
|
|
||||||
from novaclient import api_versions
|
from novaclient import api_versions
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
@ -126,3 +127,33 @@ class VolumesV249Test(VolumesTest):
|
|||||||
volume_id=None,
|
volume_id=None,
|
||||||
attachment_id="Work")
|
attachment_id="Work")
|
||||||
mock_warn.assert_called_once()
|
mock_warn.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
class VolumesV279Test(VolumesV249Test):
|
||||||
|
api_version = "2.79"
|
||||||
|
|
||||||
|
def test_create_server_volume_with_delete_on_termination(self):
|
||||||
|
v = self.cs.volumes.create_server_volume(
|
||||||
|
server_id=1234,
|
||||||
|
volume_id='15e59938-07d5-11e1-90e3-e3dffe0c5983',
|
||||||
|
device='/dev/vdb',
|
||||||
|
tag='tag1',
|
||||||
|
delete_on_termination=True
|
||||||
|
)
|
||||||
|
self.assert_request_id(v, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
|
self.cs.assert_called(
|
||||||
|
'POST', '/servers/1234/os-volume_attachments',
|
||||||
|
{'volumeAttachment': {
|
||||||
|
'volumeId': '15e59938-07d5-11e1-90e3-e3dffe0c5983',
|
||||||
|
'device': '/dev/vdb',
|
||||||
|
'tag': 'tag1',
|
||||||
|
'delete_on_termination': True}})
|
||||||
|
self.assertIsInstance(v, volumes.Volume)
|
||||||
|
|
||||||
|
def test_create_server_volume_with_delete_on_termination_pre_v279(self):
|
||||||
|
self.cs.api_version = api_versions.APIVersion('2.78')
|
||||||
|
ex = self.assertRaises(
|
||||||
|
TypeError, self.cs.volumes.create_server_volume, "1234",
|
||||||
|
volume_id='15e59938-07d5-11e1-90e3-e3dffe0c5983',
|
||||||
|
delete_on_termination=True)
|
||||||
|
self.assertIn('delete_on_termination', six.text_type(ex))
|
||||||
|
@ -2633,6 +2633,13 @@ def _translate_volume_attachments_keys(collection):
|
|||||||
default=None,
|
default=None,
|
||||||
help=_('Tag for the attached volume.'),
|
help=_('Tag for the attached volume.'),
|
||||||
start_version="2.49")
|
start_version="2.49")
|
||||||
|
@utils.arg(
|
||||||
|
'--delete-on-termination',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_('Specify if the attached volume should be deleted '
|
||||||
|
'when the server is destroyed.'),
|
||||||
|
start_version="2.79")
|
||||||
def do_volume_attach(cs, args):
|
def do_volume_attach(cs, args):
|
||||||
"""Attach a volume to a server."""
|
"""Attach a volume to a server."""
|
||||||
if args.device == 'auto':
|
if args.device == 'auto':
|
||||||
@ -2642,6 +2649,9 @@ def do_volume_attach(cs, args):
|
|||||||
if 'tag' in args and args.tag:
|
if 'tag' in args and args.tag:
|
||||||
update_kwargs['tag'] = args.tag
|
update_kwargs['tag'] = args.tag
|
||||||
|
|
||||||
|
if 'delete_on_termination' in args and args.delete_on_termination:
|
||||||
|
update_kwargs['delete_on_termination'] = args.delete_on_termination
|
||||||
|
|
||||||
volume = cs.volumes.create_server_volume(_find_server(cs, args.server).id,
|
volume = cs.volumes.create_server_volume(_find_server(cs, args.server).id,
|
||||||
args.volume,
|
args.volume,
|
||||||
args.device,
|
args.device,
|
||||||
@ -2699,6 +2709,9 @@ def do_volume_attachments(cs, args):
|
|||||||
fields = ['ID', 'DEVICE', 'SERVER ID', 'VOLUME ID']
|
fields = ['ID', 'DEVICE', 'SERVER ID', 'VOLUME ID']
|
||||||
if cs.api_version >= api_versions.APIVersion('2.70'):
|
if cs.api_version >= api_versions.APIVersion('2.70'):
|
||||||
fields.append('TAG')
|
fields.append('TAG')
|
||||||
|
# Microversion >= 2.79 returns the delete_on_termination value.
|
||||||
|
if cs.api_version >= api_versions.APIVersion('2.79'):
|
||||||
|
fields.append('DELETE ON TERMINATION')
|
||||||
utils.print_list(volumes, fields)
|
utils.print_list(volumes, fields)
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ class VolumeManager(base.Manager):
|
|||||||
return self._create("/servers/%s/os-volume_attachments" % server_id,
|
return self._create("/servers/%s/os-volume_attachments" % server_id,
|
||||||
body, "volumeAttachment")
|
body, "volumeAttachment")
|
||||||
|
|
||||||
@api_versions.wraps("2.49")
|
@api_versions.wraps("2.49", "2.78")
|
||||||
def create_server_volume(self, server_id, volume_id, device=None,
|
def create_server_volume(self, server_id, volume_id, device=None,
|
||||||
tag=None):
|
tag=None):
|
||||||
"""
|
"""
|
||||||
@ -75,6 +75,34 @@ class VolumeManager(base.Manager):
|
|||||||
return self._create("/servers/%s/os-volume_attachments" % server_id,
|
return self._create("/servers/%s/os-volume_attachments" % server_id,
|
||||||
body, "volumeAttachment")
|
body, "volumeAttachment")
|
||||||
|
|
||||||
|
@api_versions.wraps("2.79")
|
||||||
|
def create_server_volume(self, server_id, volume_id, device=None,
|
||||||
|
tag=None, delete_on_termination=False):
|
||||||
|
"""
|
||||||
|
Attach a volume identified by the volume ID to the given server ID
|
||||||
|
|
||||||
|
:param server_id: The ID of the server.
|
||||||
|
:param volume_id: The ID of the volume to attach.
|
||||||
|
:param device: The device name (optional).
|
||||||
|
:param tag: The tag (optional).
|
||||||
|
:param delete_on_termination: Marked whether to delete the attached
|
||||||
|
volume when the server is deleted
|
||||||
|
(optional).
|
||||||
|
:rtype: :class:`Volume`
|
||||||
|
"""
|
||||||
|
# TODO(mriedem): Move this body construction into a private common
|
||||||
|
# helper method for all versions of create_server_volume to use.
|
||||||
|
body = {'volumeAttachment': {'volumeId': volume_id}}
|
||||||
|
if device is not None:
|
||||||
|
body['volumeAttachment']['device'] = device
|
||||||
|
if tag is not None:
|
||||||
|
body['volumeAttachment']['tag'] = tag
|
||||||
|
if delete_on_termination:
|
||||||
|
body['volumeAttachment']['delete_on_termination'] = (
|
||||||
|
delete_on_termination)
|
||||||
|
return self._create("/servers/%s/os-volume_attachments" % server_id,
|
||||||
|
body, "volumeAttachment")
|
||||||
|
|
||||||
def update_server_volume(self, server_id, src_volid, dest_volid):
|
def update_server_volume(self, server_id, src_volid, dest_volid):
|
||||||
"""
|
"""
|
||||||
Swaps the existing volume attachment to point to a new volume.
|
Swaps the existing volume attachment to point to a new volume.
|
||||||
|
16
releasenotes/notes/microversion-v2_79-f13bc0414743dc16.yaml
Normal file
16
releasenotes/notes/microversion-v2_79-f13bc0414743dc16.yaml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Added support for `microversion 2.79`_ which includes the following
|
||||||
|
changes:
|
||||||
|
|
||||||
|
- The ``--delete-on-termination`` option is added to the
|
||||||
|
``nova volume-attach`` CLI.
|
||||||
|
- A ``DELETE ON TERMINATION`` column is added to the
|
||||||
|
``nova volume-attachments`` table.
|
||||||
|
- New kwarg called ``delete_on_termination`` added to the python API
|
||||||
|
binding:
|
||||||
|
|
||||||
|
- ``novaclient.v2.volumes.VolumeManager.create_server_volume()``
|
||||||
|
|
||||||
|
.. _microversion 2.79: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id71
|
Loading…
Reference in New Issue
Block a user