Skip to content

Commit 11de108

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Pass target host to RequestGroup.in_tree"
2 parents e463447 + 3ad82c6 commit 11de108

File tree

3 files changed

+177
-29
lines changed

3 files changed

+177
-29
lines changed

nova/scheduler/manager.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ def select_destinations(self, ctxt, request_spec=None,
133133
except exception.RequestFilterFailed as e:
134134
raise exception.NoValidHost(reason=e.message)
135135

136-
resources = utils.resources_from_request_spec(spec_obj)
136+
resources = utils.resources_from_request_spec(
137+
ctxt, spec_obj, self.driver.host_manager)
137138
res = self.placement_client.get_allocation_candidates(ctxt,
138139
resources)
139140
if res is None:

nova/scheduler/utils.py

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -412,9 +412,16 @@ def resources_from_flavor(instance, flavor):
412412
return resources
413413

414414

415-
def resources_from_request_spec(spec_obj):
415+
def resources_from_request_spec(ctxt, spec_obj, host_manager):
416416
"""Given a RequestSpec object, returns a ResourceRequest of the resources,
417417
traits, and aggregates it represents.
418+
419+
:param context: The request context.
420+
:param spec_obj: A RequestSpec object.
421+
:param host_manager: A HostManager object.
422+
423+
:return: A ResourceRequest object.
424+
:raises NoValidHost: If the specified host/node is not found in the DB.
418425
"""
419426
spec_resources = {
420427
orc.VCPU: spec_obj.vcpus,
@@ -478,18 +485,54 @@ def resources_from_request_spec(spec_obj):
478485
for group in requested_resources:
479486
res_req.add_request_group(group)
480487

488+
# values to get the destination target compute uuid
489+
target_host = None
490+
target_node = None
491+
target_cell = None
492+
481493
if 'requested_destination' in spec_obj:
482494
destination = spec_obj.requested_destination
483-
if destination and destination.aggregates:
484-
grp = res_req.get_request_group(None)
485-
grp.aggregates = [ored.split(',')
486-
for ored in destination.aggregates]
495+
if destination:
496+
if 'host' in destination:
497+
target_host = destination.host
498+
if 'node' in destination:
499+
target_node = destination.node
500+
if 'cell' in destination:
501+
target_cell = destination.cell
502+
if destination.aggregates:
503+
grp = res_req.get_request_group(None)
504+
grp.aggregates = [ored.split(',')
505+
for ored in destination.aggregates]
487506

488-
# Don't limit allocation candidates when using force_hosts or force_nodes.
489507
if 'force_hosts' in spec_obj and spec_obj.force_hosts:
490-
res_req._limit = None
508+
# Prioritize the value from requested_destination just in case
509+
# so that we don't inadvertently overwrite it to the old value
510+
# of force_hosts persisted in the DB
511+
target_host = target_host or spec_obj.force_hosts[0]
512+
491513
if 'force_nodes' in spec_obj and spec_obj.force_nodes:
492-
res_req._limit = None
514+
# Prioritize the value from requested_destination just in case
515+
# so that we don't inadvertently overwrite it to the old value
516+
# of force_nodes persisted in the DB
517+
target_node = target_node or spec_obj.force_nodes[0]
518+
519+
if target_host or target_node:
520+
nodes = host_manager.get_compute_nodes_by_host_or_node(
521+
ctxt, target_host, target_node, cell=target_cell)
522+
if not nodes:
523+
reason = (_('No such host - host: %(host)s node: %(node)s ') %
524+
{'host': target_host, 'node': target_node})
525+
raise exception.NoValidHost(reason=reason)
526+
if len(nodes) == 1:
527+
grp = res_req.get_request_group(None)
528+
grp.in_tree = nodes[0].uuid
529+
else:
530+
# Multiple nodes are found when a target host is specified
531+
# without a specific node. Since placement doesn't support
532+
# multiple uuids in the `in_tree` queryparam, what we can do here
533+
# is to remove the limit from the `GET /a_c` query to prevent
534+
# the found nodes from being filtered out in placement.
535+
res_req._limit = None
493536

494537
return res_req
495538

nova/tests/unit/scheduler/test_utils.py

Lines changed: 124 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class TestUtils(test.NoDBTestCase):
2929
def setUp(self):
3030
super(TestUtils, self).setUp()
3131
self.context = nova_context.get_admin_context()
32+
self.mock_host_manager = mock.Mock()
3233

3334
def assertResourceRequestsEqual(self, expected, observed):
3435
self.assertEqual(expected._limit, observed._limit)
@@ -54,7 +55,8 @@ def _get_image_with_traits():
5455
def _test_resources_from_request_spec(self, expected, flavor,
5556
image=objects.ImageMeta()):
5657
fake_spec = objects.RequestSpec(flavor=flavor, image=image)
57-
resources = utils.resources_from_request_spec(fake_spec)
58+
resources = utils.resources_from_request_spec(
59+
self.context, fake_spec, self.mock_host_manager)
5860
self.assertResourceRequestsEqual(expected, resources)
5961
return resources
6062

@@ -232,7 +234,8 @@ def test_get_resources_from_request_spec_bad_std_resource_class(self):
232234
"resources:DOESNT_EXIST": 0})
233235
fake_spec = objects.RequestSpec(flavor=flavor)
234236
with mock.patch("nova.scheduler.utils.LOG.warning") as mock_log:
235-
utils.resources_from_request_spec(fake_spec)
237+
utils.resources_from_request_spec(
238+
self.context, fake_spec, self.mock_host_manager)
236239
mock_log.assert_called_once()
237240
args = mock_log.call_args[0]
238241
self.assertEqual(args[0], "Received an invalid ResourceClass "
@@ -321,12 +324,14 @@ def test_resources_from_request_spec_aggregates(self):
321324
requested_destination=destination)
322325

