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 = """