Merge "When a claim is rejected, explain why"
This commit is contained in:
commit
5418039348
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
|
@ -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):
|
||||
|
@ -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=[],
|
||||
|
Loading…
Reference in New Issue
Block a user