diff --git a/etc/nova/policy.json b/etc/nova/policy.json index e4a6f4465b3d..3b16eca15205 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -65,6 +65,7 @@ "compute_extension:v3:os-attach-interfaces": "", "compute_extension:v3:os-attach-interfaces:discoverable": "", "compute_extension:baremetal_nodes": "rule:admin_api", + "compute_extension:v3:os-block-device-mapping-v1:discoverable": "", "compute_extension:cells": "rule:admin_api", "compute_extension:cells:create": "rule:admin_api", "compute_extension:cells:delete": "rule:admin_api", diff --git a/nova/api/openstack/compute/plugins/v3/block_device_mapping.py b/nova/api/openstack/compute/plugins/v3/block_device_mapping.py index 84d87b4c58ab..ef54f064dff1 100644 --- a/nova/api/openstack/compute/plugins/v3/block_device_mapping.py +++ b/nova/api/openstack/compute/plugins/v3/block_device_mapping.py @@ -20,9 +20,11 @@ from webob import exc from nova.api.openstack import extensions from nova import block_device from nova import exception +from nova.i18n import _ ALIAS = "os-block-device-mapping" ATTRIBUTE_NAME = "block_device_mapping_v2" +LEGACY_ATTRIBUTE_NAME = "block_device_mapping" class BlockDeviceMapping(extensions.V3APIExtensionBase): @@ -43,18 +45,23 @@ class BlockDeviceMapping(extensions.V3APIExtensionBase): # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' # parameter as this is placed to handle scheduler_hint extension for V2.1. def server_create(self, server_dict, create_kwargs, body_deprecated_param): - block_device_mapping = server_dict.get(ATTRIBUTE_NAME, []) + bdm = server_dict.get(ATTRIBUTE_NAME, []) + legacy_bdm = server_dict.get(LEGACY_ATTRIBUTE_NAME, []) + + if bdm and legacy_bdm: + expl = _('Using different block_device_mapping syntaxes ' + 'is not allowed in the same request.') + raise exc.HTTPBadRequest(explanation=expl) try: block_device_mapping = [ block_device.BlockDeviceDict.from_api(bdm_dict) - for bdm_dict in block_device_mapping] + for bdm_dict in bdm] except (exception.InvalidBDMFormat, exception.InvalidBDMVolumeNotBootable) as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - create_kwargs['block_device_mapping'] = block_device_mapping - - # Unset the legacy_bdm flag if we got a block device mapping. if block_device_mapping: + create_kwargs['block_device_mapping'] = block_device_mapping + # Unset the legacy_bdm flag if we got a block device mapping. create_kwargs['legacy_bdm'] = False diff --git a/nova/api/openstack/compute/plugins/v3/block_device_mapping_v1.py b/nova/api/openstack/compute/plugins/v3/block_device_mapping_v1.py new file mode 100644 index 000000000000..831a4d7bf0dd --- /dev/null +++ b/nova/api/openstack/compute/plugins/v3/block_device_mapping_v1.py @@ -0,0 +1,71 @@ +# Copyright 2013 OpenStack Foundation +# 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. + +"""The legacy block device mappings extension.""" + +from webob import exc + +from nova.api.openstack import extensions +from nova import block_device +from nova import exception +from nova.i18n import _ +from nova.openstack.common import strutils + +ALIAS = "os-block-device-mapping-v1" +ATTRIBUTE_NAME = "block_device_mapping" +ATTRIBUTE_NAME_V2 = "block_device_mapping_v2" + + +class BlockDeviceMappingV1(extensions.V3APIExtensionBase): + """Block device mapping boot support.""" + + name = "BlockDeviceMappingV1" + alias = ALIAS + version = 1 + + def get_resources(self): + return [] + + def get_controller_extensions(self): + return [] + + # use nova.api.extensions.server.extensions entry point to modify + # server create kwargs + # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' + # parameter as this is placed to handle scheduler_hint extension for V2.1. + def server_create(self, server_dict, create_kwargs, body_deprecated_param): + block_device_mapping = server_dict.get(ATTRIBUTE_NAME, []) + block_device_mapping_v2 = server_dict.get(ATTRIBUTE_NAME_V2, []) + + if block_device_mapping and block_device_mapping_v2: + expl = _('Using different block_device_mapping syntaxes ' + 'is not allowed in the same request.') + raise exc.HTTPBadRequest(explanation=expl) + + 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']) + + if block_device_mapping: + 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 diff --git a/nova/api/openstack/compute/plugins/v3/servers.py b/nova/api/openstack/compute/plugins/v3/servers.py index 3ffd5a7e9229..3f9edfd35213 100644 --- a/nova/api/openstack/compute/plugins/v3/servers.py +++ b/nova/api/openstack/compute/plugins/v3/servers.py @@ -551,7 +551,8 @@ class ServersController(wsgi.Controller): exception.InvalidBDMVolume, exception.InvalidBDMImage, exception.InvalidBDMBootSequence, - exception.InvalidBDMLocalsLimit) as error: + exception.InvalidBDMLocalsLimit, + exception.InvalidBDMVolumeNotBootable) as error: raise exc.HTTPBadRequest(explanation=error.format_message()) except (exception.PortInUse, exception.NetworkAmbiguous, diff --git a/nova/tests/api/openstack/compute/contrib/test_block_device_mapping_v1.py b/nova/tests/api/openstack/compute/contrib/test_block_device_mapping_v1.py new file mode 100644 index 000000000000..fda5f11df3a8 --- /dev/null +++ b/nova/tests/api/openstack/compute/contrib/test_block_device_mapping_v1.py @@ -0,0 +1,416 @@ +# Copyright (c) 2014 IBM Corp. +# 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. + +import mock +import mox +from oslo.config import cfg +from webob import exc + +from nova.api.openstack.compute import extensions +from nova.api.openstack.compute import plugins +from nova.api.openstack.compute.plugins.v3 import block_device_mapping_v1 as \ + block_device_mapping +from nova.api.openstack.compute.plugins.v3 import servers as servers_v3 +from nova.api.openstack.compute import servers as servers_v2 +from nova.compute import api as compute_api +from nova import exception +from nova.openstack.common import jsonutils +from nova import test +from nova.tests.api.openstack import fakes +from nova.tests.image import fake + +CONF = cfg.CONF + + +class BlockDeviceMappingTestV21(test.TestCase): + + def _setup_controller(self): + ext_info = plugins.LoadedExtensionInfo() + CONF.set_override('extensions_blacklist', 'os-block-device-mapping', + 'osapi_v3') + self.controller = servers_v3.ServersController(extension_info=ext_info) + CONF.set_override('extensions_blacklist', + ['os-block-device-mapping-v1', + 'os-block-device-mapping'], + 'osapi_v3') + self.no_volumes_controller = servers_v3.ServersController( + extension_info=ext_info) + CONF.set_override('extensions_blacklist', '', 'osapi_v3') + + def setUp(self): + super(BlockDeviceMappingTestV21, self).setUp() + self._setup_controller() + 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, + 'volume_id': self.volume_id, + 'status': 'active', + 'device_name': 'vda', + 'delete_on_termination': False, + 'volume_image_metadata': + {'test_key': 'test_value'} + }] + + def _get_servers_body(self, no_image=False): + body = { + 'server': { + 'min_count': 2, + 'name': 'server_test', + 'imageRef': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', + 'flavorRef': 'http://localhost/123/flavors/3', + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + }, + } + if no_image: + del body['server']['imageRef'] + return body + + def _test_create(self, params, no_image=False, override_controller=None): + body = self._get_servers_body(no_image) + body['server'].update(params) + + req = fakes.HTTPRequestV3.blank('/servers') + req.method = 'POST' + req.headers['content-type'] = 'application/json' + + req.body = jsonutils.dumps(body) + + if override_controller: + override_controller.create(req, body=body).obj['server'] + else: + self.controller.create(req, body=body).obj['server'] + + def test_create_instance_with_volumes_enabled(self): + params = {'block_device_mapping': self.bdm} + old_create = compute_api.API.create + + def create(*args, **kwargs): + self.assertEqual(kwargs['block_device_mapping'], self.bdm) + return old_create(*args, **kwargs) + + def _validate_bdm(*args, **kwargs): + pass + + self.stubs.Set(compute_api.API, 'create', create) + self.stubs.Set(compute_api.API, '_validate_bdm', _validate_bdm) + self._test_create(params) + + def test_create_instance_with_volumes_enabled_and_bdms_no_image(self): + """Test that the create works if there is no image supplied but + os-volumes extension is enabled and bdms are supplied + """ + self.mox.StubOutWithMock(compute_api.API, '_validate_bdm') + self.mox.StubOutWithMock(compute_api.API, '_get_bdm_image_metadata') + volume = self.bdm[0] + compute_api.API._validate_bdm(mox.IgnoreArg(), + mox.IgnoreArg(), mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(True) + compute_api.API._get_bdm_image_metadata(mox.IgnoreArg(), + self.bdm, + True).AndReturn(volume) + params = {'block_device_mapping': self.bdm} + old_create = compute_api.API.create + + def create(*args, **kwargs): + self.assertEqual(kwargs['block_device_mapping'], self.bdm) + self.assertNotIn('imageRef', kwargs) + return old_create(*args, **kwargs) + + def _validate_bdm(*args, **kwargs): + pass + + self.stubs.Set(compute_api.API, 'create', create) + self.mox.ReplayAll() + self._test_create(params, no_image=True) + + def test_create_instance_with_volumes_disabled(self): + bdm = [{'device_name': 'foo'}] + params = {'block_device_mapping': bdm} + old_create = compute_api.API.create + + def create(*args, **kwargs): + self.assertNotIn(block_device_mapping, kwargs) + return old_create(*args, **kwargs) + + self.stubs.Set(compute_api.API, 'create', create) + self._test_create(params, + override_controller=self.no_volumes_controller) + + @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', + }] + params = {'block_device_mapping': bdm} + fake_bdm_meta.side_effect = exception.InvalidBDMVolumeNotBootable(id=1) + self.assertRaises(exc.HTTPBadRequest, + self._test_create, params, no_image=True) + + def test_create_instance_with_device_name_not_string(self): + old_create = compute_api.API.create + self.params = {'block_device_mapping': self.bdm} + + def create(*args, **kwargs): + self.assertEqual(kwargs['block_device_mapping'], self.bdm) + return old_create(*args, **kwargs) + + self.stubs.Set(compute_api.API, 'create', create) + self.assertRaises(exc.HTTPBadRequest, + self._test_create, self.params) + + def test_create_instance_with_device_name_empty(self): + self.bdm[0]['device_name'] = '' + params = {'block_device_mapping': self.bdm} + old_create = compute_api.API.create + + def create(*args, **kwargs): + self.assertEqual(kwargs['block_device_mapping'], self.bdm) + return old_create(*args, **kwargs) + + self.stubs.Set(compute_api.API, 'create', create) + self.assertRaises(exc.HTTPBadRequest, + self._test_create, params) + + def test_create_instance_with_device_name_too_long(self): + self.bdm[0]['device_name'] = 'a' * 256, + params = {'block_device_mapping': self.bdm} + old_create = compute_api.API.create + + def create(*args, **kwargs): + self.assertEqual(kwargs['block_device_mapping'], self.bdm) + return old_create(*args, **kwargs) + + self.stubs.Set(compute_api.API, 'create', create) + self.assertRaises(exc.HTTPBadRequest, + self._test_create, params) + + def test_create_instance_with_space_in_device_name(self): + self.bdm[0]['device_name'] = 'vd a', + params = {'block_device_mapping': self.bdm} + old_create = compute_api.API.create + + def create(*args, **kwargs): + self.assertTrue(kwargs['legacy_bdm']) + self.assertEqual(kwargs['block_device_mapping'], self.bdm) + return old_create(*args, **kwargs) + + self.stubs.Set(compute_api.API, 'create', create) + self.assertRaises(exc.HTTPBadRequest, + self._test_create, params) + + def test_create_instance_with_invalid_size(self): + bdm = [{'delete_on_termination': 1, + 'device_name': 'vda', + 'volume_size': "hello world", + 'volume_id': '11111111-1111-1111-1111-111111111111'}] + params = {'block_device_mapping': bdm} + old_create = compute_api.API.create + + 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(exc.HTTPBadRequest, + 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', + 'delete_on_termination': True}, + {'device_name': 'foo3', 'volume_id': 'fake_vol', + 'delete_on_termination': 'invalid'}, + {'device_name': 'foo4', 'volume_id': 'fake_vol', + 'delete_on_termination': 0}, + {'device_name': 'foo5', 'volume_id': 'fake_vol', + 'delete_on_termination': False}] + expected_bdm = [ + {'device_name': 'foo1', 'volume_id': 'fake_vol', + 'delete_on_termination': True}, + {'device_name': 'foo2', 'volume_id': 'fake_vol', + 'delete_on_termination': True}, + {'device_name': 'foo3', 'volume_id': 'fake_vol', + 'delete_on_termination': False}, + {'device_name': 'foo4', 'volume_id': 'fake_vol', + 'delete_on_termination': False}, + {'device_name': 'foo5', 'volume_id': 'fake_vol', + 'delete_on_termination': False}] + params = {'block_device_mapping': bdm} + old_create = compute_api.API.create + + def create(*args, **kwargs): + self.assertEqual(expected_bdm, kwargs['block_device_mapping']) + return old_create(*args, **kwargs) + + def _validate_bdm(*args, **kwargs): + pass + + self.stubs.Set(compute_api.API, 'create', create) + self.stubs.Set(compute_api.API, '_validate_bdm', _validate_bdm) + self._test_create(params) + + def test_create_instance_decide_format_legacy(self): + ext_info = plugins.LoadedExtensionInfo() + CONF.set_override('extensions_blacklist', + ['os-block-device-mapping', + 'os-block-device-mapping-v1'], + 'osapi_v3') + controller = servers_v3.ServersController(extension_info=ext_info) + bdm = [{'device_name': 'foo1', + 'volume_id': 'fake_vol', + 'delete_on_termination': 1}] + + expected_legacy_flag = True + + old_create = compute_api.API.create + + def create(*args, **kwargs): + legacy_bdm = kwargs.get('legacy_bdm', True) + self.assertEqual(legacy_bdm, expected_legacy_flag) + return old_create(*args, **kwargs) + + def _validate_bdm(*args, **kwargs): + pass + + self.stubs.Set(compute_api.API, 'create', create) + self.stubs.Set(compute_api.API, '_validate_bdm', + _validate_bdm) + + self._test_create({}, override_controller=controller) + + params = {'block_device_mapping': bdm} + self._test_create(params, override_controller=controller) + + def test_create_instance_both_bdm_formats(self): + bdm = [{'device_name': 'foo'}] + bdm_v2 = [{'source_type': 'volume', + 'uuid': 'fake_vol'}] + params = {'block_device_mapping': bdm, + 'block_device_mapping_v2': bdm_v2} + self.assertRaises(exc.HTTPBadRequest, self._test_create, params) + + +class BlockDeviceMappingTestV2(BlockDeviceMappingTestV21): + + def _setup_controller(self): + self.ext_mgr = extensions.ExtensionManager() + self.ext_mgr.extensions = {'os-volumes': 'fake'} + self.controller = servers_v2.Controller(self.ext_mgr) + self.ext_mgr_no_vols = extensions.ExtensionManager() + self.ext_mgr_no_vols.extensions = {} + self.no_volumes_controller = servers_v2.Controller( + self.ext_mgr_no_vols) + + def test_create_instance_with_volumes_disabled(self): + bdm = [{'device_name': 'foo'}] + params = {'block_device_mapping': bdm} + old_create = compute_api.API.create + + def create(*args, **kwargs): + self.assertIsNone(kwargs['block_device_mapping']) + return old_create(*args, **kwargs) + + self.stubs.Set(compute_api.API, 'create', create) + self._test_create(params, + override_controller=self.no_volumes_controller) + + def test_create_instance_decide_format_legacy(self): + ext_mgr = extensions.ExtensionManager() + ext_mgr.extensions = {'os-volumes': 'fake', + 'os-block-device-mapping-v2-boot': 'fake'} + controller = servers_v2.Controller(self.ext_mgr) + bdm = [{'device_name': 'foo1', + 'volume_id': 'fake_vol', + 'delete_on_termination': 1}] + + expected_legacy_flag = True + + old_create = compute_api.API.create + + def create(*args, **kwargs): + legacy_bdm = kwargs.get('legacy_bdm', True) + self.assertEqual(legacy_bdm, expected_legacy_flag) + return old_create(*args, **kwargs) + + def _validate_bdm(*args, **kwargs): + pass + + self.stubs.Set(compute_api.API, 'create', create) + self.stubs.Set(compute_api.API, '_validate_bdm', + _validate_bdm) + + self._test_create({}, override_controller=controller) + + params = {'block_device_mapping': bdm} + self._test_create(params, override_controller=controller) + + +class TestServerCreateRequestXMLDeserializer(test.TestCase): + + def setUp(self): + super(TestServerCreateRequestXMLDeserializer, self).setUp() + self.deserializer = servers_v2.CreateDeserializer() + + def test_request_with_block_device_mapping(self): + serial_request = """ + + + + + + + """ + request = self.deserializer.deserialize(serial_request) + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "block_device_mapping": [ + { + "volume_id": "7329b667-50c7-46a6-b913-cb2a09dfeee0", + "device_name": "/dev/vda", + "virtual_name": "root", + "delete_on_termination": False, + }, + { + "snapshot_id": "f31efb24-34d2-43e1-8b44-316052956a39", + "device_name": "/dev/vdb", + "virtual_name": "ephemeral0", + "delete_on_termination": False, + }, + { + "device_name": "/dev/vdc", + "no_device": True, + }, + ] + }} + self.assertEqual(request['body'], expected) diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py index f282cacff701..e5d51c3bf8ab 100644 --- a/nova/tests/api/openstack/compute/test_servers.py +++ b/nova/tests/api/openstack/compute/test_servers.py @@ -23,7 +23,6 @@ import uuid import iso8601 from lxml import etree import mock -import mox from oslo.config import cfg import six.moves.urllib.parse as urlparse import testtools @@ -2595,23 +2594,6 @@ class ServersControllerCreateTest(test.TestCase): self.stubs.Set(compute_api.API, 'create', create) self._test_create_extra(params) - def test_create_instance_with_volumes_enabled(self): - self.ext_mgr.extensions = {'os-volumes': 'fake'} - bdm = [{'device_name': 'foo', 'volume_id': 'fake_vol'}] - params = {'block_device_mapping': bdm} - old_create = compute_api.API.create - - def create(*args, **kwargs): - self.assertEqual(kwargs['block_device_mapping'], bdm) - return old_create(*args, **kwargs) - - def _validate_bdm(*args, **kwargs): - pass - - self.stubs.Set(compute_api.API, 'create', create) - self.stubs.Set(compute_api.API, '_validate_bdm', _validate_bdm) - self._test_create_extra(params) - def test_create_instance_with_volumes_enabled_no_image(self): """Test that the create will fail if there is no image and no bdms supplied in the request @@ -2639,222 +2621,6 @@ class ServersControllerCreateTest(test.TestCase): self.assertRaises(webob.exc.HTTPBadRequest, self._test_create_extra, {}, no_image=True) - def test_create_instance_with_volumes_enabled_and_bdms_no_image(self): - """Test that the create works if there is no image supplied but - os-volumes extension is enabled and bdms are supplied - """ - self.ext_mgr.extensions = {'os-volumes': 'fake'} - self.mox.StubOutWithMock(compute_api.API, '_validate_bdm') - self.mox.StubOutWithMock(compute_api.API, '_get_bdm_image_metadata') - bdm = [{ - 'id': 1, - 'no_device': None, - 'virtual_name': None, - 'snapshot_id': None, - 'volume_id': self.volume_id, - 'status': 'active', - 'device_name': 'vda', - 'delete_on_termination': False, - 'volume_image_metadata': - {'test_key': 'test_value'} - }] - volume = bdm[0] - compute_api.API._validate_bdm(mox.IgnoreArg(), - mox.IgnoreArg(), mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(True) - compute_api.API._get_bdm_image_metadata(mox.IgnoreArg(), - bdm, True).AndReturn(volume) - params = {'block_device_mapping': bdm} - old_create = compute_api.API.create - - def create(*args, **kwargs): - self.assertEqual(kwargs['block_device_mapping'], bdm) - self.assertNotIn('imageRef', kwargs) - return old_create(*args, **kwargs) - - def _validate_bdm(*args, **kwargs): - pass - - self.stubs.Set(compute_api.API, 'create', create) - self.mox.ReplayAll() - self._test_create_extra(params, no_image=True) - - def test_create_instance_with_volumes_disabled(self): - bdm = [{'device_name': 'foo'}] - params = {'block_device_mapping': bdm} - old_create = compute_api.API.create - - def create(*args, **kwargs): - self.assertIsNone(kwargs['block_device_mapping']) - return old_create(*args, **kwargs) - - self.stubs.Set(compute_api.API, 'create', create) - self._test_create_extra(params) - - @mock.patch('nova.compute.api.API._get_bdm_image_metadata') - def test_create_instance_non_bootable_volume_fails(self, fake_bdm_meta): - self.ext_mgr.extensions = {'os-volumes': 'fake'} - bdm = [{ - 'id': 1, - 'bootable': False, - 'volume_id': self.volume_id, - 'status': 'active', - 'device_name': 'vda', - }] - params = {'block_device_mapping': bdm} - fake_bdm_meta.side_effect = exception.InvalidBDMVolumeNotBootable(id=1) - self.assertRaises(webob.exc.HTTPBadRequest, - self._test_create_extra, params, no_image=True) - - def test_create_instance_with_device_name_not_string(self): - self.ext_mgr.extensions = {'os-volumes': 'fake'} - old_create = compute_api.API.create - self.params = {'block_device_mapping': self.bdm} - - def create(*args, **kwargs): - self.assertEqual(kwargs['block_device_mapping'], self.bdm) - return old_create(*args, **kwargs) - - self.stubs.Set(compute_api.API, 'create', create) - self.assertRaises(webob.exc.HTTPBadRequest, - self._test_create_extra, self.params) - - def test_create_instance_with_device_name_empty(self): - self.ext_mgr.extensions = {'os-volumes': 'fake'} - self.bdm[0]['device_name'] = '' - params = {'block_device_mapping': self.bdm} - old_create = compute_api.API.create - - def create(*args, **kwargs): - self.assertEqual(kwargs['block_device_mapping'], self.bdm) - return old_create(*args, **kwargs) - - self.stubs.Set(compute_api.API, 'create', create) - self.assertRaises(webob.exc.HTTPBadRequest, - self._test_create_extra, params) - - def test_create_instance_with_device_name_too_long(self): - self.ext_mgr.extensions = {'os-volumes': 'fake'} - self.bdm[0]['device_name'] = 'a' * 256, - params = {'block_device_mapping': self.bdm} - old_create = compute_api.API.create - - def create(*args, **kwargs): - self.assertEqual(kwargs['block_device_mapping'], self.bdm) - return old_create(*args, **kwargs) - - self.stubs.Set(compute_api.API, 'create', create) - self.assertRaises(webob.exc.HTTPBadRequest, - self._test_create_extra, params) - - def test_create_instance_with_space_in_device_name(self): - self.ext_mgr.extensions = {'os-volumes': 'fake'} - self.bdm[0]['device_name'] = 'vd a', - params = {'block_device_mapping': self.bdm} - old_create = compute_api.API.create - - def create(*args, **kwargs): - self.assertTrue(kwargs['legacy_bdm']) - self.assertEqual(kwargs['block_device_mapping'], self.bdm) - return old_create(*args, **kwargs) - - self.stubs.Set(compute_api.API, 'create', create) - self.assertRaises(webob.exc.HTTPBadRequest, - self._test_create_extra, params) - - def test_create_instance_with_invalid_size(self): - self.ext_mgr.extensions = {'os-volumes': 'fake'} - bdm = [{'delete_on_termination': 1, - 'device_name': 'vda', - 'volume_size': "hello world", - 'volume_id': '11111111-1111-1111-1111-111111111111'}] - params = {'block_device_mapping': bdm} - old_create = compute_api.API.create - - 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(webob.exc.HTTPBadRequest, - self._test_create_extra, params) - - def test_create_instance_with_bdm_delete_on_termination(self): - self.ext_mgr.extensions = {'os-volumes': 'fake'} - bdm = [{'device_name': 'foo1', 'volume_id': 'fake_vol', - 'delete_on_termination': 1}, - {'device_name': 'foo2', 'volume_id': 'fake_vol', - 'delete_on_termination': True}, - {'device_name': 'foo3', 'volume_id': 'fake_vol', - 'delete_on_termination': 'invalid'}, - {'device_name': 'foo4', 'volume_id': 'fake_vol', - 'delete_on_termination': 0}, - {'device_name': 'foo5', 'volume_id': 'fake_vol', - 'delete_on_termination': False}] - expected_bdm = [ - {'device_name': 'foo1', 'volume_id': 'fake_vol', - 'delete_on_termination': True}, - {'device_name': 'foo2', 'volume_id': 'fake_vol', - 'delete_on_termination': True}, - {'device_name': 'foo3', 'volume_id': 'fake_vol', - 'delete_on_termination': False}, - {'device_name': 'foo4', 'volume_id': 'fake_vol', - 'delete_on_termination': False}, - {'device_name': 'foo5', 'volume_id': 'fake_vol', - 'delete_on_termination': False}] - params = {'block_device_mapping': bdm} - old_create = compute_api.API.create - - def create(*args, **kwargs): - self.assertEqual(expected_bdm, kwargs['block_device_mapping']) - return old_create(*args, **kwargs) - - def _validate_bdm(*args, **kwargs): - pass - - self.stubs.Set(compute_api.API, 'create', create) - self.stubs.Set(compute_api.API, '_validate_bdm', _validate_bdm) - self._test_create_extra(params) - - def test_create_instance_decide_format_legacy(self): - self.ext_mgr.extensions = {'os-volumes': 'fake', - 'os-block-device-mapping-v2-boot': 'fake'} - bdm = [{'device_name': 'foo1', - 'volume_id': 'fake_vol', - 'delete_on_termination': 1}] - - expected_legacy_flag = True - - old_create = compute_api.API.create - - def create(*args, **kwargs): - self.assertEqual(kwargs['legacy_bdm'], expected_legacy_flag) - return old_create(*args, **kwargs) - - def _validate_bdm(*args, **kwargs): - pass - - self.stubs.Set(compute_api.API, 'create', create) - self.stubs.Set(compute_api.API, '_validate_bdm', - _validate_bdm) - - self._test_create_extra({}) - - params = {'block_device_mapping': bdm} - self._test_create_extra(params) - - def test_create_instance_both_bdm_formats(self): - self.ext_mgr.extensions = {'os-volumes': 'fake', - 'os-block-device-mapping-v2-boot': 'fake'} - bdm = [{'device_name': 'foo'}] - bdm_v2 = [{'source_type': 'volume', - 'uuid': 'fake_vol'}] - params = {'block_device_mapping': bdm, - 'block_device_mapping_v2': bdm_v2} - - self.assertRaises(webob.exc.HTTPBadRequest, - self._test_create_extra, params) - def test_create_instance_with_user_data_enabled(self): self.ext_mgr.extensions = {'os-user-data': 'fake'} user_data = 'fake' @@ -3747,46 +3513,6 @@ class TestServerCreateRequestXMLDeserializer(test.TestCase): }} self.assertEqual(request['body'], expected) - def test_request_with_block_device_mapping(self): - serial_request = """ - - - - - - - """ - request = self.deserializer.deserialize(serial_request) - expected = {"server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "1", - "block_device_mapping": [ - { - "volume_id": "7329b667-50c7-46a6-b913-cb2a09dfeee0", - "device_name": "/dev/vda", - "virtual_name": "root", - "delete_on_termination": False, - }, - { - "snapshot_id": "f31efb24-34d2-43e1-8b44-316052956a39", - "device_name": "/dev/vdb", - "virtual_name": "ephemeral0", - "delete_on_termination": False, - }, - { - "device_name": "/dev/vdc", - "no_device": True, - }, - ] - }} - self.assertEqual(request['body'], expected) - def test_request_with_config_drive(self): serial_request = """