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:
Jay Pipes 2014-11-01 22:15:19 -04:00
parent aebcf022ee
commit 96ab8ab058
7 changed files with 374 additions and 300 deletions

View File

@ -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')

View File

@ -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
}

View File

@ -1056,6 +1056,7 @@ object_data = {
'TestSubclassedObject': '1.6-c63feb2f2533b7d075490c04a2cc10dd',
'VirtualInterface': '1.0-10fdac4c704102b6d57d6936d6d790d2',
'VirtualInterfaceList': '1.0-accbf02628a8063c1d885077a2bf49b6',
'VirtCPUTopology': '1.0-fc694de72e20298f7c6bab1083fd4563',
}

View File

@ -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

View File

@ -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]

View File

@ -215,347 +215,332 @@ class InstanceInfo(object):
self.__dict__ == other.__dict__)
class VirtCPUTopology(object):
def _score_cpu_topology(topology, wanttopology):
"""Calculate score for the topology against a desired configuration
def __init__(self, sockets, cores, threads):
"""Create a new CPU topology object
:param wanttopology: nova.objects.VirtCPUTopology instance for
preferred topology
:param sockets: number of sockets, at least 1
:param cores: number of cores, at least 1
:param threads: number of threads, at least 1
Calculate a score indicating how well this topology
matches against a preferred topology. A score of 3
indicates an exact match for sockets, cores and threads.
A score of 2 indicates a match of sockets & cores or
sockets & threads or cores and threads. A score of 1
indicates a match of sockets or cores or threads. A
score of 0 indicates no match
Create a new CPU topology object representing the
number of sockets, cores and threads to use for
the virtual instance.
"""
:returns: score in range 0 (worst) to 3 (best)
"""
self.sockets = sockets
self.cores = cores
self.threads = threads
score = 0
if (wanttopology.sockets != -1 and
topology.sockets == wanttopology.sockets):
score = score + 1
if (wanttopology.cores != -1 and
topology.cores == wanttopology.cores):
score = score + 1
if (wanttopology.threads != -1 and
topology.threads == wanttopology.threads):
score = score + 1
return score
def score(self, wanttopology):
"""Calculate score for the topology against a desired configuration
:param wanttopology: VirtCPUTopology instance for preferred topology
def _get_cpu_topology_constraints(flavor, image_meta):
"""Get the topology constraints declared in flavor or image
Calculate a score indicating how well this topology
matches against a preferred topology. A score of 3
indicates an exact match for sockets, cores and threads.
A score of 2 indicates a match of sockets & cores or
sockets & threads or cores and threads. A score of 1
indicates a match of sockets or cores or threads. A
score of 0 indicates no match
:param flavor: Flavor object to read extra specs from
:param image_meta: Image object to read image metadata from
:returns: score in range 0 (worst) to 3 (best)
"""
Gets the topology constraints from the configuration defined
in the flavor extra specs or the image metadata. In the flavor
this will look for
score = 0
if (wanttopology.sockets != -1 and
self.sockets == wanttopology.sockets):
score = score + 1
if (wanttopology.cores != -1 and
self.cores == wanttopology.cores):
score = score + 1
if (wanttopology.threads != -1 and
self.threads == wanttopology.threads):
score = score + 1
return score
hw:cpu_sockets - preferred socket count
hw:cpu_cores - preferred core count
hw:cpu_threads - preferred thread count
hw:cpu_maxsockets - maximum socket count
hw:cpu_maxcores - maximum core count
hw:cpu_maxthreads - maximum thread count
@staticmethod
def get_topology_constraints(flavor, image_meta):
"""Get the topology constraints declared in flavor or image
In the image metadata this will look at
:param flavor: Flavor object to read extra specs from
:param image_meta: Image object to read image metadata from
hw_cpu_sockets - preferred socket count
hw_cpu_cores - preferred core count
hw_cpu_threads - preferred thread count
hw_cpu_maxsockets - maximum socket count
hw_cpu_maxcores - maximum core count
hw_cpu_maxthreads - maximum thread count
Gets the topology constraints from the configuration defined
in the flavor extra specs or the image metadata. In the flavor
this will look for
The image metadata must be strictly lower than any values
set in the flavor. All values are, however, optional.
hw:cpu_sockets - preferred socket count
hw:cpu_cores - preferred core count
hw:cpu_threads - preferred thread count
hw:cpu_maxsockets - maximum socket count
hw:cpu_maxcores - maximum core count
hw:cpu_maxthreads - maximum thread count
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.
In the image metadata this will look at
exception.ImageVCPULimitsRangeExceeded will be raised
if the maximum counts set against the image exceed
the maximum counts set against the flavor
hw_cpu_sockets - preferred socket count
hw_cpu_cores - preferred core count
hw_cpu_threads - preferred thread count
hw_cpu_maxsockets - maximum socket count
hw_cpu_maxcores - maximum core count
hw_cpu_maxthreads - maximum thread count
exception.ImageVCPUTopologyRangeExceeded will be raised
if the preferred counts set against the image exceed
the maximum counts set against the image or flavor
The image metadata must be strictly lower than any values
set in the flavor. All values are, however, optional.
:returns: (preferred topology, maximum topology)
"""
This will return a pair of VirtCPUTopology instances,
the first giving the preferred socket/core/thread counts,
and the second giving the upper limits on socket/core/
thread counts.
# Obtain the absolute limits from the flavor
flvmaxsockets = int(flavor.extra_specs.get(
"hw:cpu_max_sockets", 65536))
flvmaxcores = int(flavor.extra_specs.get(
"hw:cpu_max_cores", 65536))
flvmaxthreads = int(flavor.extra_specs.get(
"hw:cpu_max_threads", 65536))
exception.ImageVCPULimitsRangeExceeded will be raised
if the maximum counts set against the image exceed
the maximum counts set against the flavor
LOG.debug("Flavor limits %(sockets)d:%(cores)d:%(threads)d",
{"sockets": flvmaxsockets,
"cores": flvmaxcores,
"threads": flvmaxthreads})
exception.ImageVCPUTopologyRangeExceeded will be raised
if the preferred counts set against the image exceed
the maximum counts set against the image or flavor
# Get any customized limits from the image
maxsockets = int(image_meta.get("properties", {})
.get("hw_cpu_max_sockets", flvmaxsockets))
maxcores = int(image_meta.get("properties", {})
.get("hw_cpu_max_cores", flvmaxcores))
maxthreads = int(image_meta.get("properties", {})
.get("hw_cpu_max_threads", flvmaxthreads))
:returns: (preferred topology, maximum topology)
"""
LOG.debug("Image limits %(sockets)d:%(cores)d:%(threads)d",
{"sockets": maxsockets,
"cores": maxcores,
"threads": maxthreads})
# Obtain the absolute limits from the flavor
flvmaxsockets = int(flavor.extra_specs.get(
"hw:cpu_max_sockets", 65536))
flvmaxcores = int(flavor.extra_specs.get(
"hw:cpu_max_cores", 65536))
flvmaxthreads = int(flavor.extra_specs.get(
"hw:cpu_max_threads", 65536))
# Image limits are not permitted to exceed the flavor
# limits. ie they can only lower what the flavor defines
if ((maxsockets > flvmaxsockets) or
(maxcores > flvmaxcores) or
(maxthreads > flvmaxthreads)):
raise exception.ImageVCPULimitsRangeExceeded(
sockets=maxsockets,
cores=maxcores,
threads=maxthreads,
maxsockets=flvmaxsockets,
maxcores=flvmaxcores,
maxthreads=flvmaxthreads)
LOG.debug("Flavor limits %(sockets)d:%(cores)d:%(threads)d",
{"sockets": flvmaxsockets,
"cores": flvmaxcores,
"threads": flvmaxthreads})
# Get any default preferred topology from the flavor
flvsockets = int(flavor.extra_specs.get("hw:cpu_sockets", -1))
flvcores = int(flavor.extra_specs.get("hw:cpu_cores", -1))
flvthreads = int(flavor.extra_specs.get("hw:cpu_threads", -1))
# Get any customized limits from the image
maxsockets = int(image_meta.get("properties", {})
.get("hw_cpu_max_sockets", flvmaxsockets))
maxcores = int(image_meta.get("properties", {})
.get("hw_cpu_max_cores", flvmaxcores))
maxthreads = int(image_meta.get("properties", {})
.get("hw_cpu_max_threads", flvmaxthreads))
LOG.debug("Flavor pref %(sockets)d:%(cores)d:%(threads)d",
{"sockets": flvsockets,
"cores": flvcores,
"threads": flvthreads})
LOG.debug("Image limits %(sockets)d:%(cores)d:%(threads)d",
{"sockets": maxsockets,
"cores": maxcores,
"threads": maxthreads})
# If the image limits have reduced the flavor limits
# we might need to discard the preferred topology
# from the flavor
if ((flvsockets > maxsockets) or
(flvcores > maxcores) or
(flvthreads > maxthreads)):
flvsockets = flvcores = flvthreads = -1
# Image limits are not permitted to exceed the flavor
# limits. ie they can only lower what the flavor defines
if ((maxsockets > flvmaxsockets) or
(maxcores > flvmaxcores) or
(maxthreads > flvmaxthreads)):
raise exception.ImageVCPULimitsRangeExceeded(
sockets=maxsockets,
cores=maxcores,
threads=maxthreads,
maxsockets=flvmaxsockets,
maxcores=flvmaxcores,
maxthreads=flvmaxthreads)
# Finally see if the image has provided a preferred
# topology to use
sockets = int(image_meta.get("properties", {})
.get("hw_cpu_sockets", -1))
cores = int(image_meta.get("properties", {})
.get("hw_cpu_cores", -1))
threads = int(image_meta.get("properties", {})
.get("hw_cpu_threads", -1))
# Get any default preferred topology from the flavor
flvsockets = int(flavor.extra_specs.get("hw:cpu_sockets", -1))
flvcores = int(flavor.extra_specs.get("hw:cpu_cores", -1))
flvthreads = int(flavor.extra_specs.get("hw:cpu_threads", -1))
LOG.debug("Image pref %(sockets)d:%(cores)d:%(threads)d",
{"sockets": sockets,
"cores": cores,
"threads": threads})
LOG.debug("Flavor pref %(sockets)d:%(cores)d:%(threads)d",
{"sockets": flvsockets,
"cores": flvcores,
"threads": flvthreads})
# Image topology is not permitted to exceed image/flavor
# limits
if ((sockets > maxsockets) or
(cores > maxcores) or
(threads > maxthreads)):
raise exception.ImageVCPUTopologyRangeExceeded(
sockets=sockets,
cores=cores,
threads=threads,
maxsockets=maxsockets,
maxcores=maxcores,
maxthreads=maxthreads)
# If the image limits have reduced the flavor limits
# we might need to discard the preferred topology
# from the flavor
if ((flvsockets > maxsockets) or
(flvcores > maxcores) or
(flvthreads > maxthreads)):
flvsockets = flvcores = flvthreads = -1
# If no preferred topology was set against the image
# then use the preferred topology from the flavor
# We use 'and' not 'or', since if any value is set
# against the image this invalidates the entire set
# of values from the flavor
if sockets == -1 and cores == -1 and threads == -1:
sockets = flvsockets
cores = flvcores
threads = flvthreads
# Finally see if the image has provided a preferred
# topology to use
sockets = int(image_meta.get("properties", {})
.get("hw_cpu_sockets", -1))
cores = int(image_meta.get("properties", {})
.get("hw_cpu_cores", -1))
threads = int(image_meta.get("properties", {})
.get("hw_cpu_threads", -1))
LOG.debug("Chosen %(sockets)d:%(cores)d:%(threads)d limits "
"%(maxsockets)d:%(maxcores)d:%(maxthreads)d",
{"sockets": sockets, "cores": cores,
"threads": threads, "maxsockets": maxsockets,
"maxcores": maxcores, "maxthreads": maxthreads})
LOG.debug("Image pref %(sockets)d:%(cores)d:%(threads)d",
{"sockets": sockets,
"cores": cores,
"threads": threads})
return (objects.VirtCPUTopology(sockets=sockets, cores=cores,
threads=threads),
objects.VirtCPUTopology(sockets=maxsockets, cores=maxcores,
threads=maxthreads))
# Image topology is not permitted to exceed image/flavor
# limits
if ((sockets > maxsockets) or
(cores > maxcores) or
(threads > maxthreads)):
raise exception.ImageVCPUTopologyRangeExceeded(
sockets=sockets,
cores=cores,
threads=threads,
maxsockets=maxsockets,
maxcores=maxcores,
maxthreads=maxthreads)
# If no preferred topology was set against the image
# then use the preferred topology from the flavor
# We use 'and' not 'or', since if any value is set
# against the image this invalidates the entire set
# of values from the flavor
if sockets == -1 and cores == -1 and threads == -1:
sockets = flvsockets
cores = flvcores
threads = flvthreads
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: nova.objects.VirtCPUTopology for upper limits
:param allow_threads: if the hypervisor supports CPU threads
LOG.debug("Chosen %(sockets)d:%(cores)d:%(threads)d limits "
"%(maxsockets)d:%(maxcores)d:%(maxthreads)d",
{"sockets": sockets, "cores": cores,
"threads": threads, "maxsockets": maxsockets,
"maxcores": maxcores, "maxthreads": maxthreads})
Given a total desired vCPU count and constraints on the
maximum number of sockets, cores and threads, return a
list of nova.objects.VirtCPUTopology instances that represent every
possible topology that satisfies the constraints.
return (VirtCPUTopology(sockets, cores, threads),
VirtCPUTopology(maxsockets, maxcores, maxthreads))
exception.ImageVCPULimitsRangeImpossible is raised if
it is impossible to achieve the total vcpu count given
the maximum limits on sockets, cores & threads.
@staticmethod
def get_possible_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 allow_threads: if the hypervisor supports CPU threads
:returns: list of nova.objects.VirtCPUTopology instances
"""
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
possible topology that satisfies the constraints.
# Clamp limits to number of vcpus to prevent
# iterating over insanely large list
maxsockets = min(vcpus, maxtopology.sockets)
maxcores = min(vcpus, maxtopology.cores)
maxthreads = min(vcpus, maxtopology.threads)
exception.ImageVCPULimitsRangeImpossible is raised if
it is impossible to achieve the total vcpu count given
the maximum limits on sockets, cores & threads.
if not allow_threads:
maxthreads = 1
:returns: list of VirtCPUTopology instances
"""
LOG.debug("Build topologies for %(vcpus)d vcpu(s) "
"%(maxsockets)d:%(maxcores)d:%(maxthreads)d",
{"vcpus": vcpus, "maxsockets": maxsockets,
"maxcores": maxcores, "maxthreads": maxthreads})
# Clamp limits to number of vcpus to prevent
# iterating over insanely large list
maxsockets = min(vcpus, maxtopology.sockets)
maxcores = min(vcpus, maxtopology.cores)
maxthreads = min(vcpus, maxtopology.threads)
# Figure out all possible topologies that match
# the required vcpus count and satisfy the declared
# limits. If the total vCPU count were very high
# it might be more efficient to factorize the vcpu
# count and then only iterate over its factors, but
# that's overkill right now
possible = []
for s in range(1, maxsockets + 1):
for c in range(1, maxcores + 1):
for t in range(1, maxthreads + 1):
if t * c * s == vcpus:
o = objects.VirtCPUTopology(sockets=s, cores=c,
threads=t)
if not allow_threads:
maxthreads = 1
possible.append(o)
LOG.debug("Build topologies for %(vcpus)d vcpu(s) "
"%(maxsockets)d:%(maxcores)d:%(maxthreads)d",
{"vcpus": vcpus, "maxsockets": maxsockets,
"maxcores": maxcores, "maxthreads": maxthreads})
# We want to
# - Minimize threads (ie larger sockets * cores is best)
# - Prefer sockets over cores
possible = sorted(possible, reverse=True,
key=lambda x: (x.sockets * x.cores,
x.sockets,
x.threads))
# Figure out all possible topologies that match
# the required vcpus count and satisfy the declared
# limits. If the total vCPU count were very high
# it might be more efficient to factorize the vcpu
# count and then only iterate over its factors, but
# that's overkill right now
possible = []
for s in range(1, maxsockets + 1):
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))
LOG.debug("Got %d possible topologies", len(possible))
if len(possible) == 0:
raise exception.ImageVCPULimitsRangeImpossible(vcpus=vcpus,
sockets=maxsockets,
cores=maxcores,
threads=maxthreads)
# We want to
# - Minimize threads (ie larger sockets * cores is best)
# - Prefer sockets over cores
possible = sorted(possible, reverse=True,
key=lambda x: (x.sockets * x.cores,
x.sockets,
x.threads))
return possible
LOG.debug("Got %d possible topologies", len(possible))
if len(possible) == 0:
raise exception.ImageVCPULimitsRangeImpossible(vcpus=vcpus,
sockets=maxsockets,
cores=maxcores,
threads=maxthreads)
return possible
def _sort_possible_cpu_topologies(possible, wanttopology):
"""Sort the topologies in order of preference
:param possible: list of nova.objects.VirtCPUTopology instances
:param wanttopology: nova.objects.VirtCPUTopology for preferred
topology
@staticmethod
def sort_possible_topologies(possible, wanttopology):
"""Sort the topologies in order of preference
:param possible: list of VirtCPUTopology instances
:param wanttopology: 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.
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 nova.objects.VirtCPUTopology instances
"""
:returns: sorted list of VirtCPUTopology instances
"""
# Look at possible topologies and score them according
# to how well they match the preferred topologies
# We don't use python's sort(), since we want to
# preserve the sorting done when populating the
# 'possible' list originally
scores = collections.defaultdict(list)
for topology in possible:
score = _score_cpu_topology(topology, wanttopology)
scores[score].append(topology)
# Look at possible topologies and score them according
# to how well they match the preferred topologies
# We don't use python's sort(), since we want to
# preserve the sorting done when populating the
# 'possible' list originally
scores = collections.defaultdict(list)
for topology in possible:
score = topology.score(wanttopology)
scores[score].append(topology)
# Build list of all possible topologies sorted
# by the match score, best match first
desired = []
desired.extend(scores[3])
desired.extend(scores[2])
desired.extend(scores[1])
desired.extend(scores[0])
# Build list of all possible topologies sorted
# by the match score, best match first
desired = []
desired.extend(scores[3])
desired.extend(scores[2])
desired.extend(scores[1])
desired.extend(scores[0])
return desired
return desired
@staticmethod
def get_desirable_configs(flavor, image_meta, allow_threads=True):
"""Get desired CPU topologies according to settings
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
:param image_meta: ImageMeta object to query properties from
:param allow_threads: if the hypervisor supports CPU threads
:param flavor: Flavor object to query extra specs from
:param image_meta: ImageMeta object to query properties from
:param allow_threads: if the hypervisor supports CPU threads
Look at the properties set in the flavor extra specs and
the image metadata and build up a list of all possible
valid CPU topologies that can be used in the guest. Then
return this list sorted in order of preference.
Look at the properties set in the flavor extra specs and
the image metadata and build up a list of all possible
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})
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
return desired
@staticmethod
def get_best_config(flavor, image_meta, allow_threads=True):
"""Get bst CPU topology according to settings
:param flavor: Flavor object to query extra specs from
:param image_meta: ImageMeta object to query properties from
:param allow_threads: if the hypervisor supports CPU threads
def get_best_cpu_topology(flavor, image_meta, allow_threads=True):
"""Get best CPU topology according to settings
Look at the properties set in the flavor extra specs and
the image metadata and build up a list of all possible
valid CPU topologies that can be used in the guest. Then
return the best topology to use
:param flavor: Flavor object to query extra specs from
:param image_meta: ImageMeta object to query properties from
:param allow_threads: if the hypervisor supports CPU threads
:returns: a VirtCPUTopology instance for best topology
"""
Look at the properties set in the flavor extra specs and
the image metadata and build up a list of all possible
valid CPU topologies that can be used in the guest. Then
return the best topology to use
return VirtCPUTopology.get_desirable_configs(flavor,
image_meta,
allow_threads)[0]
:returns: a nova.objects.VirtCPUTopology instance for best topology
"""
return _get_desirable_cpu_topologies(flavor, image_meta, allow_threads)[0]
class VirtPageSize(object):

View File

@ -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