323326
destination.require_aggregates(['foo', 'bar'])
324-
req = utils.resources_from_request_spec(reqspec)
327+
req = utils.resources_from_request_spec(
328+
self.context, reqspec, self.mock_host_manager)
325329
self.assertEqual([['foo', 'bar']],
326330
req.get_request_group(None).aggregates)
327331

328332
destination.require_aggregates(['baz'])
329-
req = utils.resources_from_request_spec(reqspec)
333+
req = utils.resources_from_request_spec(
334+
self.context, reqspec, self.mock_host_manager)
330335
self.assertEqual([['foo', 'bar'], ['baz']],
331336
req.get_request_group(None).aggregates)
332337

@@ -336,19 +341,23 @@ def test_resources_from_request_spec_no_aggregates(self):
336341
swap=0)
337342
reqspec = objects.RequestSpec(flavor=flavor)
338343

339-
req = utils.resources_from_request_spec(reqspec)
344+
req = utils.resources_from_request_spec(
345+
self.context, reqspec, self.mock_host_manager)
340346
self.assertEqual([], req.get_request_group(None).aggregates)
341347

342348
reqspec.requested_destination = None
343-
req = utils.resources_from_request_spec(reqspec)
349+
req = utils.resources_from_request_spec(
350+
self.context, reqspec, self.mock_host_manager)
344351
self.assertEqual([], req.get_request_group(None).aggregates)
345352

346353
reqspec.requested_destination = objects.Destination()
347-
req = utils.resources_from_request_spec(reqspec)
354+
req = utils.resources_from_request_spec(
355+
self.context, reqspec, self.mock_host_manager)
348356
self.assertEqual([], req.get_request_group(None).aggregates)
349357

350358
reqspec.requested_destination.aggregates = None
351-
req = utils.resources_from_request_spec(reqspec)
359+
req = utils.resources_from_request_spec(
360+
self.context, reqspec, self.mock_host_manager)
352361
self.assertEqual([], req.get_request_group(None).aggregates)
353362

354363
@mock.patch("nova.scheduler.utils.ResourceRequest.from_extra_specs")
@@ -360,7 +369,8 @@ def test_process_extra_specs_granular_called(self, mock_proc):
360369
swap=0,
361370
extra_specs={"resources:CUSTOM_TEST_CLASS": 1})
362371
fake_spec = objects.RequestSpec(flavor=flavor)
363-
utils.resources_from_request_spec(fake_spec)
372+
utils.resources_from_request_spec(
373+
self.context, fake_spec, self.mock_host_manager)
364374
mock_proc.assert_called_once()
365375

