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:
parent
606d25ffdc
commit
8353cb0524
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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
|
@ -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,
|
||||||
|
@ -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)
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user