Plumbing for ignoring list_records_by_skipping_down_cells

This patch adds the plumbing required to ignore the value of the
``[api]/list_records_by_skipping_down_cells`` config from the new
microversion if cell_down_support is True. This config will be
considered only if cell_down_support is False in which case we
look at the [api]/list_records_by_skipping_down_cells and
accordingly skip the records from the down cells or generate an
API exception as the response.

The description of list_records_by_skipping_down_cells is updated
in Id9f12532897912b39093f63e9286540d9029edeb when the microversion
is added.

Related to blueprint handling-down-cell

Change-Id: Icbe27c941c9b934f8f1894e9b9da1d34f047e942
This commit is contained in:
Surya Seetharaman 2019-02-06 10:14:12 +01:00 committed by Matt Riedemann
parent 5c6c816d8c
commit 646d575744
5 changed files with 83 additions and 23 deletions

View File

@ -2663,7 +2663,8 @@ class API(base.Base):
sort_dirs)
else:
insts, down_cell_uuids = instance_list.get_instance_objects_sorted(
context, filters, limit, marker, fields, sort_keys, sort_dirs)
context, filters, limit, marker, fields, sort_keys, sort_dirs,
cell_down_support=cell_down_support)
def _get_unique_filter_method():
seen_uuids = set()

View File

@ -91,12 +91,13 @@ class InstanceLister(multi_cell_list.CrossCellLister):
# replicate these for every data type we implement.
def get_instances_sorted(ctx, filters, limit, marker, columns_to_join,
sort_keys, sort_dirs, cell_mappings=None,
batch_size=None):
batch_size=None, cell_down_support=False):
instance_lister = InstanceLister(sort_keys, sort_dirs,
cells=cell_mappings,
batch_size=batch_size)
instance_generator = instance_lister.get_records_sorted(
ctx, filters, limit, marker, columns_to_join=columns_to_join)
ctx, filters, limit, marker, columns_to_join=columns_to_join,
cell_down_support=cell_down_support)
return instance_lister, instance_generator
@ -132,7 +133,7 @@ def get_instance_list_cells_batch_size(limit, cells):
def get_instance_objects_sorted(ctx, filters, limit, marker, expected_attrs,
sort_keys, sort_dirs):
sort_keys, sort_dirs, cell_down_support=False):
"""Return a list of instances and information about down cells.
This returns a tuple of (objects.InstanceList, list(of down cell
@ -161,7 +162,8 @@ def get_instance_objects_sorted(ctx, filters, limit, marker, expected_attrs,
columns_to_join = instance_obj._expected_cols(expected_attrs)
instance_lister, instance_generator = get_instances_sorted(ctx, filters,
limit, marker, columns_to_join, sort_keys, sort_dirs,
cell_mappings=cell_mappings, batch_size=batch_size)
cell_mappings=cell_mappings, batch_size=batch_size,
cell_down_support=cell_down_support)
if 'fault' in expected_attrs:
# We join fault above, so we need to make sure we don't ask

View File

@ -236,8 +236,18 @@ class CrossCellLister(object):
output of this function. Meaning, we will still query $limit from each
database, but only return $limit total results.
:param cell_down_support: True if the API (and caller) support
returning a minimal instance
construct if the relevant cell is
down. If its True, then the value of
CONF.api.list_records_by_skipping_down_cells
is ignored and if its False, results are
either skipped or erred based on the value of
CONF.api.list_records_by_skipping_down_cells.
"""
cell_down_support = kwargs.pop('cell_down_support', False)
if marker:
# A marker identifier was provided from the API. Call this
# the 'global' marker as it determines where we start the
@ -402,10 +412,20 @@ class CrossCellLister(object):
return
if context.is_cell_failure_sentinel(item._db_record):
if not CONF.api.list_records_by_skipping_down_cells:
raise exception.NovaException(
_('Cell %s is not responding but configuration '
'indicates that we should fail.') % item.cell_uuid)
if (not CONF.api.list_records_by_skipping_down_cells and
not cell_down_support):
# Value the config
# ``CONF.api.list_records_by_skipping_down_cells`` only if
# cell_down_support is False and generate the exception
# if CONF.api.list_records_by_skipping_down_cells is False.
# In all other cases the results from the down cell should
# be skipped now to either construct minimal constructs
# later if cell_down_support is True or to simply return
# the skipped results if cell_down_support is False.
raise exception.NovaException(
_('Cell %s is not responding but configuration '
'indicates that we should fail.')
% item.cell_uuid)
LOG.warning('Cell %s is not responding and hence is '
'being omitted from the results',
item.cell_uuid)

