Merge "Pull from cell0 and build_requests for instance list"

This commit is contained in:
Jenkins 2016-09-01 20:54:26 +00:00 committed by Gerrit Code Review
commit f3a1f92a1f
4 changed files with 310 additions and 7 deletions

View File

@ -2300,9 +2300,61 @@ class API(base.Base):
LOG.debug('Removing limit for DB query due to IP filter')
limit = None
instances = self._get_instances_by_filters(context, filters,
limit=limit, marker=marker, expected_attrs=expected_attrs,
sort_keys=sort_keys, sort_dirs=sort_dirs)
# The ordering of instances will be
# [sorted instances with no host] + [sorted instances with host].
# This means BuildRequest and cell0 instances first, then cell
# instances
build_requests = objects.BuildRequestList.get_by_filters(
context, filters, limit=limit, marker=marker, sort_keys=sort_keys,
sort_dirs=sort_dirs)
build_req_instances = objects.InstanceList(
objects=[build_req.instance for build_req in build_requests])
# Only subtract from limit if it is not None
limit = (limit - len(build_req_instances)) if limit else limit
try:
cell0_mapping = objects.CellMapping.get_by_uuid(context,
objects.CellMapping.CELL0_UUID)
except exception.CellMappingNotFound:
cell0_instances = objects.InstanceList(objects=[])
else:
with nova_context.target_cell(context, cell0_mapping):
cell0_instances = self._get_instances_by_filters(
context, filters, limit=limit, marker=marker,
expected_attrs=expected_attrs, sort_keys=sort_keys,
sort_dirs=sort_dirs)
# Only subtract from limit if it is not None
limit = (limit - len(cell0_instances)) if limit else limit
# There is only planned support for a single cell here. Multiple cell
# instance lists should be proxied to project Searchlight, or a similar
# alternative.
if limit is None or limit > 0:
cell_instances = self._get_instances_by_filters(context, filters,
limit=limit, marker=marker, expected_attrs=expected_attrs,
sort_keys=sort_keys, sort_dirs=sort_dirs)
else:
cell_instances = objects.InstanceList(objects=[])
def _get_unique_filter_method():
seen_uuids = set()
def _filter(instance):
if instance.uuid in seen_uuids:
return False
seen_uuids.add(instance.uuid)
return True
return _filter
filter_method = _get_unique_filter_method()
# TODO(alaski): Clean up the objects concatenation when List objects
# support it natively.
instances = objects.InstanceList(
objects=list(filter(filter_method,
build_req_instances.objects +
cell0_instances.objects +
cell_instances.objects)))
if filter_ip:
instances = self._ip_filter(instances, filters, orig_limit)

View File

@ -134,3 +134,42 @@ class ServersPreSchedulingTestCase(test.TestCase):
create_resp.body['server']['id'],
check_response_status=False)
self.assertEqual(404, get_resp.status)
def _test_instance_list_from_buildrequests(self):
image_ref = fake_image.get_valid_image_id()
body = {
'server': {
'name': 'foo',
'imageRef': image_ref,
'flavorRef': '1',
'networks': 'none',
}
}
inst1 = self.api.api_post('servers', body)
body['server']['name'] = 'bar'
inst2 = self.api.api_post('servers', body)
list_resp = self.api.get_servers()
# Default sort is created_at desc, so last created is first
self.assertEqual(2, len(list_resp))
self.assertEqual(inst2.body['server']['id'], list_resp[0]['id'])
self.assertEqual('bar', list_resp[0]['name'])
self.assertEqual(inst1.body['server']['id'], list_resp[1]['id'])
self.assertEqual('foo', list_resp[1]['name'])
# Change the sort order
list_resp = self.api.api_get(
'servers/detail?sort_key=created_at&sort_dir=asc')
list_resp = list_resp.body['servers']
self.assertEqual(2, len(list_resp))
self.assertEqual(inst1.body['server']['id'], list_resp[0]['id'])
self.assertEqual('foo', list_resp[0]['name'])
self.assertEqual(inst2.body['server']['id'], list_resp[1]['id'])
self.assertEqual('bar', list_resp[1]['name'])
def test_instance_list_from_buildrequests(self):
self.useFixture(nova_fixtures.AllServicesCurrent())
self._test_instance_list_from_buildrequests()
def test_instance_list_from_buildrequests_old_service(self):
self._test_instance_list_from_buildrequests()

View File

