Cells: cell scheduler anti-affinity filter
Adds a new cell scheduler filter which can use a 'different_cell' scheduler hint similar to the 'different_host' hint. It expects a fully qualified cell path as the value and filters out any matching cells. Change-Id: I27acd19ac9b6ba4ad61a16db3b032e55113a376c Implements: bp cells-scheduler-anti-affinity-filter
This commit is contained in:
parent
cdd56e16fd
commit
596ee8796a
|
@ -0,0 +1,62 @@
|
|||
# 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.
|
||||
|
||||
"""
|
||||
Different cell filter.
|
||||
|
||||
A scheduler hint of 'different_cell' with a value of a full cell name may be
|
||||
specified to route a build away from a particular cell.
|
||||
"""
|
||||
|
||||
import six
|
||||
|
||||
from nova.cells import filters
|
||||
from nova.cells import utils as cells_utils
|
||||
|
||||
|
||||
class DifferentCellFilter(filters.BaseCellFilter):
|
||||
"""Different cell filter. Works by specifying a scheduler hint of
|
||||
'different_cell'. The value should be the full cell path.
|
||||
"""
|
||||
def filter_all(self, cells, filter_properties):
|
||||
"""Override filter_all() which operates on the full list
|
||||
of cells...
|
||||
"""
|
||||
scheduler_hints = filter_properties.get('scheduler_hints')
|
||||
if not scheduler_hints:
|
||||
return cells
|
||||
|
||||
cell_routes = scheduler_hints.get('different_cell')
|
||||
if not cell_routes:
|
||||
return cells
|
||||
if isinstance(cell_routes, six.string_types):
|
||||
cell_routes = [cell_routes]
|
||||
|
||||
if not self.authorized(filter_properties['context']):
|
||||
# No filtering, if not authorized.
|
||||
return cells
|
||||
|
||||
routing_path = filter_properties['routing_path']
|
||||
filtered_cells = []
|
||||
for cell in cells:
|
||||
if not self._cell_state_matches(cell, routing_path, cell_routes):
|
||||
filtered_cells.append(cell)
|
||||
|
||||
return filtered_cells
|
||||
|
||||
def _cell_state_matches(self, cell_state, routing_path, cell_routes):
|
||||
cell_route = routing_path
|
||||
if not cell_state.is_me:
|
||||
cell_route += cells_utils.PATH_CELL_SEP + cell_state.name
|
||||
if cell_route in cell_routes:
|
||||
return True
|
||||
return False
|
|
@ -17,6 +17,7 @@ Unit Tests for cells scheduler filters.
|
|||
"""
|
||||
|
||||
from nova.cells import filters
|
||||
from nova.cells import state
|
||||
from nova import context
|
||||
from nova.db.sqlalchemy import models
|
||||
from nova import test
|
||||
|
@ -30,6 +31,7 @@ class FiltersTestCase(test.NoDBTestCase):
|
|||
filter_classes = filters.all_filters()
|
||||
class_names = [cls.__name__ for cls in filter_classes]
|
||||
self.assertIn("TargetCellFilter", class_names)
|
||||
self.assertIn("DifferentCellFilter", class_names)
|
||||
|
||||
|
||||
class _FilterTestClass(test.NoDBTestCase):
|
||||
|
@ -172,3 +174,57 @@ class TestTargetCellFilter(_FilterTestClass):
|
|||
'cell': 'fake!cell!path',
|
||||
'sched_kwargs': 'meow'}
|
||||
self.assertEqual(expected_info, info)
|
||||
|
||||
|
||||
class TestDifferentCellFilter(_FilterTestClass):
|
||||
filter_cls_name = 'nova.cells.filters.different_cell.DifferentCellFilter'
|
||||
|
||||
def setUp(self):
|
||||
super(TestDifferentCellFilter, self).setUp()
|
||||
# We only load one filter so we know the first one is the one we want
|
||||
self.policy.set_rules({'cells_scheduler_filter:DifferentCellFilter':
|
||||
''})
|
||||
self.cells = [state.CellState('1'),
|
||||
state.CellState('2'),
|
||||
state.CellState('3')]
|
||||
|
||||
def test_missing_scheduler_hints(self):
|
||||
filter_props = {'context': self.context}
|
||||
# No filtering
|
||||
self.assertEqual(self.cells,
|
||||
self._filter_cells(self.cells, filter_props))
|
||||
|
||||
def test_no_different_cell_hint(self):
|
||||
filter_props = {'scheduler_hints': {},
|
||||
'context': self.context}
|
||||
# No filtering
|
||||
self.assertEqual(self.cells,
|
||||
self._filter_cells(self.cells, filter_props))
|
||||
|
||||
def test_different_cell(self):
|
||||
filter_props = {'scheduler_hints': {'different_cell': 'fake!2'},
|
||||
'routing_path': 'fake',
|
||||
'context': self.context}
|
||||
filtered_cells = self._filter_cells(self.cells, filter_props)
|
||||
self.assertEqual(2, len(filtered_cells))
|
||||
self.assertNotIn(self.cells[1], filtered_cells)
|
||||
|
||||
def test_different_multiple_cells(self):
|
||||
filter_props = {'scheduler_hints':
|
||||
{'different_cell': ['fake!1', 'fake!2']},
|
||||
'routing_path': 'fake',
|
||||
'context': self.context}
|
||||
filtered_cells = self._filter_cells(self.cells, filter_props)
|
||||
self.assertEqual(1, len(filtered_cells))
|
||||
self.assertNotIn(self.cells[0], filtered_cells)
|
||||
self.assertNotIn(self.cells[1], filtered_cells)
|
||||
|
||||
def test_different_cell_specified_me_not_authorized(self):
|
||||
self.policy.set_rules({'cells_scheduler_filter:DifferentCellFilter':
|
||||
'!'})
|
||||
filter_props = {'scheduler_hints': {'different_cell': 'fake!2'},
|
||||
'routing_path': 'fake',
|
||||
'context': self.context}
|
||||
# No filtering, because not an admin.
|
||||
self.assertEqual(self.cells,
|
||||
self._filter_cells(self.cells, filter_props))
|
||||
|
|
Loading…
Reference in New Issue