Add support for microversion 2.67: BDMv2 volume_type
This adds the nova boot command and python API binding support for creating a server with block device mappings defined using a specific volume type. Depends-On: https://review.openstack.org/606398/ Depends-On: https://review.openstack.org/#/c/610349/ Part of blueprint boot-instance-specific-storage-backend Change-Id: I484ee065119b5783db212ea64efa60e87c40338c
This commit is contained in:
parent
0fdb154d9c
commit
4fef02de71
doc/source/cli
novaclient
releasenotes/notes
@ -1036,9 +1036,13 @@ Boot a new server.
|
|||||||
to 0, for others need to be specified),
|
to 0, for others need to be specified),
|
||||||
shutdown=shutdown behaviour (either preserve
|
shutdown=shutdown behaviour (either preserve
|
||||||
or remove, for local destination set to
|
or remove, for local destination set to
|
||||||
remove) and tag=device metadata tag
|
remove), tag=device metadata tag
|
||||||
(optional). (Supported by API versions '2.42'
|
(optional; supported by API versions '2.42'
|
||||||
- '2.latest')
|
- '2.latest'), and volume_type=type of volume
|
||||||
|
to create (either ID or name) when source is
|
||||||
|
`blank`, `image` or `snapshot` and dest is `volume`
|
||||||
|
(optional; supported by API versions '2.67'
|
||||||
|
- '2.latest').
|
||||||
|
|
||||||
``--swap <swap_size>``
|
``--swap <swap_size>``
|
||||||
Create and attach a local swap block device of
|
Create and attach a local swap block device of
|
||||||
|
@ -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.66")
|
API_MAX_VERSION = api_versions.APIVersion("2.67")
|
||||||
|
@ -104,7 +104,7 @@ class FixturedTestCase(testscenarios.TestWithScenarios, TestCase):
|
|||||||
if not isinstance(body, six.string_types):
|
if not isinstance(body, six.string_types):
|
||||||
# json load if the input body to match against is not a string
|
# json load if the input body to match against is not a string
|
||||||
req_data = jsonutils.loads(req_data)
|
req_data = jsonutils.loads(req_data)
|
||||||
self.assertEqual(req_data, body)
|
self.assertEqual(body, req_data)
|
||||||
|
|
||||||
|
|
||||||
class TestResponse(requests.Response):
|
class TestResponse(requests.Response):
|
||||||
|
@ -1609,3 +1609,48 @@ class ServersV263Test(ServersV257Test):
|
|||||||
'1234', fakes.FAKE_IMAGE_UUID_1,
|
'1234', fakes.FAKE_IMAGE_UUID_1,
|
||||||
trusted_image_certificates=['id1', 'id2'])
|
trusted_image_certificates=['id1', 'id2'])
|
||||||
self.assertIn('trusted_image_certificates', six.text_type(ex))
|
self.assertIn('trusted_image_certificates', six.text_type(ex))
|
||||||
|
|
||||||
|
|
||||||
|
class ServersV267Test(ServersV263Test):
|
||||||
|
"""Tests for creating a server with a block_device_mapping_v2 entry
|
||||||
|
using volume_type for microversion 2.67.
|
||||||
|
"""
|
||||||
|
api_version = '2.67'
|
||||||
|
|
||||||
|
def test_create_server_boot_from_volume_with_volume_type(self):
|
||||||
|
bdm = [{"volume_size": 1,
|
||||||
|
"uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"delete_on_termination": True,
|
||||||
|
"source_type": "snapshot",
|
||||||
|
"destination_type": "volume",
|
||||||
|
"boot_index": 0,
|
||||||
|
"volume_type": "rbd"}]
|
||||||
|
s = self.cs.servers.create(
|
||||||
|
name="bfv server", image='', flavor=1,
|
||||||
|
nics='auto', block_device_mapping_v2=bdm)
|
||||||
|
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
|
self.assert_called('POST', '/servers', {
|
||||||
|
'server': {
|
||||||
|
'flavorRef': '1',
|
||||||
|
'imageRef': '',
|
||||||
|
'name': 'bfv server',
|
||||||
|
'networks': 'auto',
|
||||||
|
'block_device_mapping_v2': bdm,
|
||||||
|
'min_count': 1,
|
||||||
|
'max_count': 1,
|
||||||
|
}})
|
||||||
|
|
||||||
|
def test_create_server_boot_from_volume_with_volume_type_pre_267(self):
|
||||||
|
self.cs.api_version = api_versions.APIVersion('2.66')
|
||||||
|
bdm = [{"volume_size": 1,
|
||||||
|
"uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"delete_on_termination": True,
|
||||||
|
"source_type": "snapshot",
|
||||||
|
"destination_type": "volume",
|
||||||
|
"boot_index": 0,
|
||||||
|
"volume_type": "rbd"}]
|
||||||
|
ex = self.assertRaises(ValueError, self.cs.servers.create,
|
||||||
|
name="bfv server", image='', flavor=1,
|
||||||
|
nics='none', block_device_mapping_v2=bdm)
|
||||||
|
self.assertIn("Block device volume_type is not supported before "
|
||||||
|
"microversion 2.67", six.text_type(ex))
|
||||||
|
@ -624,6 +624,78 @@ class ShellTest(utils.TestCase):
|
|||||||
'size=1,format=ext4,type=disk,shutdown=foobar '
|
'size=1,format=ext4,type=disk,shutdown=foobar '
|
||||||
'some-server' % FAKE_UUID_1))
|
'some-server' % FAKE_UUID_1))
|
||||||
|
|
||||||
|
def test_boot_from_volume_with_volume_type_old_microversion(self):
|
||||||
|
ex = self.assertRaises(
|
||||||
|
exceptions.CommandError, self.run_command,
|
||||||
|
'boot --flavor 1 --block-device id=%s,source=image,dest=volume,'
|
||||||
|
'size=1,bootindex=0,shutdown=remove,tag=foo,volume_type=lvm '
|
||||||
|
'bfv-server' % FAKE_UUID_1, api_version='2.66')
|
||||||
|
self.assertIn("'volume_type' in block device mapping is not supported "
|
||||||
|
"in API version", six.text_type(ex))
|
||||||
|
|
||||||
|
def test_boot_from_volume_with_volume_type(self):
|
||||||
|
"""Tests creating a volume-backed server from a source image and
|
||||||
|
specifying the type of volume to create with microversion 2.67.
|
||||||
|
"""
|
||||||
|
self.run_command(
|
||||||
|
'boot --flavor 1 --block-device id=%s,source=image,dest=volume,'
|
||||||
|
'size=1,bootindex=0,shutdown=remove,tag=foo,volume_type=lvm '
|
||||||
|
'bfv-server' % FAKE_UUID_1, api_version='2.67')
|
||||||
|
self.assert_called_anytime(
|
||||||
|
'POST', '/servers',
|
||||||
|
{'server': {
|
||||||
|
'flavorRef': '1',
|
||||||
|
'name': 'bfv-server',
|
||||||
|
'block_device_mapping_v2': [
|
||||||
|
{
|
||||||
|
'uuid': FAKE_UUID_1,
|
||||||
|
'source_type': 'image',
|
||||||
|
'destination_type': 'volume',
|
||||||
|
'volume_size': '1',
|
||||||
|
'delete_on_termination': True,
|
||||||
|
'tag': 'foo',
|
||||||
|
'boot_index': '0',
|
||||||
|
'volume_type': 'lvm'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'networks': 'auto',
|
||||||
|
'imageRef': '',
|
||||||
|
'min_count': 1,
|
||||||
|
'max_count': 1,
|
||||||
|
}})
|
||||||
|
|
||||||
|
def test_boot_from_volume_without_volume_type_2_67(self):
|
||||||
|
"""Tests creating a volume-backed server from a source image but
|
||||||
|
without specifying the type of volume to create with microversion 2.67.
|
||||||
|
The volume_type parameter should be omitted in the request to the
|
||||||
|
API server.
|
||||||
|
"""
|
||||||
|
self.run_command(
|
||||||
|
'boot --flavor 1 --block-device id=%s,source=image,dest=volume,'
|
||||||
|
'size=1,bootindex=0,shutdown=remove,tag=foo bfv-server' %
|
||||||
|
FAKE_UUID_1, api_version='2.67')
|
||||||
|
self.assert_called_anytime(
|
||||||
|
'POST', '/servers',
|
||||||
|
{'server': {
|
||||||
|
'flavorRef': '1',
|
||||||
|
'name': 'bfv-server',
|
||||||
|
'block_device_mapping_v2': [
|
||||||
|
{
|
||||||
|
'uuid': FAKE_UUID_1,
|
||||||
|
'source_type': 'image',
|
||||||
|
'destination_type': 'volume',
|
||||||
|
'volume_size': '1',
|
||||||
|
'delete_on_termination': True,
|
||||||
|
'tag': 'foo',
|
||||||
|
'boot_index': '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'networks': 'auto',
|
||||||
|
'imageRef': '',
|
||||||
|
'min_count': 1,
|
||||||
|
'max_count': 1,
|
||||||
|
}})
|
||||||
|
|
||||||
def test_boot_metadata(self):
|
def test_boot_metadata(self):
|
||||||
self.run_command('boot --image %s --flavor 1 --meta foo=bar=pants'
|
self.run_command('boot --image %s --flavor 1 --meta foo=bar=pants'
|
||||||
' --meta spam=eggs some-server ' % FAKE_UUID_1)
|
' --meta spam=eggs some-server ' % FAKE_UUID_1)
|
||||||
@ -3913,6 +3985,7 @@ class ShellTest(utils.TestCase):
|
|||||||
62, # There are no version-wrapped shell method changes for this.
|
62, # There are no version-wrapped shell method changes for this.
|
||||||
63, # There are no version-wrapped shell method changes for this.
|
63, # There are no version-wrapped shell method changes for this.
|
||||||
65, # There are no version-wrapped shell method changes for this.
|
65, # There are no version-wrapped shell method changes for this.
|
||||||
|
67, # There are no version-wrapped shell method changes for this.
|
||||||
])
|
])
|
||||||
versions_supported = set(range(0,
|
versions_supported = set(range(0,
|
||||||
novaclient.API_MAX_VERSION.ver_minor + 1))
|
novaclient.API_MAX_VERSION.ver_minor + 1))
|
||||||
|
@ -1305,6 +1305,14 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
raise exceptions.UnsupportedAttribute("trusted_image_certificates",
|
raise exceptions.UnsupportedAttribute("trusted_image_certificates",
|
||||||
"2.63")
|
"2.63")
|
||||||
|
|
||||||
|
if (block_device_mapping_v2 and
|
||||||
|
self.api_version < api_versions.APIVersion('2.67')):
|
||||||
|
for bdm in block_device_mapping_v2:
|
||||||
|
if bdm.get('volume_type'):
|
||||||
|
raise ValueError(
|
||||||
|
"Block device volume_type is not supported before "
|
||||||
|
"microversion 2.67")
|
||||||
|
|
||||||
boot_kwargs = dict(
|
boot_kwargs = dict(
|
||||||
meta=meta, files=files, userdata=userdata,
|
meta=meta, files=files, userdata=userdata,
|
||||||
reservation_id=reservation_id, min_count=min_count,
|
reservation_id=reservation_id, min_count=min_count,
|
||||||
|
@ -71,6 +71,7 @@ CLIENT_BDM2_KEYS = {
|
|||||||
'type': 'device_type',
|
'type': 'device_type',
|
||||||
'shutdown': 'delete_on_termination',
|
'shutdown': 'delete_on_termination',
|
||||||
'tag': 'tag',
|
'tag': 'tag',
|
||||||
|
'volume_type': 'volume_type' # added in 2.67
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -145,6 +146,8 @@ def _parse_block_device_mapping_v2(cs, args, image):
|
|||||||
'delete_on_termination': False}
|
'delete_on_termination': False}
|
||||||
bdm.append(bdm_dict)
|
bdm.append(bdm_dict)
|
||||||
|
|
||||||
|
supports_volume_type = cs.api_version == api_versions.APIVersion('2.67')
|
||||||
|
|
||||||
for device_spec in args.block_device:
|
for device_spec in args.block_device:
|
||||||
spec_dict = _parse_device_spec(device_spec)
|
spec_dict = _parse_device_spec(device_spec)
|
||||||
bdm_dict = {}
|
bdm_dict = {}
|
||||||
@ -155,6 +158,12 @@ def _parse_block_device_mapping_v2(cs, args, image):
|
|||||||
"in API version %(version)s.")
|
"in API version %(version)s.")
|
||||||
% {'version': cs.api_version.get_string()})
|
% {'version': cs.api_version.get_string()})
|
||||||
|
|
||||||
|
if 'volume_type' in spec_dict and not supports_volume_type:
|
||||||
|
raise exceptions.CommandError(
|
||||||
|
_("'volume_type' in block device mapping is not supported "
|
||||||
|
"in API version %(version)s.")
|
||||||
|
% {'version': cs.api_version.get_string()})
|
||||||
|
|
||||||
for key, value in spec_dict.items():
|
for key, value in spec_dict.items():
|
||||||
bdm_dict[CLIENT_BDM2_KEYS[key]] = value
|
bdm_dict[CLIENT_BDM2_KEYS[key]] = value
|
||||||
|
|
||||||
@ -709,6 +718,7 @@ def _boot(cs, args):
|
|||||||
action='append',
|
action='append',
|
||||||
default=[],
|
default=[],
|
||||||
start_version='2.42',
|
start_version='2.42',
|
||||||
|
end_version='2.66',
|
||||||
help=_("Block device mapping with the keys: "
|
help=_("Block device mapping with the keys: "
|
||||||
"id=UUID (image_id, snapshot_id or volume_id only if using source "
|
"id=UUID (image_id, snapshot_id or volume_id only if using source "
|
||||||
"image, snapshot or volume) "
|
"image, snapshot or volume) "
|
||||||
@ -732,6 +742,38 @@ def _boot(cs, args):
|
|||||||
"shutdown=shutdown behaviour (either preserve or remove, "
|
"shutdown=shutdown behaviour (either preserve or remove, "
|
||||||
"for local destination set to remove) and "
|
"for local destination set to remove) and "
|
||||||
"tag=device metadata tag (optional)."))
|
"tag=device metadata tag (optional)."))
|
||||||
|
@utils.arg(
|
||||||
|
'--block-device',
|
||||||
|
metavar="key1=value1[,key2=value2...]",
|
||||||
|
action='append',
|
||||||
|
default=[],
|
||||||
|
start_version='2.67',
|
||||||
|
help=_("Block device mapping with the keys: "
|
||||||
|
"id=UUID (image_id, snapshot_id or volume_id only if using source "
|
||||||
|
"image, snapshot or volume) "
|
||||||
|
"source=source type (image, snapshot, volume or blank), "
|
||||||
|
"dest=destination type of the block device (volume or local), "
|
||||||
|
"bus=device's bus (e.g. uml, lxc, virtio, ...; if omitted, "
|
||||||
|
"hypervisor driver chooses a suitable default, "
|
||||||
|
"honoured only if device type is supplied) "
|
||||||
|
"type=device type (e.g. disk, cdrom, ...; defaults to 'disk') "
|
||||||
|
"device=name of the device (e.g. vda, xda, ...; "
|
||||||
|
"if omitted, hypervisor driver chooses suitable device "
|
||||||
|
"depending on selected bus; note the libvirt driver always "
|
||||||
|
"uses default device names), "
|
||||||
|
"size=size of the block device in MB(for swap) and in "
|
||||||
|
"GiB(for other formats) "
|
||||||
|
"(if omitted, hypervisor driver calculates size), "
|
||||||
|
"format=device will be formatted (e.g. swap, ntfs, ...; optional), "
|
||||||
|
"bootindex=integer used for ordering the boot disks "
|
||||||
|
"(for image backed instances it is equal to 0, "
|
||||||
|
"for others need to be specified), "
|
||||||
|
"shutdown=shutdown behaviour (either preserve or remove, "
|
||||||
|
"for local destination set to remove) and "
|
||||||
|
"tag=device metadata tag (optional), "
|
||||||
|
"volume_type=type of volume to create (either ID or name) when "
|
||||||
|
"source is blank, image or snapshot and dest is volume (optional)."
|
||||||
|
))
|
||||||
@utils.arg(
|
@utils.arg(
|
||||||
'--swap',
|
'--swap',
|
||||||
metavar="<swap_size>",
|
metavar="<swap_size>",
|
||||||
|
10
releasenotes/notes/microversion-v2_67-da6d9b12730b8562.yaml
Normal file
10
releasenotes/notes/microversion-v2_67-da6d9b12730b8562.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Support is added for the `2.67 microversion`_ which allows specifying
|
||||||
|
a ``volume_type`` with the ``--block-device`` option on the ``nova boot``
|
||||||
|
command. The ``novaclient.v2.servers.ServerManager.create()`` method now
|
||||||
|
also supports a ``volume_type`` entry in the ``block_device_mapping_v2``
|
||||||
|
parameter.
|
||||||
|
|
||||||
|
.. _2.67 microversion: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id60
|
Loading…
x
Reference in New Issue
Block a user