Add microversion 2.67 to support volume_type
Add a new microversion 2.67 to support specify ``volume_type`` when boot instances. Part of bp boot-instance-specific-storage-backend Change-Id: I13102243f7ce36a5d44c1790f3a633703373ebf7
This commit is contained in:
parent
686dbc3640
commit
c7f4190af2
@ -2269,6 +2269,23 @@ device_type:
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
device_volume_type:
|
||||
description: |
|
||||
The device ``volume_type``. This can be used to specify the type of volume
|
||||
which the compute service will create and attach to the server.
|
||||
If not specified, the block storage service will provide a default volume
|
||||
type. See the `block storage volume types API <https://developer.openstack.org/api-ref/block-storage/v3/#volume-types-types>`_
|
||||
for more details.
|
||||
There are some restrictions on ``volume_type``:
|
||||
|
||||
- It can be a volume type ID or name.
|
||||
- It is only supported with ``source_type`` of ``blank``, ``image`` or
|
||||
``snapshot``.
|
||||
- It is only supported with ``destination_type`` of ``volume``.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
min_version: 2.67
|
||||
# Optional input parameter in the body for PUT /os-services/{service_id} added
|
||||
# in microversion 2.53.
|
||||
disabled_reason_2_53_in:
|
||||
|
@ -363,6 +363,7 @@ Request
|
||||
- block_device_mapping_v2.uuid: block_device_uuid
|
||||
- block_device_mapping_v2.volume_size: volume_size
|
||||
- block_device_mapping_v2.tag: device_tag_bdm
|
||||
- block_device_mapping_v2.volume_type: device_volume_type
|
||||
- config_drive: config_drive
|
||||
- imageRef: imageRef
|
||||
- key_name: key_name
|
||||
|
19
doc/api_samples/servers/v2.67/server-create-req.json
Normal file
19
doc/api_samples/servers/v2.67/server-create-req.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"server" : {
|
||||
"name" : "bfv-server-with-volume-type",
|
||||
"flavorRef" : "http://openstack.example.com/flavors/1",
|
||||
"networks" : [{
|
||||
"uuid" : "ff608d40-75e9-48cb-b745-77bb55b5eaf2",
|
||||
"tag": "nic1"
|
||||
}],
|
||||
"block_device_mapping_v2": [{
|
||||
"uuid": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"source_type": "image",
|
||||
"destination_type": "volume",
|
||||
"boot_index": 0,
|
||||
"volume_size": "1",
|
||||
"tag": "disk1",
|
||||
"volume_type": "lvm-1"
|
||||
}]
|
||||
}
|
||||
}
|
22
doc/api_samples/servers/v2.67/server-create-resp.json
Normal file
22
doc/api_samples/servers/v2.67/server-create-resp.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"server": {
|
||||
"OS-DCF:diskConfig": "AUTO",
|
||||
"adminPass": "S5wqy9sPYUvU",
|
||||
"id": "97108291-2fd7-4dc2-a909-eaae0306a6a9",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/97108291-2fd7-4dc2-a909-eaae0306a6a9",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/97108291-2fd7-4dc2-a909-eaae0306a6a9",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"security_groups": [
|
||||
{
|
||||
"name": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.66",
|
||||
"version": "2.67",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.66",
|
||||
"version": "2.67",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -161,6 +161,14 @@ fields (in addition to the ones that were already there):
|
||||
usage is to set it to 0 for the boot device and leave it as None for any
|
||||
other devices.
|
||||
|
||||
* volume_type - Added in microversion 2.67 to the servers create API to
|
||||
support specifying volume type when booting instances. When we snapshot a
|
||||
volume-backed server, the block_device_mapping_v2 image metadata will
|
||||
include the volume_type from the BDM record so if the user then creates
|
||||
another server from that snapshot, the volume that nova creates from that
|
||||
snapshot will use the same volume_type. If a user wishes to change that
|
||||
volume type in the image metadata, they can do so via the image API.
|
||||
|
||||
Valid source / dest combinations
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -162,6 +162,8 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
``updated_at`` time to filter nova resources, the resources
|
||||
include the servers API, os-instance-action API and
|
||||
os-migrations API.
|
||||
* 2.67 - Adds the optional ``volume_type`` field to the
|
||||
``block_device_mapping_v2`` parameter when creating a server.
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
@ -170,7 +172,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
# Note(cyeoh): This only applies for the v2.1 API once microversions
|
||||
# support is fully merged. It does not affect the V2 API.
|
||||
_MIN_API_VERSION = "2.1"
|
||||
_MAX_API_VERSION = "2.66"
|
||||
_MAX_API_VERSION = "2.67"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
# Almost all proxy APIs which are related to network, images and baremetal
|
||||
|
@ -349,6 +349,13 @@ base_create_v263['properties']['server']['properties'][
|
||||
'trusted_image_certificates'] = parameter_types.trusted_certs
|
||||
|
||||
|
||||
# Add volume type in block_device_mapping_v2.
|
||||
base_create_v267 = copy.deepcopy(base_create_v263)
|
||||
base_create_v267['properties']['server']['properties'][
|
||||
'block_device_mapping_v2']['items'][
|
||||
'properties']['volume_type'] = parameter_types.volume_type
|
||||
|
||||
|
||||
base_update = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
|
@ -417,7 +417,8 @@ class ServersController(wsgi.Controller):
|
||||
@validation.schema(schema_servers.base_create_v242, '2.42', '2.51')
|
||||
@validation.schema(schema_servers.base_create_v252, '2.52', '2.56')
|
||||
@validation.schema(schema_servers.base_create_v257, '2.57', '2.62')
|
||||
@validation.schema(schema_servers.base_create_v263, '2.63')
|
||||
@validation.schema(schema_servers.base_create_v263, '2.63', '2.66')
|
||||
@validation.schema(schema_servers.base_create_v267, '2.67')
|
||||
def create(self, req, body):
|
||||
"""Creates a new server for a given user."""
|
||||
context = req.environ['nova.context']
|
||||
@ -646,6 +647,7 @@ class ServersController(wsgi.Controller):
|
||||
exception.InvalidBDMEphemeralSize,
|
||||
exception.InvalidBDMFormat,
|
||||
exception.InvalidBDMSwapSize,
|
||||
exception.VolumeTypeNotFound,
|
||||
exception.AutoDiskConfigDisabledByImage,
|
||||
exception.ImageCPUPinningForbidden,
|
||||
exception.ImageCPUThreadPolicyForbidden,
|
||||
@ -673,6 +675,7 @@ class ServersController(wsgi.Controller):
|
||||
exception.NetworkAmbiguous,
|
||||
exception.NoUniqueMatch,
|
||||
exception.MultiattachSupportNotYetAvailable,
|
||||
exception.VolumeTypeSupportNotYetAvailable,
|
||||
exception.CertificateValidationNotYetAvailable) as error:
|
||||
raise exc.HTTPConflict(explanation=error.format_message())
|
||||
|
||||
|
@ -374,6 +374,11 @@ volume_id = {
|
||||
}
|
||||
|
||||
|
||||
volume_type = {
|
||||
'type': ['string', 'null'], 'minLength': 0, 'maxLength': 255
|
||||
}
|
||||
|
||||
|
||||
network_id = {
|
||||
'type': 'string', 'format': 'uuid'
|
||||
}
|
||||
|
@ -173,6 +173,7 @@ class BlockDeviceDict(dict):
|
||||
source_type = api_dict.get('source_type')
|
||||
device_uuid = api_dict.get('uuid')
|
||||
destination_type = api_dict.get('destination_type')
|
||||
volume_type = api_dict.get('volume_type')
|
||||
|
||||
if source_type == 'blank' and device_uuid:
|
||||
raise exception.InvalidBDMFormat(
|
||||
@ -191,12 +192,24 @@ class BlockDeviceDict(dict):
|
||||
boot_index = -1
|
||||
boot_index = int(boot_index)
|
||||
|
||||
# if this bdm is generated from --image ,then
|
||||
# if this bdm is generated from --image, then
|
||||
# source_type = image and destination_type = local is allowed
|
||||
if not (image_uuid_specified and boot_index == 0):
|
||||
raise exception.InvalidBDMFormat(
|
||||
details=_("Mapping image to local is not supported."))
|
||||
|
||||
if destination_type == 'local' and volume_type:
|
||||
raise exception.InvalidBDMFormat(
|
||||
details=_("Specifying a volume_type with destination_type="
|
||||
"local is not supported."))
|
||||
|
||||
# Specifying a volume_type with a pre-existing source volume is
|
||||
# not supported.
|
||||
if source_type == 'volume' and volume_type:
|
||||
raise exception.InvalidBDMFormat(
|
||||
details=_("Specifying volume type to existing volume is "
|
||||
"not supported."))
|
||||
|
||||
api_dict.pop('uuid', None)
|
||||
return cls(api_dict)
|
||||
|
||||
|
@ -1723,6 +1723,13 @@ class CinderFixtureNewAttachFlow(fixtures.Fixture):
|
||||
'target_lun': '1'}}}
|
||||
return attachment_ref
|
||||
|
||||
def fake_get_all_volume_types(*args, **kwargs):
|
||||
return [{
|
||||
# This is used in the 2.67 API sample test.
|
||||
'id': '5f9204ec-3e94-4f27-9beb-fe7bb73b6eb9',
|
||||
'name': 'lvm-1'
|
||||
}]
|
||||
|
||||
self.test.stub_out('nova.volume.cinder.API.attachment_create',
|
||||
fake_attachment_create)
|
||||
self.test.stub_out('nova.volume.cinder.API.attachment_delete',
|
||||
@ -1746,6 +1753,8 @@ class CinderFixtureNewAttachFlow(fixtures.Fixture):
|
||||
lambda *args, **kwargs: None)
|
||||
self.test.stub_out('nova.volume.cinder.API.check_attached',
|
||||
lambda *args, **kwargs: None)
|
||||
self.test.stub_out('nova.volume.cinder.API.get_all_volume_types',
|
||||
fake_get_all_volume_types)
|
||||
|
||||
|
||||
class PlacementApiClient(object):
|
||||
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"server" : {
|
||||
"name" : "bfv-server-with-volume-type",
|
||||
"flavorRef" : "%(host)s/flavors/1",
|
||||
"networks" : [{
|
||||
"uuid" : "ff608d40-75e9-48cb-b745-77bb55b5eaf2",
|
||||
"tag": "nic1"
|
||||
}],
|
||||
"block_device_mapping_v2": [{
|
||||
"uuid": "%(image_id)s",
|
||||
"source_type": "image",
|
||||
"destination_type": "volume",
|
||||
"boot_index": 0,
|
||||
"volume_size": "1",
|
||||
"tag": "disk1",
|
||||
"volume_type": "lvm-1"
|
||||
}]
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"server": {
|
||||
"OS-DCF:diskConfig": "AUTO",
|
||||
"adminPass": "%(password)s",
|
||||
"id": "%(id)s",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(versioned_compute_endpoint)s/servers/%(uuid)s",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "%(compute_endpoint)s/servers/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"security_groups": [
|
||||
{
|
||||
"name": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import six
|
||||
|
||||
from nova.api.openstack import api_version_request as avr
|
||||
import nova.conf
|
||||
from nova.tests import fixtures as nova_fixtures
|
||||
from nova.tests.functional.api_sample_tests import api_sample_base
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
from nova.tests.unit.image import fake
|
||||
@ -353,6 +354,18 @@ class ServersSampleJson266Test(ServersSampleBase):
|
||||
'servers-details-with-changes-before', subs, response, 200)
|
||||
|
||||
|
||||
class ServersSampleJson267Test(ServersSampleBase):
|
||||
microversion = '2.67'
|
||||
scenarios = [('v2_67', {'api_major_version': 'v2.1'})]
|
||||
|
||||
def setUp(self):
|
||||
super(ServersSampleJson267Test, self).setUp()
|
||||
self.useFixture(nova_fixtures.CinderFixtureNewAttachFlow(self))
|
||||
|
||||
def test_servers_post(self):
|
||||
return self._post_server(use_common_server_api_samples=False)
|
||||
|
||||
|
||||
class ServersUpdateSampleJsonTest(ServersSampleBase):
|
||||
|
||||
def test_update_server(self):
|
||||
|
@ -6409,6 +6409,92 @@ class ServersControllerCreateTestV263(ServersControllerCreateTest):
|
||||
six.text_type(ex))
|
||||
|
||||
|
||||
class ServersControllerCreateTestV267(ServersControllerCreateTest):
|
||||
def setUp(self):
|
||||
super(ServersControllerCreateTestV267, self).setUp()
|
||||
|
||||
self.block_device_mapping_v2 = [
|
||||
{'uuid': '70a599e0-31e7-49b7-b260-868f441e862b',
|
||||
'source_type': 'image',
|
||||
'destination_type': 'volume',
|
||||
'boot_index': 0,
|
||||
'volume_size': '1',
|
||||
'volume_type': 'fake-lvm-1'
|
||||
}]
|
||||
|
||||
def _test_create_extra(self, *args, **kwargs):
|
||||
self.req.api_version_request = \
|
||||
api_version_request.APIVersionRequest('2.67')
|
||||
return super(ServersControllerCreateTestV267, self)._test_create_extra(
|
||||
*args, **kwargs)
|
||||
|
||||
@mock.patch('nova.objects.Service.get_minimum_version',
|
||||
return_value=compute_api.MIN_COMPUTE_VOLUME_TYPE)
|
||||
def test_create_server_with_trusted_volume_type_pre_2_67_fails(self,
|
||||
get_min_ver):
|
||||
"""Make sure we can't use volume_type before 2.67"""
|
||||
self.body['server'].update(
|
||||
{'block_device_mapping_v2': self.block_device_mapping_v2})
|
||||
self.req.body = jsonutils.dump_as_bytes(self.block_device_mapping_v2)
|
||||
self.req.api_version_request = \
|
||||
api_version_request.APIVersionRequest('2.66')
|
||||
ex = self.assertRaises(
|
||||
exception.ValidationError, self.controller.create, self.req,
|
||||
body=self.body)
|
||||
self.assertIn("'volume_type' was unexpected", six.text_type(ex))
|
||||
|
||||
@mock.patch.object(compute_api.API, 'create',
|
||||
side_effect=exception.VolumeTypeNotFound(
|
||||
id_or_name='fake-lvm-1'))
|
||||
def test_create_instance_with_volume_type_not_found(self, mock_create):
|
||||
"""Trying to boot from volume with a volume type that does not exist
|
||||
will result in a 400 error.
|
||||
"""
|
||||
params = {'block_device_mapping_v2': self.block_device_mapping_v2}
|
||||
ex = self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self._test_create_extra, params)
|
||||
self.assertIn('Volume type fake-lvm-1 could not be found',
|
||||
six.text_type(ex))
|
||||
|
||||
@mock.patch('nova.objects.service.get_minimum_version_all_cells',
|
||||
return_value=compute_api.MIN_COMPUTE_VOLUME_TYPE - 1)
|
||||
def test_check_volume_type_new_inst_old_compute(self, get_min_version):
|
||||
"""Trying to boot from volume with a volume_type but not all computes
|
||||
are upgraded will result in a 409 error.
|
||||
"""
|
||||
params = {'block_device_mapping_v2': self.block_device_mapping_v2}
|
||||
ex = self.assertRaises(webob.exc.HTTPConflict,
|
||||
self._test_create_extra, params)
|
||||
self.assertIn('Volume type support is not yet available',
|
||||
six.text_type(ex))
|
||||
|
||||
def test_create_instance_with_volume_type_empty_string(self):
|
||||
"""Test passing volume_type='' which is accepted but not used."""
|
||||
self.block_device_mapping_v2[0]['volume_type'] = ''
|
||||
params = {'block_device_mapping_v2': self.block_device_mapping_v2}
|
||||
self._test_create_extra(params)
|
||||
|
||||
def test_create_instance_with_none_volume_type(self):
|
||||
"""Test passing volume_type=None which is accepted but not used."""
|
||||
self.block_device_mapping_v2[0]['volume_type'] = None
|
||||
params = {'block_device_mapping_v2': self.block_device_mapping_v2}
|
||||
self._test_create_extra(params)
|
||||
|
||||
def test_create_instance_without_volume_type(self):
|
||||
"""Test passing without volume_type which is accepted but not used."""
|
||||
self.block_device_mapping_v2[0].pop('volume_type')
|
||||
params = {'block_device_mapping_v2': self.block_device_mapping_v2}
|
||||
self._test_create_extra(params)
|
||||
|
||||
def test_create_instance_with_volume_type_too_long(self):
|
||||
"""Tests the maxLength schema validation on volume_type."""
|
||||
self.block_device_mapping_v2[0]['volume_type'] = 'X' * 256
|
||||
params = {'block_device_mapping_v2': self.block_device_mapping_v2}
|
||||
ex = self.assertRaises(exception.ValidationError,
|
||||
self._test_create_extra, params)
|
||||
self.assertIn('is too long', six.text_type(ex))
|
||||
|
||||
|
||||
class ServersControllerCreateTestWithMock(test.TestCase):
|
||||
image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6'
|
||||
flavor_ref = 'http://localhost/123/flavors/3'
|
||||
|
@ -17,6 +17,7 @@
|
||||
Tests for Block Device utility functions.
|
||||
"""
|
||||
from oslo_utils.fixture import uuidsentinel as uuids
|
||||
import six
|
||||
|
||||
from nova import block_device
|
||||
from nova import exception
|
||||
@ -599,6 +600,45 @@ class TestBlockDeviceDict(test.NoDBTestCase):
|
||||
self.assertEqual(retexp,
|
||||
block_device.BlockDeviceDict.from_api(api_dict, True))
|
||||
|
||||
def test_from_api_invalid_oneof_image_id_or_destination_local_mapping(
|
||||
self):
|
||||
api_dict = {'id': 1,
|
||||
'source_type': 'image',
|
||||
'destination_type': 'local',
|
||||
'uuid': 'fake-volume-id-1',
|
||||
'volume_type': 'fake-lvm-1',
|
||||
'boot_index': 1}
|
||||
ex = self.assertRaises(exception.InvalidBDMFormat,
|
||||
block_device.BlockDeviceDict.from_api,
|
||||
api_dict, False)
|
||||
self.assertIn('Mapping image to local is not supported',
|
||||
six.text_type(ex))
|
||||
|
||||
def test_from_api_invalid_volume_type_to_destination_local_mapping(self):
|
||||
api_dict = {'id': 1,
|
||||
'source_type': 'volume',
|
||||
'destination_type': 'local',
|
||||
'uuid': 'fake-volume-id-1',
|
||||
'volume_type': 'fake-lvm-1'}
|
||||
ex = self.assertRaises(exception.InvalidBDMFormat,
|
||||
block_device.BlockDeviceDict.from_api,
|
||||
api_dict, False)
|
||||
self.assertIn('Specifying a volume_type with destination_type=local '
|
||||
'is not supported', six.text_type(ex))
|
||||
|
||||
def test_from_api_invalid_specify_volume_type_with_source_volume_mapping(
|
||||
self):
|
||||
api_dict = {'id': 1,
|
||||
'source_type': 'volume',
|
||||
'destination_type': 'volume',
|
||||
'uuid': 'fake-volume-id-1',
|
||||
'volume_type': 'fake-lvm-1'}
|
||||
ex = self.assertRaises(exception.InvalidBDMFormat,
|
||||
block_device.BlockDeviceDict.from_api,
|
||||
api_dict, False)
|
||||
self.assertIn('Specifying volume type to existing volume is '
|
||||
'not supported', six.text_type(ex))
|
||||
|
||||
def test_legacy(self):
|
||||
for legacy, new in zip(self.legacy_mapping, self.new_mapping):
|
||||
self.assertThat(
|
||||
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
features:
|
||||
- Microversion 2.67 adds the optional parameter ``volume_type`` to
|
||||
block_device_mapping_v2, which can be used to specify ``volume_type``
|
||||
when creating a server.
|
||||
|
||||
This would only apply to BDMs with ``source_type`` of `blank`, `image` and
|
||||
`snapshot` and ``destination_type`` of `volume`.
|
||||
|
||||
The compute API will reject server create requests with a specified
|
||||
``volume_type`` until all nova-compute services are upgraded to Stein.
|
Loading…
x
Reference in New Issue
Block a user