View File

@ -5679,7 +5679,7 @@ class _ComputeAPIUnitTestMixIn(object):
cell_down_support=False)
fields = ['metadata', 'info_cache', 'security_groups']
mock_inst_get.assert_called_once_with(self.context, {}, None, None,
fields, None, None)
fields, None, None, cell_down_support=False)
for i, instance in enumerate(cell_instances):
self.assertEqual(instance, insts[i])
mock_get_ims.assert_not_called()
@ -5708,8 +5708,8 @@ class _ComputeAPIUnitTestMixIn(object):
cell_down_support=True)
fields = ['metadata', 'info_cache', 'security_groups']
mock_inst_get.assert_called_once_with(self.context, {},
3, None, fields, None,
None)
3, None, fields, None, None,
cell_down_support=True)
for i, instance in enumerate(partial_instances + full_instances):
self.assertTrue(obj_base.obj_equal_prims(instance, insts[i]))
# With an original limit of 3, and 0 build requests but 2 instances
@ -6019,7 +6019,7 @@ class _ComputeAPIUnitTestMixIn(object):
fields = ['metadata', 'info_cache', 'security_groups']
mock_inst_get.assert_called_once_with(
self.context, {'foo': 'bar'}, None, None,
fields, ['baz'], ['desc'])
fields, ['baz'], ['desc'], cell_down_support=False)
for i, instance in enumerate(build_req_instances + cell_instances):
self.assertEqual(instance, instances[i])
@ -6054,7 +6054,7 @@ class _ComputeAPIUnitTestMixIn(object):
fields = ['metadata', 'info_cache', 'security_groups']
mock_inst_get.assert_called_once_with(
self.context, {'foo': 'bar'}, None, None,
fields, ['baz'], ['desc'])
fields, ['baz'], ['desc'], cell_down_support=False)
for i, instance in enumerate(build_req_instances + cell_instances):
self.assertEqual(instance, instances[i])
@ -6089,7 +6089,7 @@ class _ComputeAPIUnitTestMixIn(object):
fields = ['metadata', 'info_cache', 'security_groups']
mock_inst_get.assert_called_once_with(
self.context, {'foo': 'bar'}, 8, None,
fields, ['baz'], ['desc'])
fields, ['baz'], ['desc'], cell_down_support=False)
for i, instance in enumerate(build_req_instances + cell_instances):
self.assertEqual(instance, instances[i])
@ -6127,7 +6127,7 @@ class _ComputeAPIUnitTestMixIn(object):
mock_inst_get.assert_called_once_with(
mock.ANY, {'foo': 'bar'},
8, None,
fields, ['baz'], ['desc'])
fields, ['baz'], ['desc'], cell_down_support=False)
for i, instance in enumerate(build_req_instances +
cell_instances):
self.assertEqual(instance, instances[i])
@ -6625,7 +6625,7 @@ class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase):
fields = ['metadata', 'info_cache', 'security_groups']
mock_inst_get.assert_called_once_with(
self.context, {'ip': 'fake', 'uuid': ['fake_device_id']},
None, None, fields, ['baz'], ['desc'])
None, None, fields, ['baz'], ['desc'], cell_down_support=False)
@mock.patch.object(neutron_api.API, 'has_substr_port_filtering_extension')
@mock.patch.object(neutron_api.API, 'list_ports')
@ -6653,7 +6653,7 @@ class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase):
fields = ['metadata', 'info_cache', 'security_groups']
mock_inst_get.assert_called_once_with(
self.context, {'ip6': 'fake', 'uuid': ['fake_device_id']},
None, None, fields, ['baz'], ['desc'])
None, None, fields, ['baz'], ['desc'], cell_down_support=False)
@mock.patch.object(neutron_api.API, 'has_substr_port_filtering_extension')
@mock.patch.object(neutron_api.API, 'list_ports')
@ -6688,7 +6688,7 @@ class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase):
mock_inst_get.assert_called_once_with(
self.context, {'ip': 'fake1', 'ip6': 'fake2',
'uuid': ['fake_device_id', 'fake_device_id']},
None, None, fields, ['baz'], ['desc'])
None, None, fields, ['baz'], ['desc'], cell_down_support=False)
@mock.patch.object(neutron_api.API, 'has_substr_port_filtering_extension')
@mock.patch.object(neutron_api.API, 'list_ports')

View File

@ -100,7 +100,8 @@ class TestInstanceList(test.NoDBTestCase):
mock_gi.assert_called_once_with(user_context, {}, None, None, [],
None, None,
cell_mappings=mock_cm.return_value,
batch_size=1000)
batch_size=1000,
cell_down_support=False)
@mock.patch('nova.context.CELLS', new=FAKE_CELLS)
@mock.patch('nova.context.load_cells')
@ -117,7 +118,8 @@ class TestInstanceList(test.NoDBTestCase):
mock_gi.assert_called_once_with(admin_context, {}, None, None, [],
None, None,
cell_mappings=FAKE_CELLS,
batch_size=100)
batch_size=100,
cell_down_support=False)
mock_cm.assert_not_called()
mock_lc.assert_called_once_with()
@ -136,7 +138,8 @@ class TestInstanceList(test.NoDBTestCase):
mock_gi.assert_called_once_with(user_context, {}, None, None, [],
None, None,
cell_mappings=FAKE_CELLS,
batch_size=100)
batch_size=100,
cell_down_support=False)
mock_lc.assert_called_once_with()
@mock.patch('nova.context.CELLS', new=FAKE_CELLS)
@ -156,7 +159,8 @@ class TestInstanceList(test.NoDBTestCase):
mock_gi.assert_called_once_with(admin_context, {}, None, None, [],
None, None,
cell_mappings=FAKE_CELLS,
batch_size=100)
batch_size=100,
cell_down_support=False)
mock_cm.assert_not_called()
mock_lc.assert_called_once_with()
@ -213,6 +217,39 @@ class TestInstanceList(test.NoDBTestCase):
None, [], None, None)
self.assertIn('configuration indicates', six.text_type(exp))
@mock.patch('nova.context.scatter_gather_cells')
def test_get_instances_with_cell_down_support(self, mock_sg):
self.flags(list_records_by_skipping_down_cells=False, group='api')
inst_cell0 = self.insts[uuids.cell0]
# storing the uuids of the instances from the up cell
uuid_initial = [inst['uuid'] for inst in inst_cell0]
def wrap(thing):
return multi_cell_list.RecordWrapper(ctx, self.context, thing)
ctx = nova_context.RequestContext()
instances = [wrap(inst) for inst in inst_cell0]
# creating one up cell and two down cells
ret_val = {}
ret_val[uuids.cell0] = instances
ret_val[uuids.cell1] = [wrap(exception.BuildRequestNotFound(uuid='f'))]
ret_val[uuids.cell2] = [wrap(nova_context.did_not_respond_sentinel)]
mock_sg.return_value = ret_val
# From the new microversion (2.68) if cell_down_support is True
# then CONF.api.list_records_by_skipping_down_cells will be ignored.
# Exception will not be raised even if its False.
obj, res = instance_list.get_instances_sorted(self.context, {}, None,
None, [], None, None,
cell_down_support=True)
uuid_final = [inst['uuid'] for inst in res]
# return the results from the up cell, ignoring the down cell and
# constructing partial results later.
self.assertEqual(uuid_initial, uuid_final)
def test_batch_size_fixed(self):
fixed_size = 200
self.flags(instance_list_cells_batch_strategy='fixed', group='api')