8e1ca5bf34
oslo.utils release 3.37.0 [1] introduced uuidsentinel [2]. This change rips out nova's uuidsentinel and replaces it with the one from oslo.utils. [1] https://review.openstack.org/#/c/599754/ [2] https://review.openstack.org/#/c/594179/ Change-Id: I7f5f08691ca3f73073c66c29dddb996fb2c2b266 Depends-On: https://review.openstack.org/600041
683 lines
26 KiB
Python
683 lines
26 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
from oslo_utils.fixture import uuidsentinel as uuids
|
|
|
|
from nova.compute import provider_tree
|
|
from nova import objects
|
|
from nova import test
|
|
|
|
|
|
class TestProviderTree(test.NoDBTestCase):
|
|
|
|
def setUp(self):
|
|
super(TestProviderTree, self).setUp()
|
|
self.compute_node1 = objects.ComputeNode(
|
|
uuid=uuids.cn1,
|
|
hypervisor_hostname='compute-node-1',
|
|
)
|
|
self.compute_node2 = objects.ComputeNode(
|
|
uuid=uuids.cn2,
|
|
hypervisor_hostname='compute-node-2',
|
|
)
|
|
self.compute_nodes = objects.ComputeNodeList(
|
|
objects=[self.compute_node1, self.compute_node2],
|
|
)
|
|
|
|
def _pt_with_cns(self):
|
|
pt = provider_tree.ProviderTree()
|
|
for cn in self.compute_nodes:
|
|
pt.new_root(cn.hypervisor_hostname, cn.uuid, generation=0)
|
|
return pt
|
|
|
|
def test_tree_ops(self):
|
|
cn1 = self.compute_node1
|
|
cn2 = self.compute_node2
|
|
pt = self._pt_with_cns()
|
|
|
|
self.assertRaises(
|
|
ValueError,
|
|
pt.new_root,
|
|
cn1.hypervisor_hostname,
|
|
cn1.uuid,
|
|
)
|
|
|
|
self.assertTrue(pt.exists(cn1.uuid))
|
|
self.assertTrue(pt.exists(cn1.hypervisor_hostname))
|
|
self.assertFalse(pt.exists(uuids.non_existing_rp))
|
|
self.assertFalse(pt.exists('noexist'))
|
|
|
|
self.assertEqual([cn1.uuid],
|
|
pt.get_provider_uuids(name_or_uuid=cn1.uuid))
|
|
self.assertEqual(set([cn1.uuid, cn2.uuid]),
|
|
set(pt.get_provider_uuids()))
|
|
|
|
numa_cell0_uuid = pt.new_child('numa_cell0', cn1.uuid)
|
|
numa_cell1_uuid = pt.new_child('numa_cell1', cn1.hypervisor_hostname)
|
|
|
|
self.assertEqual(cn1.uuid, pt.data(numa_cell1_uuid).parent_uuid)
|
|
|
|
self.assertTrue(pt.exists(numa_cell0_uuid))
|
|
self.assertTrue(pt.exists('numa_cell0'))
|
|
|
|
self.assertTrue(pt.exists(numa_cell1_uuid))
|
|
self.assertTrue(pt.exists('numa_cell1'))
|
|
|
|
pf1_cell0_uuid = pt.new_child('pf1_cell0', numa_cell0_uuid)
|
|
self.assertTrue(pt.exists(pf1_cell0_uuid))
|
|
self.assertTrue(pt.exists('pf1_cell0'))
|
|
|
|
# Now we've got a 3-level tree under cn1 - check provider UUIDs again
|
|
self.assertEqual(
|
|
set([cn1.uuid, numa_cell0_uuid, pf1_cell0_uuid, numa_cell1_uuid]),
|
|
set(pt.get_provider_uuids(name_or_uuid=cn1.uuid)))
|
|
self.assertEqual(
|
|
set([cn1.uuid, cn2.uuid, numa_cell0_uuid, pf1_cell0_uuid,
|
|
numa_cell1_uuid]),
|
|
set(pt.get_provider_uuids()))
|
|
|
|
self.assertRaises(
|
|
ValueError,
|
|
pt.new_child,
|
|
'pf1_cell0',
|
|
uuids.non_existing_rp,
|
|
)
|
|
|
|
# Fail attempting to add a child that already exists in the tree
|
|
# Existing provider is a child; search by name
|
|
self.assertRaises(ValueError, pt.new_child, 'numa_cell0', cn1.uuid)
|
|
# Existing provider is a root; search by UUID
|
|
self.assertRaises(ValueError, pt.new_child, cn1.uuid, cn2.uuid)
|
|
|
|
# Test data().
|
|
# Root, by UUID
|
|
cn1_snap = pt.data(cn1.uuid)
|
|
# Fields were faithfully copied
|
|
self.assertEqual(cn1.uuid, cn1_snap.uuid)
|
|
self.assertEqual(cn1.hypervisor_hostname, cn1_snap.name)
|
|
self.assertIsNone(cn1_snap.parent_uuid)
|
|
self.assertEqual({}, cn1_snap.inventory)
|
|
self.assertEqual(set(), cn1_snap.traits)
|
|
self.assertEqual(set(), cn1_snap.aggregates)
|
|
# Validate read-only-ness
|
|
self.assertRaises(AttributeError, setattr, cn1_snap, 'name', 'foo')
|
|
|
|
cn3 = objects.ComputeNode(
|
|
uuid=uuids.cn3,
|
|
hypervisor_hostname='compute-node-3',
|
|
)
|
|
self.assertFalse(pt.exists(cn3.uuid))
|
|
self.assertFalse(pt.exists(cn3.hypervisor_hostname))
|
|
pt.new_root(cn3.hypervisor_hostname, cn3.uuid)
|
|
|
|
self.assertTrue(pt.exists(cn3.uuid))
|
|
self.assertTrue(pt.exists(cn3.hypervisor_hostname))
|
|
|
|
self.assertRaises(
|
|
ValueError,
|
|
pt.new_root,
|
|
cn3.hypervisor_hostname,
|
|
cn3.uuid,
|
|
)
|
|
|
|
self.assertRaises(
|
|
ValueError,
|
|
pt.remove,
|
|
uuids.non_existing_rp,
|
|
)
|
|
|
|
pt.remove(numa_cell1_uuid)
|
|
self.assertFalse(pt.exists(numa_cell1_uuid))
|
|
self.assertTrue(pt.exists(pf1_cell0_uuid))
|
|
self.assertTrue(pt.exists(numa_cell0_uuid))
|
|
self.assertTrue(pt.exists(uuids.cn1))
|
|
|
|
# Now remove the root and check that children no longer exist
|
|
pt.remove(uuids.cn1)
|
|
self.assertFalse(pt.exists(pf1_cell0_uuid))
|
|
self.assertFalse(pt.exists(numa_cell0_uuid))
|
|
self.assertFalse(pt.exists(uuids.cn1))
|
|
|
|
def test_populate_from_iterable_empty(self):
|
|
pt = provider_tree.ProviderTree()
|
|
# Empty list is a no-op
|
|
pt.populate_from_iterable([])
|
|
self.assertEqual([], pt.get_provider_uuids())
|
|
|
|
def test_populate_from_iterable_error_orphan_cycle(self):
|
|
pt = provider_tree.ProviderTree()
|
|
|
|
# Error trying to populate with an orphan
|
|
grandchild1_1 = {
|
|
'uuid': uuids.grandchild1_1,
|
|
'name': 'grandchild1_1',
|
|
'generation': 11,
|
|
'parent_provider_uuid': uuids.child1,
|
|
}
|
|
|
|
self.assertRaises(ValueError,
|
|
pt.populate_from_iterable, [grandchild1_1])
|
|
|
|
# Create a cycle so there are no orphans, but no path to a root
|
|
cycle = {
|
|
'uuid': uuids.child1,
|
|
'name': 'child1',
|
|
'generation': 1,
|
|
# There's a country song about this
|
|
'parent_provider_uuid': uuids.grandchild1_1,
|
|
}
|
|
|
|
self.assertRaises(ValueError,
|
|
pt.populate_from_iterable, [grandchild1_1, cycle])
|
|
|
|
def test_populate_from_iterable_complex(self):
|
|
# root
|
|
# +-> child1
|
|
# | +-> grandchild1_2
|
|
# | +-> ggc1_2_1
|
|
# | +-> ggc1_2_2
|
|
# | +-> ggc1_2_3
|
|
# +-> child2
|
|
# another_root
|
|
pt = provider_tree.ProviderTree()
|
|
plist = [
|
|
{
|
|
'uuid': uuids.root,
|
|
'name': 'root',
|
|
'generation': 0,
|
|
},
|
|
{
|
|
'uuid': uuids.child1,
|
|
'name': 'child1',
|
|
'generation': 1,
|
|
'parent_provider_uuid': uuids.root,
|
|
},
|
|
{
|
|
'uuid': uuids.child2,
|
|
'name': 'child2',
|
|
'generation': 2,
|
|
'parent_provider_uuid': uuids.root,
|
|
},
|
|
{
|
|
'uuid': uuids.grandchild1_2,
|
|
'name': 'grandchild1_2',
|
|
'generation': 12,
|
|
'parent_provider_uuid': uuids.child1,
|
|
},
|
|
{
|
|
'uuid': uuids.ggc1_2_1,
|
|
'name': 'ggc1_2_1',
|
|
'generation': 121,
|
|
'parent_provider_uuid': uuids.grandchild1_2,
|
|
},
|
|
{
|
|
'uuid': uuids.ggc1_2_2,
|
|
'name': 'ggc1_2_2',
|
|
'generation': 122,
|
|
'parent_provider_uuid': uuids.grandchild1_2,
|
|
},
|
|
{
|
|
'uuid': uuids.ggc1_2_3,
|
|
'name': 'ggc1_2_3',
|
|
'generation': 123,
|
|
'parent_provider_uuid': uuids.grandchild1_2,
|
|
},
|
|
{
|
|
'uuid': uuids.another_root,
|
|
'name': 'another_root',
|
|
'generation': 911,
|
|
},
|
|
]
|
|
pt.populate_from_iterable(plist)
|
|
|
|
def validate_root(expected_uuids):
|
|
# Make sure we have all and only the expected providers
|
|
self.assertEqual(expected_uuids, set(pt.get_provider_uuids()))
|
|
# Now make sure they're in the right hierarchy. Cheat: get the
|
|
# actual _Provider to make it easier to walk the tree (ProviderData
|
|
# doesn't include children).
|
|
root = pt._find_with_lock(uuids.root)
|
|
self.assertEqual(uuids.root, root.uuid)
|
|
self.assertEqual('root', root.name)
|
|
self.assertEqual(0, root.generation)
|
|
self.assertIsNone(root.parent_uuid)
|
|
self.assertEqual(2, len(list(root.children)))
|
|
for child in root.children.values():
|
|
self.assertTrue(child.name.startswith('child'))
|
|
if child.name == 'child1':
|
|
if uuids.grandchild1_1 in expected_uuids:
|
|
self.assertEqual(2, len(list(child.children)))
|
|
else:
|
|
self.assertEqual(1, len(list(child.children)))
|
|
for grandchild in child.children.values():
|
|
self.assertTrue(grandchild.name.startswith(
|
|
'grandchild1_'))
|
|
if grandchild.name == 'grandchild1_1':
|
|
self.assertEqual(0, len(list(grandchild.children)))
|
|
if grandchild.name == 'grandchild1_2':
|
|
self.assertEqual(3, len(list(grandchild.children)))
|
|
for ggc in grandchild.children.values():
|
|
self.assertTrue(ggc.name.startswith('ggc1_2_'))
|
|
another_root = pt._find_with_lock(uuids.another_root)
|
|
self.assertEqual(uuids.another_root, another_root.uuid)
|
|
self.assertEqual('another_root', another_root.name)
|
|
self.assertEqual(911, another_root.generation)
|
|
self.assertIsNone(another_root.parent_uuid)
|
|
self.assertEqual(0, len(list(another_root.children)))
|
|
if uuids.new_root in expected_uuids:
|
|
new_root = pt._find_with_lock(uuids.new_root)
|
|
self.assertEqual(uuids.new_root, new_root.uuid)
|
|
self.assertEqual('new_root', new_root.name)
|
|
self.assertEqual(42, new_root.generation)
|
|
self.assertIsNone(new_root.parent_uuid)
|
|
self.assertEqual(0, len(list(new_root.children)))
|
|
|
|
expected_uuids = set([
|
|
uuids.root, uuids.child1, uuids.child2, uuids.grandchild1_2,
|
|
uuids.ggc1_2_1, uuids.ggc1_2_2, uuids.ggc1_2_3,
|
|
uuids.another_root])
|
|
|
|
validate_root(expected_uuids)
|
|
|
|
# Merge an orphan - still an error
|
|
orphan = {
|
|
'uuid': uuids.orphan,
|
|
'name': 'orphan',
|
|
'generation': 86,
|
|
'parent_provider_uuid': uuids.mystery,
|
|
}
|
|
self.assertRaises(ValueError, pt.populate_from_iterable, [orphan])
|
|
|
|
# And the tree didn't change
|
|
validate_root(expected_uuids)
|
|
|
|
# Merge a list with a new grandchild and a new root
|
|
plist = [
|
|
{
|
|
'uuid': uuids.grandchild1_1,
|
|
'name': 'grandchild1_1',
|
|
'generation': 11,
|
|
'parent_provider_uuid': uuids.child1,
|
|
},
|
|
{
|
|
'uuid': uuids.new_root,
|
|
'name': 'new_root',
|
|
'generation': 42,
|
|
},
|
|
]
|
|
pt.populate_from_iterable(plist)
|
|
|
|
expected_uuids |= set([uuids.grandchild1_1, uuids.new_root])
|
|
|
|
validate_root(expected_uuids)
|
|
|
|
# Merge an empty list - still a no-op
|
|
pt.populate_from_iterable([])
|
|
validate_root(expected_uuids)
|
|
|
|
# Since we have a complex tree, test the ordering of get_provider_uuids
|
|
# We can't predict the order of siblings, or where nephews will appear
|
|
# relative to their uncles, but we can guarantee that any given child
|
|
# always comes after its parent (and by extension, its ancestors too).
|
|
puuids = pt.get_provider_uuids()
|
|
for desc in (uuids.child1, uuids.child2):
|
|
self.assertGreater(puuids.index(desc), puuids.index(uuids.root))
|
|
for desc in (uuids.grandchild1_1, uuids.grandchild1_2):
|
|
self.assertGreater(puuids.index(desc), puuids.index(uuids.child1))
|
|
for desc in (uuids.ggc1_2_1, uuids.ggc1_2_2, uuids.ggc1_2_3):
|
|
self.assertGreater(
|
|
puuids.index(desc), puuids.index(uuids.grandchild1_2))
|
|
|
|
def test_populate_from_iterable_with_root_update(self):
|
|
# Ensure we can update hierarchies, including adding children, in a
|
|
# tree that's already populated. This tests the case where a given
|
|
# provider exists both in the tree and in the input. We must replace
|
|
# that provider *before* we inject its descendants; otherwise the
|
|
# descendants will be lost. Note that this test case is not 100%
|
|
# reliable, as we can't predict the order over which hashed values are
|
|
# iterated.
|
|
|
|
pt = provider_tree.ProviderTree()
|
|
|
|
# Let's create a root
|
|
plist = [
|
|
{
|
|
'uuid': uuids.root,
|
|
'name': 'root',
|
|
'generation': 0,
|
|
},
|
|
]
|
|
pt.populate_from_iterable(plist)
|
|
expected_uuids = [uuids.root]
|
|
self.assertEqual(expected_uuids, pt.get_provider_uuids())
|
|
|
|
# Let's add a child updating the name and generation for the root.
|
|
# root
|
|
# +-> child1
|
|
plist = [
|
|
{
|
|
'uuid': uuids.root,
|
|
'name': 'root_with_new_name',
|
|
'generation': 1,
|
|
},
|
|
{
|
|
'uuid': uuids.child1,
|
|
'name': 'child1',
|
|
'generation': 1,
|
|
'parent_provider_uuid': uuids.root,
|
|
},
|
|
]
|
|
pt.populate_from_iterable(plist)
|
|
expected_uuids = [uuids.root, uuids.child1]
|
|
self.assertEqual(expected_uuids, pt.get_provider_uuids())
|
|
|
|
def test_populate_from_iterable_disown_grandchild(self):
|
|
# Start with:
|
|
# root
|
|
# +-> child
|
|
# | +-> grandchild
|
|
# Then send in [child] and grandchild should disappear.
|
|
child = {
|
|
'uuid': uuids.child,
|
|
'name': 'child',
|
|
'generation': 1,
|
|
'parent_provider_uuid': uuids.root,
|
|
}
|
|
pt = provider_tree.ProviderTree()
|
|
plist = [
|
|
{
|
|
'uuid': uuids.root,
|
|
'name': 'root',
|
|
'generation': 0,
|
|
},
|
|
child,
|
|
{
|
|
'uuid': uuids.grandchild,
|
|
'name': 'grandchild',
|
|
'generation': 2,
|
|
'parent_provider_uuid': uuids.child,
|
|
},
|
|
]
|
|
pt.populate_from_iterable(plist)
|
|
self.assertEqual([uuids.root, uuids.child, uuids.grandchild],
|
|
pt.get_provider_uuids())
|
|
self.assertTrue(pt.exists(uuids.grandchild))
|
|
pt.populate_from_iterable([child])
|
|
self.assertEqual([uuids.root, uuids.child], pt.get_provider_uuids())
|
|
self.assertFalse(pt.exists(uuids.grandchild))
|
|
|
|
def test_has_inventory_changed_no_existing_rp(self):
|
|
pt = self._pt_with_cns()
|
|
self.assertRaises(
|
|
ValueError,
|
|
pt.has_inventory_changed,
|
|
uuids.non_existing_rp,
|
|
{}
|
|
)
|
|
|
|
def test_update_inventory_no_existing_rp(self):
|
|
pt = self._pt_with_cns()
|
|
self.assertRaises(
|
|
ValueError,
|
|
pt.update_inventory,
|
|
uuids.non_existing_rp,
|
|
{},
|
|
)
|
|
|
|
def test_has_inventory_changed(self):
|
|
cn = self.compute_node1
|
|
pt = self._pt_with_cns()
|
|
rp_gen = 1
|
|
|
|
cn_inv = {
|
|
'VCPU': {
|
|
'total': 8,
|
|
'min_unit': 1,
|
|
'max_unit': 8,
|
|
'step_size': 1,
|
|
'allocation_ratio': 16.0,
|
|
},
|
|
'MEMORY_MB': {
|
|
'total': 1024,
|
|
'reserved': 512,
|
|
'min_unit': 64,
|
|
'max_unit': 1024,
|
|
'step_size': 64,
|
|
'allocation_ratio': 1.5,
|
|
},
|
|
'DISK_GB': {
|
|
'total': 1000,
|
|
'reserved': 100,
|
|
'min_unit': 10,
|
|
'max_unit': 1000,
|
|
'step_size': 10,
|
|
'allocation_ratio': 1.0,
|
|
},
|
|
}
|
|
self.assertTrue(pt.has_inventory_changed(cn.uuid, cn_inv))
|
|
self.assertTrue(pt.update_inventory(cn.uuid, cn_inv,
|
|
generation=rp_gen))
|
|
|
|
# Updating with the same inventory info should return False
|
|
self.assertFalse(pt.has_inventory_changed(cn.uuid, cn_inv))
|
|
self.assertFalse(pt.update_inventory(cn.uuid, cn_inv,
|
|
generation=rp_gen))
|
|
|
|
# A data-grab's inventory should be "equal" to the original
|
|
cndata = pt.data(cn.uuid)
|
|
self.assertFalse(pt.has_inventory_changed(cn.uuid, cndata.inventory))
|
|
|
|
cn_inv['VCPU']['total'] = 6
|
|
self.assertTrue(pt.has_inventory_changed(cn.uuid, cn_inv))
|
|
self.assertTrue(pt.update_inventory(cn.uuid, cn_inv,
|
|
generation=rp_gen))
|
|
|
|
# The data() result was not affected; now the tree's copy is different
|
|
self.assertTrue(pt.has_inventory_changed(cn.uuid, cndata.inventory))
|
|
|
|
self.assertFalse(pt.has_inventory_changed(cn.uuid, cn_inv))
|
|
self.assertFalse(pt.update_inventory(cn.uuid, cn_inv,
|
|
generation=rp_gen))
|
|
|
|
# Deleting a key in the new record should NOT result in changes being
|
|
# recorded...
|
|
del cn_inv['VCPU']['allocation_ratio']
|
|
self.assertFalse(pt.has_inventory_changed(cn.uuid, cn_inv))
|
|
self.assertFalse(pt.update_inventory(cn.uuid, cn_inv,
|
|
generation=rp_gen))
|
|
|
|
del cn_inv['MEMORY_MB']
|
|
self.assertTrue(pt.has_inventory_changed(cn.uuid, cn_inv))
|
|
self.assertTrue(pt.update_inventory(cn.uuid, cn_inv,
|
|
generation=rp_gen))
|
|
|
|
# ...but *adding* a key in the new record *should* result in changes
|
|
# being recorded
|
|
cn_inv['VCPU']['reserved'] = 0
|
|
self.assertTrue(pt.has_inventory_changed(cn.uuid, cn_inv))
|
|
self.assertTrue(pt.update_inventory(cn.uuid, cn_inv,
|
|
generation=rp_gen))
|
|
|
|
def test_have_traits_changed_no_existing_rp(self):
|
|
pt = self._pt_with_cns()
|
|
self.assertRaises(
|
|
ValueError, pt.have_traits_changed, uuids.non_existing_rp, [])
|
|
|
|
def test_update_traits_no_existing_rp(self):
|
|
pt = self._pt_with_cns()
|
|
self.assertRaises(
|
|
ValueError, pt.update_traits, uuids.non_existing_rp, [])
|
|
|
|
def test_have_traits_changed(self):
|
|
cn = self.compute_node1
|
|
pt = self._pt_with_cns()
|
|
rp_gen = 1
|
|
|
|
traits = [
|
|
"HW_GPU_API_DIRECT3D_V7_0",
|
|
"HW_NIC_OFFLOAD_SG",
|
|
"HW_CPU_X86_AVX",
|
|
]
|
|
self.assertTrue(pt.have_traits_changed(cn.uuid, traits))
|
|
# A data-grab's traits are the same
|
|
cnsnap = pt.data(cn.uuid)
|
|
self.assertFalse(pt.have_traits_changed(cn.uuid, cnsnap.traits))
|
|
self.assertTrue(pt.has_traits(cn.uuid, []))
|
|
self.assertFalse(pt.has_traits(cn.uuid, traits))
|
|
self.assertFalse(pt.has_traits(cn.uuid, traits[:1]))
|
|
self.assertTrue(pt.update_traits(cn.uuid, traits, generation=rp_gen))
|
|
self.assertTrue(pt.has_traits(cn.uuid, traits))
|
|
self.assertTrue(pt.has_traits(cn.uuid, traits[:1]))
|
|
|
|
# Updating with the same traits info should return False
|
|
self.assertFalse(pt.have_traits_changed(cn.uuid, traits))
|
|
# But the generation should get updated
|
|
rp_gen = 2
|
|
self.assertFalse(pt.update_traits(cn.uuid, traits, generation=rp_gen))
|
|
self.assertFalse(pt.have_traits_changed(cn.uuid, traits))
|
|
self.assertEqual(rp_gen, pt.data(cn.uuid).generation)
|
|
self.assertTrue(pt.has_traits(cn.uuid, traits))
|
|
self.assertTrue(pt.has_traits(cn.uuid, traits[:1]))
|
|
|
|
# Make a change to the traits list
|
|
traits.append("HW_GPU_RESOLUTION_W800H600")
|
|
self.assertTrue(pt.have_traits_changed(cn.uuid, traits))
|
|
# The previously-taken data now differs
|
|
self.assertTrue(pt.have_traits_changed(cn.uuid, cnsnap.traits))
|
|
self.assertFalse(pt.has_traits(cn.uuid, traits[-1:]))
|
|
# Don't update the generation
|
|
self.assertTrue(pt.update_traits(cn.uuid, traits))
|
|
self.assertEqual(rp_gen, pt.data(cn.uuid).generation)
|
|
self.assertTrue(pt.has_traits(cn.uuid, traits[-1:]))
|
|
|
|
def test_add_remove_traits(self):
|
|
cn = self.compute_node1
|
|
pt = self._pt_with_cns()
|
|
self.assertEqual(set([]), pt.data(cn.uuid).traits)
|
|
# Test adding with no trait provided for a bogus provider
|
|
pt.add_traits('bogus-uuid')
|
|
self.assertEqual(
|
|
set([]),
|
|
pt.data(cn.uuid).traits
|
|
)
|
|
# Add a couple of traits
|
|
pt.add_traits(cn.uuid, "HW_GPU_API_DIRECT3D_V7_0", "HW_NIC_OFFLOAD_SG")
|
|
self.assertEqual(
|
|
set(["HW_GPU_API_DIRECT3D_V7_0", "HW_NIC_OFFLOAD_SG"]),
|
|
pt.data(cn.uuid).traits)
|
|
# set() behavior: add a trait that's already there, and one that's not.
|
|
# The unrelated one is unaffected.
|
|
pt.add_traits(cn.uuid, "HW_GPU_API_DIRECT3D_V7_0", "HW_CPU_X86_AVX")
|
|
self.assertEqual(
|
|
set(["HW_GPU_API_DIRECT3D_V7_0", "HW_NIC_OFFLOAD_SG",
|
|
"HW_CPU_X86_AVX"]),
|
|
pt.data(cn.uuid).traits)
|
|
# Test removing with no trait provided for a bogus provider
|
|
pt.remove_traits('bogus-uuid')
|
|
self.assertEqual(
|
|
set(["HW_GPU_API_DIRECT3D_V7_0", "HW_NIC_OFFLOAD_SG",
|
|
"HW_CPU_X86_AVX"]),
|
|
pt.data(cn.uuid).traits)
|
|
# Now remove a trait
|
|
pt.remove_traits(cn.uuid, "HW_NIC_OFFLOAD_SG")
|
|
self.assertEqual(
|
|
set(["HW_GPU_API_DIRECT3D_V7_0", "HW_CPU_X86_AVX"]),
|
|
pt.data(cn.uuid).traits)
|
|
# set() behavior: remove a trait that's there, and one that's not.
|
|
# The unrelated one is unaffected.
|
|
pt.remove_traits(cn.uuid,
|
|
"HW_NIC_OFFLOAD_SG", "HW_GPU_API_DIRECT3D_V7_0")
|
|
self.assertEqual(set(["HW_CPU_X86_AVX"]), pt.data(cn.uuid).traits)
|
|
# Remove the last trait, and an unrelated one
|
|
pt.remove_traits(cn.uuid, "CUSTOM_FOO", "HW_CPU_X86_AVX")
|
|
self.assertEqual(set([]), pt.data(cn.uuid).traits)
|
|
|
|
def test_have_aggregates_changed_no_existing_rp(self):
|
|
pt = self._pt_with_cns()
|
|
self.assertRaises(
|
|
ValueError, pt.have_aggregates_changed, uuids.non_existing_rp, [])
|
|
|
|
def test_update_aggregates_no_existing_rp(self):
|
|
pt = self._pt_with_cns()
|
|
self.assertRaises(
|
|
ValueError, pt.update_aggregates, uuids.non_existing_rp, [])
|
|
|
|
def test_have_aggregates_changed(self):
|
|
cn = self.compute_node1
|
|
pt = self._pt_with_cns()
|
|
rp_gen = 1
|
|
|
|
aggregates = [
|
|
uuids.agg1,
|
|
uuids.agg2,
|
|
]
|
|
self.assertTrue(pt.have_aggregates_changed(cn.uuid, aggregates))
|
|
self.assertTrue(pt.in_aggregates(cn.uuid, []))
|
|
self.assertFalse(pt.in_aggregates(cn.uuid, aggregates))
|
|
self.assertFalse(pt.in_aggregates(cn.uuid, aggregates[:1]))
|
|
self.assertTrue(pt.update_aggregates(cn.uuid, aggregates,
|
|
generation=rp_gen))
|
|
self.assertTrue(pt.in_aggregates(cn.uuid, aggregates))
|
|
self.assertTrue(pt.in_aggregates(cn.uuid, aggregates[:1]))
|
|
|
|
# data() gets the same aggregates
|
|
cnsnap = pt.data(cn.uuid)
|
|
self.assertFalse(
|
|
pt.have_aggregates_changed(cn.uuid, cnsnap.aggregates))
|
|
|
|
# Updating with the same aggregates info should return False
|
|
self.assertFalse(pt.have_aggregates_changed(cn.uuid, aggregates))
|
|
# But the generation should get updated
|
|
rp_gen = 2
|
|
self.assertFalse(pt.update_aggregates(cn.uuid, aggregates,
|
|
generation=rp_gen))
|
|
self.assertFalse(pt.have_aggregates_changed(cn.uuid, aggregates))
|
|
self.assertEqual(rp_gen, pt.data(cn.uuid).generation)
|
|
self.assertTrue(pt.in_aggregates(cn.uuid, aggregates))
|
|
self.assertTrue(pt.in_aggregates(cn.uuid, aggregates[:1]))
|
|
|
|
# Make a change to the aggregates list
|
|
aggregates.append(uuids.agg3)
|
|
self.assertTrue(pt.have_aggregates_changed(cn.uuid, aggregates))
|
|
self.assertFalse(pt.in_aggregates(cn.uuid, aggregates[-1:]))
|
|
# Don't update the generation
|
|
self.assertTrue(pt.update_aggregates(cn.uuid, aggregates))
|
|
self.assertEqual(rp_gen, pt.data(cn.uuid).generation)
|
|
self.assertTrue(pt.in_aggregates(cn.uuid, aggregates[-1:]))
|
|
# Previously-taken data now differs
|
|
self.assertTrue(pt.have_aggregates_changed(cn.uuid, cnsnap.aggregates))
|
|
|
|
def test_add_remove_aggregates(self):
|
|
cn = self.compute_node1
|
|
pt = self._pt_with_cns()
|
|
self.assertEqual(set([]), pt.data(cn.uuid).aggregates)
|
|
# Add a couple of aggregates
|
|
pt.add_aggregates(cn.uuid, uuids.agg1, uuids.agg2)
|
|
self.assertEqual(
|
|
set([uuids.agg1, uuids.agg2]),
|
|
pt.data(cn.uuid).aggregates)
|
|
# set() behavior: add an aggregate that's already there, and one that's
|
|
# not. The unrelated one is unaffected.
|
|
pt.add_aggregates(cn.uuid, uuids.agg1, uuids.agg3)
|
|
self.assertEqual(set([uuids.agg1, uuids.agg2, uuids.agg3]),
|
|
pt.data(cn.uuid).aggregates)
|
|
# Now remove an aggregate
|
|
pt.remove_aggregates(cn.uuid, uuids.agg2)
|
|
self.assertEqual(set([uuids.agg1, uuids.agg3]),
|
|
pt.data(cn.uuid).aggregates)
|
|
# set() behavior: remove an aggregate that's there, and one that's not.
|
|
# The unrelated one is unaffected.
|
|
pt.remove_aggregates(cn.uuid, uuids.agg2, uuids.agg3)
|
|
self.assertEqual(set([uuids.agg1]), pt.data(cn.uuid).aggregates)
|
|
# Remove the last aggregate, and an unrelated one
|
|
pt.remove_aggregates(cn.uuid, uuids.agg4, uuids.agg1)
|
|
self.assertEqual(set([]), pt.data(cn.uuid).aggregates)
|