Pass target host to RequestGroup.in_tree

This patch retrieves target host info from RequestSpec object,
looks up the database to translate it into the compute node uuid,
and pass it to the new `RequestGroup.in_tree` field.

Change-Id: If7ea02df42d220c5042947efdef4777509492a0b
Blueprint: use-placement-in-tree
This commit is contained in:
Tetsuro Nakamura 2019-04-07 03:25:08 +00:00
parent 57465aae77
commit 3ad82c696f
3 changed files with 178 additions and 30 deletions

View File

@ -133,7 +133,8 @@ class SchedulerManager(manager.Manager):
except exception.RequestFilterFailed as e:
raise exception.NoValidHost(reason=e.message)
resources = utils.resources_from_request_spec(spec_obj)
resources = utils.resources_from_request_spec(
ctxt, spec_obj, self.driver.host_manager)
res = self.placement_client.get_allocation_candidates(ctxt,
resources)
if res is None:

View File

@ -412,9 +412,16 @@ def resources_from_flavor(instance, flavor):
return resources
def resources_from_request_spec(spec_obj):
def resources_from_request_spec(ctxt, spec_obj, host_manager):
"""Given a RequestSpec object, returns a ResourceRequest of the resources,
traits, and aggregates it represents.
:param context: The request context.
:param spec_obj: A RequestSpec object.
:param host_manager: A HostManager object.
:return: A ResourceRequest object.
:raises NoValidHost: If the specified host/node is not found in the DB.
"""
spec_resources = {
orc.VCPU: spec_obj.vcpus,
@ -478,18 +485,54 @@ def resources_from_request_spec(spec_obj):
for group in requested_resources:
res_req.add_request_group(group)
# values to get the destination target compute uuid
target_host = None
target_node = None
target_cell = None
if 'requested_destination' in spec_obj:
destination = spec_obj.requested_destination
if destination and destination.aggregates:
grp = res_req.get_request_group(None)
grp.aggregates = [ored.split(',')
for ored in destination.aggregates]
if destination:
if 'host' in destination:
target_host = destination.host
if 'node' in destination:
target_node = destination.node
if 'cell' in destination:
target_cell = destination.cell
if destination.aggregates:
grp = res_req.get_request_group(None)
grp.aggregates = [ored.split(',')
for ored in destination.aggregates]
# Don't limit allocation candidates when using force_hosts or force_nodes.
if 'force_hosts' in spec_obj and spec_obj.force_hosts:
res_req._limit = None
# Prioritize the value from requested_destination just in case
# so that we don't inadvertently overwrite it to the old value
# of force_hosts persisted in the DB
target_host = target_host or spec_obj.force_hosts[0]
if 'force_nodes' in spec_obj and spec_obj.force_nodes:
res_req._limit = None
# Prioritize the value from requested_destination just in case
# so that we don't inadvertently overwrite it to the old value
# of force_nodes persisted in the DB
target_node = target_node or spec_obj.force_nodes[0]
if target_host or target_node:
nodes = host_manager.get_compute_nodes_by_host_or_node(
ctxt, target_host, target_node, cell=target_cell)
if not nodes:
reason = (_('No such host - host: %(host)s node: %(node)s ') %
{'host': target_host, 'node': target_node})
raise exception.NoValidHost(reason=reason)
if len(nodes) == 1:
grp = res_req.get_request_group(None)
grp.in_tree = nodes[0].uuid
else:
# Multiple nodes are found when a target host is specified
# without a specific node. Since placement doesn't support
# multiple uuids in the `in_tree` queryparam, what we can do here
# is to remove the limit from the `GET /a_c` query to prevent
# the found nodes from being filtered out in placement.
res_req._limit = None
return res_req

View File

@ -29,6 +29,7 @@ class TestUtils(test.NoDBTestCase):
def setUp(self):
super(TestUtils, self).setUp()
self.context = nova_context.get_admin_context()
self.mock_host_manager = mock.Mock()
def assertResourceRequestsEqual(self, expected, observed):
self.assertEqual(expected._limit, observed._limit)
@ -54,7 +55,8 @@ class TestUtils(test.NoDBTestCase):
def _test_resources_from_request_spec(self, expected, flavor,
image=objects.ImageMeta()):
fake_spec = objects.RequestSpec(flavor=flavor, image=image)
resources = utils.resources_from_request_spec(fake_spec)
resources = utils.resources_from_request_spec(
self.context, fake_spec, self.mock_host_manager)
self.assertResourceRequestsEqual(expected, resources)
return resources
@ -232,7 +234,8 @@ class TestUtils(test.NoDBTestCase):
"resources:DOESNT_EXIST": 0})
fake_spec = objects.RequestSpec(flavor=flavor)
with mock.patch("nova.scheduler.utils.LOG.warning") as mock_log:
utils.resources_from_request_spec(fake_spec)
utils.resources_from_request_spec(
self.context, fake_spec, self.mock_host_manager)
mock_log.assert_called_once()
args = mock_log.call_args[0]
self.assertEqual(args[0], "Received an invalid ResourceClass "
@ -321,12 +324,14 @@ class TestUtils(test.NoDBTestCase):
requested_destination=destination)
destination.require_aggregates(['foo', 'bar'])
req = utils.resources_from_request_spec(reqspec)
req = utils.resources_from_request_spec(
self.context, reqspec, self.mock_host_manager)
self.assertEqual([['foo', 'bar']],
req.get_request_group(None).aggregates)
destination.require_aggregates(['baz'])
req = utils.resources_from_request_spec(reqspec)
req = utils.resources_from_request_spec(
self.context, reqspec, self.mock_host_manager)
self.assertEqual([['foo', 'bar'], ['baz']],
req.get_request_group(None).aggregates)
@ -336,19 +341,23 @@ class TestUtils(test.NoDBTestCase):
swap=0)
reqspec = objects.RequestSpec(flavor=flavor)
req = utils.resources_from_request_spec(reqspec)
req = utils.resources_from_request_spec(
self.context, reqspec, self.mock_host_manager)
self.assertEqual([], req.get_request_group(None).aggregates)
reqspec.requested_destination = None
req = utils.resources_from_request_spec(reqspec)
req = utils.resources_from_request_spec(
self.context, reqspec, self.mock_host_manager)
self.assertEqual([], req.get_request_group(None).aggregates)
reqspec.requested_destination = objects.Destination()
req = utils.resources_from_request_spec(reqspec)
req = utils.resources_from_request_spec(
self.context, reqspec, self.mock_host_manager)
self.assertEqual([], req.get_request_group(None).aggregates)
reqspec.requested_destination.aggregates = None
req = utils.resources_from_request_spec(reqspec)
req = utils.resources_from_request_spec(
self.context, reqspec, self.mock_host_manager)
self.assertEqual([], req.get_request_group(None).aggregates)
@mock.patch("nova.scheduler.utils.ResourceRequest.from_extra_specs")
@ -360,7 +369,8 @@ class TestUtils(test.NoDBTestCase):
swap=0,
extra_specs={"resources:CUSTOM_TEST_CLASS": 1})
fake_spec = objects.RequestSpec(flavor=flavor)
utils.resources_from_request_spec(fake_spec)
utils.resources_from_request_spec(
self.context, fake_spec, self.mock_host_manager)
mock_proc.assert_called_once()
@mock.patch("nova.scheduler.utils.ResourceRequest.from_extra_specs")
@ -371,7 +381,8 @@ class TestUtils(test.NoDBTestCase):
ephemeral_gb=5,
swap=0)
fake_spec = objects.RequestSpec(flavor=flavor)
utils.resources_from_request_spec(fake_spec)
utils.resources_from_request_spec(
self.context, fake_spec, self.mock_host_manager)
mock_proc.assert_not_called()
def test_process_missing_extra_specs_value(self):
@ -383,7 +394,8 @@ class TestUtils(test.NoDBTestCase):
swap=0,
extra_specs={"resources:CUSTOM_TEST_CLASS": ""})
fake_spec = objects.RequestSpec(flavor=flavor)
utils.resources_from_request_spec(fake_spec)
utils.resources_from_request_spec(
self.context, fake_spec, self.mock_host_manager)
def test_process_no_force_hosts_or_force_nodes(self):
flavor = objects.Flavor(vcpus=1,
@ -408,6 +420,11 @@ class TestUtils(test.NoDBTestCase):
self.assertEqual(expected_querystring, rr.to_querystring())
def test_process_use_force_nodes(self):
fake_nodes = objects.ComputeNodeList(objects=[
objects.ComputeNode(host='fake-host',
uuid='12345678-1234-1234-1234-123456789012')])
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
return_value = fake_nodes
flavor = objects.Flavor(vcpus=1,
memory_mb=1024,
root_gb=15,
@ -422,16 +439,58 @@ class TestUtils(test.NoDBTestCase):
'MEMORY_MB': 1024,
'DISK_GB': 15,
},
in_tree='12345678-1234-1234-1234-123456789012',
)
expected._limit = None
resources = utils.resources_from_request_spec(fake_spec)
resources = utils.resources_from_request_spec(
self.context, fake_spec, self.mock_host_manager)
self.assertResourceRequestsEqual(expected, resources)
expected_querystring = (
'resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1'
)
'limit=1000&resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1')
self.assertEqual(expected_querystring, resources.to_querystring())
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
assert_called_once_with(self.context, None, 'test', cell=None)
def test_process_use_force_hosts(self):
fake_nodes = objects.ComputeNodeList(objects=[
objects.ComputeNode(host='fake-host',
uuid='12345678-1234-1234-1234-123456789012')
])
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
return_value = fake_nodes
flavor = objects.Flavor(vcpus=1,
memory_mb=1024,
root_gb=15,
ephemeral_gb=0,
swap=0)
fake_spec = objects.RequestSpec(flavor=flavor, force_hosts=['test'])
expected = utils.ResourceRequest()
expected._rg_by_id[None] = objects.RequestGroup(
use_same_provider=False,
resources={
'VCPU': 1,
'MEMORY_MB': 1024,
'DISK_GB': 15,
},
in_tree='12345678-1234-1234-1234-123456789012',
)
resources = utils.resources_from_request_spec(
self.context, fake_spec, self.mock_host_manager)
self.assertResourceRequestsEqual(expected, resources)
expected_querystring = (
'limit=1000&resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1')
self.assertEqual(expected_querystring, resources.to_querystring())
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
assert_called_once_with(self.context, 'test', None, cell=None)
def test_process_use_force_hosts_multinodes_found(self):
fake_nodes = objects.ComputeNodeList(objects=[
objects.ComputeNode(host='fake-host',
uuid='12345678-1234-1234-1234-123456789012'),
objects.ComputeNode(host='fake-host',
uuid='87654321-4321-4321-4321-210987654321'),
])
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
return_value = fake_nodes
flavor = objects.Flavor(vcpus=1,
memory_mb=1024,
root_gb=15,
@ -447,13 +506,55 @@ class TestUtils(test.NoDBTestCase):
'DISK_GB': 15,
},
)
# Validate that the limit is unset
expected._limit = None
resources = utils.resources_from_request_spec(fake_spec)
resources = utils.resources_from_request_spec(
self.context, fake_spec, self.mock_host_manager)
self.assertResourceRequestsEqual(expected, resources)
# Validate that the limit is unset
expected_querystring = (
'resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1')
self.assertEqual(expected_querystring, resources.to_querystring())
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
assert_called_once_with(self.context, 'test', None, cell=None)
def test_process_use_requested_destination(self):
fake_cell = objects.CellMapping(uuid=uuids.cell1, name='foo')
destination = objects.Destination(
host='fake-host', node='fake-node', cell=fake_cell)
fake_nodes = objects.ComputeNodeList(objects=[
objects.ComputeNode(host='fake-host',
uuid='12345678-1234-1234-1234-123456789012')
])
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
return_value = fake_nodes
flavor = objects.Flavor(vcpus=1,
memory_mb=1024,
root_gb=15,
ephemeral_gb=0,
swap=0)
fake_spec = objects.RequestSpec(
flavor=flavor, requested_destination=destination)
expected = utils.ResourceRequest()
expected._rg_by_id[None] = objects.RequestGroup(
use_same_provider=False,
resources={
'VCPU': 1,
'MEMORY_MB': 1024,
'DISK_GB': 15,
},
in_tree='12345678-1234-1234-1234-123456789012',
)
resources = utils.resources_from_request_spec(
self.context, fake_spec, self.mock_host_manager)
self.assertResourceRequestsEqual(expected, resources)
expected_querystring = (
'resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1'
)
'limit=1000&resources=DISK_GB%3A15%2CMEMORY_MB%3A1024%2CVCPU%3A1')
self.assertEqual(expected_querystring, resources.to_querystring())
self.mock_host_manager.get_compute_nodes_by_host_or_node.\
assert_called_once_with(
self.context, 'fake-host', 'fake-node', cell=fake_cell)
def test_resources_from_request_spec_having_requested_resources(self):
flavor = objects.Flavor(
@ -466,7 +567,8 @@ class TestUtils(test.NoDBTestCase):
rg2 = objects.RequestGroup()
reqspec = objects.RequestSpec(flavor=flavor,
requested_resources=[rg1, rg2])
req = utils.resources_from_request_spec(reqspec)
req = utils.resources_from_request_spec(
self.context, reqspec, self.mock_host_manager)
self.assertEqual({'MEMORY_MB': 1024, 'DISK_GB': 15, 'VCPU': 1},
req.get_request_group(None).resources)
self.assertIs(rg1, req.get_request_group(1))
@ -480,13 +582,15 @@ class TestUtils(test.NoDBTestCase):
ephemeral_gb=5,
swap=0)
reqspec = objects.RequestSpec(flavor=flavor)
req = utils.resources_from_request_spec(reqspec)
req = utils.resources_from_request_spec(
self.context, reqspec, self.mock_host_manager)
self.assertEqual({'MEMORY_MB': 1024, 'DISK_GB': 15, 'VCPU': 1},
req.get_request_group(None).resources)
self.assertEqual(1, len(list(req.resource_groups())))
reqspec = objects.RequestSpec(flavor=flavor, requested_resources=[])
req = utils.resources_from_request_spec(reqspec)
req = utils.resources_from_request_spec(
self.context, reqspec, self.mock_host_manager)
self.assertEqual({'MEMORY_MB': 1024, 'DISK_GB': 15, 'VCPU': 1},
req.get_request_group(None).resources)
self.assertEqual(1, len(list(req.resource_groups())))