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:
parent
5c6c816d8c
commit
646d575744
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue