Scheduler set_inventory_for_provider does nested
SchedulerReportClient.set_inventory_for_provider and its SchedulerClient wrapper now accept a parent_provider_uuid kwarg, which must be specified for any provider that isn't a root. If the method winds up creating the provider, and parent_provider_uuid is None (the default), the provider is created as a root - this is the previous behavior. If parent_provider_uuid is specified, and the method winds up creating the provider, it is created as a child of the provider indicated. Change-Id: I7cfbc80a9a41e97623950deaab9a7b0604fa487d blueprint: nested-resource-providers
This commit is contained in:
parent
a0ff3e2936
commit
b75b35f482
|
@ -57,11 +57,13 @@ class SchedulerClient(object):
|
||||||
def delete_aggregate(self, context, aggregate):
|
def delete_aggregate(self, context, aggregate):
|
||||||
self.queryclient.delete_aggregate(context, aggregate)
|
self.queryclient.delete_aggregate(context, aggregate)
|
||||||
|
|
||||||
def set_inventory_for_provider(self, rp_uuid, rp_name, inv_data):
|
def set_inventory_for_provider(self, rp_uuid, rp_name, inv_data,
|
||||||
|
parent_provider_uuid=None):
|
||||||
self.reportclient.set_inventory_for_provider(
|
self.reportclient.set_inventory_for_provider(
|
||||||
rp_uuid,
|
rp_uuid,
|
||||||
rp_name,
|
rp_name,
|
||||||
inv_data,
|
inv_data,
|
||||||
|
parent_provider_uuid=parent_provider_uuid,
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_compute_node(self, compute_node):
|
def update_compute_node(self, compute_node):
|
||||||
|
|
|
@ -491,7 +491,8 @@ class SchedulerReportClient(object):
|
||||||
LOG.error(msg, args)
|
LOG.error(msg, args)
|
||||||
raise exception.ResourceProviderCreationFailed(name=name)
|
raise exception.ResourceProviderCreationFailed(name=name)
|
||||||
|
|
||||||
def _ensure_resource_provider(self, uuid, name=None):
|
def _ensure_resource_provider(self, uuid, name=None,
|
||||||
|
parent_provider_uuid=None):
|
||||||
"""Ensures that the placement API has a record of a resource provider
|
"""Ensures that the placement API has a record of a resource provider
|
||||||
with the supplied UUID. If not, creates the resource provider record in
|
with the supplied UUID. If not, creates the resource provider record in
|
||||||
the placement API for the supplied UUID, passing in a name for the
|
the placement API for the supplied UUID, passing in a name for the
|
||||||
|
@ -510,7 +511,11 @@ class SchedulerReportClient(object):
|
||||||
:param name: Optional name for the resource provider if the record
|
:param name: Optional name for the resource provider if the record
|
||||||
does not exist. If empty, the name is set to the UUID
|
does not exist. If empty, the name is set to the UUID
|
||||||
value
|
value
|
||||||
|
:param parent_provider_uuid: Optional UUID of the immediate parent
|
||||||
"""
|
"""
|
||||||
|
# NOTE(efried): We currently have no code path where we need to set the
|
||||||
|
# parent_provider_uuid on a previously-parent-less provider - so we do
|
||||||
|
# NOT handle that scenario here.
|
||||||
if self._provider_tree.exists(uuid):
|
if self._provider_tree.exists(uuid):
|
||||||
self._refresh_aggregate_map(uuid)
|
self._refresh_aggregate_map(uuid)
|
||||||
return uuid
|
return uuid
|
||||||
|
@ -519,13 +524,25 @@ class SchedulerReportClient(object):
|
||||||
# the placement API.
|
# the placement API.
|
||||||
rp = self._get_resource_provider(uuid)
|
rp = self._get_resource_provider(uuid)
|
||||||
if rp is None:
|
if rp is None:
|
||||||
rp = self._create_resource_provider(uuid, name or uuid)
|
rp = self._create_resource_provider(
|
||||||
|
uuid, name or uuid, parent_provider_uuid=parent_provider_uuid)
|
||||||
|
|
||||||
# If there had been no resource provider record, force refreshing
|
# If there had been no resource provider record, force refreshing
|
||||||
# the aggregate map.
|
# the aggregate map.
|
||||||
self._refresh_aggregate_map(uuid, force=True)
|
self._refresh_aggregate_map(uuid, force=True)
|
||||||
|
|
||||||
return self._provider_tree.new_root(rp['name'], uuid, rp['generation'])
|
# If this is a root node (no parent), create it as such
|
||||||
|
if parent_provider_uuid is None:
|
||||||
|
return self._provider_tree.new_root(
|
||||||
|
rp['name'], uuid, rp['generation'])
|
||||||
|
|
||||||
|
# Not a root - we have to insert it into the proper place in the tree.
|
||||||
|
# NOTE(efried): We populate self._provider_tree from the top down, so
|
||||||
|
# we can count on the parent being in the tree - we don't have to
|
||||||
|
# retrieve it from placement.
|
||||||
|
return self._provider_tree.new_child(rp['name'], parent_provider_uuid,
|
||||||
|
uuid=uuid,
|
||||||
|
generation=rp['generation'])
|
||||||
|
|
||||||
def _get_inventory(self, rp_uuid):
|
def _get_inventory(self, rp_uuid):
|
||||||
url = '/resource_providers/%s/inventories' % rp_uuid
|
url = '/resource_providers/%s/inventories' % rp_uuid
|
||||||
|
@ -799,7 +816,8 @@ class SchedulerReportClient(object):
|
||||||
msg_args['err'] = r.text
|
msg_args['err'] = r.text
|
||||||
LOG.error(msg, msg_args)
|
LOG.error(msg, msg_args)
|
||||||
|
|
||||||
def set_inventory_for_provider(self, rp_uuid, rp_name, inv_data):
|
def set_inventory_for_provider(self, rp_uuid, rp_name, inv_data,
|
||||||
|
parent_provider_uuid=None):
|
||||||
"""Given the UUID of a provider, set the inventory records for the
|
"""Given the UUID of a provider, set the inventory records for the
|
||||||
provider to the supplied dict of resources.
|
provider to the supplied dict of resources.
|
||||||
|
|
||||||
|
@ -808,11 +826,16 @@ class SchedulerReportClient(object):
|
||||||
a record for it in the placement API
|
a record for it in the placement API
|
||||||
:param inv_data: Dict, keyed by resource class name, of inventory data
|
:param inv_data: Dict, keyed by resource class name, of inventory data
|
||||||
to set against the provider
|
to set against the provider
|
||||||
|
:param parent_provider_uuid:
|
||||||
|
If the provider is not a root, this is required, and represents
|
||||||
|
the UUID of the immediate parent, which is a provider for which
|
||||||
|
this method has already been invoked.
|
||||||
|
|
||||||
:raises: exc.InvalidResourceClass if a supplied custom resource class
|
:raises: exc.InvalidResourceClass if a supplied custom resource class
|
||||||
name does not meet the placement API's format requirements.
|
name does not meet the placement API's format requirements.
|
||||||
"""
|
"""
|
||||||
self._ensure_resource_provider(rp_uuid, rp_name)
|
self._ensure_resource_provider(
|
||||||
|
rp_uuid, rp_name, parent_provider_uuid=parent_provider_uuid)
|
||||||
|
|
||||||
# Auto-create custom resource classes coming from a virt driver
|
# Auto-create custom resource classes coming from a virt driver
|
||||||
list(map(self._ensure_resource_class,
|
list(map(self._ensure_resource_class,
|
||||||
|
|
|
@ -1155,8 +1155,8 @@ class TestProviderOperations(SchedulerReportClientTestCase):
|
||||||
self.client._ensure_resource_provider, uuids.compute_node)
|
self.client._ensure_resource_provider, uuids.compute_node)
|
||||||
|
|
||||||
get_rp_mock.assert_called_once_with(uuids.compute_node)
|
get_rp_mock.assert_called_once_with(uuids.compute_node)
|
||||||
create_rp_mock.assert_called_once_with(uuids.compute_node,
|
create_rp_mock.assert_called_once_with(
|
||||||
uuids.compute_node)
|
uuids.compute_node, uuids.compute_node, parent_provider_uuid=None)
|
||||||
self.assertFalse(self.client._provider_tree.exists(uuids.compute_node))
|
self.assertFalse(self.client._provider_tree.exists(uuids.compute_node))
|
||||||
self.assertFalse(get_agg_mock.called)
|
self.assertFalse(get_agg_mock.called)
|
||||||
self.assertEqual({}, self.client._provider_aggregate_map)
|
self.assertEqual({}, self.client._provider_aggregate_map)
|
||||||
|
@ -1178,7 +1178,11 @@ class TestProviderOperations(SchedulerReportClientTestCase):
|
||||||
'name': 'compute-name',
|
'name': 'compute-name',
|
||||||
'generation': 1,
|
'generation': 1,
|
||||||
}
|
}
|
||||||
self.client._ensure_resource_provider(uuids.compute_node)
|
self.assertEqual(
|
||||||
|
uuids.compute_node,
|
||||||
|
self.client._ensure_resource_provider(uuids.compute_node))
|
||||||
|
self._validate_provider(uuids.compute_node, name='compute-name',
|
||||||
|
generation=1, parent_uuid=None)
|
||||||
|
|
||||||
get_agg_mock.assert_called_once_with(uuids.compute_node)
|
get_agg_mock.assert_called_once_with(uuids.compute_node)
|
||||||
self.assertIn(uuids.compute_node, self.client._provider_aggregate_map)
|
self.assertIn(uuids.compute_node, self.client._provider_aggregate_map)
|
||||||
|
@ -1190,16 +1194,77 @@ class TestProviderOperations(SchedulerReportClientTestCase):
|
||||||
create_rp_mock.assert_called_once_with(
|
create_rp_mock.assert_called_once_with(
|
||||||
uuids.compute_node,
|
uuids.compute_node,
|
||||||
uuids.compute_node, # name param defaults to UUID if None
|
uuids.compute_node, # name param defaults to UUID if None
|
||||||
|
parent_provider_uuid=None,
|
||||||
)
|
)
|
||||||
self.assertTrue(self.client._provider_tree.exists(uuids.compute_node))
|
self.assertTrue(self.client._provider_tree.exists(uuids.compute_node))
|
||||||
|
|
||||||
create_rp_mock.reset_mock()
|
create_rp_mock.reset_mock()
|
||||||
|
|
||||||
self.client._ensure_resource_provider(uuids.compute_node,
|
self.assertEqual(
|
||||||
'compute-name')
|
uuids.compute_node,
|
||||||
|
self.client._ensure_resource_provider(uuids.compute_node))
|
||||||
|
self._validate_provider(uuids.compute_node, name='compute-name',
|
||||||
|
generation=1, parent_uuid=None)
|
||||||
|
|
||||||
# Shouldn't be called now that provider is in cache...
|
# Shouldn't be called now that provider is in cache...
|
||||||
self.assertFalse(create_rp_mock.called)
|
self.assertFalse(create_rp_mock.called)
|
||||||
|
|
||||||
|
# Validate the path where we specify a name (don't default to the UUID)
|
||||||
|
self.client._ensure_resource_provider(uuids.cn2, 'a-name')
|
||||||
|
create_rp_mock.assert_called_once_with(
|
||||||
|
uuids.cn2, 'a-name', parent_provider_uuid=None)
|
||||||
|
|
||||||
|
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||||
|
'_refresh_aggregate_map', new=mock.Mock())
|
||||||
|
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||||
|
'_create_resource_provider')
|
||||||
|
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||||
|
'_get_resource_provider')
|
||||||
|
def test_ensure_resource_provider_tree(self, get_rp_mock, create_rp_mock):
|
||||||
|
"""Test _ensure_resource_provider with a tree of providers."""
|
||||||
|
def _create_resource_provider(uuid, name, parent_provider_uuid=None):
|
||||||
|
"""Mock side effect for creating the RP with the specified args."""
|
||||||
|
return {
|
||||||
|
'uuid': uuid,
|
||||||
|
'name': name,
|
||||||
|
'generation': 0,
|
||||||
|
'parent_provider_uuid': parent_provider_uuid
|
||||||
|
}
|
||||||
|
create_rp_mock.side_effect = _create_resource_provider
|
||||||
|
|
||||||
|
# Not initially in the placement database, so we have to create it.
|
||||||
|
get_rp_mock.return_value = None
|
||||||
|
|
||||||
|
# Create the root
|
||||||
|
root = self.client._ensure_resource_provider(uuids.root)
|
||||||
|
self.assertEqual(uuids.root, root)
|
||||||
|
|
||||||
|
# Now create a child
|
||||||
|
child1 = self.client._ensure_resource_provider(
|
||||||
|
uuids.child1, name='junior', parent_provider_uuid=uuids.root)
|
||||||
|
self.assertEqual(uuids.child1, child1)
|
||||||
|
|
||||||
|
# If we re-ensure the child, we get the object from the tree, not a
|
||||||
|
# newly-created one - i.e. the early .find() works like it should.
|
||||||
|
self.assertIs(child1,
|
||||||
|
self.client._ensure_resource_provider(uuids.child1))
|
||||||
|
|
||||||
|
# Make sure we can create a grandchild
|
||||||
|
grandchild = self.client._ensure_resource_provider(
|
||||||
|
uuids.grandchild, parent_provider_uuid=uuids.child1)
|
||||||
|
self.assertEqual(uuids.grandchild, grandchild)
|
||||||
|
|
||||||
|
# Now create a second child of the root and make sure it doesn't wind
|
||||||
|
# up in some crazy wrong place like under child1 or grandchild
|
||||||
|
child2 = self.client._ensure_resource_provider(
|
||||||
|
uuids.child2, parent_provider_uuid=uuids.root)
|
||||||
|
self.assertEqual(uuids.child2, child2)
|
||||||
|
|
||||||
|
# At this point we should get all the providers.
|
||||||
|
self.assertEqual(
|
||||||
|
set([uuids.root, uuids.child1, uuids.child2, uuids.grandchild]),
|
||||||
|
self.client._provider_tree.get_provider_uuids())
|
||||||
|
|
||||||
def test_get_allocation_candidates(self):
|
def test_get_allocation_candidates(self):
|
||||||
resp_mock = mock.Mock(status_code=200)
|
resp_mock = mock.Mock(status_code=200)
|
||||||
json_data = {
|
json_data = {
|
||||||
|
@ -2455,6 +2520,7 @@ There was a conflict when trying to complete your request.
|
||||||
mock_erp.assert_called_once_with(
|
mock_erp.assert_called_once_with(
|
||||||
mock.sentinel.rp_uuid,
|
mock.sentinel.rp_uuid,
|
||||||
mock.sentinel.rp_name,
|
mock.sentinel.rp_name,
|
||||||
|
parent_provider_uuid=None,
|
||||||
)
|
)
|
||||||
# No custom resource classes to ensure...
|
# No custom resource classes to ensure...
|
||||||
self.assertFalse(mock_erc.called)
|
self.assertFalse(mock_erc.called)
|
||||||
|
@ -2489,6 +2555,7 @@ There was a conflict when trying to complete your request.
|
||||||
mock_erp.assert_called_once_with(
|
mock_erp.assert_called_once_with(
|
||||||
mock.sentinel.rp_uuid,
|
mock.sentinel.rp_uuid,
|
||||||
mock.sentinel.rp_name,
|
mock.sentinel.rp_name,
|
||||||
|
parent_provider_uuid=None,
|
||||||
)
|
)
|
||||||
self.assertFalse(mock_gocr.called)
|
self.assertFalse(mock_gocr.called)
|
||||||
self.assertFalse(mock_erc.called)
|
self.assertFalse(mock_erc.called)
|
||||||
|
@ -2554,6 +2621,7 @@ There was a conflict when trying to complete your request.
|
||||||
mock_erp.assert_called_once_with(
|
mock_erp.assert_called_once_with(
|
||||||
mock.sentinel.rp_uuid,
|
mock.sentinel.rp_uuid,
|
||||||
mock.sentinel.rp_name,
|
mock.sentinel.rp_name,
|
||||||
|
parent_provider_uuid=None,
|
||||||
)
|
)
|
||||||
mock_erc.assert_called_once_with('CUSTOM_IRON_SILVER')
|
mock_erc.assert_called_once_with('CUSTOM_IRON_SILVER')
|
||||||
mock_upd.assert_called_once_with(
|
mock_upd.assert_called_once_with(
|
||||||
|
@ -2563,6 +2631,19 @@ There was a conflict when trying to complete your request.
|
||||||
self.assertFalse(mock_gocr.called)
|
self.assertFalse(mock_gocr.called)
|
||||||
self.assertFalse(mock_del.called)
|
self.assertFalse(mock_del.called)
|
||||||
|
|
||||||
|
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||||
|
'_delete_inventory', new=mock.Mock())
|
||||||
|
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||||
|
'_ensure_resource_class', new=mock.Mock())
|
||||||
|
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||||
|
'_ensure_resource_provider')
|
||||||
|
def test_set_inventory_for_provider_with_parent(self, mock_erp):
|
||||||
|
"""Ensure parent UUID is sent through."""
|
||||||
|
self.client.set_inventory_for_provider(
|
||||||
|
uuids.child, 'junior', {}, parent_provider_uuid=uuids.parent)
|
||||||
|
mock_erp.assert_called_once_with(
|
||||||
|
uuids.child, 'junior', parent_provider_uuid=uuids.parent)
|
||||||
|
|
||||||
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||||
'put')
|
'put')
|
||||||
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||||
|
|
|
@ -114,4 +114,19 @@ class SchedulerClientTestCase(test.NoDBTestCase):
|
||||||
mock.sentinel.rp_uuid,
|
mock.sentinel.rp_uuid,
|
||||||
mock.sentinel.rp_name,
|
mock.sentinel.rp_name,
|
||||||
mock.sentinel.inv_data,
|
mock.sentinel.inv_data,
|
||||||
|
parent_provider_uuid=None,
|
||||||
|
)
|
||||||
|
# Pass the optional parent_provider_uuid
|
||||||
|
mock_set.reset_mock()
|
||||||
|
self.client.set_inventory_for_provider(
|
||||||
|
mock.sentinel.child_uuid,
|
||||||
|
mock.sentinel.child_name,
|
||||||
|
mock.sentinel.inv_data2,
|
||||||
|
parent_provider_uuid=mock.sentinel.rp_uuid,
|
||||||
|
)
|
||||||
|
mock_set.assert_called_once_with(
|
||||||
|
mock.sentinel.child_uuid,
|
||||||
|
mock.sentinel.child_name,
|
||||||
|
mock.sentinel.inv_data2,
|
||||||
|
parent_provider_uuid=mock.sentinel.rp_uuid,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue