Merge "Instances with NUMA will be packed onto hosts"

This commit is contained in:
Jenkins 2014-11-21 10:29:39 +00:00 committed by Gerrit Code Review
commit 87bc95e098
8 changed files with 61 additions and 47 deletions

View File

@ -268,8 +268,7 @@ and try to match it with the topology exposed by the host, accounting for the
``ram_allocation_ratio`` and ``cpu_allocation_ratio`` for over-subscription. The
filtering is done in the following manner:
* Filter will try to match the exact NUMA cells of the instance to those of
the host. It *will not* attempt to pack the instance onto the host.
* Filter will attempt to pack instance cells onto host cells.
* It will consider the standard over-subscription limits for each host NUMA cell,
and provide limits to the compute host accordingly (as mentioned above).
* If instance has no topology defined, it will be considered for any host.

View File

@ -36,6 +36,7 @@ class NopClaim(object):
def __init__(self, migration=None):
self.migration = migration
self.claimed_numa_topology = None
@property
def disk_gb(self):
@ -201,13 +202,22 @@ class Claim(NopClaim):
def _test_numa_topology(self, resources, limit):
host_topology = resources.get('numa_topology')
if host_topology and limit:
requested_topology = (self.numa_topology and
self.numa_topology.topology_from_obj())
if host_topology:
host_topology = hardware.VirtNUMAHostTopology.from_json(
host_topology)
instances_topology = (
[self.numa_topology] if self.numa_topology else [])
return hardware.VirtNUMAHostTopology.claim_test(
host_topology, instances_topology, limit)
instance_topology = (
hardware.VirtNUMAHostTopology.fit_instance_to_host(
host_topology, requested_topology,
limits_topology=limit))
if requested_topology and not instance_topology:
return (_("Requested instance NUMA topology cannot fit "
"the given host NUMA topology"))
elif instance_topology:
self.claimed_numa_topology = (
objects.InstanceNUMATopology.obj_from_topology(
instance_topology))
def _test(self, type_, unit, total, used, requested, limit):
"""Test if the given type of resource needed for a claim can be safely
@ -264,8 +274,11 @@ class ResizeClaim(Claim):
@property
def numa_topology(self):
return hardware.VirtNUMAInstanceTopology.get_constraints(
instance_topology = hardware.VirtNUMAInstanceTopology.get_constraints(
self.instance_type, self.image_meta)
if instance_topology:
return objects.InstanceNUMATopology.obj_from_topology(
instance_topology)
def _test_pci(self):
pci_requests = objects.InstancePCIRequests.\

View File

@ -1402,7 +1402,7 @@ class ComputeManager(manager.Manager):
rt = self._get_resource_tracker(node)
try:
limits = filter_properties.get('limits', {})
with rt.instance_claim(context, instance, limits):
with rt.instance_claim(context, instance, limits) as inst_claim:
# NOTE(russellb) It's important that this validation be done
# *after* the resource tracker instance claim, as that is where
# the host is set on the instance.
@ -1422,6 +1422,7 @@ class ComputeManager(manager.Manager):
instance.vm_state = vm_states.BUILDING
instance.task_state = task_states.BLOCK_DEVICE_MAPPING
instance.numa_topology = inst_claim.claimed_numa_topology
instance.save()
block_device_info = self._prep_block_device(
@ -2089,7 +2090,7 @@ class ComputeManager(manager.Manager):
extra_usage_info={'image_name': image_name})
try:
rt = self._get_resource_tracker(node)
with rt.instance_claim(context, instance, limits):
with rt.instance_claim(context, instance, limits) as inst_claim:
# NOTE(russellb) It's important that this validation be done
# *after* the resource tracker instance claim, as that is where
# the host is set on the instance.
@ -2100,6 +2101,7 @@ class ComputeManager(manager.Manager):
block_device_mapping) as resources:
instance.vm_state = vm_states.BUILDING
instance.task_state = task_states.SPAWNING
instance.numa_topology = inst_claim.claimed_numa_topology
instance.save(expected_task_state=
task_states.BLOCK_DEVICE_MAPPING)
block_device_info = resources['block_device_info']

View File

@ -130,6 +130,7 @@ class ResourceTracker(object):
overhead=overhead, limits=limits)
self._set_instance_host_and_node(context, instance_ref)
instance_ref['numa_topology'] = claim.claimed_numa_topology
# Mark resources in-use and update stats
self._update_usage_from_instance(context, self.compute_node,
@ -596,9 +597,16 @@ class ResourceTracker(object):
instance['system_metadata'])
if itype:
host_topology = resources.get('numa_topology')
if host_topology:
host_topology = hardware.VirtNUMAHostTopology.from_json(
host_topology)
numa_topology = (
hardware.VirtNUMAInstanceTopology.get_constraints(
itype, image_meta))
numa_topology = (
hardware.VirtNUMAHostTopology.fit_instance_to_host(
host_topology, numa_topology))
usage = self._get_usage_dict(
itype, numa_topology=numa_topology)
if self.pci_tracker:

View File

@ -28,34 +28,28 @@ class NUMATopologyFilter(filters.BaseHostFilter):
cpu_ratio = CONF.cpu_allocation_ratio
request_spec = filter_properties.get('request_spec', {})
instance = request_spec.get('instance_properties', {})
instance_topology = hardware.instance_topology_from_instance(instance)
requested_topology = hardware.instance_topology_from_instance(instance)
host_topology, _fmt = hardware.host_topology_and_format_from_host(
host_state)
if instance_topology:
if host_topology:
if not hardware.VirtNUMAHostTopology.can_fit_instances(
host_topology, [instance_topology]):
return False
limit_cells = []
usage_after_instance = (
hardware.VirtNUMAHostTopology.usage_from_instances(
host_topology, [instance_topology]))
for cell in usage_after_instance.cells:
max_cell_memory = int(cell.memory * ram_ratio)
max_cell_cpu = len(cell.cpuset) * cpu_ratio
if (cell.memory_usage > max_cell_memory or
cell.cpu_usage > max_cell_cpu):
return False
limit_cells.append(
hardware.VirtNUMATopologyCellLimit(
cell.id, cell.cpuset, cell.memory,
max_cell_cpu, max_cell_memory))
host_state.limits['numa_topology'] = (
hardware.VirtNUMALimitTopology(
cells=limit_cells).to_json())
return True
else:
if requested_topology and host_topology:
limit_cells = []
for cell in host_topology.cells:
max_cell_memory = int(cell.memory * ram_ratio)
max_cell_cpu = len(cell.cpuset) * cpu_ratio
limit_cells.append(hardware.VirtNUMATopologyCellLimit(
cell.id, cell.cpuset, cell.memory,
max_cell_cpu, max_cell_memory))
limits = hardware.VirtNUMALimitTopology(cells=limit_cells)
instance_topology = (
hardware.VirtNUMAHostTopology.fit_instance_to_host(
host_topology, requested_topology,
limits_topology=limits))
if not instance_topology:
return False
host_state.limits['numa_topology'] = limits.to_json()
instance['numa_topology'] = instance_topology.to_json()
return True
elif requested_topology:
return False
else:
return True

View File

@ -236,7 +236,7 @@ class ClaimTestCase(test.NoDBTestCase):
def test_numa_topology_no_limit(self, mock_get):
huge_instance = hardware.VirtNUMAInstanceTopology(
cells=[hardware.VirtNUMATopologyCellInstance(
1, set([1, 2, 3, 4, 5]), 2048)])
1, set([1, 2]), 512)])
self._claim(numa_topology=huge_instance)
def test_numa_topology_fails(self, mock_get):
@ -256,7 +256,7 @@ class ClaimTestCase(test.NoDBTestCase):
def test_numa_topology_passes(self, mock_get):
huge_instance = hardware.VirtNUMAInstanceTopology(
cells=[hardware.VirtNUMATopologyCellInstance(
1, set([1, 2, 3, 4, 5]), 2048)])
1, set([1, 2]), 512)])
limit_topo = hardware.VirtNUMALimitTopology(
cells=[hardware.VirtNUMATopologyCellLimit(
1, [1, 2], 512, cpu_limit=5, memory_limit=4096),

View File

@ -868,8 +868,8 @@ class InstanceClaimTestCase(BaseTrackerTestCase):
memory_mb = FAKE_VIRT_MEMORY_MB * 2
root_gb = ephemeral_gb = FAKE_VIRT_LOCAL_GB
vcpus = FAKE_VIRT_VCPUS * 2
claim_topology = self._claim_topology(memory_mb)
instance_topology = self._instance_topology(memory_mb)
claim_topology = self._claim_topology(3)
instance_topology = self._instance_topology(3)
limits = {'memory_mb': memory_mb + FAKE_VIRT_MEMORY_OVERHEAD,
'disk_gb': root_gb * 2,

View File

@ -1122,14 +1122,12 @@ def instance_topology_from_instance(instance):
# Remove when request_spec is a proper object itself!
dict_cells = instance_numa_topology.get('cells')
if dict_cells:
cells = [objects.InstanceNUMACell(id=cell['id'],
cpuset=set(cell['cpuset']),
memory=cell['memory'],
pagesize=cell.get(
'pagesize'))
cells = [VirtNUMATopologyCellInstance(cell['id'],
set(cell['cpuset']),
cell['memory'],
cell.get('pagesize'))
for cell in dict_cells]
instance_numa_topology = (
objects.InstanceNUMATopology(cells=cells))
instance_numa_topology = VirtNUMAInstanceTopology(cells=cells)
return instance_numa_topology