Merge "When a claim is rejected, explain why"

This commit is contained in:
Jenkins 2014-03-03 19:53:00 +00:00 committed by Gerrit Code Review
commit 5418039348
6 changed files with 138 additions and 95 deletions

View File

@ -17,6 +17,7 @@
Claim objects for use with resource tracking.
"""
from nova import exception
from nova.objects import instance as instance_obj
from nova.openstack.common.gettextutils import _
from nova.openstack.common import jsonutils
@ -71,7 +72,8 @@ class Claim(NopClaim):
correct decisions with respect to host selection.
"""
def __init__(self, instance, tracker, overhead=None):
def __init__(self, instance, tracker, resources, overhead=None,
limits=None):
super(Claim, self).__init__()
# Stash a copy of the instance at the current point of time
if isinstance(instance, instance_obj.Instance):
@ -88,6 +90,10 @@ class Claim(NopClaim):
self.overhead = overhead
# Check claim at constuctor to avoid mess code
# Raise exception ComputeResourcesUnavailable if claim failed
self._claim_test(resources, limits)
@property
def disk_gb(self):
return self.instance['root_gb'] + self.instance['ephemeral_gb']
@ -107,7 +113,7 @@ class Claim(NopClaim):
LOG.debug(_("Aborting claim: %s") % self, instance=self.instance)
self.tracker.abort_instance_claim(self.instance)
def test(self, resources, limits=None):
def _claim_test(self, resources, limits=None):
"""Test if this claim can be satisfied given available resources and
optional oversubscription limits
@ -132,21 +138,19 @@ class Claim(NopClaim):
'vcpus': self.vcpus}
LOG.audit(msg % params, instance=self.instance)
# Test for resources:
can_claim = (self._test_memory(resources, memory_mb_limit) and
self._test_disk(resources, disk_gb_limit) and
self._test_cpu(resources, vcpu_limit) and
self._test_pci())
reasons = [self._test_memory(resources, memory_mb_limit),
self._test_disk(resources, disk_gb_limit),
self._test_cpu(resources, vcpu_limit),
self._test_pci()]
reasons = [r for r in reasons if r is not None]
if len(reasons) > 0:
raise exception.ComputeResourcesUnavailable(reason=
"; ".join(reasons))
if can_claim:
LOG.audit(_("Claim successful"), instance=self.instance)
else:
LOG.audit(_("Claim failed"), instance=self.instance)
return can_claim
LOG.audit(_('Claim successful'), instance=self.instance)
def _test_memory(self, resources, limit):
type_ = _("Memory")
type_ = _("memory")
unit = "MB"
total = resources['memory_mb']
used = resources['memory_mb_used']
@ -155,7 +159,7 @@ class Claim(NopClaim):
return self._test(type_, unit, total, used, requested, limit)
def _test_disk(self, resources, limit):
type_ = _("Disk")
type_ = _("disk")
unit = "GB"
total = resources['local_gb']
used = resources['local_gb_used']
@ -165,12 +169,15 @@ class Claim(NopClaim):
def _test_pci(self):
pci_requests = pci_request.get_instance_pci_requests(self.instance)
if not pci_requests:
return True
return self.tracker.pci_tracker.stats.support_requests(pci_requests)
if pci_requests:
can_claim = self.tracker.pci_tracker.stats.support_requests(
pci_requests)
if not can_claim:
return _('Claim pci failed.')
def _test_cpu(self, resources, limit):
type_ = _("CPU")
type_ = _("CPUs")
unit = "VCPUs"
total = resources['vcpus']
used = resources['vcpus_used']
@ -191,7 +198,7 @@ class Claim(NopClaim):
# treat resource as unlimited:
LOG.audit(_('%(type)s limit not specified, defaulting to '
'unlimited'), {'type': type_}, instance=self.instance)
return True
return
free = limit - used
@ -201,25 +208,22 @@ class Claim(NopClaim):
{'type': type_, 'limit': limit, 'free': free, 'unit': unit},
instance=self.instance)
can_claim = requested <= free
if not can_claim:
LOG.info(_('Unable to claim resources. Free %(type)s %(free).02f '
'%(unit)s < requested %(requested)d %(unit)s'),
{'type': type_, 'free': free, 'unit': unit,
'requested': requested},
instance=self.instance)
return can_claim
if requested > free:
return (_('Free %(type)s %(free).02f '
'%(unit)s < requested %(requested)d %(unit)s') %
{'type': type_, 'free': free, 'unit': unit,
'requested': requested})
class ResizeClaim(Claim):
"""Claim used for holding resources for an incoming resize/migration
operation.
"""
def __init__(self, instance, instance_type, tracker, overhead=None):
super(ResizeClaim, self).__init__(instance, tracker, overhead=overhead)
def __init__(self, instance, instance_type, tracker, resources,
overhead=None, limits=None):
self.instance_type = instance_type
super(ResizeClaim, self).__init__(instance, tracker, resources,
overhead=overhead, limits=limits)
self.migration = None
@property
@ -238,10 +242,11 @@ class ResizeClaim(Claim):
def _test_pci(self):
pci_requests = pci_request.get_instance_pci_requests(
self.instance, 'new_')
if not pci_requests:
return True
return self.tracker.pci_tracker.stats.support_requests(pci_requests)
if pci_requests:
claim = self.tracker.pci_tracker.stats.support_requests(
pci_requests)
if not claim:
return _('Claim pci failed.')
def abort(self):
"""Compute operation requiring claimed resources has failed or

