Add extension block_device_mapping_v1 for v2.1

In the beginning v3 didn't support legacy block_device_mapping request.
For v2.1, we want to keep back-compatibility with v2 api, so we need
add legacy block_device_mapping.

This patch add new extension block_device_mapping_v1 for v2.1. Also
share legacy block_device_mapping related unittests between v2 and v2.1.

Partially implements blueprint v2-on-v3-api

Change-Id: I841833d8fd0995ddf49febd9c1611cf711742f5a
This commit is contained in:
He Jie Xu 2014-08-20 13:51:21 +08:00
parent 606d25ffdc
commit 8353cb0524
7 changed files with 503 additions and 280 deletions

View File

@ -65,6 +65,7 @@
"compute_extension:v3:os-attach-interfaces": "", "compute_extension:v3:os-attach-interfaces": "",
"compute_extension:v3:os-attach-interfaces:discoverable": "", "compute_extension:v3:os-attach-interfaces:discoverable": "",
"compute_extension:baremetal_nodes": "rule:admin_api", "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": "rule:admin_api",
"compute_extension:cells:create": "rule:admin_api", "compute_extension:cells:create": "rule:admin_api",
"compute_extension:cells:delete": "rule:admin_api", "compute_extension:cells:delete": "rule:admin_api",

View File

@ -20,9 +20,11 @@ from webob import exc
from nova.api.openstack import extensions from nova.api.openstack import extensions
from nova import block_device from nova import block_device
from nova import exception from nova import exception
from nova.i18n import _
ALIAS = "os-block-device-mapping" ALIAS = "os-block-device-mapping"
ATTRIBUTE_NAME = "block_device_mapping_v2" ATTRIBUTE_NAME = "block_device_mapping_v2"
LEGACY_ATTRIBUTE_NAME = "block_device_mapping"
class BlockDeviceMapping(extensions.V3APIExtensionBase): class BlockDeviceMapping(extensions.V3APIExtensionBase):
@ -43,18 +45,23 @@ class BlockDeviceMapping(extensions.V3APIExtensionBase):
# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' # 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. # parameter as this is placed to handle scheduler_hint extension for V2.1.
def server_create(self, server_dict, create_kwargs, body_deprecated_param): 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: try:
block_device_mapping = [ block_device_mapping = [
block_device.BlockDeviceDict.from_api(bdm_dict) block_device.BlockDeviceDict.from_api(bdm_dict)
for bdm_dict in block_device_mapping] for bdm_dict in bdm]
except (exception.InvalidBDMFormat, except (exception.InvalidBDMFormat,
exception.InvalidBDMVolumeNotBootable) as e: exception.InvalidBDMVolumeNotBootable) as e:
raise exc.HTTPBadRequest(explanation=e.format_message()) 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: 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 create_kwargs['legacy_bdm'] = False

View File

@ -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

View File

