Merge "Add claims testing to VirtNUMAHostTopology class"
This commit is contained in:
commit
c886168aae
|
@ -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)
|
||||
|
|
|
@ -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."))
|
||||
|
|
Loading…
Reference in New Issue