@ -10256,19 +10256,25 @@ class ComputeAPIIpFilterTestCase(test.NoDBTestCase):
expected_ids = expected_ids[:expected_len]
self.assertEqual(expected_ids, [inst.id for inst in insts])
def test_ip_filtering_no_limit_to_db(self):
@mock.patch.object(objects.CellMapping, 'get_by_uuid',
side_effect=exception.CellMappingNotFound(uuid='fake'))
def test_ip_filtering_no_limit_to_db(self, _mock_cell_map_get):
c = context.get_admin_context()
# Limit is not supplied to the DB when using an IP filter
with mock.patch('nova.objects.InstanceList.get_by_filters') as m_get:
m_get.return_value = objects.InstanceList(objects=[])
self.compute_api.get_all(c, search_opts={'ip': '.10'}, limit=1)
self.assertEqual(1, m_get.call_count)
kwargs = m_get.call_args[1]
self.assertIsNone(kwargs['limit'])
def test_ip_filtering_pass_limit_to_db(self):
@mock.patch.object(objects.CellMapping, 'get_by_uuid',
side_effect=exception.CellMappingNotFound(uuid='fake'))
def test_ip_filtering_pass_limit_to_db(self, _mock_cell_map_get):
c = context.get_admin_context()
# No IP filter, verify that the limit is passed
with mock.patch('nova.objects.InstanceList.get_by_filters') as m_get:
m_get.return_value = objects.InstanceList(objects=[])
self.compute_api.get_all(c, search_opts={}, limit=1)
self.assertEqual(1, m_get.call_count)
kwargs = m_get.call_args[1]

View File

