Merge "Instances with NUMA will be packed onto hosts"
This commit is contained in:
commit
87bc95e098
|
@ -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.
|
||||
|
|
|
@ -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.\
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue