nova/nova/tests/unit/virt/test_hardware.py

1421 lines
56 KiB
Python

# Copyright 2014 Red Hat, Inc.
#
# 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.
import uuid
import mock
from oslo.serialization import jsonutils
import six
from nova import context
from nova import exception
from nova import objects
from nova.objects import base as base_obj
from nova import test
from nova.virt import hardware as hw
class InstanceInfoTests(test.NoDBTestCase):
def test_instance_info_default(self):
ii = hw.InstanceInfo()
self.assertIsNone(ii.state)
self.assertIsNone(ii.id)
self.assertEqual(0, ii.max_mem_kb)
self.assertEqual(0, ii.mem_kb)
self.assertEqual(0, ii.num_cpu)
self.assertEqual(0, ii.cpu_time_ns)
def test_instance_info(self):
ii = hw.InstanceInfo(state='fake-state',
max_mem_kb=1,
mem_kb=2,
num_cpu=3,
cpu_time_ns=4,
id='fake-id')
self.assertEqual('fake-state', ii.state)
self.assertEqual('fake-id', ii.id)
self.assertEqual(1, ii.max_mem_kb)
self.assertEqual(2, ii.mem_kb)
self.assertEqual(3, ii.num_cpu)
self.assertEqual(4, ii.cpu_time_ns)
def test_instance_infoi_equals(self):
ii1 = hw.InstanceInfo(state='fake-state',
max_mem_kb=1,
mem_kb=2,
num_cpu=3,
cpu_time_ns=4,
id='fake-id')
ii2 = hw.InstanceInfo(state='fake-state',
max_mem_kb=1,
mem_kb=2,
num_cpu=3,
cpu_time_ns=4,
id='fake-id')
ii3 = hw.InstanceInfo(state='fake-estat',
max_mem_kb=11,
mem_kb=22,
num_cpu=33,
cpu_time_ns=44,
id='fake-di')
self.assertEqual(ii1, ii2)
self.assertNotEqual(ii1, ii3)
class CpuSetTestCase(test.NoDBTestCase):
def test_get_vcpu_pin_set(self):
self.flags(vcpu_pin_set="1-3,5,^2")
cpuset_ids = hw.get_vcpu_pin_set()
self.assertEqual(set([1, 3, 5]), cpuset_ids)
def test_parse_cpu_spec_none_returns_none(self):
self.flags(vcpu_pin_set=None)
cpuset_ids = hw.get_vcpu_pin_set()
self.assertIsNone(cpuset_ids)
def test_parse_cpu_spec_valid_syntax_works(self):
cpuset_ids = hw.parse_cpu_spec("1")
self.assertEqual(set([1]), cpuset_ids)
cpuset_ids = hw.parse_cpu_spec("1,2")
self.assertEqual(set([1, 2]), cpuset_ids)
cpuset_ids = hw.parse_cpu_spec(", , 1 , ,, 2, ,")
self.assertEqual(set([1, 2]), cpuset_ids)
cpuset_ids = hw.parse_cpu_spec("1-1")
self.assertEqual(set([1]), cpuset_ids)
cpuset_ids = hw.parse_cpu_spec(" 1 - 1, 1 - 2 , 1 -3")
self.assertEqual(set([1, 2, 3]), cpuset_ids)
cpuset_ids = hw.parse_cpu_spec("1,^2")
self.assertEqual(set([1]), cpuset_ids)
cpuset_ids = hw.parse_cpu_spec("1-2, ^1")
self.assertEqual(set([2]), cpuset_ids)
cpuset_ids = hw.parse_cpu_spec("1-3,5,^2")
self.assertEqual(set([1, 3, 5]), cpuset_ids)
cpuset_ids = hw.parse_cpu_spec(" 1 - 3 , ^2, 5")
self.assertEqual(set([1, 3, 5]), cpuset_ids)
cpuset_ids = hw.parse_cpu_spec(" 1,1, ^1")
self.assertEqual(set([]), cpuset_ids)
def test_parse_cpu_spec_invalid_syntax_raises(self):
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
" -1-3,5,^2")
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
"1-3-,5,^2")
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
"-3,5,^2")
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
"1-,5,^2")
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
"1-3,5,^2^")
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
"1-3,5,^2-")
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
"--13,^^5,^2")
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
"a-3,5,^2")
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
"1-a,5,^2")
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
"1-3,b,^2")
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
"1-3,5,^c")
self.assertRaises(exception.Invalid,
hw.parse_cpu_spec,
"3 - 1, 5 , ^ 2 ")
def test_format_cpu_spec(self):
cpus = set([])
spec = hw.format_cpu_spec(cpus)
self.assertEqual("", spec)
cpus = []
spec = hw.format_cpu_spec(cpus)
self.assertEqual("", spec)
cpus = set([1, 3])
spec = hw.format_cpu_spec(cpus)
self.assertEqual("1,3", spec)
cpus = [1, 3]
spec = hw.format_cpu_spec(cpus)
self.assertEqual("1,3", spec)
cpus = set([1, 2, 4, 6])
spec = hw.format_cpu_spec(cpus)
self.assertEqual("1-2,4,6", spec)
cpus = [1, 2, 4, 6]
spec = hw.format_cpu_spec(cpus)
self.assertEqual("1-2,4,6", spec)
cpus = set([10, 11, 13, 14, 15, 16, 19, 20, 40, 42, 48])
spec = hw.format_cpu_spec(cpus)
self.assertEqual("10-11,13-16,19-20,40,42,48", spec)
cpus = [10, 11, 13, 14, 15, 16, 19, 20, 40, 42, 48]
spec = hw.format_cpu_spec(cpus)
self.assertEqual("10-11,13-16,19-20,40,42,48", spec)
cpus = set([1, 2, 4, 6])
spec = hw.format_cpu_spec(cpus, allow_ranges=False)
self.assertEqual("1,2,4,6", spec)
cpus = [1, 2, 4, 6]
spec = hw.format_cpu_spec(cpus, allow_ranges=False)
self.assertEqual("1,2,4,6", spec)
cpus = set([10, 11, 13, 14, 15, 16, 19, 20, 40, 42, 48])
spec = hw.format_cpu_spec(cpus, allow_ranges=False)
self.assertEqual("10,11,13,14,15,16,19,20,40,42,48", spec)
cpus = [10, 11, 13, 14, 15, 16, 19, 20, 40, 42, 48]
spec = hw.format_cpu_spec(cpus, allow_ranges=False)
self.assertEqual("10,11,13,14,15,16,19,20,40,42,48", spec)
class VCPUTopologyTest(test.NoDBTestCase):
def test_validate_config(self):
testdata = [
{ # Flavor sets preferred topology only
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_sockets": "8",
"hw:cpu_cores": "2",
"hw:cpu_threads": "1",
}),
"image": {
"properties": {}
},
"expect": (
8, 2, 1, 65536, 65536, 65536
)
},
{ # Image topology overrides flavor
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_sockets": "8",
"hw:cpu_cores": "2",
"hw:cpu_threads": "1",
"hw:cpu_max_threads": "2",
}),
"image": {
"properties": {
"hw_cpu_sockets": "4",
"hw_cpu_cores": "2",
"hw_cpu_threads": "2",
}
},
"expect": (
4, 2, 2, 65536, 65536, 2,
)
},
{ # Partial image topology overrides flavor
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_sockets": "8",
"hw:cpu_cores": "2",
"hw:cpu_threads": "1",
}),
"image": {
"properties": {
"hw_cpu_sockets": "2",
}
},
"expect": (
2, -1, -1, 65536, 65536, 65536,
)
},
{ # Restrict use of threads
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_max_threads": "2",
}),
"image": {
"properties": {
"hw_cpu_max_threads": "1",
}
},
"expect": (
-1, -1, -1, 65536, 65536, 1,
)
},
{ # Force use of at least two sockets
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_max_cores": "8",
"hw:cpu_max_threads": "1",
}),
"image": {
"properties": {}
},
"expect": (
-1, -1, -1, 65536, 8, 1
)
},
{ # Image limits reduce flavor
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_max_cores": "8",
"hw:cpu_max_threads": "1",
}),
"image": {
"properties": {
"hw_cpu_max_cores": "4",
}
},
"expect": (
-1, -1, -1, 65536, 4, 1
)
},
{ # Image limits kill flavor preferred
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_sockets": "2",
"hw:cpu_cores": "8",
"hw:cpu_threads": "1",
}),
"image": {
"properties": {
"hw_cpu_max_cores": "4",
}
},
"expect": (
-1, -1, -1, 65536, 4, 65536
)
},
{ # Image limits cannot exceed flavor
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_max_cores": "8",
"hw:cpu_max_threads": "1",
}),
"image": {
"properties": {
"hw_cpu_max_cores": "16",
}
},
"expect": exception.ImageVCPULimitsRangeExceeded,
},
{ # Image preferred cannot exceed flavor
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_max_cores": "8",
"hw:cpu_max_threads": "1",
}),
"image": {
"properties": {
"hw_cpu_cores": "16",
}
},
"expect": exception.ImageVCPUTopologyRangeExceeded,
},
]
for topo_test in testdata:
if type(topo_test["expect"]) == tuple:
(preferred,
maximum) = hw._get_cpu_topology_constraints(
topo_test["flavor"],
topo_test["image"])
self.assertEqual(topo_test["expect"][0], preferred.sockets)
self.assertEqual(topo_test["expect"][1], preferred.cores)
self.assertEqual(topo_test["expect"][2], preferred.threads)
self.assertEqual(topo_test["expect"][3], maximum.sockets)
self.assertEqual(topo_test["expect"][4], maximum.cores)
self.assertEqual(topo_test["expect"][5], maximum.threads)
else:
self.assertRaises(topo_test["expect"],
hw._get_cpu_topology_constraints,
topo_test["flavor"],
topo_test["image"])
def test_possible_topologies(self):
testdata = [
{
"allow_threads": True,
"vcpus": 8,
"maxsockets": 8,
"maxcores": 8,
"maxthreads": 2,
"expect": [
[8, 1, 1],
[4, 2, 1],
[2, 4, 1],
[1, 8, 1],
[4, 1, 2],
[2, 2, 2],
[1, 4, 2],
]
},
{
"allow_threads": False,
"vcpus": 8,
"maxsockets": 8,
"maxcores": 8,
"maxthreads": 2,
"expect": [
[8, 1, 1],
[4, 2, 1],
[2, 4, 1],
[1, 8, 1],
]
},
{
"allow_threads": True,
"vcpus": 8,
"maxsockets": 1024,
"maxcores": 1024,
"maxthreads": 2,
"expect": [
[8, 1, 1],
[4, 2, 1],
[2, 4, 1],
[1, 8, 1],
[4, 1, 2],
[2, 2, 2],
[1, 4, 2],
]
},
{
"allow_threads": True,
"vcpus": 8,
"maxsockets": 1024,
"maxcores": 1,
"maxthreads": 2,
"expect": [
[8, 1, 1],
[4, 1, 2],
]
},
{
"allow_threads": True,
"vcpus": 7,
"maxsockets": 8,
"maxcores": 8,
"maxthreads": 2,
"expect": [
[7, 1, 1],
[1, 7, 1],
]
},
{
"allow_threads": True,
"vcpus": 8,
"maxsockets": 2,
"maxcores": 1,
"maxthreads": 1,
"expect": exception.ImageVCPULimitsRangeImpossible,
},
{
"allow_threads": False,
"vcpus": 8,
"maxsockets": 2,
"maxcores": 1,
"maxthreads": 4,
"expect": exception.ImageVCPULimitsRangeImpossible,
},
]
for topo_test in testdata:
if type(topo_test["expect"]) == list:
actual = []
for topology in hw._get_possible_cpu_topologies(
topo_test["vcpus"],
objects.VirtCPUTopology(
sockets=topo_test["maxsockets"],
cores=topo_test["maxcores"],
threads=topo_test["maxthreads"]),
topo_test["allow_threads"]):
actual.append([topology.sockets,
topology.cores,
topology.threads])
self.assertEqual(topo_test["expect"], actual)
else:
self.assertRaises(topo_test["expect"],
hw._get_possible_cpu_topologies,
topo_test["vcpus"],
objects.VirtCPUTopology(
sockets=topo_test["maxsockets"],
cores=topo_test["maxcores"],
threads=topo_test["maxthreads"]),
topo_test["allow_threads"])
def test_sorting_topologies(self):
testdata = [
{
"allow_threads": True,
"vcpus": 8,
"maxsockets": 8,
"maxcores": 8,
"maxthreads": 2,
"sockets": 4,
"cores": 2,
"threads": 1,
"expect": [
[4, 2, 1], # score = 2
[8, 1, 1], # score = 1
[2, 4, 1], # score = 1
[1, 8, 1], # score = 1
[4, 1, 2], # score = 1
[2, 2, 2], # score = 1
[1, 4, 2], # score = 1
]
},
{
"allow_threads": True,
"vcpus": 8,
"maxsockets": 1024,
"maxcores": 1024,
"maxthreads": 2,
"sockets": -1,
"cores": 4,
"threads": -1,
"expect": [
[2, 4, 1], # score = 1
[1, 4, 2], # score = 1
[8, 1, 1], # score = 0
[4, 2, 1], # score = 0
[1, 8, 1], # score = 0
[4, 1, 2], # score = 0
[2, 2, 2], # score = 0
]
},
{
"allow_threads": True,
"vcpus": 8,
"maxsockets": 1024,
"maxcores": 1,
"maxthreads": 2,
"sockets": -1,
"cores": -1,
"threads": 2,
"expect": [
[4, 1, 2], # score = 1
[8, 1, 1], # score = 0
]
},
{
"allow_threads": False,
"vcpus": 8,
"maxsockets": 1024,
"maxcores": 1,
"maxthreads": 2,
"sockets": -1,
"cores": -1,
"threads": 2,
"expect": [
[8, 1, 1], # score = 0
]
},
]
for topo_test in testdata:
actual = []
possible = hw._get_possible_cpu_topologies(
topo_test["vcpus"],
objects.VirtCPUTopology(sockets=topo_test["maxsockets"],
cores=topo_test["maxcores"],
threads=topo_test["maxthreads"]),
topo_test["allow_threads"])
tops = hw._sort_possible_cpu_topologies(
possible,
objects.VirtCPUTopology(sockets=topo_test["sockets"],
cores=topo_test["cores"],
threads=topo_test["threads"]))
for topology in tops:
actual.append([topology.sockets,
topology.cores,
topology.threads])
self.assertEqual(topo_test["expect"], actual)
def test_best_config(self):
testdata = [
{ # Flavor sets preferred topology only
"allow_threads": True,
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_sockets": "8",
"hw:cpu_cores": "2",
"hw:cpu_threads": "1"
}),
"image": {
"properties": {}
},
"expect": [8, 2, 1],
},
{ # Image topology overrides flavor
"allow_threads": True,
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_sockets": "8",
"hw:cpu_cores": "2",
"hw:cpu_threads": "1",
"hw:cpu_maxthreads": "2",
}),
"image": {
"properties": {
"hw_cpu_sockets": "4",
"hw_cpu_cores": "2",
"hw_cpu_threads": "2",
}
},
"expect": [4, 2, 2],
},
{ # Image topology overrides flavor
"allow_threads": False,
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_sockets": "8",
"hw:cpu_cores": "2",
"hw:cpu_threads": "1",
"hw:cpu_maxthreads": "2",
}),
"image": {
"properties": {
"hw_cpu_sockets": "4",
"hw_cpu_cores": "2",
"hw_cpu_threads": "2",
}
},
"expect": [8, 2, 1],
},
{ # Partial image topology overrides flavor
"allow_threads": True,
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_sockets": "8",
"hw:cpu_cores": "2",
"hw:cpu_threads": "1"
}),
"image": {
"properties": {
"hw_cpu_sockets": "2"
}
},
"expect": [2, 8, 1],
},
{ # Restrict use of threads
"allow_threads": True,
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_max_threads": "1"
}),
"image": {
"properties": {}
},
"expect": [16, 1, 1]
},
{ # Force use of at least two sockets
"allow_threads": True,
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_max_cores": "8",
"hw:cpu_max_threads": "1",
}),
"image": {
"properties": {}
},
"expect": [16, 1, 1]
},
{ # Image limits reduce flavor
"allow_threads": True,
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_max_sockets": "8",
"hw:cpu_max_cores": "8",
"hw:cpu_max_threads": "1",
}),
"image": {
"properties": {
"hw_cpu_max_sockets": 4,
}
},
"expect": [4, 4, 1]
},
{ # Image limits kill flavor preferred
"allow_threads": True,
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
extra_specs={
"hw:cpu_sockets": "2",
"hw:cpu_cores": "8",
"hw:cpu_threads": "1",
}),
"image": {
"properties": {
"hw_cpu_max_cores": 4,
}
},
"expect": [16, 1, 1]
},
]
for topo_test in testdata:
topology = hw._get_desirable_cpu_topologies(
topo_test["flavor"],
topo_test["image"],
topo_test["allow_threads"])[0]
self.assertEqual(topo_test["expect"][0], topology.sockets)
self.assertEqual(topo_test["expect"][1], topology.cores)
self.assertEqual(topo_test["expect"][2], topology.threads)
class NUMATopologyTest(test.NoDBTestCase):
def test_topology_constraints(self):
testdata = [
{
"flavor": objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={
}),
"image": {
},
"expect": None,
},
{
"flavor": objects.Flavor(vcpus=8, memory_mb=2048, extra_specs={
"hw:numa_nodes": 2
}),
"image": {
},
"expect": objects.InstanceNUMATopology(cells=
[
objects.InstanceNUMACell(
id=0, cpuset=set([0, 1, 2, 3]), memory=1024),
objects.InstanceNUMACell(
id=1, cpuset=set([4, 5, 6, 7]), memory=1024),
]),
},
{
# vcpus is not a multiple of nodes, so it
# is an error to not provide cpu/mem mapping
"flavor": objects.Flavor(vcpus=8, memory_mb=2048, extra_specs={
"hw:numa_nodes": 3
}),
"image": {
},
"expect": exception.ImageNUMATopologyAsymmetric,
},
{
"flavor": objects.Flavor(vcpus=8, memory_mb=2048, extra_specs={
"hw:numa_nodes": 3,
"hw:numa_cpus.0": "0-3",
"hw:numa_mem.0": "1024",
"hw:numa_cpus.1": "4,6",
"hw:numa_mem.1": "512",
"hw:numa_cpus.2": "5,7",
"hw:numa_mem.2": "512",
}),
"image": {
},
"expect": objects.InstanceNUMATopology(cells=
[
objects.InstanceNUMACell(
id=0, cpuset=set([0, 1, 2, 3]), memory=1024),
objects.InstanceNUMACell(
id=1, cpuset=set([4, 6]), memory=512),
objects.InstanceNUMACell(
id=2, cpuset=set([5, 7]), memory=512),
]),
},
{
# Request a CPU that is out of range
# wrt vCPU count
"flavor": objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={
"hw:numa_nodes": 1,
"hw:numa_cpus.0": "0-16",
"hw:numa_mem.0": "2048",
}),
"image": {
},
"expect": exception.ImageNUMATopologyCPUOutOfRange,
},
{
# Request the same CPU in two nodes
"flavor": objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={
"hw:numa_nodes": 2,
"hw:numa_cpus.0": "0-7",
"hw:numa_mem.0": "1024",
"hw:numa_cpus.1": "0-7",
"hw:numa_mem.1": "1024",
}),
"image": {
},
"expect": exception.ImageNUMATopologyCPUDuplicates,
},
{
# Request with some CPUs not assigned
"flavor": objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={
"hw:numa_nodes": 2,
"hw:numa_cpus.0": "0-2",
"hw:numa_mem.0": "1024",
"hw:numa_cpus.1": "3-4",
"hw:numa_mem.1": "1024",
}),
"image": {
},
"expect": exception.ImageNUMATopologyCPUsUnassigned,
},
{
# Request too little memory vs flavor total
"flavor": objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={
"hw:numa_nodes": 2,
"hw:numa_cpus.0": "0-3",
"hw:numa_mem.0": "512",
"hw:numa_cpus.1": "4-7",
"hw:numa_mem.1": "512",
}),
"image": {
},
"expect": exception.ImageNUMATopologyMemoryOutOfRange,
},
{
# Request too much memory vs flavor total
"flavor": objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={
"hw:numa_nodes": 2,
"hw:numa_cpus.0": "0-3",
"hw:numa_mem.0": "1576",
"hw:numa_cpus.1": "4-7",
"hw:numa_mem.1": "1576",
}),
"image": {
},
"expect": exception.ImageNUMATopologyMemoryOutOfRange,
},
{
# Request missing mem.0
"flavor": objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={
"hw:numa_nodes": 2,
"hw:numa_cpus.0": "0-3",
"hw:numa_mem.1": "1576",
}),
"image": {
},
"expect": exception.ImageNUMATopologyIncomplete,
},
{
# Request missing cpu.0
"flavor": objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={
"hw:numa_nodes": 2,
"hw:numa_mem.0": "1576",
"hw:numa_cpus.1": "4-7",
}),
"image": {
},
"expect": exception.ImageNUMATopologyIncomplete,
},
{
# Image attempts to override flavor
"flavor": objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={
"hw:numa_nodes": 2,
}),
"image": {
"hw_numa_nodes": 4,
},
"expect": exception.ImageNUMATopologyForbidden,
},
]
for testitem in testdata:
if testitem["expect"] is None:
topology = hw.numa_get_constraints(
testitem["flavor"], testitem["image"])
self.assertIsNone(topology)
elif type(testitem["expect"]) == type:
self.assertRaises(testitem["expect"],
hw.numa_get_constraints,
testitem["flavor"],
testitem["image"])
else:
topology = hw.numa_get_constraints(
testitem["flavor"], testitem["image"])
self.assertEqual(len(testitem["expect"].cells),
len(topology.cells))
for i in range(len(topology.cells)):
self.assertEqual(testitem["expect"].cells[i].cpuset,
topology.cells[i].cpuset)
self.assertEqual(testitem["expect"].cells[i].memory,
topology.cells[i].memory)
def test_host_usage_contiguous(self):
hosttopo = objects.NUMATopology(cells=[
objects.NUMACell(id=0, cpuset=set([0, 1, 2, 3]), memory=1024,
cpu_usage=0, memory_usage=0),
objects.NUMACell(id=1, cpuset=set([4, 6]), memory=512,
cpu_usage=0, memory_usage=0),
objects.NUMACell(id=2, cpuset=set([5, 7]), memory=512,
cpu_usage=0, memory_usage=0),
])
instance1 = objects.InstanceNUMATopology(cells=[
objects.InstanceNUMACell(id=0, cpuset=set([0, 1, 2]), memory=256),
objects.InstanceNUMACell(id=1, cpuset=set([4]), memory=256),
])
instance2 = objects.InstanceNUMATopology(cells=[
objects.InstanceNUMACell(id=0, cpuset=set([0, 1]), memory=256),
objects.InstanceNUMACell(id=1, cpuset=set([5, 7]), memory=256),
])
hostusage = hw.numa_usage_from_instances(
hosttopo, [instance1, instance2])
self.assertEqual(len(hosttopo), len(hostusage))
self.assertIsInstance(hostusage.cells[0], objects.NUMACell)
self.assertEqual(hosttopo.cells[0].cpuset,
hostusage.cells[0].cpuset)
self.assertEqual(hosttopo.cells[0].memory,
hostusage.cells[0].memory)
self.assertEqual(hostusage.cells[0].cpu_usage, 5)
self.assertEqual(hostusage.cells[0].memory_usage, 512)
self.assertIsInstance(hostusage.cells[1], objects.NUMACell)
self.assertEqual(hosttopo.cells[1].cpuset,
hostusage.cells[1].cpuset)
self.assertEqual(hosttopo.cells[1].memory,
hostusage.cells[1].memory)
self.assertEqual(hostusage.cells[1].cpu_usage, 3)
self.assertEqual(hostusage.cells[1].memory_usage, 512)
self.assertIsInstance(hostusage.cells[2], objects.NUMACell)
self.assertEqual(hosttopo.cells[2].cpuset,
hostusage.cells[2].cpuset)
self.assertEqual(hosttopo.cells[2].memory,
hostusage.cells[2].memory)
self.assertEqual(hostusage.cells[2].cpu_usage, 0)
self.assertEqual(hostusage.cells[2].memory_usage, 0)
def test_host_usage_sparse(self):
hosttopo = objects.NUMATopology(cells=[
objects.NUMACell(id=0, cpuset=set([0, 1, 2, 3]), memory=1024,
cpu_usage=0, memory_usage=0),
objects.NUMACell(id=5, cpuset=set([4, 6]), memory=512,
cpu_usage=0, memory_usage=0),
objects.NUMACell(id=6, cpuset=set([5, 7]), memory=512,
cpu_usage=0, memory_usage=0),
])
instance1 = objects.InstanceNUMATopology(cells=[
objects.InstanceNUMACell(id=0, cpuset=set([0, 1, 2]), memory=256),
objects.InstanceNUMACell(id=6, cpuset=set([4]), memory=256),
])
instance2 = objects.InstanceNUMATopology(cells=[
objects.InstanceNUMACell(id=0, cpuset=set([0, 1]), memory=256,
cpu_usage=0, memory_usage=0),
objects.InstanceNUMACell(id=5, cpuset=set([5, 7]), memory=256,
cpu_usage=0, memory_usage=0),
])
hostusage = hw.numa_usage_from_instances(
hosttopo, [instance1, instance2])
self.assertEqual(len(hosttopo), len(hostusage))
self.assertIsInstance(hostusage.cells[0], objects.NUMACell)
self.assertEqual(hosttopo.cells[0].id,
hostusage.cells[0].id)
self.assertEqual(hosttopo.cells[0].cpuset,
hostusage.cells[0].cpuset)
self.assertEqual(hosttopo.cells[0].memory,
hostusage.cells[0].memory)
self.assertEqual(hostusage.cells[0].cpu_usage, 5)
self.assertEqual(hostusage.cells[0].memory_usage, 512)
self.assertIsInstance(hostusage.cells[1], objects.NUMACell)
self.assertEqual(hosttopo.cells[1].id,
hostusage.cells[1].id)
self.assertEqual(hosttopo.cells[1].cpuset,
hostusage.cells[1].cpuset)
self.assertEqual(hosttopo.cells[1].memory,
hostusage.cells[1].memory)
self.assertEqual(hostusage.cells[1].cpu_usage, 2)
self.assertEqual(hostusage.cells[1].memory_usage, 256)
self.assertIsInstance(hostusage.cells[2], objects.NUMACell)
self.assertEqual(hosttopo.cells[2].cpuset,
hostusage.cells[2].cpuset)
self.assertEqual(hosttopo.cells[2].memory,
hostusage.cells[2].memory)
self.assertEqual(hostusage.cells[2].cpu_usage, 1)
self.assertEqual(hostusage.cells[2].memory_usage, 256)
def test_host_usage_culmulative_with_free(self):
hosttopo = objects.NUMATopology(cells=[
objects.NUMACell(id=0, cpuset=set([0, 1, 2, 3]), memory=1024,
cpu_usage=2, memory_usage=512),
objects.NUMACell(id=1, cpuset=set([4, 6]), memory=512,
cpu_usage=1, memory_usage=512),
objects.NUMACell(id=2, cpuset=set([5, 7]), memory=256,
cpu_usage=0, memory_usage=0),
])
instance1 = objects.InstanceNUMATopology(cells=[
objects.InstanceNUMACell(id=0, cpuset=set([0, 1, 2]), memory=512),
objects.InstanceNUMACell(id=1, cpuset=set([3]), memory=256),
objects.InstanceNUMACell(id=2, cpuset=set([4]), memory=256)])
hostusage = hw.numa_usage_from_instances(
hosttopo, [instance1])
self.assertIsInstance(hostusage.cells[0], objects.NUMACell)
self.assertEqual(hostusage.cells[0].cpu_usage, 5)
self.assertEqual(hostusage.cells[0].memory_usage, 1024)
self.assertIsInstance(hostusage.cells[1], objects.NUMACell)
self.assertEqual(hostusage.cells[1].cpu_usage, 2)
self.assertEqual(hostusage.cells[1].memory_usage, 768)
self.assertIsInstance(hostusage.cells[2], objects.NUMACell)
self.assertEqual(hostusage.cells[2].cpu_usage, 1)
self.assertEqual(hostusage.cells[2].memory_usage, 256)
# Test freeing of resources
hostusage = hw.numa_usage_from_instances(
hostusage, [instance1], free=True)
self.assertEqual(hostusage.cells[0].cpu_usage, 2)
self.assertEqual(hostusage.cells[0].memory_usage, 512)
self.assertEqual(hostusage.cells[1].cpu_usage, 1)
self.assertEqual(hostusage.cells[1].memory_usage, 512)
self.assertEqual(hostusage.cells[2].cpu_usage, 0)
self.assertEqual(hostusage.cells[2].memory_usage, 0)
def test_topo_usage_none(self):
hosttopo = objects.NUMATopology(cells=[
objects.NUMACell(id=0, cpuset=set([0, 1]), memory=512,
cpu_usage=0, memory_usage=0),
objects.NUMACell(id=1, cpuset=set([2, 3]), memory=512,
cpu_usage=0, memory_usage=0),
])
instance1 = objects.InstanceNUMATopology(cells=[
objects.InstanceNUMACell(id=0, cpuset=set([0, 1]), memory=256),
objects.InstanceNUMACell(id=2, cpuset=set([2]), memory=256),
])
hostusage = hw.numa_usage_from_instances(
None, [instance1])
self.assertIsNone(hostusage)
hostusage = hw.numa_usage_from_instances(
hosttopo, [])
self.assertEqual(hostusage.cells[0].cpu_usage, 0)
self.assertEqual(hostusage.cells[0].memory_usage, 0)
self.assertEqual(hostusage.cells[1].cpu_usage, 0)
self.assertEqual(hostusage.cells[1].memory_usage, 0)
hostusage = hw.numa_usage_from_instances(
hosttopo, None)
self.assertEqual(hostusage.cells[0].cpu_usage, 0)
self.assertEqual(hostusage.cells[0].memory_usage, 0)
self.assertEqual(hostusage.cells[1].cpu_usage, 0)
self.assertEqual(hostusage.cells[1].memory_usage, 0)
def assertNUMACellMatches(self, expected_cell, got_cell):
attrs = ('cpuset', 'memory', 'id')
if isinstance(expected_cell, objects.NUMATopology):
attrs += ('cpu_usage', 'memory_usage')
for attr in attrs:
self.assertEqual(getattr(expected_cell, attr),
getattr(got_cell, attr))
def test_json(self):
expected = objects.NUMATopology(
cells=[
objects.NUMACell(id=1, cpuset=set([1, 2]), memory=1024,
cpu_usage=0, memory_usage=0),
objects.NUMACell(id=2, cpuset=set([3, 4]), memory=1024,
cpu_usage=0, memory_usage=0)])
got = objects.NUMATopology.obj_from_db_obj(expected._to_json())
for exp_cell, got_cell in zip(expected.cells, got.cells):
self.assertNUMACellMatches(exp_cell, got_cell)
class VirtNUMATopologyCellUsageTestCase(test.NoDBTestCase):
def test_fit_instance_cell_success_no_limit(self):
host_cell = objects.NUMACell(id=4, cpuset=set([1, 2]), memory=1024,
cpu_usage=0, memory_usage=0)
instance_cell = objects.InstanceNUMACell(
id=0, cpuset=set([1, 2]), memory=1024)
fitted_cell = hw._numa_fit_instance_cell(host_cell, instance_cell)
self.assertIsInstance(fitted_cell, objects.InstanceNUMACell)
self.assertEqual(host_cell.id, fitted_cell.id)
def test_fit_instance_cell_success_w_limit(self):
host_cell = objects.NUMACell(id=4, cpuset=set([1, 2]), memory=1024,
cpu_usage=2,
memory_usage=1024)
limit_cell = hw.VirtNUMATopologyCellLimit(
4, cpuset=set([1, 2]), memory=1024,
cpu_limit=4, memory_limit=2048)
instance_cell = objects.InstanceNUMACell(
id=0, cpuset=set([1, 2]), memory=1024)
fitted_cell = hw._numa_fit_instance_cell(
host_cell, instance_cell, limit_cell=limit_cell)
self.assertIsInstance(fitted_cell, objects.InstanceNUMACell)
self.assertEqual(host_cell.id, fitted_cell.id)
def test_fit_instance_cell_self_overcommit(self):
host_cell = objects.NUMACell(id=4, cpuset=set([1, 2]), memory=1024,
cpu_usage=0, memory_usage=0)
limit_cell = hw.VirtNUMATopologyCellLimit(
4, cpuset=set([1, 2]), memory=1024,
cpu_limit=4, memory_limit=2048)
instance_cell = objects.InstanceNUMACell(
id=0, cpuset=set([1, 2, 3]), memory=4096)
fitted_cell = hw._numa_fit_instance_cell(
host_cell, instance_cell, limit_cell=limit_cell)
self.assertIsNone(fitted_cell)
def test_fit_instance_cell_fail_w_limit(self):
host_cell = objects.NUMACell(id=4, cpuset=set([1, 2]), memory=1024,
cpu_usage=2,
memory_usage=1024)
limit_cell = hw.VirtNUMATopologyCellLimit(
4, cpuset=set([1, 2]), memory=1024,
cpu_limit=4, memory_limit=2048)
instance_cell = objects.InstanceNUMACell(
id=0, cpuset=set([1, 2]), memory=4096)
fitted_cell = hw._numa_fit_instance_cell(
host_cell, instance_cell, limit_cell=limit_cell)
self.assertIsNone(fitted_cell)
instance_cell = objects.InstanceNUMACell(
id=0, cpuset=set([1, 2, 3, 4, 5]), memory=1024)
fitted_cell = hw._numa_fit_instance_cell(
host_cell, instance_cell, limit_cell=limit_cell)
self.assertIsNone(fitted_cell)
class VirtNUMAHostTopologyTestCase(test.NoDBTestCase):
def setUp(self):
super(VirtNUMAHostTopologyTestCase, self).setUp()
self.host = objects.NUMATopology(
cells=[
objects.NUMACell(id=1, cpuset=set([1, 2]), memory=2048,
cpu_usage=2, memory_usage=2048),
objects.NUMACell(id=2, cpuset=set([3, 4]), memory=2048,
cpu_usage=2, memory_usage=2048)])
self.limits = hw.VirtNUMALimitTopology(
cells=[
hw.VirtNUMATopologyCellLimit(
1, cpuset=set([1, 2]), memory=2048,
cpu_limit=4, memory_limit=4096),
hw.VirtNUMATopologyCellLimit(
2, cpuset=set([3, 4]), memory=2048,
cpu_limit=4, memory_limit=3072)])
self.instance1 = objects.InstanceNUMATopology(
cells=[
objects.InstanceNUMACell(
id=0, cpuset=set([1, 2]), memory=2048)])
self.instance2 = objects.InstanceNUMATopology(
cells=[
objects.InstanceNUMACell(
id=0, cpuset=set([1, 2, 3, 4]), memory=1024)])
self.instance3 = objects.InstanceNUMATopology(
cells=[
objects.InstanceNUMACell(
id=0, cpuset=set([1, 2]), memory=1024)])
def test_get_fitting_success_no_limits(self):
fitted_instance1 = hw.numa_fit_instance_to_host(
self.host, self.instance1)
self.assertIsInstance(fitted_instance1, objects.InstanceNUMATopology)
self.host = hw.numa_usage_from_instances(self.host,
[fitted_instance1])
fitted_instance2 = hw.numa_fit_instance_to_host(
self.host, self.instance3)
self.assertIsInstance(fitted_instance2, objects.InstanceNUMATopology)
def test_get_fitting_success_limits(self):
fitted_instance = hw.numa_fit_instance_to_host(
self.host, self.instance3, self.limits)
self.assertIsInstance(fitted_instance, objects.InstanceNUMATopology)
self.assertEqual(1, fitted_instance.cells[0].id)
def test_get_fitting_fails_no_limits(self):
fitted_instance = hw.numa_fit_instance_to_host(
self.host, self.instance2, self.limits)
self.assertIsNone(fitted_instance)
def test_get_fitting_culmulative_fails_limits(self):
fitted_instance1 = hw.numa_fit_instance_to_host(
self.host, self.instance1, self.limits)
self.assertIsInstance(fitted_instance1, objects.InstanceNUMATopology)
self.assertEqual(1, fitted_instance1.cells[0].id)
self.host = hw.numa_usage_from_instances(self.host,
[fitted_instance1])
fitted_instance2 = hw.numa_fit_instance_to_host(
self.host, self.instance1, self.limits)
self.assertIsNone(fitted_instance2)
def test_get_fitting_culmulative_success_limits(self):
fitted_instance1 = hw.numa_fit_instance_to_host(
self.host, self.instance1, self.limits)
self.assertIsInstance(fitted_instance1, objects.InstanceNUMATopology)
self.assertEqual(1, fitted_instance1.cells[0].id)
self.host = hw.numa_usage_from_instances(self.host,
[fitted_instance1])
fitted_instance2 = hw.numa_fit_instance_to_host(
self.host, self.instance3, self.limits)
self.assertIsInstance(fitted_instance2, objects.InstanceNUMATopology)
self.assertEqual(2, fitted_instance2.cells[0].id)
class NumberOfSerialPortsTest(test.NoDBTestCase):
def test_flavor(self):
flavor = objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={"hw:serial_port_count": 3})
num_ports = hw.get_number_of_serial_ports(flavor, None)
self.assertEqual(3, num_ports)
def test_image_meta(self):
flavor = objects.Flavor(vcpus=8, memory_mb=2048, extra_specs={})
image_meta = {"properties": {"hw_serial_port_count": 2}}
num_ports = hw.get_number_of_serial_ports(flavor, image_meta)
self.assertEqual(2, num_ports)
def test_flavor_invalid_value(self):
flavor = objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={"hw:serial_port_count": 'foo'})
image_meta = {"properties": {}}
self.assertRaises(exception.ImageSerialPortNumberInvalid,
hw.get_number_of_serial_ports,
flavor, image_meta)
def test_image_meta_invalid_value(self):
flavor = objects.Flavor(vcpus=8, memory_mb=2048, extra_specs={})
image_meta = {"properties": {"hw_serial_port_count": 'bar'}}
self.assertRaises(exception.ImageSerialPortNumberInvalid,
hw.get_number_of_serial_ports,
flavor, image_meta)
def test_image_meta_smaller_than_flavor(self):
flavor = objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={"hw:serial_port_count": 3})
image_meta = {"properties": {"hw_serial_port_count": 2}}
num_ports = hw.get_number_of_serial_ports(flavor, image_meta)
self.assertEqual(2, num_ports)
def test_flavor_smaller_than_image_meta(self):
flavor = objects.Flavor(vcpus=8, memory_mb=2048,
extra_specs={"hw:serial_port_count": 3})
image_meta = {"properties": {"hw_serial_port_count": 4}}
self.assertRaises(exception.ImageSerialPortNumberExceedFlavorValue,
hw.get_number_of_serial_ports,
flavor, image_meta)
class HelperMethodsTestCase(test.NoDBTestCase):
def setUp(self):
super(HelperMethodsTestCase, self).setUp()
self.hosttopo = objects.NUMATopology(cells=[
objects.NUMACell(id=0, cpuset=set([0, 1]), memory=512,
memory_usage=0, cpu_usage=0),
objects.NUMACell(id=1, cpuset=set([2, 3]), memory=512,
memory_usage=0, cpu_usage=0),
])
self.instancetopo = objects.InstanceNUMATopology(
instance_uuid='fake-uuid',
cells=[
objects.InstanceNUMACell(
id=0, cpuset=set([0, 1]), memory=256, pagesize=2048),
objects.InstanceNUMACell(
id=1, cpuset=set([2]), memory=256, pagesize=2048),
])
self.context = context.RequestContext('fake-user',
'fake-project')
def _check_usage(self, host_usage):
self.assertEqual(2, host_usage.cells[0].cpu_usage)
self.assertEqual(256, host_usage.cells[0].memory_usage)
self.assertEqual(1, host_usage.cells[1].cpu_usage)
self.assertEqual(256, host_usage.cells[1].memory_usage)
def test_dicts_json(self):
host = {'numa_topology': self.hosttopo._to_json()}
instance = {'numa_topology': self.instancetopo._to_json()}
res = hw.get_host_numa_usage_from_instance(host, instance)
self.assertIsInstance(res, six.string_types)
self._check_usage(objects.NUMATopology.obj_from_db_obj(res))
def test_dicts_instance_json(self):
host = {'numa_topology': self.hosttopo}
instance = {'numa_topology': self.instancetopo._to_json()}
res = hw.get_host_numa_usage_from_instance(host, instance)
self.assertIsInstance(res, objects.NUMATopology)
self._check_usage(res)
def test_dicts_instance_json_old(self):
host = {'numa_topology': self.hosttopo}
instance = {'numa_topology':
jsonutils.dumps(self.instancetopo._to_dict())}
res = hw.get_host_numa_usage_from_instance(host, instance)
self.assertIsInstance(res, objects.NUMATopology)
self._check_usage(res)
def test_dicts_host_json(self):
host = {'numa_topology': self.hosttopo._to_json()}
instance = {'numa_topology': self.instancetopo}
res = hw.get_host_numa_usage_from_instance(host, instance)
self.assertIsInstance(res, six.string_types)
self._check_usage(objects.NUMATopology.obj_from_db_obj(res))
def test_dicts_host_json_old(self):
host = {'numa_topology': jsonutils.dumps(
self.hosttopo._to_dict())}
instance = {'numa_topology': self.instancetopo}
res = hw.get_host_numa_usage_from_instance(host, instance)
self.assertIsInstance(res, six.string_types)
self._check_usage(objects.NUMATopology.obj_from_db_obj(res))
def test_object_host_instance_json(self):
host = objects.ComputeNode(numa_topology=self.hosttopo._to_json())
instance = {'numa_topology': self.instancetopo._to_json()}
res = hw.get_host_numa_usage_from_instance(host, instance)
self.assertIsInstance(res, six.string_types)
self._check_usage(objects.NUMATopology.obj_from_db_obj(res))
def test_object_host_instance(self):
host = objects.ComputeNode(numa_topology=self.hosttopo._to_json())
instance = {'numa_topology': self.instancetopo}
res = hw.get_host_numa_usage_from_instance(host, instance)
self.assertIsInstance(res, six.string_types)
self._check_usage(objects.NUMATopology.obj_from_db_obj(res))
def test_instance_with_fetch(self):
host = objects.ComputeNode(numa_topology=self.hosttopo._to_json())
fake_uuid = str(uuid.uuid4())
instance = {'uuid': fake_uuid}
with mock.patch.object(objects.InstanceNUMATopology,
'get_by_instance_uuid', return_value=None) as get_mock:
res = hw.get_host_numa_usage_from_instance(host, instance)
self.assertIsInstance(res, six.string_types)
self.assertTrue(get_mock.called)
def test_object_instance_with_load(self):
host = objects.ComputeNode(numa_topology=self.hosttopo._to_json())
fake_uuid = str(uuid.uuid4())
instance = objects.Instance(context=self.context, uuid=fake_uuid)
with mock.patch.object(objects.InstanceNUMATopology,
'get_by_instance_uuid', return_value=None) as get_mock:
res = hw.get_host_numa_usage_from_instance(host, instance)
self.assertIsInstance(res, six.string_types)
self.assertTrue(get_mock.called)
def test_instance_serialized_by_build_request_spec(self):
host = objects.ComputeNode(numa_topology=self.hosttopo._to_json())
fake_uuid = str(uuid.uuid4())
instance = objects.Instance(context=self.context, id=1, uuid=fake_uuid,
numa_topology=self.instancetopo)
# NOTE (ndipanov): This emulates scheduler.utils.build_request_spec
# We can remove this test once we no longer use that method.
instance_raw = jsonutils.to_primitive(
base_obj.obj_to_primitive(instance))
res = hw.get_host_numa_usage_from_instance(host, instance_raw)
self.assertIsInstance(res, six.string_types)
self._check_usage(objects.NUMATopology.obj_from_db_obj(res))
def test_attr_host(self):
class Host(object):
def __init__(obj):
obj.numa_topology = self.hosttopo._to_json()
host = Host()
instance = {'numa_topology': self.instancetopo._to_json()}
res = hw.get_host_numa_usage_from_instance(host, instance)
self.assertIsInstance(res, six.string_types)
self._check_usage(objects.NUMATopology.obj_from_db_obj(res))
def test_never_serialize_result(self):
host = {'numa_topology': self.hosttopo._to_json()}
instance = {'numa_topology': self.instancetopo}
res = hw.get_host_numa_usage_from_instance(host, instance,
never_serialize_result=True)
self.assertIsInstance(res, objects.NUMATopology)
self._check_usage(res)
class VirtMemoryPagesTestCase(test.NoDBTestCase):
def test_virt_pages_topology(self):
pages = hw.VirtPagesTopology(4, 1024, 512)
self.assertEqual(4, pages.size_kb)
self.assertEqual(1024, pages.total)
self.assertEqual(512, pages.used)
def test_cell_instance_pagesize(self):
cell = objects.InstanceNUMACell(
id=0, cpuset=set([0]), memory=1024, pagesize=2048)
self.assertEqual(0, cell.id)
self.assertEqual(set([0]), cell.cpuset)
self.assertEqual(1024, cell.memory)
self.assertEqual(2048, cell.pagesize)