Merge "Add API schema for v2.1 block_device_mapping_v1"
This commit is contained in:
commit
9145635fb8
@ -18,9 +18,9 @@
|
||||
from oslo.utils import strutils
|
||||
from webob import exc
|
||||
|
||||
from nova.api.openstack.compute.schemas.v3 import block_device_mapping_v1 as \
|
||||
schema_block_device_mapping
|
||||
from nova.api.openstack import extensions
|
||||
from nova import block_device
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
|
||||
ALIAS = "os-block-device-mapping-v1"
|
||||
@ -54,17 +54,7 @@ class BlockDeviceMappingV1(extensions.V3APIExtensionBase):
|
||||
'is not allowed in the same request.')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
if not isinstance(block_device_mapping, list):
|
||||
msg = _('block_device_mapping must be a list')
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
for bdm in block_device_mapping:
|
||||
try:
|
||||
block_device.validate_device_name(bdm.get("device_name"))
|
||||
block_device.validate_and_default_volume_size(bdm)
|
||||
except exception.InvalidBDMFormat as e:
|
||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
||||
|
||||
if 'delete_on_termination' in bdm:
|
||||
bdm['delete_on_termination'] = strutils.bool_from_string(
|
||||
bdm['delete_on_termination'])
|
||||
@ -73,3 +63,6 @@ class BlockDeviceMappingV1(extensions.V3APIExtensionBase):
|
||||
create_kwargs['block_device_mapping'] = block_device_mapping
|
||||
# Sets the legacy_bdm flag if we got a legacy block device mapping.
|
||||
create_kwargs['legacy_bdm'] = True
|
||||
|
||||
def get_server_create_schema(self):
|
||||
return schema_block_device_mapping.server_create
|
||||
|
@ -12,17 +12,13 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
from nova.api.openstack.compute.schemas.v3 import block_device_mapping_v1
|
||||
from nova.api.validation import parameter_types
|
||||
|
||||
server_create = {
|
||||
'block_device_mapping_v2': {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'virtual_name': {
|
||||
'type': 'string', 'maxLength': 255,
|
||||
},
|
||||
|
||||
block_device_mapping_new_item = {
|
||||
# defined in nova/block_device.py:from_api()
|
||||
# NOTE: Client can specify the Id with the combination of
|
||||
# source_type and uuid, or a single attribute like volume_id/
|
||||
@ -35,11 +31,7 @@ server_create = {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255,
|
||||
'pattern': '^[a-zA-Z0-9._-]*$',
|
||||
},
|
||||
'volume_id': parameter_types.volume_id,
|
||||
'image_id': parameter_types.image_id,
|
||||
'snapshot_id': parameter_types.image_id,
|
||||
|
||||
'volume_size': parameter_types.non_negative_integer,
|
||||
# Defined as varchar(255) in column "destination_type" in table
|
||||
# "block_device_mapping"
|
||||
'destination_type': {
|
||||
@ -65,22 +57,15 @@ server_create = {
|
||||
'type': ['integer', 'string'],
|
||||
'pattern': '^-?[0-9]+$',
|
||||
},
|
||||
# Do not allow empty device names and number values and
|
||||
# containing spaces(defined in nova/block_device.py:from_api())
|
||||
'device_name': {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255,
|
||||
'pattern': '^[a-zA-Z0-9._-r/]*$',
|
||||
},
|
||||
# Defined as boolean in nova/block_device.py:from_api()
|
||||
'delete_on_termination': parameter_types.boolean,
|
||||
'no_device': {},
|
||||
# Defined as mediumtext in column "connection_info" in table
|
||||
# "block_device_mapping"
|
||||
'connection_info': {
|
||||
'type': 'string', 'maxLength': 16777215
|
||||
},
|
||||
},
|
||||
'additionalProperties': False
|
||||
}]
|
||||
}
|
||||
|
||||
block_device_mapping = copy.deepcopy(
|
||||
block_device_mapping_v1.legacy_block_device_mapping)
|
||||
block_device_mapping['properties'].update(block_device_mapping_new_item)
|
||||
|
||||
server_create = {
|
||||
'block_device_mapping_v2': {
|
||||
'type': 'array',
|
||||
'items': [block_device_mapping]
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
# Copyright 2014 NEC Corporation. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from nova.api.validation import parameter_types
|
||||
|
||||
legacy_block_device_mapping = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'virtual_name': {
|
||||
'type': 'string', 'maxLength': 255,
|
||||
},
|
||||
'volume_id': parameter_types.volume_id,
|
||||
'snapshot_id': parameter_types.image_id,
|
||||
'volume_size': parameter_types.non_negative_integer,
|
||||
# Do not allow empty device names and number values and
|
||||
# containing spaces(defined in nova/block_device.py:from_api())
|
||||
'device_name': {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255,
|
||||
'pattern': '^[a-zA-Z0-9._-r/]*$',
|
||||
},
|
||||
# Defined as boolean in nova/block_device.py:from_api()
|
||||
'delete_on_termination': parameter_types.boolean,
|
||||
'no_device': {},
|
||||
# Defined as mediumtext in column "connection_info" in table
|
||||
# "block_device_mapping"
|
||||
'connection_info': {
|
||||
'type': 'string', 'maxLength': 16777215
|
||||
},
|
||||
},
|
||||
'additionalProperties': False
|
||||
}
|
||||
|
||||
server_create = {
|
||||
'block_device_mapping': {
|
||||
'type': 'array',
|
||||
'items': [legacy_block_device_mapping]
|
||||
}
|
||||
}
|
@ -165,7 +165,7 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
@mock.patch.object(compute_api.API, 'create')
|
||||
def test_create_instance_with_bdm_param_not_list(self, mock_create):
|
||||
self.params = {'block_device_mapping': '/dev/vdb'}
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.assertRaises(self.validation_error,
|
||||
self._test_create, self.params)
|
||||
|
||||
def test_create_instance_with_device_name_empty(self):
|
||||
|
@ -35,6 +35,7 @@ CONF = cfg.CONF
|
||||
|
||||
|
||||
class BlockDeviceMappingTestV21(test.TestCase):
|
||||
validation_error = exception.ValidationError
|
||||
|
||||
def _setup_controller(self):
|
||||
ext_info = plugins.LoadedExtensionInfo()
|
||||
@ -55,16 +56,11 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
fake.stub_out_image_service(self.stubs)
|
||||
self.volume_id = fakes.FAKE_UUID
|
||||
self.bdm = [{
|
||||
'id': 1,
|
||||
'no_device': None,
|
||||
'virtual_name': None,
|
||||
'snapshot_id': None,
|
||||
'virtual_name': 'root',
|
||||
'volume_id': self.volume_id,
|
||||
'status': 'active',
|
||||
'device_name': 'vda',
|
||||
'delete_on_termination': False,
|
||||
'volume_image_metadata':
|
||||
{'test_key': 'test_value'}
|
||||
'delete_on_termination': False
|
||||
}]
|
||||
|
||||
def _get_servers_body(self, no_image=False):
|
||||
@ -119,7 +115,12 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
"""
|
||||
self.mox.StubOutWithMock(compute_api.API, '_validate_bdm')
|
||||
self.mox.StubOutWithMock(compute_api.API, '_get_bdm_image_metadata')
|
||||
volume = self.bdm[0]
|
||||
volume = {
|
||||
'id': 1,
|
||||
'status': 'active',
|
||||
'volume_image_metadata':
|
||||
{'test_key': 'test_value'}
|
||||
}
|
||||
compute_api.API._validate_bdm(mox.IgnoreArg(),
|
||||
mox.IgnoreArg(), mox.IgnoreArg(),
|
||||
mox.IgnoreArg()).AndReturn(True)
|
||||
@ -157,11 +158,8 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
@mock.patch('nova.compute.api.API._get_bdm_image_metadata')
|
||||
def test_create_instance_non_bootable_volume_fails(self, fake_bdm_meta):
|
||||
bdm = [{
|
||||
'id': 1,
|
||||
'bootable': False,
|
||||
'volume_id': self.volume_id,
|
||||
'status': 'active',
|
||||
'device_name': 'vda',
|
||||
'device_name': 'vda'
|
||||
}]
|
||||
params = {'block_device_mapping': bdm}
|
||||
fake_bdm_meta.side_effect = exception.InvalidBDMVolumeNotBootable(id=1)
|
||||
@ -169,6 +167,7 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
self._test_create, params, no_image=True)
|
||||
|
||||
def test_create_instance_with_device_name_not_string(self):
|
||||
self.bdm[0]['device_name'] = 123
|
||||
old_create = compute_api.API.create
|
||||
self.params = {'block_device_mapping': self.bdm}
|
||||
|
||||
@ -177,13 +176,32 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
return old_create(*args, **kwargs)
|
||||
|
||||
self.stubs.Set(compute_api.API, 'create', create)
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.assertRaises(self.validation_error,
|
||||
self._test_create, self.params)
|
||||
|
||||
def test_create_instance_with_snapshot_volume_id_none(self):
|
||||
old_create = compute_api.API.create
|
||||
bdm = [{
|
||||
'no_device': None,
|
||||
'snapshot_id': None,
|
||||
'volume_id': None,
|
||||
'device_name': 'vda',
|
||||
'delete_on_termination': False
|
||||
}]
|
||||
self.params = {'block_device_mapping': bdm}
|
||||
|
||||
def create(*args, **kwargs):
|
||||
self.assertEqual(kwargs['block_device_mapping'], bdm)
|
||||
return old_create(*args, **kwargs)
|
||||
|
||||
self.stubs.Set(compute_api.API, 'create', create)
|
||||
self.assertRaises(self.validation_error,
|
||||
self._test_create, self.params)
|
||||
|
||||
@mock.patch.object(compute_api.API, 'create')
|
||||
def test_create_instance_with_bdm_param_not_list(self, mock_create):
|
||||
self.params = {'block_device_mapping': '/dev/vdb'}
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.assertRaises(self.validation_error,
|
||||
self._test_create, self.params)
|
||||
|
||||
def test_create_instance_with_device_name_empty(self):
|
||||
@ -196,7 +214,7 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
return old_create(*args, **kwargs)
|
||||
|
||||
self.stubs.Set(compute_api.API, 'create', create)
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.assertRaises(self.validation_error,
|
||||
self._test_create, params)
|
||||
|
||||
def test_create_instance_with_device_name_too_long(self):
|
||||
@ -209,7 +227,7 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
return old_create(*args, **kwargs)
|
||||
|
||||
self.stubs.Set(compute_api.API, 'create', create)
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.assertRaises(self.validation_error,
|
||||
self._test_create, params)
|
||||
|
||||
def test_create_instance_with_space_in_device_name(self):
|
||||
@ -223,11 +241,11 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
return old_create(*args, **kwargs)
|
||||
|
||||
self.stubs.Set(compute_api.API, 'create', create)
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.assertRaises(self.validation_error,
|
||||
self._test_create, params)
|
||||
|
||||
def test_create_instance_with_invalid_size(self):
|
||||
bdm = [{'delete_on_termination': 1,
|
||||
bdm = [{'delete_on_termination': True,
|
||||
'device_name': 'vda',
|
||||
'volume_size': "hello world",
|
||||
'volume_id': '11111111-1111-1111-1111-111111111111'}]
|
||||
@ -239,30 +257,30 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
return old_create(*args, **kwargs)
|
||||
|
||||
self.stubs.Set(compute_api.API, 'create', create)
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.assertRaises(self.validation_error,
|
||||
self._test_create, params)
|
||||
|
||||
def test_create_instance_with_bdm_delete_on_termination(self):
|
||||
bdm = [{'device_name': 'foo1', 'volume_id': 'fake_vol',
|
||||
'delete_on_termination': 1},
|
||||
{'device_name': 'foo2', 'volume_id': 'fake_vol',
|
||||
bdm = [{'device_name': 'foo1', 'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': 'True'},
|
||||
{'device_name': 'foo2', 'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': True},
|
||||
{'device_name': 'foo3', 'volume_id': 'fake_vol',
|
||||
{'device_name': 'foo3', 'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': 'invalid'},
|
||||
{'device_name': 'foo4', 'volume_id': 'fake_vol',
|
||||
'delete_on_termination': 0},
|
||||
{'device_name': 'foo5', 'volume_id': 'fake_vol',
|
||||
{'device_name': 'foo4', 'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': False},
|
||||
{'device_name': 'foo5', 'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': False}]
|
||||
expected_bdm = [
|
||||
{'device_name': 'foo1', 'volume_id': 'fake_vol',
|
||||
{'device_name': 'foo1', 'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': True},
|
||||
{'device_name': 'foo2', 'volume_id': 'fake_vol',
|
||||
{'device_name': 'foo2', 'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': True},
|
||||
{'device_name': 'foo3', 'volume_id': 'fake_vol',
|
||||
{'device_name': 'foo3', 'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': False},
|
||||
{'device_name': 'foo4', 'volume_id': 'fake_vol',
|
||||
{'device_name': 'foo4', 'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': False},
|
||||
{'device_name': 'foo5', 'volume_id': 'fake_vol',
|
||||
{'device_name': 'foo5', 'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': False}]
|
||||
params = {'block_device_mapping': bdm}
|
||||
old_create = compute_api.API.create
|
||||
@ -286,8 +304,8 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
'osapi_v3')
|
||||
controller = servers_v3.ServersController(extension_info=ext_info)
|
||||
bdm = [{'device_name': 'foo1',
|
||||
'volume_id': 'fake_vol',
|
||||
'delete_on_termination': 1}]
|
||||
'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': True}]
|
||||
|
||||
expected_legacy_flag = True
|
||||
|
||||
@ -320,6 +338,7 @@ class BlockDeviceMappingTestV21(test.TestCase):
|
||||
|
||||
|
||||
class BlockDeviceMappingTestV2(BlockDeviceMappingTestV21):
|
||||
validation_error = exc.HTTPBadRequest
|
||||
|
||||
def _setup_controller(self):
|
||||
self.ext_mgr = extensions.ExtensionManager()
|
||||
@ -349,7 +368,7 @@ class BlockDeviceMappingTestV2(BlockDeviceMappingTestV21):
|
||||
'os-block-device-mapping-v2-boot': 'fake'}
|
||||
controller = servers_v2.Controller(self.ext_mgr)
|
||||
bdm = [{'device_name': 'foo1',
|
||||
'volume_id': 'fake_vol',
|
||||
'volume_id': fakes.FAKE_UUID,
|
||||
'delete_on_termination': 1}]
|
||||
|
||||
expected_legacy_flag = True
|
||||
|
Loading…
x
Reference in New Issue
Block a user