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
|
* ``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)
|
* ``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
|
* ``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:
|
Examples:
|
||||||
|
|
||||||
|
|
|
@ -68,20 +68,35 @@ def generate_agents(compute_nodes, accommodation, unique):
|
||||||
# sort nodes to interleave hosts from different zones
|
# sort nodes to interleave hosts from different zones
|
||||||
compute_nodes = prepare_for_cross_az(compute_nodes, zones)
|
compute_nodes = prepare_for_cross_az(compute_nodes, zones)
|
||||||
|
|
||||||
|
best_effort = accommodation.get('best_effort', False)
|
||||||
compute_nodes_requested = accommodation.get('compute_nodes')
|
compute_nodes_requested = accommodation.get('compute_nodes')
|
||||||
if compute_nodes_requested:
|
if compute_nodes_requested:
|
||||||
if compute_nodes_requested > len(compute_nodes):
|
if compute_nodes_requested > len(compute_nodes):
|
||||||
raise DeploymentException(
|
if best_effort:
|
||||||
'Not enough compute nodes %(cn)s for requested '
|
LOG.warn('Allowing best_effort accommodation: '
|
||||||
'instance accommodation %(acc)s' %
|
'compute nodes requested: %(req)d: '
|
||||||
dict(cn=compute_nodes, acc=accommodation))
|
'available: %(avail)d available' %
|
||||||
compute_nodes = random.sample(compute_nodes, compute_nodes_requested)
|
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)
|
cn_count = len(compute_nodes)
|
||||||
iterations = cn_count * density
|
iterations = cn_count * density
|
||||||
|
|
||||||
if 'single_room' in accommodation and 'pair' in accommodation:
|
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]
|
node_formula = lambda x: compute_nodes[x % cn_count]
|
||||||
|
|
||||||
agents = {}
|
agents = {}
|
||||||
|
|
|
@ -31,7 +31,7 @@ mapping:
|
||||||
matching: any
|
matching: any
|
||||||
sequence:
|
sequence:
|
||||||
- type: str
|
- 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
|
- type: map
|
||||||
mapping:
|
mapping:
|
||||||
density:
|
density:
|
||||||
|
|
|
@ -146,6 +146,56 @@ class TestDeploy(testtools.TestCase):
|
||||||
self.assertRaises(deploy.DeploymentException, deploy.generate_agents,
|
self.assertRaises(deploy.DeploymentException, deploy.generate_agents,
|
||||||
['uno'], accommodation, unique)
|
['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):
|
def test_generate_agents_pair_single_room_compute_nodes_not_enough(self):
|
||||||
unique = 'UU1D'
|
unique = 'UU1D'
|
||||||
accommodation = deploy.normalize_accommodation(
|
accommodation = deploy.normalize_accommodation(
|
||||||
|
@ -153,6 +203,31 @@ class TestDeploy(testtools.TestCase):
|
||||||
self.assertRaises(deploy.DeploymentException, deploy.generate_agents,
|
self.assertRaises(deploy.DeploymentException, deploy.generate_agents,
|
||||||
['uno'], accommodation, unique)
|
['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):
|
def test_generate_agents_pair_double_room(self):
|
||||||
unique = 'UU1D'
|
unique = 'UU1D'
|
||||||
expected = {
|
expected = {
|
||||||
|
@ -332,6 +407,41 @@ class TestDeploy(testtools.TestCase):
|
||||||
unique)
|
unique)
|
||||||
self.assertEqual(expected, actual)
|
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')
|
@mock.patch('random.sample')
|
||||||
def test_generate_agents_alone_single_room_density_compute_nodes(self, mr):
|
def test_generate_agents_alone_single_room_density_compute_nodes(self, mr):
|
||||||
unique = 'UU1D'
|
unique = 'UU1D'
|
||||||
|
|
Loading…
Reference in New Issue