366376
@mock.patch("nova.scheduler.utils.ResourceRequest.from_extra_specs")
@@ -371,7 +381,8 @@ def test_process_extra_specs_granular_not_called(self, mock_proc):
371381
ephemeral_gb=5,
372382
swap=0)
373383
fake_spec = objects.RequestSpec(flavor=flavor)
374-
utils.resources_from_request_spec(fake_spec)
384+
utils.resources_from_request_spec(
385+
self.context, fake_spec, self.mock_host_manager)
375386
mock_proc.assert_not_called()
376387

377388
def test_process_missing_extra_specs_value(self):
@@ -383,7 +394,8 @@ def test_process_missing_extra_specs_value(self):
383394
swap=0,
384395
extra_specs={"resources:CUSTOM_TEST_CLASS": ""})
385396
fake_spec = objects.RequestSpec(flavor=flavor)
386-
utils.resources_from_request_spec(fake_spec)
397+
utils.resources_from_request_spec(
398+
self.context, fake_spec, self.mock_host_manager)
387399

388400
def test_process_no_force_hosts_or_force_nodes(self):
389401
flavor = objects.Flavor(vcpus=1,
@@ -408,6 +420,11 @@ def test_process_no_force_hosts_or_force_nodes(self):
408420
self.assertEqual(expected_querystring, rr.to_querystring())
409421

410422
def test_process_use_force_nodes(self):
423+
fake_nodes = objects.ComputeNodeList(objects=[
424+
objects.ComputeNode(host='fake-host',
425+
uuid='12345678-1234-1234-1234-123456789012')])
426+
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
427+
return_value = fake_nodes
411428
flavor = objects.Flavor(vcpus=1,
412429
memory_mb=1024,
413430
root_gb=15,
@@ -422,16 +439,24 @@ def test_process_use_force_nodes(self):
422439
'MEMORY_MB': 1024,
423440
'DISK_GB': 15,
424441
},
442+
in_tree='12345678-1234-1234-1234-123456789012',
425443
)
426-
expected._limit = None
427-
resources = utils.resources_from_request_spec(fake_spec)
444+
resources = utils.resources_from_request_spec(
445+
self.context, fake_spec, self.mock_host_manager)
428446
self.assertResourceRequestsEqual(expected, resources)
429447
expected_querystring = (
430-
'resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1'
431-
)
448+
'limit=1000&resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1')
432449
self.assertEqual(expected_querystring, resources.to_querystring())
450+
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
451+
assert_called_once_with(self.context, None, 'test', cell=None)
433452