View File

@ -118,23 +118,19 @@ class ResourceTracker(object):
"MB"), {'flavor': instance_ref['memory_mb'],
'overhead': overhead['memory_mb']})
claim = claims.Claim(instance_ref, self, overhead=overhead)
claim = claims.Claim(instance_ref, self, self.compute_node,
overhead=overhead, limits=limits)
if claim.test(self.compute_node, limits):
self._set_instance_host_and_node(context, instance_ref)
self._set_instance_host_and_node(context, instance_ref)
# Mark resources in-use and update stats
self._update_usage_from_instance(self.compute_node, instance_ref)
# Mark resources in-use and update stats
self._update_usage_from_instance(self.compute_node, instance_ref)
elevated = context.elevated()
# persist changes to the compute node:
self._update(elevated, self.compute_node)
elevated = context.elevated()
# persist changes to the compute node:
self._update(elevated, self.compute_node)
return claim
else:
raise exception.ComputeResourcesUnavailable()
return claim
@utils.synchronized(COMPUTE_RESOURCE_SEMAPHORE)
def resize_claim(self, context, instance, instance_type, limits=None):
@ -164,25 +160,21 @@ class ResourceTracker(object):
instance_ref = obj_base.obj_to_primitive(instance)
claim = claims.ResizeClaim(instance_ref, instance_type, self,
overhead=overhead)
self.compute_node, overhead=overhead,
limits=limits)
if claim.test(self.compute_node, limits):
migration = self._create_migration(context, instance_ref,
instance_type)
claim.migration = migration
migration = self._create_migration(context, instance_ref,
instance_type)
claim.migration = migration
# Mark the resources in-use for the resize landing on this
# compute host:
self._update_usage_from_migration(context, instance_ref,
# Mark the resources in-use for the resize landing on this
# compute host:
self._update_usage_from_migration(context, instance_ref,
self.compute_node, migration)
elevated = context.elevated()
self._update(elevated, self.compute_node)
elevated = context.elevated()
self._update(elevated, self.compute_node)
return claim
else:
raise exception.ComputeResourcesUnavailable()
return claim
def _create_migration(self, context, instance, instance_type):
"""Create a migration record for the upcoming resize. This should

View File

@ -391,7 +391,7 @@ class ServiceUnavailable(Invalid):
class ComputeResourcesUnavailable(ServiceUnavailable):
msg_fmt = _("Insufficient compute resources.")
msg_fmt = _("Insufficient compute resources: %(reason)s.")
class HypervisorUnavailable(NovaException):

View File

@ -15,9 +15,11 @@
"""Tests for resource tracker claims."""
import re
import uuid
from nova.compute import claims
from nova import exception
from nova.openstack.common import jsonutils
from nova.pci import pci_manager
from nova import test
@ -45,11 +47,12 @@ class ClaimTestCase(test.NoDBTestCase):
self.resources = self._fake_resources()
self.tracker = DummyTracker()
def _claim(self, overhead=None, **kwargs):
def _claim(self, limits=None, overhead=None, **kwargs):
instance = self._fake_instance(**kwargs)
if overhead is None:
overhead = {'memory_mb': 0}
return claims.Claim(instance, self.tracker, overhead=overhead)
return claims.Claim(instance, self.tracker, self.resources,
overhead=overhead, limits=limits)
def _fake_instance(self, **kwargs):
instance = {
@ -90,63 +93,90 @@ class ClaimTestCase(test.NoDBTestCase):
resources.update(values)
return resources
# TODO(lxsli): Remove once Py2.6 is deprecated
def assertRaisesRegexp(self, re_obj, e, fn, *a, **kw):
try:
fn(*a, **kw)
self.fail("Expected exception not raised")
except e as ee:
self.assertTrue(re.search(re_obj, str(ee)))
def test_cpu_unlimited(self):
claim = self._claim(vcpus=100000)
self.assertTrue(claim.test(self.resources))
def test_memory_unlimited(self):
claim = self._claim(memory_mb=99999999)
self.assertTrue(claim.test(self.resources))
def test_disk_unlimited_root(self):
claim = self._claim(root_gb=999999)
self.assertTrue(claim.test(self.resources))
def test_disk_unlimited_ephemeral(self):
claim = self._claim(ephemeral_gb=999999)
self.assertTrue(claim.test(self.resources))
def test_cpu_oversubscription(self):
claim = self._claim(vcpus=8)
limits = {'vcpu': 16}
self.assertTrue(claim.test(self.resources, limits))
claim = self._claim(limits, vcpus=8)
def test_memory_with_overhead(self):
overhead = {'memory_mb': 8}
claim = self._claim(memory_mb=2040, overhead=overhead)
limits = {'memory_mb': 2048}
self.assertTrue(claim.test(self.resources, limits))
claim = self._claim(memory_mb=2040, limits=limits,
overhead=overhead)
def test_memory_with_overhead_insufficient(self):
overhead = {'memory_mb': 9}
claim = self._claim(memory_mb=2040, overhead=overhead)
limits = {'memory_mb': 2048}
self.assertFalse(claim.test(self.resources, limits))
self.assertRaises(exception.ComputeResourcesUnavailable,
self._claim, limits=limits, overhead=overhead,
memory_mb=2040)
def test_cpu_insufficient(self):
claim = self._claim(vcpus=17)
limits = {'vcpu': 16}
self.assertFalse(claim.test(self.resources, limits))
self.assertRaises(exception.ComputeResourcesUnavailable,
self._claim, limits=limits, vcpus=17)
def test_memory_oversubscription(self):
claim = self._claim(memory_mb=4096)
limits = {'memory_mb': 8192}
self.assertTrue(claim.test(self.resources, limits))
claim = self._claim(memory_mb=4096)
def test_memory_insufficient(self):
claim = self._claim(memory_mb=16384)
limits = {'memory_mb': 8192}
self.assertFalse(claim.test(self.resources, limits))
self.assertRaises(exception.ComputeResourcesUnavailable,
self._claim, limits=limits, memory_mb=16384)
def test_disk_oversubscription(self):
claim = self._claim(root_gb=10, ephemeral_gb=40)
limits = {'disk_gb': 60}
self.assertTrue(claim.test(self.resources, limits))
claim = self._claim(root_gb=10, ephemeral_gb=40,
limits=limits)
def test_disk_insufficient(self):
claim = self._claim(root_gb=10, ephemeral_gb=40)
limits = {'disk_gb': 45}
self.assertFalse(claim.test(self.resources, limits))
self.assertRaisesRegexp(re.compile("disk", re.IGNORECASE),
exception.ComputeResourcesUnavailable,
self._claim, limits=limits, root_gb=10, ephemeral_gb=40)
def test_disk_and_memory_insufficient(self):
limits = {'disk_gb': 45, 'memory_mb': 8192}
self.assertRaisesRegexp(re.compile("memory.*disk", re.IGNORECASE),
exception.ComputeResourcesUnavailable,
self._claim, limits=limits, root_gb=10, ephemeral_gb=40,
memory_mb=16384)
def test_disk_and_cpu_insufficient(self):
limits = {'disk_gb': 45, 'vcpu': 16}
self.assertRaisesRegexp(re.compile("disk.*vcpus", re.IGNORECASE),
exception.ComputeResourcesUnavailable,
self._claim, limits=limits, root_gb=10, ephemeral_gb=40,
vcpus=17)
def test_disk_and_cpu_and_memory_insufficient(self):
limits = {'disk_gb': 45, 'vcpu': 16, 'memory_mb': 8192}
pat = "memory.*disk.*vcpus"
self.assertRaisesRegexp(re.compile(pat, re.IGNORECASE),
exception.ComputeResourcesUnavailable,
self._claim, limits=limits, root_gb=10, ephemeral_gb=40,
vcpus=17, memory_mb=16384)
def test_pci_pass(self):
dev_dict = {
@ -159,7 +189,7 @@ class ClaimTestCase(test.NoDBTestCase):
self.tracker.pci_tracker.set_hvdevs([dev_dict])
claim = self._claim()
self._set_pci_request(claim)
self.assertTrue(claim._test_pci())
claim._test_pci()
def _set_pci_request(self, claim):
request = [{'count': 1,
@ -180,7 +210,7 @@ class ClaimTestCase(test.NoDBTestCase):
self.tracker.pci_tracker.set_hvdevs([dev_dict])
claim = self._claim()
self._set_pci_request(claim)
self.assertFalse(claim._test_pci())
self.assertEqual("Claim pci failed.", claim._test_pci())
def test_pci_pass_no_requests(self):
dev_dict = {
@ -193,7 +223,7 @@ class ClaimTestCase(test.NoDBTestCase):
self.tracker.pci_tracker.set_hvdevs([dev_dict])
claim = self._claim()
self._set_pci_request(claim)
self.assertTrue(claim._test_pci())
claim._test_pci()
def test_abort(self):
claim = self._abort()
@ -216,12 +246,13 @@ class ResizeClaimTestCase(ClaimTestCase):
super(ResizeClaimTestCase, self).setUp()
self.instance = self._fake_instance()
def _claim(self, overhead=None, **kwargs):
def _claim(self, limits=None, overhead=None, **kwargs):
instance_type = self._fake_instance_type(**kwargs)
if overhead is None:
overhead = {'memory_mb': 0}
return claims.ResizeClaim(self.instance, instance_type, self.tracker,
overhead=overhead)
self.resources, overhead=overhead,
limits=limits)
def _set_pci_request(self, claim):
request = [{'count': 1,

View File

@ -1229,7 +1229,8 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
self.instance['uuid'], mock.ANY, 'conductor'))
def test_reschedule_on_resources_unavailable(self):
exc = exception.ComputeResourcesUnavailable()
reason = 'resource unavailable'
exc = exception.ComputeResourcesUnavailable(reason=reason)
class FakeResourceTracker(object):
def instance_claim(self, context, instance, limits):

View File

@ -29,6 +29,20 @@ CONF = cfg.CONF
CONF.import_opt('shelved_offload_time', 'nova.compute.manager')
def _fake_resources():
resources = {
'memory_mb': 2048,
'memory_mb_used': 0,
'free_ram_mb': 2048,
'local_gb': 20,
'local_gb_used': 0,
'free_disk_gb': 20,
'vcpus': 2,
'vcpus_used': 0
}
return resources
class ShelveComputeManagerTestCase(test_compute.BaseTestCase):
def _shelve_instance(self, shelved_offload_time):
CONF.set_override('shelved_offload_time', shelved_offload_time)
@ -200,7 +214,7 @@ class ShelveComputeManagerTestCase(test_compute.BaseTestCase):
db_instance['key_data'] = None
db_instance['auto_disk_config'] = None
self.rt.instance_claim(self.context, instance, limits).AndReturn(
claims.Claim(db_instance, self.rt))
claims.Claim(db_instance, self.rt, _fake_resources()))
self.compute.driver.spawn(self.context, instance, image,
injected_files=[], admin_password=None,
network_info=[],
@ -266,7 +280,7 @@ class ShelveComputeManagerTestCase(test_compute.BaseTestCase):
db_instance['key_data'] = None
db_instance['auto_disk_config'] = None
self.rt.instance_claim(self.context, instance, limits).AndReturn(
claims.Claim(db_instance, self.rt))
claims.Claim(db_instance, self.rt, _fake_resources()))
self.compute.driver.spawn(self.context, instance, None,
injected_files=[], admin_password=None,
network_info=[],