Merge extended_volumes extension response into server view builder

As nova extensions have been deprecated already, the goal is to
merge all scattered code into main controller side.
Currently schema and request/response extended code are there
among all extensions.

This commit merges the extended_volumes extension response into server
view builder.

Partially implements: blueprint api-extensions-merge-stein

Change-Id: I412ccfd5495b088c941a819b5946dc56ed86ae22
This commit is contained in:
Surya 2018-08-24 13:50:57 +02:00 committed by Matt Riedemann
parent a899e09cfe
commit c5788293aa
6 changed files with 179 additions and 331 deletions

View File

@ -1,94 +0,0 @@
# Copyright 2013 OpenStack Foundation
#
# 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 Extended Volumes API extension."""
from oslo_log import log as logging
from nova.api.openstack import api_version_request
from nova.api.openstack import wsgi
from nova import context
from nova import objects
LOG = logging.getLogger(__name__)
class ExtendedVolumesController(wsgi.Controller):
def _extend_server(self, server, req, bdms):
volumes_attached = []
for bdm in bdms:
if bdm.get('volume_id'):
volume_attached = {'id': bdm['volume_id']}
if api_version_request.is_supported(req, min_version='2.3'):
volume_attached['delete_on_termination'] = (
bdm['delete_on_termination'])
volumes_attached.append(volume_attached)
# NOTE(mriedem): The os-extended-volumes prefix should not be used for
# new attributes after v2.1. They are only in v2.1 for backward compat
# with v2.0.
key = "os-extended-volumes:volumes_attached"
server[key] = volumes_attached
@wsgi.extends
def show(self, req, resp_obj, id):
context = req.environ['nova.context']
server = resp_obj.obj['server']
bdms = objects.BlockDeviceMappingList.bdms_by_instance_uuid(
context, [server['id']])
instance_bdms = self._get_instance_bdms(bdms, server)
self._extend_server(server, req, instance_bdms)
@staticmethod
def _get_instance_bdms_in_multiple_cells(ctxt, servers):
instance_uuids = [server['id'] for server in servers]
inst_maps = objects.InstanceMappingList.get_by_instance_uuids(
ctxt, instance_uuids)
cell_mappings = {}
for inst_map in inst_maps:
if (inst_map.cell_mapping is not None and
inst_map.cell_mapping.uuid not in cell_mappings):
cell_mappings.update(
{inst_map.cell_mapping.uuid: inst_map.cell_mapping})
bdms = {}
results = context.scatter_gather_cells(
ctxt, cell_mappings.values(), context.CELL_TIMEOUT,
objects.BlockDeviceMappingList.bdms_by_instance_uuid,
instance_uuids)
for cell_uuid, result in results.items():
if result is context.raised_exception_sentinel:
LOG.warning('Failed to get block device mappings for cell %s',
cell_uuid)
elif result is context.did_not_respond_sentinel:
LOG.warning('Timeout getting block device mappings for cell '
'%s', cell_uuid)
else:
bdms.update(result)
return bdms
@wsgi.extends
def detail(self, req, resp_obj):
context = req.environ['nova.context']
servers = list(resp_obj.obj['servers'])
bdms = self._get_instance_bdms_in_multiple_cells(context, servers)
for server in servers:
instance_bdms = self._get_instance_bdms(bdms, server)
self._extend_server(server, req, instance_bdms)
def _get_instance_bdms(self, bdms, server):
# server['id'] is guaranteed to be in the cache due to
# the core API adding it in the 'detail' or 'show' method.
# If that instance has since been deleted, it won't be in the
# 'bdms' dictionary though, so use 'get' to avoid KeyErrors.
return bdms.get(server['id'], [])

View File

@ -35,7 +35,6 @@ from nova.api.openstack.compute import consoles
from nova.api.openstack.compute import create_backup
from nova.api.openstack.compute import deferred_delete
from nova.api.openstack.compute import evacuate
from nova.api.openstack.compute import extended_volumes
from nova.api.openstack.compute import extension_info
from nova.api.openstack.compute import fixed_ips
from nova.api.openstack.compute import flavor_access
@ -265,7 +264,6 @@ security_group_rules_controller = functools.partial(_create_controller,
server_controller = functools.partial(_create_controller,
servers.ServersController,
[
extended_volumes.ExtendedVolumesController,
hide_server_addresses.Controller,
],
[

View File

@ -725,7 +725,8 @@ class ServersController(wsgi.Controller):
show_keypair=False,
show_srv_usg=False,
show_sec_grp=False,
show_extended_status=False)
show_extended_status=False,
show_extended_volumes=False)
except exception.InstanceNotFound:
msg = _("Instance could not be found")
raise exc.HTTPNotFound(explanation=msg)
@ -1001,7 +1002,8 @@ class ServersController(wsgi.Controller):
show_keypair=show_keypair,
show_srv_usg=False,
show_sec_grp=False,
show_extended_status=False)
show_extended_status=False,
show_extended_volumes=False)
# Add on the admin_password attribute since the view doesn't do it
# unless instance passwords are disabled

