Merge "Convert ironic virt driver to update_provider_tree"
This commit is contained in:
commit
697eeae50c
|
@ -912,19 +912,6 @@ class ResourceTracker(object):
|
|||
self.scheduler_client.update_compute_node(context,
|
||||
compute_node)
|
||||
|
||||
try:
|
||||
traits = self.driver.get_traits(nodename)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
else:
|
||||
# NOTE(mgoddard): set_traits_for_provider does not refresh the
|
||||
# provider tree in the report client, so we rely on the above
|
||||
# call to set_inventory_for_provider or update_compute_node to
|
||||
# ensure that the resource provider exists in the tree and has
|
||||
# had its cached traits refreshed.
|
||||
self.reportclient.set_traits_for_provider(
|
||||
context, compute_node.uuid, traits)
|
||||
|
||||
if self.pci_tracker:
|
||||
self.pci_tracker.save(context)
|
||||
|
||||
|
|
|
@ -1714,24 +1714,6 @@ class ProviderTreeTests(integrated_helpers.ProviderUsageBaseTestCase):
|
|||
self.assertNotIn('FOO', traits)
|
||||
|
||||
|
||||
class TraitsTrackingTests(integrated_helpers.ProviderUsageBaseTestCase):
|
||||
compute_driver = 'fake.SmallFakeDriver'
|
||||
|
||||
@mock.patch.object(fake.SmallFakeDriver, 'get_traits')
|
||||
def test_resource_provider_traits(self, mock_traits):
|
||||
traits = ['CUSTOM_FOO', 'HW_CPU_X86_VMX']
|
||||
mock_traits.return_value = traits
|
||||
|
||||
self.assertNotIn('CUSTOM_FOO', self._get_all_traits())
|
||||
self.assertEqual([], self._get_all_providers())
|
||||
|
||||
self.compute = self._start_compute(host='host1')
|
||||
|
||||
rp_uuid = self._get_provider_uuid_by_host('host1')
|
||||
self.assertEqual(traits, sorted(self._get_provider_traits(rp_uuid)))
|
||||
self.assertIn('CUSTOM_FOO', self._get_all_traits())
|
||||
|
||||
|
||||
class ServerMovingTests(integrated_helpers.ProviderUsageBaseTestCase):
|
||||
"""Tests moving servers while checking the resource allocations and usages
|
||||
|
||||
|
|
|
@ -438,7 +438,6 @@ def setup_rt(hostname, virt_resources=_VIRT_DRIVER_AVAIL_RESOURCES,
|
|||
virt_resources = copy.deepcopy(virt_resources)
|
||||
vd.get_available_resource.return_value = virt_resources
|
||||
vd.get_inventory.side_effect = NotImplementedError
|
||||
vd.get_traits.side_effect = NotImplementedError
|
||||
vd.update_provider_tree.side_effect = NotImplementedError
|
||||
vd.get_host_ip_addr.return_value = _NODENAME
|
||||
vd.estimate_instance_overhead.side_effect = estimate_overhead
|
||||
|
@ -1209,7 +1208,6 @@ class TestUpdateComputeNode(BaseTestCase):
|
|||
self.driver_mock.get_inventory.assert_called_once_with(_NODENAME)
|
||||
ucn_mock = self.sched_client_mock.update_compute_node
|
||||
ucn_mock.assert_called_once_with(mock.sentinel.ctx, new_compute)
|
||||
self.driver_mock.get_traits.assert_called_once_with(_NODENAME)
|
||||
|
||||
@mock.patch('nova.objects.ComputeNode.save')
|
||||
def test_existing_compute_node_updated_diff_updated_at(self, save_mock):
|
||||
|
@ -1258,7 +1256,6 @@ class TestUpdateComputeNode(BaseTestCase):
|
|||
self.assertFalse(norm_mock.called)
|
||||
ucn_mock = self.sched_client_mock.update_compute_node
|
||||
ucn_mock.assert_called_once_with(mock.sentinel.ctx, new_compute)
|
||||
self.driver_mock.get_traits.assert_called_once_with(_NODENAME)
|
||||
|
||||
@mock.patch('nova.compute.resource_tracker.'
|
||||
'_normalize_inventory_from_cn_obj')
|
||||
|
@ -1274,7 +1271,7 @@ class TestUpdateComputeNode(BaseTestCase):
|
|||
"""
|
||||
self._setup_rt()
|
||||
|
||||
# Emulate a driver that has implemented the new get_inventory() virt
|
||||
# Emulate a driver that has implemented the newish get_inventory() virt
|
||||
# driver method
|
||||
self.driver_mock.get_inventory.side_effect = [mock.sentinel.inv_data]
|
||||
|
||||
|
@ -1297,35 +1294,6 @@ class TestUpdateComputeNode(BaseTestCase):
|
|||
mock.sentinel.inv_data,
|
||||
)
|
||||
self.assertFalse(ucn_mock.called)
|
||||
self.driver_mock.get_traits.assert_called_once_with(_NODENAME)
|
||||
|
||||
def test_existing_node_get_traits_implemented(self):
|
||||
"""The get_traits() virt driver method is only implemented for some
|
||||
virt drivers. This method returns traits information for a
|
||||
node/provider, and if this method doesn't raise a NotImplementedError,
|
||||
this triggers _update() to call the set_traits_for_provider() method of
|
||||
the reporting client.
|
||||
"""
|
||||
self._setup_rt()
|
||||
rc = self.rt.reportclient
|
||||
rc.set_traits_for_provider = mock.MagicMock()
|
||||
|
||||
# Emulate a driver that has implemented the new get_traits() virt
|
||||
# driver method
|
||||
self.driver_mock.get_traits.side_effect = [mock.sentinel.traits]
|
||||
|
||||
orig_compute = _COMPUTE_NODE_FIXTURES[0].obj_clone()
|
||||
self.rt.compute_nodes[_NODENAME] = orig_compute
|
||||
self.rt.old_resources[_NODENAME] = orig_compute
|
||||
new_compute = orig_compute.obj_clone()
|
||||
|
||||
self.rt._update(mock.sentinel.ctx, new_compute)
|
||||
rc.set_traits_for_provider.assert_called_once_with(
|
||||
mock.sentinel.ctx,
|
||||
new_compute.uuid,
|
||||
mock.sentinel.traits,
|
||||
)
|
||||
self.driver_mock.get_traits.assert_called_once_with(_NODENAME)
|
||||
|
||||
@mock.patch('nova.objects.ComputeNode.save')
|
||||
def test_existing_node_update_provider_tree_implemented(self, save_mock):
|
||||
|
|
|
@ -27,6 +27,7 @@ from tooz import hashring as hash_ring
|
|||
from nova.api.metadata import base as instance_metadata
|
||||
from nova import block_device
|
||||
from nova.compute import power_state as nova_states
|
||||
from nova.compute import provider_tree
|
||||
from nova.compute import task_states
|
||||
from nova.compute import vm_states
|
||||
from nova.console import type as console_type
|
||||
|
@ -119,6 +120,9 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
self.ctx = nova_context.get_admin_context()
|
||||
self.instance_uuid = uuidutils.generate_uuid()
|
||||
|
||||
self.ptree = provider_tree.ProviderTree()
|
||||
self.ptree.new_root(mock.sentinel.nodename, mock.sentinel.nodename)
|
||||
|
||||
# mock retries configs to avoid sleeps and make tests run quicker
|
||||
CONF.set_default('api_max_retries', default=1, group='ironic')
|
||||
CONF.set_default('api_retry_interval', default=0, group='ironic')
|
||||
|
@ -709,8 +713,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
'_node_resources_unavailable', return_value=False)
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_resource')
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_from_cache')
|
||||
def test_get_inventory_no_rc(self, mock_nfc, mock_nr, mock_res_unavail,
|
||||
mock_res_used):
|
||||
def test_update_provider_tree_no_rc(self, mock_nfc, mock_nr,
|
||||
mock_res_unavail, mock_res_used):
|
||||
"""Ensure that when node.resource_class is missing, that we return the
|
||||
legacy VCPU, MEMORY_MB and DISK_GB resources for inventory.
|
||||
"""
|
||||
|
@ -724,7 +728,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
'resource_class': None,
|
||||
}
|
||||
|
||||
result = self.driver.get_inventory(mock.sentinel.nodename)
|
||||
self.driver.update_provider_tree(self.ptree, mock.sentinel.nodename)
|
||||
|
||||
expected = {
|
||||
fields.ResourceClass.VCPU: {
|
||||
|
@ -756,6 +760,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
mock_nr.assert_called_once_with(mock_nfc.return_value)
|
||||
mock_res_used.assert_called_once_with(mock_nfc.return_value)
|
||||
mock_res_unavail.assert_called_once_with(mock_nfc.return_value)
|
||||
result = self.ptree.data(mock.sentinel.nodename).inventory
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch.object(ironic_driver.IronicDriver,
|
||||
|
@ -764,8 +769,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
'_node_resources_unavailable', return_value=False)
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_resource')
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_from_cache')
|
||||
def test_get_inventory_with_rc(self, mock_nfc, mock_nr, mock_res_unavail,
|
||||
mock_res_used):
|
||||
def test_update_provider_tree_with_rc(self, mock_nfc, mock_nr,
|
||||
mock_res_unavail, mock_res_used):
|
||||
"""Ensure that when node.resource_class is present, that we return the
|
||||
legacy VCPU, MEMORY_MB and DISK_GB resources for inventory in addition
|
||||
to the custom resource class inventory record.
|
||||
|
@ -780,7 +785,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
'resource_class': 'iron-nfv',
|
||||
}
|
||||
|
||||
result = self.driver.get_inventory(mock.sentinel.nodename)
|
||||
self.driver.update_provider_tree(self.ptree, mock.sentinel.nodename)
|
||||
|
||||
expected = {
|
||||
fields.ResourceClass.VCPU: {
|
||||
|
@ -820,6 +825,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
mock_nr.assert_called_once_with(mock_nfc.return_value)
|
||||
mock_res_used.assert_called_once_with(mock_nfc.return_value)
|
||||
mock_res_unavail.assert_called_once_with(mock_nfc.return_value)
|
||||
result = self.ptree.data(mock.sentinel.nodename).inventory
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch.object(ironic_driver.IronicDriver,
|
||||
|
@ -828,8 +834,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
'_node_resources_unavailable', return_value=False)
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_resource')
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_from_cache')
|
||||
def test_get_inventory_only_rc(self, mock_nfc, mock_nr, mock_res_unavail,
|
||||
mock_res_used):
|
||||
def test_update_provider_tree_only_rc(self, mock_nfc, mock_nr,
|
||||
mock_res_unavail, mock_res_used):
|
||||
"""Ensure that when node.resource_class is present, that we return the
|
||||
legacy VCPU, MEMORY_MB and DISK_GB resources for inventory in addition
|
||||
to the custom resource class inventory record.
|
||||
|
@ -844,7 +850,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
'resource_class': 'iron-nfv',
|
||||
}
|
||||
|
||||
result = self.driver.get_inventory(mock.sentinel.nodename)
|
||||
self.driver.update_provider_tree(self.ptree, mock.sentinel.nodename)
|
||||
|
||||
expected = {
|
||||
'CUSTOM_IRON_NFV': {
|
||||
|
@ -860,6 +866,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
mock_nr.assert_called_once_with(mock_nfc.return_value)
|
||||
mock_res_used.assert_called_once_with(mock_nfc.return_value)
|
||||
mock_res_unavail.assert_called_once_with(mock_nfc.return_value)
|
||||
result = self.ptree.data(mock.sentinel.nodename).inventory
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch.object(ironic_driver.IronicDriver,
|
||||
|
@ -868,8 +875,9 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
'_node_resources_unavailable', return_value=False)
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_resource')
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_from_cache')
|
||||
def test_get_inventory_with_rc_occupied(self, mock_nfc, mock_nr,
|
||||
mock_res_unavail, mock_res_used):
|
||||
def test_update_provider_tree_with_rc_occupied(self, mock_nfc, mock_nr,
|
||||
mock_res_unavail,
|
||||
mock_res_used):
|
||||
"""Ensure that when a node is used, we report the inventory matching
|
||||
the consumed resources.
|
||||
"""
|
||||
|
@ -883,7 +891,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
'resource_class': 'iron-nfv',
|
||||
}
|
||||
|
||||
result = self.driver.get_inventory(mock.sentinel.nodename)
|
||||
self.driver.update_provider_tree(self.ptree, mock.sentinel.nodename)
|
||||
|
||||
expected = {
|
||||
fields.ResourceClass.VCPU: {
|
||||
|
@ -923,6 +931,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
mock_nr.assert_called_once_with(mock_nfc.return_value)
|
||||
mock_res_used.assert_called_once_with(mock_nfc.return_value)
|
||||
self.assertFalse(mock_res_unavail.called)
|
||||
result = self.ptree.data(mock.sentinel.nodename).inventory
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch.object(ironic_driver.IronicDriver,
|
||||
|
@ -930,37 +939,80 @@ class IronicDriverTestCase(test.NoDBTestCase):
|
|||
@mock.patch.object(ironic_driver.IronicDriver,
|
||||
'_node_resources_unavailable', return_value=True)
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_from_cache')
|
||||
def test_get_inventory_disabled_node(self, mock_nfc, mock_res_unavail,
|
||||
mock_res_used):
|
||||
"""Ensure that when a node is disabled, that get_inventory() returns
|
||||
an empty dict.
|
||||
def test_update_provider_tree_disabled_node(self, mock_nfc,
|
||||
mock_res_unavail,
|
||||
mock_res_used):
|
||||
"""Ensure that when a node is disabled, that update_provider_tree()
|
||||
sets inventory to an empty dict.
|
||||
"""
|
||||
result = self.driver.get_inventory(mock.sentinel.nodename)
|
||||
self.driver.update_provider_tree(self.ptree, mock.sentinel.nodename)
|
||||
mock_nfc.assert_called_once_with(mock.sentinel.nodename)
|
||||
mock_res_used.assert_called_once_with(mock_nfc.return_value)
|
||||
mock_res_unavail.assert_called_once_with(mock_nfc.return_value)
|
||||
result = self.ptree.data(mock.sentinel.nodename).inventory
|
||||
self.assertEqual({}, result)
|
||||
|
||||
@mock.patch.object(ironic_driver.IronicDriver,
|
||||
'_node_resources_used', return_value=True)
|
||||
@mock.patch.object(ironic_driver.IronicDriver,
|
||||
'_node_resources_unavailable', return_value=False)
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_resource')
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_from_cache')
|
||||
def test_get_traits_no_traits(self, mock_nfc):
|
||||
"""Ensure that when the node has no traits, we return no traits."""
|
||||
node = _get_cached_node()
|
||||
mock_nfc.return_value = node
|
||||
result = self.driver.get_traits(node.uuid)
|
||||
def test_update_provider_tree_no_traits(self, mock_nfc, mock_nr,
|
||||
mock_res_unavail, mock_res_used):
|
||||
"""Ensure that when the node has no traits, we set no traits."""
|
||||
mock_nr.return_value = {
|
||||
'vcpus': 24,
|
||||
'vcpus_used': 24,
|
||||
'memory_mb': 1024,
|
||||
'memory_mb_used': 1024,
|
||||
'local_gb': 100,
|
||||
'local_gb_used': 100,
|
||||
'resource_class': 'iron-nfv',
|
||||
}
|
||||
|
||||
mock_nfc.assert_called_once_with(node.uuid)
|
||||
self.assertEqual([], result)
|
||||
mock_nfc.return_value = _get_cached_node(uuid=mock.sentinel.nodename)
|
||||
|
||||
self.driver.update_provider_tree(self.ptree, mock.sentinel.nodename)
|
||||
|
||||
mock_nfc.assert_called_once_with(mock.sentinel.nodename)
|
||||
mock_nr.assert_called_once_with(mock_nfc.return_value)
|
||||
mock_res_used.assert_called_once_with(mock_nfc.return_value)
|
||||
self.assertFalse(mock_res_unavail.called)
|
||||
result = self.ptree.data(mock.sentinel.nodename).traits
|
||||
self.assertEqual(set(), result)
|
||||
|
||||
@mock.patch.object(ironic_driver.IronicDriver,
|
||||
'_node_resources_used', return_value=True)
|
||||
@mock.patch.object(ironic_driver.IronicDriver,
|
||||
'_node_resources_unavailable', return_value=False)
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_resource')
|
||||
@mock.patch.object(ironic_driver.IronicDriver, '_node_from_cache')
|
||||
def test_get_traits_with_traits(self, mock_nfc):
|
||||
"""Ensure that when the node has traits, we return the traits."""
|
||||
node = _get_cached_node(traits=['trait1', 'trait2'])
|
||||
mock_nfc.return_value = node
|
||||
result = self.driver.get_traits(node.uuid)
|
||||
def test_update_provider_tree_with_traits(self, mock_nfc, mock_nr,
|
||||
mock_res_unavail, mock_res_used):
|
||||
"""Ensure that when the node has traits, we set the traits."""
|
||||
mock_nr.return_value = {
|
||||
'vcpus': 24,
|
||||
'vcpus_used': 24,
|
||||
'memory_mb': 1024,
|
||||
'memory_mb_used': 1024,
|
||||
'local_gb': 100,
|
||||
'local_gb_used': 100,
|
||||
'resource_class': 'iron-nfv',
|
||||
}
|
||||
|
||||
expected = ['trait1', 'trait2']
|
||||
mock_nfc.assert_called_once_with(node.uuid)
|
||||
self.assertEqual(expected, result)
|
||||
traits = ['trait1', 'trait2']
|
||||
mock_nfc.return_value = _get_cached_node(
|
||||
uuid=mock.sentinel.nodename, traits=traits)
|
||||
|
||||
self.driver.update_provider_tree(self.ptree, mock.sentinel.nodename)
|
||||
|
||||
mock_nfc.assert_called_once_with(mock.sentinel.nodename)
|
||||
mock_nr.assert_called_once_with(mock_nfc.return_value)
|
||||
mock_res_used.assert_called_once_with(mock_nfc.return_value)
|
||||
self.assertFalse(mock_res_unavail.called)
|
||||
result = self.ptree.data(mock.sentinel.nodename).traits
|
||||
self.assertEqual(set(traits), result)
|
||||
|
||||
@mock.patch.object(FAKE_CLIENT.node, 'get')
|
||||
@mock.patch.object(FAKE_CLIENT.node, 'list')
|
||||
|
|
|
@ -883,17 +883,6 @@ class ComputeDriver(object):
|
|||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_traits(self, nodename):
|
||||
"""Get the traits for a given node.
|
||||
|
||||
Any custom traits returned are not required to exist in the placement
|
||||
service - the caller will ensure their existence.
|
||||
|
||||
:param nodename: the name of the node.
|
||||
:returns: an iterable of string trait names for the supplied node.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_available_resource(self, nodename):
|
||||
"""Retrieve resource information.
|
||||
|
||||
|
|
|
@ -734,9 +734,21 @@ class IronicDriver(virt_driver.ComputeDriver):
|
|||
|
||||
return node_uuids
|
||||
|
||||
def get_inventory(self, nodename):
|
||||
"""Return a dict, keyed by resource class, of inventory information for
|
||||
the supplied node.
|
||||
def update_provider_tree(self, provider_tree, nodename):
|
||||
"""Update a ProviderTree object with current resource provider and
|
||||
inventory information.
|
||||
|
||||
:param nova.compute.provider_tree.ProviderTree provider_tree:
|
||||
A nova.compute.provider_tree.ProviderTree object representing all
|
||||
the providers in the tree associated with the compute node, and any
|
||||
sharing providers (those with the ``MISC_SHARES_VIA_AGGREGATE``
|
||||
trait) associated via aggregate with any of those providers (but
|
||||
not *their* tree- or aggregate-associated providers), as currently
|
||||
known by placement.
|
||||
:param nodename:
|
||||
String name of the compute node (i.e.
|
||||
ComputeNode.hypervisor_hostname) for which the caller is requesting
|
||||
updated provider information.
|
||||
"""
|
||||
# nodename is the ironic node's UUID.
|
||||
node = self._node_from_cache(nodename)
|
||||
|
@ -788,19 +800,10 @@ class IronicDriver(virt_driver.ComputeDriver):
|
|||
'allocation_ratio': 1.0,
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def get_traits(self, nodename):
|
||||
"""Get the traits for a given node.
|
||||
|
||||
Any custom traits returned are not required to exist in the placement
|
||||
service - the caller will ensure their existence.
|
||||
|
||||
:param nodename: the UUID of the node.
|
||||
:returns: an iterable of string trait names for the supplied node.
|
||||
"""
|
||||
node = self._node_from_cache(nodename)
|
||||
return list(node.traits)
|
||||
provider_tree.update_inventory(nodename, result)
|
||||
# TODO(efried): *Unset* traits that are "owned" by ironic virt but not
|
||||
# set on the node object.
|
||||
provider_tree.add_traits(nodename, *node.traits)
|
||||
|
||||
def get_available_resource(self, nodename):
|
||||
"""Retrieve resource information.
|
||||
|
|
Loading…
Reference in New Issue