Convert hardware.VirtCPUTopology to nova object
All objects that have data that may eventually be serialized across the wire in RPC comms, stored in a database, or potentially change their schema, should be nova.objects. This patch changes the staticmethods on the nova.virt.hardware.VirtCPUTopology class to a collection of private (and one public) regular functions in nova.virt.hardware that consume and produce nova.objects.virt_cpu_topology.VirtCPUTopology objects. Change-Id: I2244aa7125b11568bc04d35e5de3eb2ee8c15167
This commit is contained in:
parent
aebcf022ee
commit
96ab8ab058
|
@ -52,4 +52,5 @@ def register_all():
|
|||
__import__('nova.objects.security_group')
|
||||
__import__('nova.objects.security_group_rule')
|
||||
__import__('nova.objects.service')
|
||||
__import__('nova.objects.virt_cpu_topology')
|
||||
__import__('nova.objects.virtual_interface')
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# 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 nova.objects import base
|
||||
from nova.objects import fields
|
||||
|
||||
|
||||
class VirtCPUTopology(base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'sockets': fields.IntegerField(nullable=True, default=1),
|
||||
'cores': fields.IntegerField(nullable=True, default=1),
|
||||
'threads': fields.IntegerField(nullable=True, default=1),
|
||||
}
|
||||
|
||||
# NOTE(jaypipes): for backward compatibility, the virt CPU topology
|
||||
# data is stored in the database as a nested dict.
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
return cls(sockets=data.get('sockets'),
|
||||
cores=data.get('cores'),
|
||||
threads=data.get('threads'))
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'sockets': self.sockets,
|
||||
'cores': self.cores,
|
||||
'threads': self.threads
|
||||
}
|
|
@ -1056,6 +1056,7 @@ object_data = {
|
|||
'TestSubclassedObject': '1.6-c63feb2f2533b7d075490c04a2cc10dd',
|
||||
'VirtualInterface': '1.0-10fdac4c704102b6d57d6936d6d790d2',
|
||||
'VirtualInterfaceList': '1.0-accbf02628a8063c1d885077a2bf49b6',
|
||||
'VirtCPUTopology': '1.0-fc694de72e20298f7c6bab1083fd4563',
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
# 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 nova import objects
|
||||
from nova.tests.unit.objects import test_objects
|
||||
|
||||
|
||||
_top_dict = {
|
||||
'sockets': 2,
|
||||
'cores': 4,
|
||||
'threads': 8
|
||||
}
|
||||
|
||||
|
||||
class _TestVirtCPUTopologyObject(object):
|
||||
|
||||
def test_object_from_dict(self):
|
||||
top_obj = objects.VirtCPUTopology.from_dict(_top_dict)
|
||||
self.compare_obj(top_obj, _top_dict)
|
||||
|
||||
def test_object_to_dict(self):
|
||||
top_obj = objects.VirtCPUTopology()
|
||||
top_obj.sockets = 2
|
||||
top_obj.cores = 4
|
||||
top_obj.threads = 8
|
||||
spec = top_obj.to_dict()
|
||||
self.assertEqual(_top_dict, spec)
|
||||
|
||||
|
||||
class TestVirtCPUTopologyObject(test_objects._LocalTest,
|
||||
_TestVirtCPUTopologyObject):
|
||||
pass
|
||||
|
||||
|
||||
class TestRemoteVirtCPUTopologyObject(test_objects._RemoteTest,
|
||||
_TestVirtCPUTopologyObject):
|
||||
pass
|
|
@ -375,7 +375,7 @@ class VCPUTopologyTest(test.NoDBTestCase):
|
|||
for topo_test in testdata:
|
||||
if type(topo_test["expect"]) == tuple:
|
||||
(preferred,
|
||||
maximum) = hw.VirtCPUTopology.get_topology_constraints(
|
||||
maximum) = hw._get_cpu_topology_constraints(
|
||||
topo_test["flavor"],
|
||||
topo_test["image"])
|
||||
|
||||
|
@ -387,11 +387,11 @@ class VCPUTopologyTest(test.NoDBTestCase):
|
|||
self.assertEqual(topo_test["expect"][5], maximum.threads)
|
||||
else:
|
||||
self.assertRaises(topo_test["expect"],
|
||||
hw.VirtCPUTopology.get_topology_constraints,
|
||||
hw._get_cpu_topology_constraints,
|
||||
topo_test["flavor"],
|
||||
topo_test["image"])
|
||||
|
||||
def test_possible_configs(self):
|
||||
def test_possible_topologies(self):
|
||||
testdata = [
|
||||
{
|
||||
"allow_threads": True,
|
||||
|
@ -481,11 +481,12 @@ class VCPUTopologyTest(test.NoDBTestCase):
|
|||
for topo_test in testdata:
|
||||
if type(topo_test["expect"]) == list:
|
||||
actual = []
|
||||
for topology in hw.VirtCPUTopology.get_possible_topologies(
|
||||
for topology in hw._get_possible_cpu_topologies(
|
||||
topo_test["vcpus"],
|
||||
hw.VirtCPUTopology(topo_test["maxsockets"],
|
||||
topo_test["maxcores"],
|
||||
topo_test["maxthreads"]),
|
||||
objects.VirtCPUTopology(
|
||||
sockets=topo_test["maxsockets"],
|
||||
cores=topo_test["maxcores"],
|
||||
threads=topo_test["maxthreads"]),
|
||||
topo_test["allow_threads"]):
|
||||
actual.append([topology.sockets,
|
||||
topology.cores,
|
||||
|
@ -494,14 +495,15 @@ class VCPUTopologyTest(test.NoDBTestCase):
|
|||
self.assertEqual(topo_test["expect"], actual)
|
||||
else:
|
||||
self.assertRaises(topo_test["expect"],
|
||||
hw.VirtCPUTopology.get_possible_topologies,
|
||||
hw._get_possible_cpu_topologies,
|
||||
topo_test["vcpus"],
|
||||
hw.VirtCPUTopology(topo_test["maxsockets"],
|
||||
topo_test["maxcores"],
|
||||
topo_test["maxthreads"]),
|
||||
objects.VirtCPUTopology(
|
||||
sockets=topo_test["maxsockets"],
|
||||
cores=topo_test["maxcores"],
|
||||
threads=topo_test["maxthreads"]),
|
||||
topo_test["allow_threads"])
|
||||
|
||||
def test_sorting_configs(self):
|
||||
def test_sorting_topologies(self):
|
||||
testdata = [
|
||||
{
|
||||
"allow_threads": True,
|
||||
|
@ -572,18 +574,18 @@ class VCPUTopologyTest(test.NoDBTestCase):
|
|||
|
||||
for topo_test in testdata:
|
||||
actual = []
|
||||
possible = hw.VirtCPUTopology.get_possible_topologies(
|
||||
possible = hw._get_possible_cpu_topologies(
|
||||
topo_test["vcpus"],
|
||||
hw.VirtCPUTopology(topo_test["maxsockets"],
|
||||
topo_test["maxcores"],
|
||||
topo_test["maxthreads"]),
|
||||
objects.VirtCPUTopology(sockets=topo_test["maxsockets"],
|
||||
cores=topo_test["maxcores"],
|
||||
threads=topo_test["maxthreads"]),
|
||||
topo_test["allow_threads"])
|
||||
|
||||
tops = hw.VirtCPUTopology.sort_possible_topologies(
|
||||
tops = hw._sort_possible_cpu_topologies(
|
||||
possible,
|
||||
hw.VirtCPUTopology(topo_test["sockets"],
|
||||
topo_test["cores"],
|
||||
topo_test["threads"]))
|
||||
objects.VirtCPUTopology(sockets=topo_test["sockets"],
|
||||
cores=topo_test["cores"],
|
||||
threads=topo_test["threads"]))
|
||||
for topology in tops:
|
||||
actual.append([topology.sockets,
|
||||
topology.cores,
|
||||
|
@ -705,7 +707,7 @@ class VCPUTopologyTest(test.NoDBTestCase):
|
|||
]
|
||||
|
||||
for topo_test in testdata:
|
||||
topology = hw.VirtCPUTopology.get_desirable_configs(
|
||||
topology = hw._get_desirable_cpu_topologies(
|
||||
topo_test["flavor"],
|
||||
topo_test["image"],
|
||||
topo_test["allow_threads"])[0]
|
||||
|
|
|
@ -215,28 +215,11 @@ class InstanceInfo(object):
|
|||
self.__dict__ == other.__dict__)
|
||||
|
||||
|
||||
class VirtCPUTopology(object):
|
||||
|
||||
def __init__(self, sockets, cores, threads):
|
||||
"""Create a new CPU topology object
|
||||
|
||||
:param sockets: number of sockets, at least 1
|
||||
:param cores: number of cores, at least 1
|
||||
:param threads: number of threads, at least 1
|
||||
|
||||
Create a new CPU topology object representing the
|
||||
number of sockets, cores and threads to use for
|
||||
the virtual instance.
|
||||
"""
|
||||
|
||||
self.sockets = sockets
|
||||
self.cores = cores
|
||||
self.threads = threads
|
||||
|
||||
def score(self, wanttopology):
|
||||
def _score_cpu_topology(topology, wanttopology):
|
||||
"""Calculate score for the topology against a desired configuration
|
||||
|
||||
:param wanttopology: VirtCPUTopology instance for preferred topology
|
||||
:param wanttopology: nova.objects.VirtCPUTopology instance for
|
||||
preferred topology
|
||||
|
||||
Calculate a score indicating how well this topology
|
||||
matches against a preferred topology. A score of 3
|
||||
|
@ -251,18 +234,18 @@ class VirtCPUTopology(object):
|
|||
|
||||
score = 0
|
||||
if (wanttopology.sockets != -1 and
|
||||
self.sockets == wanttopology.sockets):
|
||||
topology.sockets == wanttopology.sockets):
|
||||
score = score + 1
|
||||
if (wanttopology.cores != -1 and
|
||||
self.cores == wanttopology.cores):
|
||||
topology.cores == wanttopology.cores):
|
||||
score = score + 1
|
||||
if (wanttopology.threads != -1 and
|
||||
self.threads == wanttopology.threads):
|
||||
topology.threads == wanttopology.threads):
|
||||
score = score + 1
|
||||
return score
|
||||
|
||||
@staticmethod
|
||||
def get_topology_constraints(flavor, image_meta):
|
||||
|
||||
def _get_cpu_topology_constraints(flavor, image_meta):
|
||||
"""Get the topology constraints declared in flavor or image
|
||||
|
||||
:param flavor: Flavor object to read extra specs from
|
||||
|
@ -291,7 +274,7 @@ class VirtCPUTopology(object):
|
|||
The image metadata must be strictly lower than any values
|
||||
set in the flavor. All values are, however, optional.
|
||||
|
||||
This will return a pair of VirtCPUTopology instances,
|
||||
This will return a pair of nova.objects.VirtCPUTopology instances,
|
||||
the first giving the preferred socket/core/thread counts,
|
||||
and the second giving the upper limits on socket/core/
|
||||
thread counts.
|
||||
|
@ -407,26 +390,28 @@ class VirtCPUTopology(object):
|
|||
"threads": threads, "maxsockets": maxsockets,
|
||||
"maxcores": maxcores, "maxthreads": maxthreads})
|
||||
|
||||
return (VirtCPUTopology(sockets, cores, threads),
|
||||
VirtCPUTopology(maxsockets, maxcores, maxthreads))
|
||||
return (objects.VirtCPUTopology(sockets=sockets, cores=cores,
|
||||
threads=threads),
|
||||
objects.VirtCPUTopology(sockets=maxsockets, cores=maxcores,
|
||||
threads=maxthreads))
|
||||
|
||||
@staticmethod
|
||||
def get_possible_topologies(vcpus, maxtopology, allow_threads):
|
||||
|
||||
def _get_possible_cpu_topologies(vcpus, maxtopology, allow_threads):
|
||||
"""Get a list of possible topologies for a vCPU count
|
||||
:param vcpus: total number of CPUs for guest instance
|
||||
:param maxtopology: VirtCPUTopology for upper limits
|
||||
:param maxtopology: nova.objects.VirtCPUTopology for upper limits
|
||||
:param allow_threads: if the hypervisor supports CPU threads
|
||||
|
||||
Given a total desired vCPU count and constraints on the
|
||||
maximum number of sockets, cores and threads, return a
|
||||
list of VirtCPUTopology instances that represent every
|
||||
list of nova.objects.VirtCPUTopology instances that represent every
|
||||
possible topology that satisfies the constraints.
|
||||
|
||||
exception.ImageVCPULimitsRangeImpossible is raised if
|
||||
it is impossible to achieve the total vcpu count given
|
||||
the maximum limits on sockets, cores & threads.
|
||||
|
||||
:returns: list of VirtCPUTopology instances
|
||||
:returns: list of nova.objects.VirtCPUTopology instances
|
||||
"""
|
||||
|
||||
# Clamp limits to number of vcpus to prevent
|
||||
|
@ -454,7 +439,10 @@ class VirtCPUTopology(object):
|
|||
for c in range(1, maxcores + 1):
|
||||
for t in range(1, maxthreads + 1):
|
||||
if t * c * s == vcpus:
|
||||
possible.append(VirtCPUTopology(s, c, t))
|
||||
o = objects.VirtCPUTopology(sockets=s, cores=c,
|
||||
threads=t)
|
||||
|
||||
possible.append(o)
|
||||
|
||||
# We want to
|
||||
# - Minimize threads (ie larger sockets * cores is best)
|
||||
|
@ -473,17 +461,18 @@ class VirtCPUTopology(object):
|
|||
|
||||
return possible
|
||||
|
||||
@staticmethod
|
||||
def sort_possible_topologies(possible, wanttopology):
|
||||
|
||||
def _sort_possible_cpu_topologies(possible, wanttopology):
|
||||
"""Sort the topologies in order of preference
|
||||
:param possible: list of VirtCPUTopology instances
|
||||
:param wanttopology: VirtCPUTopology for preferred topology
|
||||
:param possible: list of nova.objects.VirtCPUTopology instances
|
||||
:param wanttopology: nova.objects.VirtCPUTopology for preferred
|
||||
topology
|
||||
|
||||
This takes the list of possible topologies and resorts
|
||||
it such that those configurations which most closely
|
||||
match the preferred topology are first.
|
||||
|
||||
:returns: sorted list of VirtCPUTopology instances
|
||||
:returns: sorted list of nova.objects.VirtCPUTopology instances
|
||||
"""
|
||||
|
||||
# Look at possible topologies and score them according
|
||||
|
@ -493,7 +482,7 @@ class VirtCPUTopology(object):
|
|||
# 'possible' list originally
|
||||
scores = collections.defaultdict(list)
|
||||
for topology in possible:
|
||||
score = topology.score(wanttopology)
|
||||
score = _score_cpu_topology(topology, wanttopology)
|
||||
scores[score].append(topology)
|
||||
|
||||
# Build list of all possible topologies sorted
|
||||
|
@ -506,8 +495,8 @@ class VirtCPUTopology(object):
|
|||
|
||||
return desired
|
||||
|
||||
@staticmethod
|
||||
def get_desirable_configs(flavor, image_meta, allow_threads=True):
|
||||
|
||||
def _get_desirable_cpu_topologies(flavor, image_meta, allow_threads=True):
|
||||
"""Get desired CPU topologies according to settings
|
||||
|
||||
:param flavor: Flavor object to query extra specs from
|
||||
|
@ -519,27 +508,25 @@ class VirtCPUTopology(object):
|
|||
valid CPU topologies that can be used in the guest. Then
|
||||
return this list sorted in order of preference.
|
||||
|
||||
:returns: sorted list of VirtCPUTopology instances
|
||||
:returns: sorted list of nova.objects.VirtCPUTopology instances
|
||||
"""
|
||||
|
||||
LOG.debug("Getting desirable topologies for flavor %(flavor)s "
|
||||
"and image_meta %(image_meta)s",
|
||||
{"flavor": flavor, "image_meta": image_meta})
|
||||
|
||||
preferred, maximum = (
|
||||
VirtCPUTopology.get_topology_constraints(flavor,
|
||||
image_meta))
|
||||
preferred, maximum = _get_cpu_topology_constraints(flavor, image_meta)
|
||||
|
||||
possible = VirtCPUTopology.get_possible_topologies(
|
||||
flavor.vcpus, maximum, allow_threads)
|
||||
desired = VirtCPUTopology.sort_possible_topologies(
|
||||
possible, preferred)
|
||||
possible = _get_possible_cpu_topologies(flavor.vcpus,
|
||||
maximum,
|
||||
allow_threads)
|
||||
desired = _sort_possible_cpu_topologies(possible, preferred)
|
||||
|
||||
return desired
|
||||
|
||||
@staticmethod
|
||||
def get_best_config(flavor, image_meta, allow_threads=True):
|
||||
"""Get bst CPU topology according to settings
|
||||
|
||||
def get_best_cpu_topology(flavor, image_meta, allow_threads=True):
|
||||
"""Get best CPU topology according to settings
|
||||
|
||||
:param flavor: Flavor object to query extra specs from
|
||||
:param image_meta: ImageMeta object to query properties from
|
||||
|
@ -550,12 +537,10 @@ class VirtCPUTopology(object):
|
|||
valid CPU topologies that can be used in the guest. Then
|
||||
return the best topology to use
|
||||
|
||||
:returns: a VirtCPUTopology instance for best topology
|
||||
:returns: a nova.objects.VirtCPUTopology instance for best topology
|
||||
"""
|
||||
|
||||
return VirtCPUTopology.get_desirable_configs(flavor,
|
||||
image_meta,
|
||||
allow_threads)[0]
|
||||
return _get_desirable_cpu_topologies(flavor, image_meta, allow_threads)[0]
|
||||
|
||||
|
||||
class VirtPageSize(object):
|
||||
|
|
|
@ -3392,8 +3392,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
if cpu is None:
|
||||
return None
|
||||
|
||||
topology = hardware.VirtCPUTopology.get_best_config(flavor,
|
||||
image)
|
||||
topology = hardware.get_best_cpu_topology(flavor, image)
|
||||
|
||||
cpu.sockets = topology.sockets
|
||||
cpu.cores = topology.cores
|
||||
|
|
Loading…
Reference in New Issue