View File

@ -92,7 +92,7 @@ class ViewBuilder(common.ViewBuilder):
def basic(self, request, instance, show_extra_specs=False,
show_extended_attr=None, show_host_status=None,
show_sec_grp=None):
show_sec_grp=None, bdms=None):
"""Generic, non-detailed view of an instance."""
return {
"server": {
@ -128,7 +128,8 @@ class ViewBuilder(common.ViewBuilder):
show_extra_specs=None, show_AZ=True, show_config_drive=True,
show_extended_attr=None, show_host_status=None,
show_keypair=True, show_srv_usg=True, show_sec_grp=True,
show_extended_status=True):
show_extended_status=True, show_extended_volumes=True,
bdms=None):
"""Detailed view of a single instance."""
ip_v4 = instance.get('access_ip_v4')
ip_v6 = instance.get('access_ip_v6')
@ -239,6 +240,18 @@ class ViewBuilder(common.ViewBuilder):
# compat with v2.0.
key = "%s:%s" % ('OS-EXT-STS', state)
server["server"][key] = instance[state]
if show_extended_volumes:
# NOTE(mriedem): The os-extended-volumes prefix should not be used
# for new attributes after v2.1. They are only in v2.1 for backward
# compat with v2.0.
add_delete_on_termination = api_version_request.is_supported(
request, min_version='2.3')
if bdms is None:
bdms = objects.BlockDeviceMappingList.bdms_by_instance_uuid(
context, [instance["uuid"]])
self._add_volumes_attachments(server["server"],
bdms,
add_delete_on_termination)
if (api_version_request.is_supported(request, min_version='2.16')):
if show_host_status is None:
show_host_status = context.can(
@ -292,6 +305,11 @@ class ViewBuilder(common.ViewBuilder):
if (api_version_request.is_supported(request, min_version='2.16')):
show_host_status = context.can(
servers_policies.SERVERS % 'show:host_status', fatal=False)
instance_uuids = [inst['uuid'] for inst in instances]
bdms = self._get_instance_bdms_in_multiple_cells(context,
instance_uuids)
# NOTE(gmann): pass show_sec_grp=False in _list_view() because
# security groups for detail method will be added by separate
# call to self._add_security_grps by passing the all servers
@ -300,7 +318,8 @@ class ViewBuilder(common.ViewBuilder):
coll_name, show_extra_specs,
show_extended_attr=show_extended_attr,
show_host_status=show_host_status,
show_sec_grp=False)
show_sec_grp=False,
bdms=bdms)
self._add_security_grps(request, list(servers_dict["servers"]),
instances)
@ -308,7 +327,7 @@ class ViewBuilder(common.ViewBuilder):
def _list_view(self, func, request, servers, coll_name, show_extra_specs,
show_extended_attr=None, show_host_status=None,
show_sec_grp=False):
show_sec_grp=False, bdms=None):
"""Provide a view for a list of servers.
:param func: Function used to format the server data
@ -322,13 +341,14 @@ class ViewBuilder(common.ViewBuilder):
the response dict.
:param show_sec_grp: If the security group should be included in
the response dict.
:param bdms: Instances bdms info from multiple cells.
:returns: Server data in dictionary format
"""
server_list = [func(request, server,
show_extra_specs=show_extra_specs,
show_extended_attr=show_extended_attr,
show_host_status=show_host_status,
show_sec_grp=show_sec_grp)["server"]
show_sec_grp=show_sec_grp, bdms=bdms)["server"]
for server in servers]
servers_links = self._get_collection_links(request,
servers,
@ -496,3 +516,53 @@ class ViewBuilder(common.ViewBuilder):
# request add default since that is the group it is part of
servers[0]['security_groups'] = req_obj['server'].get(
'security_groups', [{'name': 'default'}])
@staticmethod
def _get_instance_bdms_in_multiple_cells(ctxt, instance_uuids):
inst_maps = objects.InstanceMappingList.get_by_instance_uuids(
ctxt, instance_uuids)
cell_mappings = {}
for inst_map in inst_maps:
if (inst_map.cell_mapping is not None and
inst_map.cell_mapping.uuid not in cell_mappings):
cell_mappings.update(
{inst_map.cell_mapping.uuid: inst_map.cell_mapping})
bdms = {}
results = nova_context.scatter_gather_cells(
ctxt, cell_mappings.values(),
nova_context.CELL_TIMEOUT,
objects.BlockDeviceMappingList.bdms_by_instance_uuid,
instance_uuids)
for cell_uuid, result in results.items():
if result is nova_context.raised_exception_sentinel:
LOG.warning('Failed to get block device mappings for cell %s',
cell_uuid)
elif result is nova_context.did_not_respond_sentinel:
LOG.warning('Timeout getting block device mappings for cell '
'%s', cell_uuid)
else:
bdms.update(result)
return bdms
def _add_volumes_attachments(self, server, bdms,
add_delete_on_termination):
# server['id'] is guaranteed to be in the cache due to
# the core API adding it in the 'detail' or 'show' method.
# If that instance has since been deleted, it won't be in the
# 'bdms' dictionary though, so use 'get' to avoid KeyErrors.
instance_bdms = bdms.get(server['id'], [])
volumes_attached = []
for bdm in instance_bdms:
if bdm.get('volume_id'):
volume_attached = {'id': bdm['volume_id']}
if add_delete_on_termination:
volume_attached['delete_on_termination'] = (
bdm['delete_on_termination'])
volumes_attached.append(volume_attached)
# NOTE(mriedem): The os-extended-volumes prefix should not be used for
# new attributes after v2.1. They are only in v2.1 for backward compat
# with v2.0.
key = "os-extended-volumes:volumes_attached"
server[key] = volumes_attached

View File

@ -1,222 +0,0 @@
# 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.
import mock
from oslo_serialization import jsonutils
from oslo_utils.fixture import uuidsentinel as uuids
from nova.api.openstack.compute import (extended_volumes
as extended_volumes_v21)
from nova.api.openstack import wsgi as os_wsgi
from nova import context as nova_context
from nova import objects
from nova.objects import instance as instance_obj
from nova import test
from nova.tests.unit.api.openstack import fakes
from nova.tests.unit import fake_block_device
from nova.tests.unit import fake_instance
UUID1 = '00000000-0000-0000-0000-000000000001'
UUID2 = '00000000-0000-0000-0000-000000000002'
UUID3 = '00000000-0000-0000-0000-000000000003'
def fake_compute_get(*args, **kwargs):
inst = fakes.stub_instance(1, uuid=UUID1)
return fake_instance.fake_instance_obj(args[1], **inst)
def fake_compute_get_all(*args, **kwargs):
db_list = [
fakes.stub_instance(1, uuid=UUID1),
fakes.stub_instance(2, uuid=UUID2),
]
fields = instance_obj.INSTANCE_DEFAULT_FIELDS
return instance_obj._make_instance_list(args[1],
objects.InstanceList(),
db_list, fields)
def fake_bdms_get_all_by_instance_uuids(*args, **kwargs):
return [
fake_block_device.FakeDbBlockDeviceDict({
'id': 1,
'volume_id': 'some_volume_1',
'instance_uuid': UUID1,
'source_type': 'volume',
'destination_type': 'volume',
'delete_on_termination': True,
}),
fake_block_device.FakeDbBlockDeviceDict({
'id': 2,
'volume_id': 'some_volume_2',
'instance_uuid': UUID2,
'source_type': 'volume',
'destination_type': 'volume',
'delete_on_termination': False,
}),
fake_block_device.FakeDbBlockDeviceDict({
'id': 3,
'volume_id': 'some_volume_3',
'instance_uuid': UUID2,
'source_type': 'volume',
'destination_type': 'volume',
'delete_on_termination': False,
}),
]
class ExtendedVolumesTestV21(test.TestCase):
content_type = 'application/json'
prefix = 'os-extended-volumes:'
exp_volumes_show = [{'id': 'some_volume_1'}]
exp_volumes_detail = [
[{'id': 'some_volume_1'}],
[{'id': 'some_volume_2'}, {'id': 'some_volume_3'}],
]
wsgi_api_version = os_wsgi.DEFAULT_API_VERSION
def setUp(self):
super(ExtendedVolumesTestV21, self).setUp()
fakes.stub_out_nw_api(self)
fakes.stub_out_secgroup_api(self)
self.stub_out('nova.compute.api.API.get', fake_compute_get)
self.stub_out('nova.compute.api.API.get_all', fake_compute_get_all)
self.stub_out(
'nova.db.api.block_device_mapping_get_all_by_instance_uuids',
fake_bdms_get_all_by_instance_uuids)
self._setUp()
self.app = self._setup_app()
return_server = fakes.fake_instance_get()
self.stub_out('nova.db.api.instance_get_by_uuid', return_server)
def _setup_app(self):
return fakes.wsgi_app_v21()
def _setUp(self):
self.Controller = extended_volumes_v21.ExtendedVolumesController()
self.stub_out('nova.volume.cinder.API.get', lambda *a, **k: None)
def _make_request(self, url, body=None):
req = fakes.HTTPRequest.blank('/v2/fake/servers' + url)
req.headers['Accept'] = self.content_type
req.headers = {os_wsgi.API_VERSION_REQUEST_HEADER:
'compute %s' % self.wsgi_api_version}
if body:
req.body = jsonutils.dump_as_bytes(body)
req.method = 'POST'
req.content_type = self.content_type
res = req.get_response(self.app)
return res
def _get_server(self, body):
return jsonutils.loads(body).get('server')
def _get_servers(self, body):
return jsonutils.loads(body).get('servers')
def test_show(self):
res = self._make_request('/%s' % UUID1)
self.assertEqual(200, res.status_int)
server = self._get_server(res.body)
actual = server.get('%svolumes_attached' % self.prefix)
self.assertEqual(self.exp_volumes_show, actual)
@mock.patch.object(objects.InstanceMappingList, 'get_by_instance_uuids')
def test_detail(self, mock_get_by_instance_uuids):
mock_get_by_instance_uuids.return_value = [
objects.InstanceMapping(
instance_uuid=UUID1,
cell_mapping=objects.CellMapping(
uuid=uuids.cell1,
transport_url='fake://nowhere/',
database_connection=uuids.cell1)),
objects.InstanceMapping(
instance_uuid=UUID2,
cell_mapping=objects.CellMapping(
uuid=uuids.cell1,
transport_url='fake://nowhere/',
database_connection=uuids.cell1))]
res = self._make_request('/detail')
mock_get_by_instance_uuids.assert_called_once_with(
test.MatchType(nova_context.RequestContext), [UUID1, UUID2])
self.assertEqual(200, res.status_int)
for i, server in enumerate(self._get_servers(res.body)):
actual = server.get('%svolumes_attached' % self.prefix)
self.assertEqual(self.exp_volumes_detail[i], actual)
@mock.patch.object(objects.InstanceMappingList, 'get_by_instance_uuids')
@mock.patch('nova.context.scatter_gather_cells')
def test_detail_with_cell_failures(self, mock_sg,
mock_get_by_instance_uuids):
mock_get_by_instance_uuids.return_value = [
objects.InstanceMapping(
instance_uuid=UUID1,
cell_mapping=objects.CellMapping(
uuid=uuids.cell1,
transport_url='fake://nowhere/',
database_connection=uuids.cell1)),
objects.InstanceMapping(
instance_uuid=UUID2,
cell_mapping=objects.CellMapping(
uuid=uuids.cell2,
transport_url='fake://nowhere/',
database_connection=uuids.cell2))
]
bdm = fake_bdms_get_all_by_instance_uuids()
fake_bdm = fake_block_device.fake_bdm_object(
nova_context.RequestContext, bdm[0])
mock_sg.return_value = {
uuids.cell1: {UUID1: [fake_bdm]},
uuids.cell2: nova_context.raised_exception_sentinel
}
res = self._make_request('/detail')
mock_get_by_instance_uuids.assert_called_once_with(
test.MatchType(nova_context.RequestContext), [UUID1, UUID2])
self.assertEqual(200, res.status_int)
# we would get an empty list for the second instance
# which is in the down cell, however this would printed
# in the logs.
for i, server in enumerate(self._get_servers(res.body)):
actual = server.get('%svolumes_attached' % self.prefix)
if i == 0:
self.assertEqual(self.exp_volumes_detail[i], actual)
else:
self.assertEqual([], actual)
class ExtendedVolumesTestV23(ExtendedVolumesTestV21):
exp_volumes_show = [
{'id': 'some_volume_1', 'delete_on_termination': True},
]
exp_volumes_detail = [
[
{'id': 'some_volume_1', 'delete_on_termination': True},
],
[
{'id': 'some_volume_2', 'delete_on_termination': False},
{'id': 'some_volume_3', 'delete_on_termination': False},
],
]
wsgi_api_version = '2.3'

View File

@ -63,6 +63,7 @@ from nova import policy
from nova import test
from nova.tests import fixtures as nova_fixtures
from nova.tests.unit.api.openstack import fakes
from nova.tests.unit import fake_block_device
from nova.tests.unit import fake_flavor
from nova.tests.unit import fake_instance
from nova.tests.unit import fake_network
@ -147,6 +148,40 @@ def fake_compute_get_empty_az(*args, **kwargs):
return fake_instance.fake_instance_obj(args[1], **inst)
def fake_bdms_get_all_by_instance_uuids(*args, **kwargs):
return [
fake_block_device.FakeDbBlockDeviceDict({
'id': 1,
'volume_id': 'some_volume_1',
'instance_uuid': FAKE_UUID,
'source_type': 'volume',
'destination_type': 'volume',
'delete_on_termination': True,
}),
fake_block_device.FakeDbBlockDeviceDict({
'id': 2,
'volume_id': 'some_volume_2',
'instance_uuid': FAKE_UUID,
'source_type': 'volume',
'destination_type': 'volume',
'delete_on_termination': False,
}),
]
def fake_get_inst_mappings_by_instance_uuids_from_db(*args, **kwargs):
return [{
'id': 1,
'instance_uuid': UUID1,
'cell_mapping': {
'id': 1, 'uuid': uuids.cell1, 'name': 'fake',
'transport_url': 'fake://nowhere/', 'updated_at': None,
'database_connection': uuids.cell1, 'created_at': None,
'disabled': False},
'project_id': 'fake-project'
}]
class MockSetAdminPassword(object):
def __init__(self):
self.instance_id = None
@ -190,6 +225,12 @@ class ControllerTest(test.TestCase):
compute_api.API, 'get', side_effect=return_server)).mock
self.stub_out('nova.db.api.instance_update_and_get_original',
instance_update_and_get_original)
self.stub_out('nova.db.api.'
'block_device_mapping_get_all_by_instance_uuids',
fake_bdms_get_all_by_instance_uuids)
self.stub_out('nova.objects.InstanceMappingList.'
'_get_by_instance_uuids_from_db',
fake_get_inst_mappings_by_instance_uuids_from_db)
self.flags(group='glance', api_servers=['http://localhost:9292'])
self.controller = servers.ServersController()
@ -410,7 +451,11 @@ class ServersControllerTest(ControllerTest):
{'name': 'fake-0-1'}],
"OS-EXT-STS:task_state": None,
"OS-EXT-STS:vm_state": vm_states.ACTIVE,
"OS-EXT-STS:power_state": 1
"OS-EXT-STS:power_state": 1,
"os-extended-volumes:volumes_attached": [
{'id': 'some_volume_1'},
{'id': 'some_volume_2'},
]
}
}
@ -1560,6 +1605,9 @@ class ServersControllerTestV23(ServersControllerTest):
server_dict['server']["OS-EXT-STS:task_state"] = None
server_dict['server']["OS-EXT-STS:vm_state"] = vm_states.ACTIVE
server_dict['server']["OS-EXT-STS:power_state"] = 1
server_dict['server']["os-extended-volumes:volumes_attached"] = [
{'id': 'some_volume_1', 'delete_on_termination': True},
{'id': 'some_volume_2', 'delete_on_termination': False}]
return server_dict
def test_show(self):
@ -1674,6 +1722,9 @@ class ServersControllerTestV29(ServersControllerTest):
server_dict['server']["OS-EXT-STS:task_state"] = None
server_dict['server']["OS-EXT-STS:vm_state"] = vm_states.ACTIVE
server_dict['server']["OS-EXT-STS:power_state"] = 1
server_dict['server']["os-extended-volumes:volumes_attached"] = [
{'id': 'some_volume_1', 'delete_on_termination': True},
{'id': 'some_volume_2', 'delete_on_termination': False}]
return server_dict
def _test_get_server_with_lock(self, locked_by):
@ -1845,6 +1896,9 @@ class ServersControllerTestV216(ServersControllerTest):
server_dict['server']["OS-EXT-STS:task_state"] = None
server_dict['server']["OS-EXT-STS:vm_state"] = vm_states.ACTIVE
server_dict['server']["OS-EXT-STS:power_state"] = 1
server_dict['server']["os-extended-volumes:volumes_attached"] = [
{'id': 'some_volume_1', 'delete_on_termination': True},
{'id': 'some_volume_2', 'delete_on_termination': False}]
return server_dict
@ -1963,6 +2017,9 @@ class ServersControllerTestV219(ServersControllerTest):
server_dict['server']["OS-EXT-STS:task_state"] = None
server_dict['server']["OS-EXT-STS:vm_state"] = vm_states.ACTIVE
server_dict['server']["OS-EXT-STS:power_state"] = 1
server_dict['server']["os-extended-volumes:volumes_attached"] = [
{'id': 'some_volume_1', 'delete_on_termination': True},
{'id': 'some_volume_2', 'delete_on_termination': False}]
return server_dict
@ -6309,7 +6366,7 @@ class ServersViewBuilderTest(test.TestCase):
db_inst = fakes.stub_instance(
id=1,
image_ref="5",
uuid="deadbeef-feed-edee-beef-d0ea7beefedd",
uuid=FAKE_UUID,
display_name="test_server",
include_fake_metadata=False,
availability_zone='nova',
@ -6339,6 +6396,12 @@ class ServersViewBuilderTest(test.TestCase):
'ips': [dict(ip=ip) for ip in privates]})]
fakes.stub_out_nw_api_get_instance_nw_info(self, nw_info)
self.stub_out('nova.db.api.'
'block_device_mapping_get_all_by_instance_uuids',
fake_bdms_get_all_by_instance_uuids)
self.stub_out('nova.objects.InstanceMappingList.'
'_get_by_instance_uuids_from_db',
fake_get_inst_mappings_by_instance_uuids_from_db)
self.uuid = db_inst['uuid']
self.view_builder = views.servers.ViewBuilder()
@ -6393,6 +6456,21 @@ class ServersViewBuilderTest(test.TestCase):
False)
self.assertEqual(result, expected)
@mock.patch('nova.context.scatter_gather_cells')
def test_get_volumes_attached_with_faily_cells(self, mock_sg):
bdms = fake_bdms_get_all_by_instance_uuids()
# just faking a nova list scenario
mock_sg.return_value = {
uuids.cell1: bdms[0],
uuids.cell2: context.raised_exception_sentinel
}
ctxt = context.RequestContext('fake', 'fake')
result = self.view_builder._get_instance_bdms_in_multiple_cells(
ctxt, [self.instance.uuid])
# will get the result from cell1
self.assertEqual(result, bdms[0])
mock_sg.assert_called_once()
def test_build_server(self):
expected_server = {
"server": {
@ -6511,7 +6589,11 @@ class ServersViewBuilderTest(test.TestCase):
{'name': 'fake-0-1'}],
"OS-EXT-STS:task_state": None,
"OS-EXT-STS:vm_state": vm_states.ACTIVE,
"OS-EXT-STS:power_state": 1
"OS-EXT-STS:power_state": 1,
"os-extended-volumes:volumes_attached": [
{'id': 'some_volume_1'},
{'id': 'some_volume_2'},
]
}
}
@ -6603,7 +6685,11 @@ class ServersViewBuilderTest(test.TestCase):
{'name': 'fake-0-1'}],
"OS-EXT-STS:task_state": None,
"OS-EXT-STS:vm_state": vm_states.ERROR,
"OS-EXT-STS:power_state": 1
"OS-EXT-STS:power_state": 1,
"os-extended-volumes:volumes_attached": [
{'id': 'some_volume_1'},
{'id': 'some_volume_2'},
]
}
}
@ -6794,7 +6880,11 @@ class ServersViewBuilderTest(test.TestCase):
{'name': 'fake-0-1'}],
"OS-EXT-STS:task_state": None,
"OS-EXT-STS:vm_state": vm_states.ACTIVE,
"OS-EXT-STS:power_state": 1
"OS-EXT-STS:power_state": 1,
"os-extended-volumes:volumes_attached": [
{'id': 'some_volume_1'},
{'id': 'some_volume_2'},
]
}
}
@ -6883,7 +6973,11 @@ class ServersViewBuilderTest(test.TestCase):
{'name': 'fake-0-1'}],
"OS-EXT-STS:task_state": None,
"OS-EXT-STS:vm_state": vm_states.ACTIVE,
"OS-EXT-STS:power_state": 1
"OS-EXT-STS:power_state": 1,
"os-extended-volumes:volumes_attached": [
{'id': 'some_volume_1'},
{'id': 'some_volume_2'},
]
}
}