@ -3868,8 +3868,11 @@ class _ComputeAPIUnitTestMixIn(object):
self.assertEqual(ephemeral_size, bdms[2].volume_size)
@mock.patch.object(compute_api.API, '_get_instances_by_filters')
def test_tenant_to_project_conversion(self, mock_get):
mock_get.return_value = []
@mock.patch.object(objects.CellMapping, 'get_by_uuid')
def test_tenant_to_project_conversion(self, mock_cell_map_get, mock_get):
mock_cell_map_get.side_effect = exception.CellMappingNotFound(
uuid='fake')
mock_get.return_value = objects.InstanceList(objects=[])
api = compute_api.API()
api.get_all(self.context, search_opts={'tenant_id': 'foo'})
filters = mock_get.call_args_list[0][0][1]
@ -4241,6 +4244,209 @@ class _ComputeAPIUnitTestMixIn(object):
'security_groups',
'info_cache'])
def _list_of_instances(self, length=1):
instances = []
for i in range(length):
instances.append(
fake_instance.fake_instance_obj(self.context, objects.Instance,
uuid=uuidutils.generate_uuid())
)
return instances
@mock.patch.object(objects.BuildRequestList, 'get_by_filters')
@mock.patch.object(objects.CellMapping, 'get_by_uuid',
side_effect=exception.CellMappingNotFound(uuid='fake'))
def test_get_all_includes_build_requests(self, mock_cell_mapping_get,
mock_buildreq_get):
build_req_instances = self._list_of_instances(2)
build_reqs = [objects.BuildRequest(self.context, instance=instance)
for instance in build_req_instances]
mock_buildreq_get.return_value = objects.BuildRequestList(self.context,
objects=build_reqs)
cell_instances = self._list_of_instances(2)
with mock.patch.object(self.compute_api,
'_get_instances_by_filters') as mock_inst_get:
mock_inst_get.return_value = objects.InstanceList(
self.context, objects=cell_instances)
instances = self.compute_api.get_all(
self.context, search_opts={'foo': 'bar'},
limit=None, marker='fake-marker', sort_keys=['baz'],
sort_dirs=['desc'])
mock_buildreq_get.assert_called_once_with(
self.context, {'foo': 'bar'}, limit=None, marker='fake-marker',
sort_keys=['baz'], sort_dirs=['desc'])
mock_inst_get.assert_called_once_with(
self.context, {'foo': 'bar'}, limit=None, marker='fake-marker',
expected_attrs=None, sort_keys=['baz'], sort_dirs=['desc'])
for i, instance in enumerate(build_req_instances + cell_instances):
self.assertEqual(instance, instances[i])
@mock.patch.object(objects.BuildRequestList, 'get_by_filters')
@mock.patch.object(objects.CellMapping, 'get_by_uuid',
side_effect=exception.CellMappingNotFound(uuid='fake'))
def test_get_all_includes_build_requests_filter_dupes(self,
mock_cell_mapping_get, mock_buildreq_get):
build_req_instances = self._list_of_instances(2)
build_reqs = [objects.BuildRequest(self.context, instance=instance)
for instance in build_req_instances]
mock_buildreq_get.return_value = objects.BuildRequestList(self.context,
objects=build_reqs)
cell_instances = self._list_of_instances(2)
with mock.patch.object(self.compute_api,
'_get_instances_by_filters') as mock_inst_get:
# Insert one of the build_req_instances here so it shows up twice
mock_inst_get.return_value = objects.InstanceList(
self.context, objects=build_req_instances[:1] + cell_instances)
instances = self.compute_api.get_all(
self.context, search_opts={'foo': 'bar'},
limit=None, marker='fake-marker', sort_keys=['baz'],
sort_dirs=['desc'])
mock_buildreq_get.assert_called_once_with(
self.context, {'foo': 'bar'}, limit=None, marker='fake-marker',
sort_keys=['baz'], sort_dirs=['desc'])
mock_inst_get.assert_called_once_with(
self.context, {'foo': 'bar'}, limit=None, marker='fake-marker',
expected_attrs=None, sort_keys=['baz'], sort_dirs=['desc'])
for i, instance in enumerate(build_req_instances + cell_instances):
self.assertEqual(instance, instances[i])
@mock.patch.object(objects.BuildRequestList, 'get_by_filters')
@mock.patch.object(objects.CellMapping, 'get_by_uuid',
side_effect=exception.CellMappingNotFound(uuid='fake'))
def test_get_all_build_requests_decrement_limit(self,
mock_cell_mapping_get,
mock_buildreq_get):
build_req_instances = self._list_of_instances(2)
build_reqs = [objects.BuildRequest(self.context, instance=instance)
for instance in build_req_instances]
mock_buildreq_get.return_value = objects.BuildRequestList(self.context,
objects=build_reqs)
cell_instances = self._list_of_instances(2)
with mock.patch.object(self.compute_api,
'_get_instances_by_filters') as mock_inst_get:
mock_inst_get.return_value = objects.InstanceList(
self.context, objects=cell_instances)
instances = self.compute_api.get_all(
self.context, search_opts={'foo': 'bar'},
limit=10, marker='fake-marker', sort_keys=['baz'],
sort_dirs=['desc'])
mock_buildreq_get.assert_called_once_with(
self.context, {'foo': 'bar'}, limit=10, marker='fake-marker',
sort_keys=['baz'], sort_dirs=['desc'])
mock_inst_get.assert_called_once_with(
self.context, {'foo': 'bar'}, limit=8, marker='fake-marker',
expected_attrs=None, sort_keys=['baz'], sort_dirs=['desc'])
for i, instance in enumerate(build_req_instances + cell_instances):
self.assertEqual(instance, instances[i])
@mock.patch.object(context, 'target_cell')
@mock.patch.object(objects.BuildRequestList, 'get_by_filters',
return_value=objects.BuildRequestList(objects=[]))
@mock.patch.object(objects.CellMapping, 'get_by_uuid')
def test_get_all_includes_cell0(self, mock_cell_mapping_get,
mock_buildreq_get, mock_target_cell):
cell0_instances = self._list_of_instances(2)
cell_instances = self._list_of_instances(2)
cell_mapping = objects.CellMapping()
mock_cell_mapping_get.return_value = cell_mapping
with mock.patch.object(self.compute_api,
'_get_instances_by_filters') as mock_inst_get:
mock_inst_get.side_effect = [objects.InstanceList(
self.context,
objects=cell0_instances),
objects.InstanceList(
self.context,
objects=cell_instances)]
instances = self.compute_api.get_all(
self.context, search_opts={'foo': 'bar'},
limit=10, marker='fake-marker', sort_keys=['baz'],
sort_dirs=['desc'])
mock_target_cell.assert_called_once_with(self.context,
cell_mapping)
inst_get_calls = [mock.call(self.context, {'foo': 'bar'},
limit=10, marker='fake-marker',
expected_attrs=None, sort_keys=['baz'],
sort_dirs=['desc']),
mock.call(self.context, {'foo': 'bar'},
limit=8, marker='fake-marker',
expected_attrs=None, sort_keys=['baz'],
sort_dirs=['desc'])
]
self.assertEqual(2, mock_inst_get.call_count)
mock_inst_get.assert_has_calls(inst_get_calls)
for i, instance in enumerate(cell0_instances + cell_instances):
self.assertEqual(instance, instances[i])
@mock.patch.object(context, 'target_cell')
@mock.patch.object(objects.BuildRequestList, 'get_by_filters')
@mock.patch.object(objects.CellMapping, 'get_by_uuid')
def test_get_all_includes_build_request_cell0(self, mock_cell_mapping_get,
mock_buildreq_get, mock_target_cell):
build_req_instances = self._list_of_instances(2)
build_reqs = [objects.BuildRequest(self.context, instance=instance)
for instance in build_req_instances]
mock_buildreq_get.return_value = objects.BuildRequestList(self.context,
objects=build_reqs)
cell0_instances = self._list_of_instances(2)
cell_instances = self._list_of_instances(2)
cell_mapping = objects.CellMapping()
mock_cell_mapping_get.return_value = cell_mapping
with mock.patch.object(self.compute_api,
'_get_instances_by_filters') as mock_inst_get:
mock_inst_get.side_effect = [objects.InstanceList(
self.context,
objects=cell0_instances),
objects.InstanceList(
self.context,
objects=cell_instances)]
instances = self.compute_api.get_all(
self.context, search_opts={'foo': 'bar'},
limit=10, marker='fake-marker', sort_keys=['baz'],
sort_dirs=['desc'])
mock_target_cell.assert_called_once_with(self.context,
cell_mapping)
inst_get_calls = [mock.call(self.context, {'foo': 'bar'},
limit=8, marker='fake-marker',
expected_attrs=None, sort_keys=['baz'],
sort_dirs=['desc']),
mock.call(self.context, {'foo': 'bar'},
limit=6, marker='fake-marker',
expected_attrs=None, sort_keys=['baz'],
sort_dirs=['desc'])
]
self.assertEqual(2, mock_inst_get.call_count)
mock_inst_get.assert_has_calls(inst_get_calls)
for i, instance in enumerate(build_req_instances +
cell0_instances +
cell_instances):
self.assertEqual(instance, instances[i])
class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase):
def setUp(self):