@ -551,7 +551,8 @@ class ServersController(wsgi.Controller):
exception.InvalidBDMVolume, exception.InvalidBDMVolume,
exception.InvalidBDMImage, exception.InvalidBDMImage,
exception.InvalidBDMBootSequence, exception.InvalidBDMBootSequence,
exception.InvalidBDMLocalsLimit) as error: exception.InvalidBDMLocalsLimit,
exception.InvalidBDMVolumeNotBootable) as error:
raise exc.HTTPBadRequest(explanation=error.format_message()) raise exc.HTTPBadRequest(explanation=error.format_message())
except (exception.PortInUse, except (exception.PortInUse,
exception.NetworkAmbiguous, exception.NetworkAmbiguous,

View File

@ -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 = """
<server xmlns="http://docs.openstack.org/compute/api/v2"
name="new-server-test" imageRef="1" flavorRef="1">
<block_device_mapping>
<mapping volume_id="7329b667-50c7-46a6-b913-cb2a09dfeee0"
device_name="/dev/vda" virtual_name="root"
delete_on_termination="False" />
<mapping snapshot_id="f31efb24-34d2-43e1-8b44-316052956a39"
device_name="/dev/vdb" virtual_name="ephemeral0"
delete_on_termination="False" />
<mapping device_name="/dev/vdc" no_device="True" />
</block_device_mapping>
</server>"""
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)

View File

@ -23,7 +23,6 @@ import uuid
import iso8601 import iso8601
from lxml import etree from lxml import etree
import mock import mock
import mox
from oslo.config import cfg from oslo.config import cfg
import six.moves.urllib.parse as urlparse import six.moves.urllib.parse as urlparse
import testtools import testtools
@ -2595,23 +2594,6 @@ class ServersControllerCreateTest(test.TestCase):
self.stubs.Set(compute_api.API, 'create', create) self.stubs.Set(compute_api.API, 'create', create)
self._test_create_extra(params) 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): def test_create_instance_with_volumes_enabled_no_image(self):
"""Test that the create will fail if there is no image """Test that the create will fail if there is no image
and no bdms supplied in the request and no bdms supplied in the request
@ -2639,222 +2621,6 @@ class ServersControllerCreateTest(test.TestCase):
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self._test_create_extra, {}, no_image=True) 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): def test_create_instance_with_user_data_enabled(self):
self.ext_mgr.extensions = {'os-user-data': 'fake'} self.ext_mgr.extensions = {'os-user-data': 'fake'}
user_data = 'fake' user_data = 'fake'
@ -3747,46 +3513,6 @@ class TestServerCreateRequestXMLDeserializer(test.TestCase):
}} }}
self.assertEqual(request['body'], expected) self.assertEqual(request['body'], expected)
def test_request_with_block_device_mapping(self):
serial_request = """
<server xmlns="http://docs.openstack.org/compute/api/v2"
name="new-server-test" imageRef="1" flavorRef="1">
<block_device_mapping>
<mapping volume_id="7329b667-50c7-46a6-b913-cb2a09dfeee0"
device_name="/dev/vda" virtual_name="root"
delete_on_termination="False" />
<mapping snapshot_id="f31efb24-34d2-43e1-8b44-316052956a39"
device_name="/dev/vdb" virtual_name="ephemeral0"
delete_on_termination="False" />
<mapping device_name="/dev/vdc" no_device="True" />
</block_device_mapping>
</server>"""
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): def test_request_with_config_drive(self):
serial_request = """ serial_request = """
<server xmlns="http://docs.openstack.org/compute/api/v2" <server xmlns="http://docs.openstack.org/compute/api/v2"

View File

@ -118,6 +118,7 @@ nova.api.v3.extensions.server.create =
access_ips = nova.api.openstack.compute.plugins.v3.access_ips:AccessIPs access_ips = nova.api.openstack.compute.plugins.v3.access_ips:AccessIPs
availability_zone = nova.api.openstack.compute.plugins.v3.availability_zone:AvailabilityZone availability_zone = nova.api.openstack.compute.plugins.v3.availability_zone:AvailabilityZone
block_device_mapping = nova.api.openstack.compute.plugins.v3.block_device_mapping:BlockDeviceMapping block_device_mapping = nova.api.openstack.compute.plugins.v3.block_device_mapping:BlockDeviceMapping
block_device_mapping_v1 = nova.api.openstack.compute.plugins.v3.block_device_mapping_v1:BlockDeviceMappingV1
config_drive = nova.api.openstack.compute.plugins.v3.config_drive:ConfigDrive config_drive = nova.api.openstack.compute.plugins.v3.config_drive:ConfigDrive
keypairs_create = nova.api.openstack.compute.plugins.v3.keypairs:Keypairs keypairs_create = nova.api.openstack.compute.plugins.v3.keypairs:Keypairs
multiple_create = nova.api.openstack.compute.plugins.v3.multiple_create:MultipleCreate multiple_create = nova.api.openstack.compute.plugins.v3.multiple_create:MultipleCreate