Merge "Use dict object for request_specs_dict in the _list_view"
This commit is contained in:
@@ -5721,6 +5721,7 @@ pinned_availability_zone:
|
||||
Also when default_schedule_zone config option set to specific AZ, in that
|
||||
case, instance would be pinned to that specific AZ, and instance will be
|
||||
scheduled on host belonging to pinned AZ.
|
||||
In case of no pinned availability zone, this value is set to `null`.
|
||||
in: body
|
||||
type: string
|
||||
min_version: 2.96
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
@@ -41,6 +39,7 @@ from nova import utils
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
AZ_NOT_IN_REQUEST_SPEC = object()
|
||||
SCHED_HINTS_NOT_IN_REQUEST_SPEC = object()
|
||||
|
||||
|
||||
@@ -225,16 +224,20 @@ class ViewBuilder(common.ViewBuilder):
|
||||
return unknown_only
|
||||
|
||||
def _get_pinned_az(self, context, instance, provided_az):
|
||||
pinned_az = ''
|
||||
if provided_az is not None:
|
||||
if provided_az is AZ_NOT_IN_REQUEST_SPEC:
|
||||
# Case the provided_az is pre fetched, but not specified
|
||||
pinned_az = None
|
||||
elif provided_az is not None:
|
||||
# Case the provided_az is pre fetched, and specified
|
||||
pinned_az = provided_az
|
||||
else:
|
||||
# Case the provided_az is not pre fethed.
|
||||
try:
|
||||
req_spec = objects.RequestSpec.get_by_instance_uuid(
|
||||
context, instance.uuid)
|
||||
pinned_az = req_spec.availability_zone
|
||||
except exception.RequestSpecNotFound:
|
||||
pinned_az = ''
|
||||
pinned_az = None
|
||||
return pinned_az
|
||||
|
||||
def _get_scheduler_hints(self, context, instance, provided_sched_hints):
|
||||
@@ -543,15 +546,16 @@ class ViewBuilder(common.ViewBuilder):
|
||||
:returns: Server data in dictionary format
|
||||
"""
|
||||
req_specs = None
|
||||
req_specs_dict = collections.defaultdict(str)
|
||||
req_specs_dict = {}
|
||||
sched_hints_dict = {}
|
||||
if api_version_request.is_supported(request, min_version='2.96'):
|
||||
context = request.environ['nova.context']
|
||||
instance_uuids = [s.uuid for s in servers]
|
||||
req_specs = objects.RequestSpec.get_by_instance_uuids(
|
||||
context, instance_uuids)
|
||||
req_specs_dict = {req.instance_uuid: req.availability_zone
|
||||
for req in req_specs}
|
||||
req_specs_dict.update({req.instance_uuid: req.availability_zone
|
||||
for req in req_specs
|
||||
if req.availability_zone is not None})
|
||||
if api_version_request.is_supported(request, min_version='2.100'):
|
||||
sched_hints_dict.update({
|
||||
req.instance_uuid: req.scheduler_hints
|
||||
@@ -565,7 +569,8 @@ class ViewBuilder(common.ViewBuilder):
|
||||
show_host_status=show_host_status,
|
||||
show_sec_grp=show_sec_grp, bdms=bdms,
|
||||
cell_down_support=cell_down_support,
|
||||
provided_az=req_specs_dict[server.uuid],
|
||||
provided_az=req_specs_dict.get(
|
||||
server.uuid, AZ_NOT_IN_REQUEST_SPEC),
|
||||
provided_sched_hints=sched_hints_dict.get(
|
||||
server.uuid, SCHED_HINTS_NOT_IN_REQUEST_SPEC)
|
||||
)["server"]
|
||||
|
||||
@@ -8466,8 +8466,74 @@ class ServersViewBuilderTestV296(_ServersViewBuilderTest):
|
||||
availability_zone=self.instance.availability_zone)]
|
||||
|
||||
req = self.req('/%s/servers' % self.project_id)
|
||||
self.assertRaises(KeyError, self.view_builder.index,
|
||||
req, self.instances, False)
|
||||
output = self.view_builder.index(req, self.instances, False)
|
||||
|
||||
self.assertEqual(2, len(output['servers']))
|
||||
|
||||
@mock.patch('nova.objects.RequestSpec.get_by_instance_uuids')
|
||||
def test_list_detail_view_with_missing_request_specs(self, m_rs):
|
||||
|
||||
self.instances = [
|
||||
self.instance,
|
||||
self.create_instance(2, uuids.fake1, 'fake-server'),
|
||||
self.create_instance(3, uuids.fake2, 'fake-server2')
|
||||
]
|
||||
# First instance's request spec has pinned availability zone
|
||||
# Second instance's request spec has no pinned availability zone
|
||||
m_rs.return_value = [
|
||||
objects.RequestSpec(
|
||||
instance_uuid=self.instance.uuid,
|
||||
availability_zone=self.instance.availability_zone),
|
||||
objects.RequestSpec(
|
||||
instance_uuid=self.instance.uuid,
|
||||
availability_zone=None)
|
||||
]
|
||||
|
||||
req = self.req('/%s/servers/detail' % self.project_id)
|
||||
output = self.view_builder.detail(req, self.instances, False)
|
||||
|
||||
self.assertEqual(3, len(output['servers']))
|
||||
# first instance has pinned az
|
||||
self.assertEqual('nova',
|
||||
output['servers'][0]['pinned_availability_zone'])
|
||||
# second or later has no pinned az
|
||||
for s in output['servers'][1:]:
|
||||
self.assertIsNone(s['pinned_availability_zone'])
|
||||
|
||||
@mock.patch('nova.objects.RequestSpec.get_by_instance_uuid')
|
||||
def test_show_view_with_missing_request_specs(self, m_rs):
|
||||
|
||||
self.instances = [
|
||||
self.instance,
|
||||
self.create_instance(2, uuids.fake1, 'fake-server'),
|
||||
self.create_instance(3, uuids.fake2, 'fake-server2')
|
||||
]
|
||||
# First instance's request spec has pinned availability zone
|
||||
# Second instance's request spec has no pinned availability zone
|
||||
m_rs.side_effect = [
|
||||
objects.RequestSpec(
|
||||
instance_uuid=self.instance.uuid,
|
||||
availability_zone=self.instance.availability_zone),
|
||||
objects.RequestSpec(
|
||||
instance_uuid=self.instance.uuid,
|
||||
availability_zone=None),
|
||||
exception.RequestSpecNotFound(instance_uuid='3')
|
||||
]
|
||||
|
||||
# Instance show with request spec and pinned az
|
||||
req = self.req('/%s/servers/1' % self.project_id)
|
||||
output = self.view_builder.show(req, self.instances[0])
|
||||
self.assertEqual('nova', output['server']['pinned_availability_zone'])
|
||||
|
||||
# Instance show with request spec and no pinned az
|
||||
req = self.req('/%s/servers/2' % self.project_id)
|
||||
output = self.view_builder.show(req, self.instances[1])
|
||||
self.assertIsNone(output['server']['pinned_availability_zone'])
|
||||
|
||||
# Instance show without request spec
|
||||
req = self.req('/%s/servers/3' % self.project_id)
|
||||
output = self.view_builder.show(req, self.instances[2])
|
||||
self.assertIsNone(output['server']['pinned_availability_zone'])
|
||||
|
||||
|
||||
class ServersViewBuilderTestV2100(_ServersViewBuilderTest):
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
`Bug #2095364`_: Fixed the List Server API and the List Server Detail API
|
||||
500 Internal Server Error issue in v2.96 or later API microversion if
|
||||
one or more instance has no request spec object. One usecase was when cloud
|
||||
user tried to create instance which exceeded their quota, the request does
|
||||
not create instance request spec. Once the no request spec instance is
|
||||
created in cloud user project, the server list API and the list server
|
||||
details API return 500 Internal Server Error for the project until the
|
||||
cloud user deletes the no request spec object instance.
|
||||
After this fix, the v2.96 or later returns `null` at the
|
||||
`pinned_availability_zone` value if not specified.
|
||||
|
||||
.. _Bug #2095364: https://launchpad.net/bugs/2095364
|
||||
Reference in New Issue
Block a user