434453
def test_process_use_force_hosts(self):
454+
fake_nodes = objects.ComputeNodeList(objects=[
455+
objects.ComputeNode(host='fake-host',
456+
uuid='12345678-1234-1234-1234-123456789012')
457+
])
458+
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
459+
return_value = fake_nodes
435460
flavor = objects.Flavor(vcpus=1,
436461
memory_mb=1024,
437462
root_gb=15,
@@ -446,14 +471,90 @@ def test_process_use_force_hosts(self):
446471
'MEMORY_MB': 1024,
447472
'DISK_GB': 15,
448473
},
474+
in_tree='12345678-1234-1234-1234-123456789012',
449475
)
476+
resources = utils.resources_from_request_spec(
477+
self.context, fake_spec, self.mock_host_manager)
478+
self.assertResourceRequestsEqual(expected, resources)
479+
expected_querystring = (
480+
'limit=1000&resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1')
481+
self.assertEqual(expected_querystring, resources.to_querystring())
482+
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
483+
assert_called_once_with(self.context, 'test', None, cell=None)
484+
485+
def test_process_use_force_hosts_multinodes_found(self):
486+
fake_nodes = objects.ComputeNodeList(objects=[
487+
objects.ComputeNode(host='fake-host',
488+
uuid='12345678-1234-1234-1234-123456789012'),
489+
objects.ComputeNode(host='fake-host',
490+
uuid='87654321-4321-4321-4321-210987654321'),
491+
])
492+
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
493+
return_value = fake_nodes
494+
flavor = objects.Flavor(vcpus=1,
495+
memory_mb=1024,
496+
root_gb=15,
497+
ephemeral_gb=0,
498+
swap=0)
499+
fake_spec = objects.RequestSpec(flavor=flavor, force_hosts=['test'])
500+
expected = utils.ResourceRequest()
501+
expected._rg_by_id[None] = objects.RequestGroup(
502+
use_same_provider=False,
503+
resources={
504+
'VCPU': 1,
505+
'MEMORY_MB': 1024,
506+
'DISK_GB': 15,
507+
},
508+
)
509+
# Validate that the limit is unset
450510
expected._limit = None
451-
resources = utils.resources_from_request_spec(fake_spec)
511+
512+
resources = utils.resources_from_request_spec(
513+
self.context, fake_spec, self.mock_host_manager)
452514
self.assertResourceRequestsEqual(expected, resources)
515+
# Validate that the limit is unset
453516
expected_querystring = (
454-
'resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1'
517+
'resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1')
518+
self.assertEqual(expected_querystring, resources.to_querystring())
519+
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
520+
assert_called_once_with(self.context, 'test', None, cell=None)
521+
522+
def test_process_use_requested_destination(self):
523+
fake_cell = objects.CellMapping(uuid=uuids.cell1, name='foo')
524+
destination = objects.Destination(
525+
host='fake-host', node='fake-node', cell=fake_cell)
526+
fake_nodes = objects.ComputeNodeList(objects=[
527+
objects.ComputeNode(host='fake-host',
528+
uuid='12345678-1234-1234-1234-123456789012')
529+
])
530+
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
531+
return_value = fake_nodes
532+
flavor = objects.Flavor(vcpus=1,
533+
memory_mb=1024,
534+
root_gb=15,
535+
ephemeral_gb=0,
536+
swap=0)
537+
fake_spec = objects.RequestSpec(
538+
flavor=flavor, requested_destination=destination)
539+
expected = utils.ResourceRequest()
540+
expected._rg_by_id[None] = objects.RequestGroup(
541+
use_same_provider=False,
542+
resources={
543+
'VCPU': 1,
544+
'MEMORY_MB': 1024,
545+
'DISK_GB': 15,
546+
},
547+
in_tree='12345678-1234-1234-1234-123456789012',
455548
)
549+
resources = utils.resources_from_request_spec(
550+
self.context, fake_spec, self.mock_host_manager)
551+
self.assertResourceRequestsEqual(expected, resources)
552+
expected_querystring = (
553+
'limit=1000&resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1')
456554
self.assertEqual(expected_querystring, resources.to_querystring())
555+
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
556+
assert_called_once_with(
557+
self.context, 'fake-host', 'fake-node', cell=fake_cell)
457558

458559
def test_resources_from_request_spec_having_requested_resources(self):
459560
flavor = objects.Flavor(
@@ -466,7 +567,8 @@ def test_resources_from_request_spec_having_requested_resources(self):
466567
rg2 = objects.RequestGroup()
467568
reqspec = objects.RequestSpec(flavor=flavor,
468569
requested_resources=[rg1, rg2])
469-
req = utils.resources_from_request_spec(reqspec)
570+
req = utils.resources_from_request_spec(
571+
self.context, reqspec, self.mock_host_manager)
470572
self.assertEqual({'MEMORY_MB': 1024, 'DISK_GB': 15, 'VCPU': 1},
471573
req.get_request_group(None).resources)
472574
self.assertIs(rg1, req.get_request_group(1))
@@ -480,13 +582,15 @@ def test_resources_from_request_spec_requested_resources_unfilled(self):
480582
ephemeral_gb=5,
481583
swap=0)
482584
reqspec = objects.RequestSpec(flavor=flavor)
483-
req = utils.resources_from_request_spec(reqspec)
585+
req = utils.resources_from_request_spec(
586+
self.context, reqspec, self.mock_host_manager)
484587
self.assertEqual({'MEMORY_MB': 1024, 'DISK_GB': 15, 'VCPU': 1},
485588
req.get_request_group(None).resources)
486589
self.assertEqual(1, len(list(req.resource_groups())))
487590

488591
reqspec = objects.RequestSpec(flavor=flavor, requested_resources=[])
489-
req = utils.resources_from_request_spec(reqspec)
592+
req = utils.resources_from_request_spec(
593+
self.context, reqspec, self.mock_host_manager)
490594
self.assertEqual({'MEMORY_MB': 1024, 'DISK_GB': 15, 'VCPU': 1},
491595
req.get_request_group(None).resources)
492596
self.assertEqual(1, len(list(req.resource_groups())))

0 commit comments

Comments
 (0)