Add support for best-effort accommodations
Adds a new accommodation instruction 'best_effort' that allows the deployment to proceed even if the number of available compute nodes is less than what was requested. For example, this will create a single pair of agents on separate compute nodes if possible, but will fall back to creating them on the same compute node if only one is available: accommodation: pair, single_room, best_effort, compute_nodes: 2 Change-Id: I1986f550ac0dc934bfa5db84b40a6419d6608c7e Closes-Bug: 1807630
This commit is contained in:
parent
249ad453a4
commit
3d1d37b96a
@ -89,6 +89,7 @@ that allow control the scheduling precisely:
|
||||
* ``density: N`` - the multiplier for number of instances per compute node
|
||||
* ``compute_nodes: N`` - how many compute nodes should be used (by default Shaker use all of them \*see note below)
|
||||
* ``zones: [Z1, Z2]`` - list of Nova availability zones to use
|
||||
* ``best_effort`` - proceed even if the number of available compute nodes is less than what was requested
|
||||
|
||||
Examples:
|
||||
|
||||
|
@ -68,20 +68,35 @@ def generate_agents(compute_nodes, accommodation, unique):
|
||||
# sort nodes to interleave hosts from different zones
|
||||
compute_nodes = prepare_for_cross_az(compute_nodes, zones)
|
||||
|
||||
best_effort = accommodation.get('best_effort', False)
|
||||
compute_nodes_requested = accommodation.get('compute_nodes')
|
||||
if compute_nodes_requested:
|
||||
if compute_nodes_requested > len(compute_nodes):
|
||||
raise DeploymentException(
|
||||
'Not enough compute nodes %(cn)s for requested '
|
||||
'instance accommodation %(acc)s' %
|
||||
dict(cn=compute_nodes, acc=accommodation))
|
||||
compute_nodes = random.sample(compute_nodes, compute_nodes_requested)
|
||||
if best_effort:
|
||||
LOG.warn('Allowing best_effort accommodation: '
|
||||
'compute nodes requested: %(req)d: '
|
||||
'available: %(avail)d available' %
|
||||
dict(req=compute_nodes_requested,
|
||||
avail=len(compute_nodes)))
|
||||
else:
|
||||
raise DeploymentException(
|
||||
'Not enough compute nodes %(cn)s for requested '
|
||||
'instance accommodation %(acc)s' %
|
||||
dict(cn=compute_nodes, acc=accommodation))
|
||||
else:
|
||||
compute_nodes = random.sample(compute_nodes,
|
||||
compute_nodes_requested)
|
||||
|
||||
cn_count = len(compute_nodes)
|
||||
iterations = cn_count * density
|
||||
|
||||
if 'single_room' in accommodation and 'pair' in accommodation:
|
||||
iterations //= 2
|
||||
# special case to allow pair, single_room on single compute node
|
||||
if best_effort and iterations == 1:
|
||||
LOG.warn('Allowing best_effort accommodation: '
|
||||
'single_room, pair on one compute node')
|
||||
else:
|
||||
iterations //= 2
|
||||
node_formula = lambda x: compute_nodes[x % cn_count]
|
||||
|
||||
agents = {}
|
||||
|
@ -31,7 +31,7 @@ mapping:
|
||||
matching: any
|
||||
sequence:
|
||||
- type: str
|
||||
enum: [pair, alone, double_room, single_room, mixed_room, cross_az]
|
||||
enum: [pair, alone, double_room, single_room, mixed_room, cross_az, best_effort]
|
||||
- type: map
|
||||
mapping:
|
||||
density:
|
||||
|
@ -146,6 +146,56 @@ class TestDeploy(testtools.TestCase):
|
||||
self.assertRaises(deploy.DeploymentException, deploy.generate_agents,
|
||||
['uno'], accommodation, unique)
|
||||
|
||||
def test_generate_agents_pair_single_room_best_effort(self):
|
||||
unique = 'UU1D'
|
||||
expected = {
|
||||
'UU1D_master_0': {
|
||||
'id': 'UU1D_master_0',
|
||||
'mode': 'master',
|
||||
'availability_zone': '%s:uno' % ZONE,
|
||||
'node': 'uno',
|
||||
'zone': ZONE,
|
||||
'slave_id': 'UU1D_slave_0'},
|
||||
'UU1D_slave_0': {
|
||||
'id': 'UU1D_slave_0',
|
||||
'master_id': 'UU1D_master_0',
|
||||
'mode': 'slave',
|
||||
'availability_zone': '%s:uno' % ZONE,
|
||||
'zone': ZONE,
|
||||
'node': 'uno'},
|
||||
}
|
||||
accommodation = deploy.normalize_accommodation(
|
||||
['pair', 'single_room', 'best_effort'])
|
||||
actual = deploy.generate_agents(nodes_helper('uno'),
|
||||
accommodation,
|
||||
unique)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_generate_agents_pair_single_room_best_effort_three_nodes(self):
|
||||
unique = 'UU1D'
|
||||
expected = {
|
||||
'UU1D_master_0': {
|
||||
'id': 'UU1D_master_0',
|
||||
'mode': 'master',
|
||||
'availability_zone': '%s:uno' % ZONE,
|
||||
'node': 'uno',
|
||||
'zone': ZONE,
|
||||
'slave_id': 'UU1D_slave_0'},
|
||||
'UU1D_slave_0': {
|
||||
'id': 'UU1D_slave_0',
|
||||
'master_id': 'UU1D_master_0',
|
||||
'mode': 'slave',
|
||||
'availability_zone': '%s:dos' % ZONE,
|
||||
'zone': ZONE,
|
||||
'node': 'dos'},
|
||||
}
|
||||
accommodation = deploy.normalize_accommodation(
|
||||
['pair', 'single_room', 'best_effort'])
|
||||
actual = deploy.generate_agents(nodes_helper('uno', 'dos', 'tre'),
|
||||
accommodation,
|
||||
unique)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_generate_agents_pair_single_room_compute_nodes_not_enough(self):
|
||||
unique = 'UU1D'
|
||||
accommodation = deploy.normalize_accommodation(
|
||||
@ -153,6 +203,31 @@ class TestDeploy(testtools.TestCase):
|
||||
self.assertRaises(deploy.DeploymentException, deploy.generate_agents,
|
||||
['uno'], accommodation, unique)
|
||||
|
||||
def test_generate_agents_pair_single_room_compute_nodes_best_effort(self):
|
||||
unique = 'UU1D'
|
||||
expected = {
|
||||
'UU1D_master_0': {
|
||||
'id': 'UU1D_master_0',
|
||||
'mode': 'master',
|
||||
'availability_zone': '%s:uno' % ZONE,
|
||||
'node': 'uno',
|
||||
'zone': ZONE,
|
||||
'slave_id': 'UU1D_slave_0'},
|
||||
'UU1D_slave_0': {
|
||||
'id': 'UU1D_slave_0',
|
||||
'master_id': 'UU1D_master_0',
|
||||
'mode': 'slave',
|
||||
'availability_zone': '%s:uno' % ZONE,
|
||||
'zone': ZONE,
|
||||
'node': 'uno'},
|
||||
}
|
||||
accommodation = deploy.normalize_accommodation(
|
||||
['pair', 'single_room', 'best_effort', {'compute_nodes': 2}])
|
||||
actual = deploy.generate_agents(nodes_helper('uno'),
|
||||
accommodation,
|
||||
unique)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_generate_agents_pair_double_room(self):
|
||||
unique = 'UU1D'
|
||||
expected = {
|
||||
@ -332,6 +407,41 @@ class TestDeploy(testtools.TestCase):
|
||||
unique)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_generate_agents_alone_single_room_compute_nodes_not_enough(self):
|
||||
unique = 'UU1D'
|
||||
compute_nodes = nodes_helper('uno', 'duo')
|
||||
accommodation = deploy.normalize_accommodation(
|
||||
['single_room', {'compute_nodes': 3}])
|
||||
self.assertRaises(deploy.DeploymentException, deploy.generate_agents,
|
||||
compute_nodes, accommodation, unique)
|
||||
|
||||
@mock.patch('random.sample')
|
||||
def test_generate_agents_alone_single_room_compute_nodes_best_effort(self,
|
||||
mr):
|
||||
unique = 'UU1D'
|
||||
expected = {
|
||||
'UU1D_agent_0': {
|
||||
'id': 'UU1D_agent_0',
|
||||
'mode': 'alone',
|
||||
'availability_zone': '%s:uno' % ZONE,
|
||||
'zone': ZONE,
|
||||
'node': 'uno'},
|
||||
'UU1D_agent_1': {
|
||||
'id': 'UU1D_agent_1',
|
||||
'mode': 'alone',
|
||||
'availability_zone': '%s:duo' % ZONE,
|
||||
'zone': ZONE,
|
||||
'node': 'duo'},
|
||||
}
|
||||
compute_nodes = nodes_helper('uno', 'duo')
|
||||
mr.side_effect = lambda x, n: x[:n]
|
||||
accommodation = deploy.normalize_accommodation(
|
||||
['single_room', 'best_effort', {'compute_nodes': 3}])
|
||||
actual = deploy.generate_agents(compute_nodes,
|
||||
accommodation,
|
||||
unique)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
@mock.patch('random.sample')
|
||||
def test_generate_agents_alone_single_room_density_compute_nodes(self, mr):
|
||||
unique = 'UU1D'
|
||||
|
Loading…
x
Reference in New Issue
Block a user