Merge "Add claims testing to VirtNUMAHostTopology class"

This commit is contained in:
Jenkins 2014-09-11 13:02:51 +00:00 committed by Gerrit Code Review
commit c886168aae
2 changed files with 174 additions and 7 deletions

View File

@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import six
from nova import exception
from nova import test
from nova.tests import matchers
@ -1008,11 +1010,8 @@ class NUMATopologyTest(test.NoDBTestCase):
got_cell = cell_class._from_dict(data_dict)
self.assertNUMACellMatches(expected_cell, got_cell)
def _test_topo_from_dict(self, data_dict, expected_topo, with_usage=False):
topology_class = (
hw.VirtNUMAHostTopology
if with_usage else hw.VirtNUMAInstanceTopology)
got_topo = topology_class._from_dict(
def _test_topo_from_dict(self, data_dict, expected_topo):
got_topo = expected_topo.__class__._from_dict(
data_dict)
for got_cell, expected_cell in zip(
got_topo.cells, expected_topo.cells):
@ -1026,6 +1025,14 @@ class NUMATopologyTest(test.NoDBTestCase):
self._test_to_dict(cell, cell_dict)
self._test_cell_from_dict(cell_dict, cell)
def test_numa_limit_cell_dict(self):
cell = hw.VirtNUMATopologyCellLimit(1, set([1, 2]), 512, 4, 2048)
cell_dict = {'cpus': '1,2', 'cpu_limit': 4,
'mem': {'total': 512, 'limit': 2048},
'id': 1}
self._test_to_dict(cell, cell_dict)
self._test_cell_from_dict(cell_dict, cell)
def test_numa_cell_usage_dict(self):
cell = hw.VirtNUMATopologyCellUsage(1, set([1, 2]), 512)
cell_dict = {'cpus': '1,2', 'cpu_usage': 0,
@ -1047,7 +1054,24 @@ class NUMATopologyTest(test.NoDBTestCase):
'mem': {'total': 1024},
'id': 2}]}
self._test_to_dict(topo, topo_dict)
self._test_topo_from_dict(topo_dict, topo, with_usage=False)
self._test_topo_from_dict(topo_dict, topo)
def test_numa_limits_topo_dict(self):
topo = hw.VirtNUMALimitTopology(
cells=[
hw.VirtNUMATopologyCellLimit(
1, set([1, 2]), 1024, 4, 2048),
hw.VirtNUMATopologyCellLimit(
2, set([3, 4]), 1024, 4, 2048)])
topo_dict = {'cells': [
{'cpus': '1,2', 'cpu_limit': 4,
'mem': {'total': 1024, 'limit': 2048},
'id': 1},
{'cpus': '3,4', 'cpu_limit': 4,
'mem': {'total': 1024, 'limit': 2048},
'id': 2}]}
self._test_to_dict(topo, topo_dict)
self._test_topo_from_dict(topo_dict, topo)
def test_numa_topo_dict_with_usage(self):
topo = hw.VirtNUMAHostTopology(
@ -1064,7 +1088,7 @@ class NUMATopologyTest(test.NoDBTestCase):
'mem': {'total': 1024, 'used': 0},
'id': 2}]}
self._test_to_dict(topo, topo_dict)
self._test_topo_from_dict(topo_dict, topo, with_usage=True)
self._test_topo_from_dict(topo_dict, topo)
def test_json(self):
expected = hw.VirtNUMAHostTopology(
@ -1117,3 +1141,73 @@ class NumberOfSerialPortsTest(test.NoDBTestCase):
self.assertRaises(exception.ImageSerialPortNumberExceedFlavorValue,
hw.get_number_of_serial_ports,
flavor, image_meta)
class NUMATopologyClaimsTest(test.NoDBTestCase):
def setUp(self):
super(NUMATopologyClaimsTest, self).setUp()
self.host = hw.VirtNUMAHostTopology(
cells=[
hw.VirtNUMATopologyCellUsage(
1, set([1, 2, 3, 4]), 2048,
cpu_usage=1, memory_usage=512),
hw.VirtNUMATopologyCellUsage(
2, set([5, 6]), 1024)])
self.limits = hw.VirtNUMALimitTopology(
cells=[
hw.VirtNUMATopologyCellLimit(
1, set([1, 2, 3, 4]), 2048,
cpu_limit=8, memory_limit=4096),
hw.VirtNUMATopologyCellLimit(
2, set([5, 6]), 1024,
cpu_limit=4, memory_limit=2048)])
self.large_instance = hw.VirtNUMAInstanceTopology(
cells=[
hw.VirtNUMATopologyCell(1, set([1, 2, 3, 4, 5, 6]), 8192),
hw.VirtNUMATopologyCell(2, set([7, 8]), 4096)])
self.medium_instance = hw.VirtNUMAInstanceTopology(
cells=[
hw.VirtNUMATopologyCell(1, set([1, 2, 3, 4]), 1024),
hw.VirtNUMATopologyCell(2, set([7, 8]), 2048)])
self.small_instance = hw.VirtNUMAInstanceTopology(
cells=[
hw.VirtNUMATopologyCell(1, set([1]), 256),
hw.VirtNUMATopologyCell(2, set([5]), 1024)])
def test_claim_not_enough_info(self):
# No limits supplied
self.assertIsNone(
hw.VirtNUMAHostTopology.claim_test(
self.host, [self.large_instance]))
# Empty topology
self.assertIsNone(
hw.VirtNUMAHostTopology.claim_test(
hw.VirtNUMAHostTopology(), [self.large_instance],
limits=self.limits))
# No instances to claim
self.assertIsNone(
hw.VirtNUMAHostTopology.claim_test(self.host, [], self.limits))
def test_claim_succeeds(self):
self.assertIsNone(
hw.VirtNUMAHostTopology.claim_test(
self.host, [self.small_instance], self.limits))
self.assertIsNone(
hw.VirtNUMAHostTopology.claim_test(
self.host, [self.medium_instance], self.limits))
def test_claim_fails(self):
self.assertIsInstance(
hw.VirtNUMAHostTopology.claim_test(
self.host, [self.large_instance], self.limits),
six.text_type)
self.assertIsInstance(
hw.VirtNUMAHostTopology.claim_test(
self.host, [self.medium_instance, self.small_instance],
self.limits),
six.text_type)

View File

@ -568,6 +568,49 @@ class VirtNUMATopologyCell(object):
return cls(cell_id, cpuset, memory)
class VirtNUMATopologyCellLimit(VirtNUMATopologyCell):
def __init__(self, id, cpuset, memory, cpu_limit, memory_limit):
"""Create a new NUMA Cell with usage
:param id: integer identifier of cell
:param cpuset: set containing list of CPU indexes
:param memory: RAM measured in KiB
:param cpu_limit: maximum number of CPUs allocated
:param memory_usage: maxumum RAM allocated in KiB
Creates a new NUMA cell object to represent the max hardware
resources and utilization. The number of CPUs specified
by the @cpu_usage parameter may be larger than the number
of bits set in @cpuset if CPU overcommit is used. Likewise
the amount of RAM specified by the @memory_usage parameter
may be larger than the available RAM in @memory if RAM
overcommit is used.
:returns: a new NUMA cell object
"""
super(VirtNUMATopologyCellLimit, self).__init__(
id, cpuset, memory)
self.cpu_limit = cpu_limit
self.memory_limit = memory_limit
def _to_dict(self):
data_dict = super(VirtNUMATopologyCellLimit, self)._to_dict()
data_dict['mem']['limit'] = self.memory_limit
data_dict['cpu_limit'] = self.cpu_limit
return data_dict
@classmethod
def _from_dict(cls, data_dict):
cpuset = parse_cpu_spec(data_dict.get('cpus', ''))
memory = data_dict.get('mem', {}).get('total', 0)
cpu_limit = data_dict.get('cpu_limit', len(cpuset))
memory_limit = data_dict.get('mem', {}).get('limit', memory)
cell_id = data_dict.get('id')
return cls(cell_id, cpuset, memory, cpu_limit, memory_limit)
class VirtNUMATopologyCellUsage(VirtNUMATopologyCell):
"""Class for reporting NUMA resources and usage in a cell
@ -781,6 +824,14 @@ class VirtNUMAInstanceTopology(VirtNUMATopology):
nodes, flavor, image_meta)
class VirtNUMALimitTopology(VirtNUMATopology):
"""Class to represent the max resources of a compute node used
for checking oversubscription limits.
"""
cell_class = VirtNUMATopologyCellLimit
class VirtNUMAHostTopology(VirtNUMATopology):
"""Class represents the NUMA configuration and utilization
@ -827,3 +878,25 @@ class VirtNUMAHostTopology(VirtNUMATopology):
cells.append(cell)
return cls(cells)
@classmethod
def claim_test(cls, host, instances, limits=None):
"""Test if we can claim an instance on the host with given limits.
:param host: VirtNUMAHostTopology with usage information
:param instances: list of VirtNUMAInstanceTopology
:param limits: VirtNUMALimitTopology with max values set. Should
match the host topology otherwise
:returns: None if the claim succeeds or text explaining the error.
"""
if not all((host, limits, instances)):
return
claimed_host = cls.usage_from_instances(host, instances)
for claimed_cell, limit_cell in zip(claimed_host.cells, limits.cells):
if (claimed_cell.memory_usage > limit_cell.memory_limit or
claimed_cell.cpu_usage > limit_cell.cpu_limit):
return (_("Requested instance NUMA topology cannot fit "
"the given host NUMA topology."))