summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/compute/claims.py186
-rw-r--r--nova/compute/manager.py2
-rw-r--r--nova/compute/resource_tracker.py265
-rw-r--r--nova/tests/compute/test_claims.py125
-rw-r--r--nova/tests/compute/test_resource_tracker.py274
5 files changed, 433 insertions, 419 deletions
diff --git a/nova/compute/claims.py b/nova/compute/claims.py
new file mode 100644
index 000000000..6415ae187
--- /dev/null
+++ b/nova/compute/claims.py
@@ -0,0 +1,186 @@
+# Copyright (c) 2012 OpenStack, LLC.
+# All Rights Reserved.
+#
+# 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.
+
+"""
+Claim objects for use with resource tracking.
+"""
+
+from nova import context
+from nova.openstack.common import jsonutils
+from nova.openstack.common import lockutils
+from nova.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+COMPUTE_RESOURCE_SEMAPHORE = "compute_resources"
+
+
+class NopClaim(object):
+ """For use with compute drivers that do not support resource tracking"""
+
+ @property
+ def disk_gb(self):
+ return 0
+
+ @property
+ def memory_mb(self):
+ return 0
+
+ @property
+ def vcpus(self):
+ return 0
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ if exc_type is not None:
+ self.abort()
+
+ def abort(self):
+ pass
+
+ def __str__(self):
+ return "[Claim: %d MB memory, %d GB disk, %d VCPUS]" % (self.memory_mb,
+ self.disk_gb, self.vcpus)
+
+
+class Claim(NopClaim):
+ """A declaration that a compute host operation will require free resources.
+ Claims serve as marker objects that resources are being held until the
+ update_available_resource audit process runs to do a full reconciliation
+ of resource usage.
+
+ This information will be used to help keep the local compute hosts's
+ ComputeNode model in sync to aid the scheduler in making efficient / more
+ correct decisions with respect to host selection.
+ """
+
+ def __init__(self, instance, tracker):
+ super(Claim, self).__init__()
+ self.instance = jsonutils.to_primitive(instance)
+ self.tracker = tracker
+
+ @property
+ def disk_gb(self):
+ return self.instance['root_gb'] + self.instance['ephemeral_gb']
+
+ @property
+ def memory_mb(self):
+ return self.instance['memory_mb']
+
+ @property
+ def vcpus(self):
+ return self.instance['vcpus']
+
+ @lockutils.synchronized(COMPUTE_RESOURCE_SEMAPHORE, 'nova-')
+ def abort(self):
+ """Compute operation requiring claimed resources has failed or
+ been aborted.
+ """
+ LOG.debug(_("Aborting claim: %s") % self, instance=self.instance)
+ self.tracker.abort_instance_claim(self.instance)
+
+ def test(self, resources, limits=None):
+ """Test if this claim can be satisfied given available resources and
+ optional oversubscription limits
+
+ This should be called before the compute node actually consumes the
+ resources required to execute the claim.
+
+ :param resources: available local compute node resources
+ :returns: Return true if resources are available to claim.
+ """
+ if not limits:
+ limits = {}
+
+ # If an individual limit is None, the resource will be considered
+ # unlimited:
+ memory_mb_limit = limits.get('memory_mb')
+ disk_gb_limit = limits.get('disk_gb')
+ vcpu_limit = limits.get('vcpu')
+
+ msg = _("Attempting claim: memory %(memory_mb)d MB, disk %(disk_gb)d "
+ "GB, VCPUs %(vcpus)d")
+ params = {'memory_mb': self.memory_mb, 'disk_gb': self.disk_gb,
+ '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))
+
+ if can_claim:
+ LOG.audit(_("Claim successful"), instance=self.instance)
+ else:
+ LOG.audit(_("Claim failed"), instance=self.instance)
+
+ return can_claim
+
+ def _test_memory(self, resources, limit):
+ type_ = _("Memory")
+ unit = "MB"
+ total = resources['memory_mb']
+ used = resources['memory_mb_used']
+ requested = self.memory_mb
+
+ return self._test(type_, unit, total, used, requested, limit)
+
+ def _test_disk(self, resources, limit):
+ type_ = _("Disk")
+ unit = "GB"
+ total = resources['local_gb']
+ used = resources['local_gb_used']
+ requested = self.disk_gb
+
+ return self._test(type_, unit, total, used, requested, limit)
+
+ def _test_cpu(self, resources, limit):
+ type_ = _("CPU")
+ unit = "VCPUs"
+ total = resources['vcpus']
+ used = resources['vcpus_used']
+ requested = self.vcpus
+
+ return self._test(type_, unit, total, used, requested, limit)
+
+ def _test(self, type_, unit, total, used, requested, limit):
+ """Test if the given type of resource needed for a claim can be safely
+ allocated.
+ """
+ msg = _("Total %(type_)s: %(total)d %(unit)s, used: %(used)d %(unit)s")
+ LOG.audit(msg % locals(), instance=self.instance)
+
+ if limit is None:
+ # treat resource as unlimited:
+ LOG.audit(_("%(type_)s limit not specified, defaulting to "
+ "unlimited") % locals(), instance=self.instance)
+ return True
+
+ free = limit - used
+
+ # Oversubscribed resource policy info:
+ msg = _("%(type_)s limit: %(limit)d %(unit)s, free: %(free)d "
+ "%(unit)s") % locals()
+ LOG.audit(msg, instance=self.instance)
+
+ can_claim = requested <= free
+
+ if not can_claim:
+ msg = _("Unable to claim resources. Free %(type_)s %(free)d "
+ "%(unit)s < requested %(requested)d %(unit)s") % locals()
+ LOG.info(msg, instance=self.instance)
+
+ return can_claim
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index de848abdd..828905c3d 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -522,7 +522,7 @@ class ComputeManager(manager.SchedulerDependentManager):
network_info = None
try:
limits = filter_properties.get('limits', {})
- with self.resource_tracker.resource_claim(context, instance,
+ with self.resource_tracker.instance_claim(context, instance,
limits):
network_info = self._allocate_network(context, instance,
diff --git a/nova/compute/resource_tracker.py b/nova/compute/resource_tracker.py
index 5e3d745bb..e4a65c081 100644
--- a/nova/compute/resource_tracker.py
+++ b/nova/compute/resource_tracker.py
@@ -19,14 +19,15 @@ scheduler with useful information about availability through the ComputeNode
model.
"""
+from nova.compute import claims
from nova.compute import vm_states
+from nova import context
from nova import db
from nova import exception
from nova import flags
from nova import notifications
from nova.openstack.common import cfg
from nova.openstack.common import importutils
-from nova.openstack.common import jsonutils
from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova import utils
@@ -45,63 +46,7 @@ FLAGS = flags.FLAGS
FLAGS.register_opts(resource_tracker_opts)
LOG = logging.getLogger(__name__)
-COMPUTE_RESOURCE_SEMAPHORE = "compute_resources"
-
-
-class Claim(object):
- """A declaration that a compute host operation will require free resources.
- Claims serve as marker objects that resources are being held until the
- update_available_resource audit process runs to do a full reconciliation
- of resource usage.
-
- This information will be used to help keep the local compute hosts's
- ComputeNode model in sync to aid the scheduler in making efficient / more
- correct decisions with respect to host selection.
- """
-
- def __init__(self, instance):
- self.instance = jsonutils.to_primitive(instance)
-
- @property
- def claim_id(self):
- return self.instance['uuid']
-
- @property
- def disk_gb(self):
- return self.instance['root_gb'] + self.instance['ephemeral_gb']
-
- @property
- def memory_mb(self):
- return self.instance['memory_mb']
-
- @property
- def vcpus(self):
- return self.instance['vcpus']
-
- def __str__(self):
- return "[Claim %s: %d MB memory, %d GB disk, %d VCPUS]" % \
- (self.claim_id, self.memory_mb, self.disk_gb, self.vcpus)
-
-
-class ResourceContextManager(object):
- def __init__(self, context, claim, tracker):
- self.context = context
- self.claim = claim
- self.tracker = tracker
-
- def __enter__(self):
- if not self.claim and not self.tracker.disabled:
- # insufficient resources to complete request
- raise exception.ComputeResourcesUnavailable()
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- if not self.claim:
- return
-
- if exc_type is None:
- self.tracker.finish_resource_claim(self.claim)
- else:
- self.tracker.abort_resource_claim(self.context, self.claim)
+COMPUTE_RESOURCE_SEMAPHORE = claims.COMPUTE_RESOURCE_SEMAPHORE
class ResourceTracker(object):
@@ -113,17 +58,11 @@ class ResourceTracker(object):
self.host = host
self.driver = driver
self.compute_node = None
- self.next_claim_id = 1
- self.claims = {}
self.stats = importutils.import_object(FLAGS.compute_stats_class)
self.tracked_instances = {}
- def resource_claim(self, context, instance_ref, limits=None):
- claim = self.begin_resource_claim(context, instance_ref, limits)
- return ResourceContextManager(context, claim, self)
-
@lockutils.synchronized(COMPUTE_RESOURCE_SEMAPHORE, 'nova-')
- def begin_resource_claim(self, context, instance_ref, limits=None):
+ def instance_claim(self, context, instance_ref, limits=None):
"""Indicate that some resources are needed for an upcoming compute
instance build operation.
@@ -134,17 +73,16 @@ class ResourceTracker(object):
:param instance_ref: instance to reserve resources for
:param limits: Dict of oversubscription limits for memory, disk,
and CPUs.
- :returns: An integer 'claim ticket'. This should be turned into
- finalize a resource claim or free resources after the
- compute operation is finished. Returns None if the claim
- failed.
+ :returns: A Claim ticket representing the reserved resources. It can
+ be used to revert the resource usage if an error occurs
+ during the instance build.
"""
if self.disabled:
# compute_driver doesn't support resource tracking, just
# set the 'host' field and continue the build:
instance_ref = self._set_instance_host(context,
- instance_ref['uuid'])
- return
+ instance_ref['uuid'])
+ return claims.NopClaim()
# sanity check:
if instance_ref['host']:
@@ -152,46 +90,23 @@ class ResourceTracker(object):
"until resources have been claimed."),
instance=instance_ref)
- if not limits:
- limits = {}
-
- # If an individual limit is None, the resource will be considered
- # unlimited:
- memory_mb_limit = limits.get('memory_mb')
- disk_gb_limit = limits.get('disk_gb')
- vcpu_limit = limits.get('vcpu')
-
- memory_mb = instance_ref['memory_mb']
- disk_gb = instance_ref['root_gb'] + instance_ref['ephemeral_gb']
- vcpus = instance_ref['vcpus']
+ claim = claims.Claim(instance_ref, self)
- msg = _("Attempting claim: memory %(memory_mb)d MB, disk %(disk_gb)d "
- "GB, VCPUs %(vcpus)d") % locals()
- LOG.audit(msg)
+ if claim.test(self.compute_node, limits):
- # Test for resources:
- if not self._can_claim_memory(memory_mb, memory_mb_limit):
- return
-
- if not self._can_claim_disk(disk_gb, disk_gb_limit):
- return
-
- if not self._can_claim_cpu(vcpus, vcpu_limit):
- return
+ instance_ref = self._set_instance_host(context,
+ instance_ref['uuid'])
- instance_ref = self._set_instance_host(context, instance_ref['uuid'])
+ # Mark resources in-use and update stats
+ self._update_usage_from_instance(self.compute_node, instance_ref)
- # keep track of this claim until we know whether the compute operation
- # was successful/completed:
- claim = Claim(instance_ref)
- self.claims[claim.claim_id] = claim
+ # persist changes to the compute node:
+ self._update(context, self.compute_node)
- # Mark resources in-use and update stats
- self._update_usage_from_instance(self.compute_node, instance_ref)
+ return claim
- # persist changes to the compute node:
- self._update(context, self.compute_node)
- return claim
+ else:
+ raise exception.ComputeResourcesUnavailable()
def _set_instance_host(self, context, instance_uuid):
"""Tag the instance as belonging to this host. This should be done
@@ -204,130 +119,15 @@ class ResourceTracker(object):
notifications.send_update(context, old_ref, instance_ref)
return instance_ref
- def _can_claim_memory(self, memory_mb, memory_mb_limit):
- """Test if memory needed for a claim can be safely allocated"""
- # Installed memory and usage info:
- msg = _("Total memory: %(total_mem)d MB, used: %(used_mem)d MB, free: "
- "%(free_mem)d MB") % dict(
- total_mem=self.compute_node['memory_mb'],
- used_mem=self.compute_node['memory_mb_used'],
- free_mem=self.compute_node['local_gb_used'])
- LOG.audit(msg)
-
- if memory_mb_limit is None:
- # treat memory as unlimited:
- LOG.audit(_("Memory limit not specified, defaulting to unlimited"))
- return True
-
- free_ram_mb = memory_mb_limit - self.compute_node['memory_mb_used']
-
- # Oversubscribed memory policy info:
- msg = _("Memory limit: %(memory_mb_limit)d MB, free: "
- "%(free_ram_mb)d MB") % locals()
- LOG.audit(msg)
-
- can_claim_mem = memory_mb <= free_ram_mb
-
- if not can_claim_mem:
- msg = _("Unable to claim resources. Free memory %(free_ram_mb)d "
- "MB < requested memory %(memory_mb)d MB") % locals()
- LOG.info(msg)
-
- return can_claim_mem
-
- def _can_claim_disk(self, disk_gb, disk_gb_limit):
- """Test if disk space needed can be safely allocated"""
- # Installed disk and usage info:
- msg = _("Total disk: %(total_disk)d GB, used: %(used_disk)d GB, free: "
- "%(free_disk)d GB") % dict(
- total_disk=self.compute_node['local_gb'],
- used_disk=self.compute_node['local_gb_used'],
- free_disk=self.compute_node['free_disk_gb'])
- LOG.audit(msg)
-
- if disk_gb_limit is None:
- # treat disk as unlimited:
- LOG.audit(_("Disk limit not specified, defaulting to unlimited"))
- return True
-
- free_disk_gb = disk_gb_limit - self.compute_node['local_gb_used']
-
- # Oversubscribed disk policy info:
- msg = _("Disk limit: %(disk_gb_limit)d GB, free: "
- "%(free_disk_gb)d GB") % locals()
- LOG.audit(msg)
-
- can_claim_disk = disk_gb <= free_disk_gb
- if not can_claim_disk:
- msg = _("Unable to claim resources. Free disk %(free_disk_gb)d GB"
- " < requested disk %(disk_gb)d GB") % dict(
- free_disk_gb=self.compute_node['free_disk_gb'],
- disk_gb=disk_gb)
- LOG.info(msg)
-
- return can_claim_disk
-
- def _can_claim_cpu(self, vcpus, vcpu_limit):
- """Test if CPUs can be safely allocated according to given policy."""
-
- msg = _("Total VCPUs: %(total_vcpus)d, used: %(used_vcpus)d") \
- % dict(total_vcpus=self.compute_node['vcpus'],
- used_vcpus=self.compute_node['vcpus_used'])
- LOG.audit(msg)
-
- if vcpu_limit is None:
- # treat cpu as unlimited:
- LOG.audit(_("VCPU limit not specified, defaulting to unlimited"))
- return True
-
- # Oversubscribed disk policy info:
- msg = _("CPU limit: %(vcpu_limit)d") % locals()
- LOG.audit(msg)
-
- free_vcpus = vcpu_limit - self.compute_node['vcpus_used']
- can_claim_cpu = vcpus <= free_vcpus
-
- if not can_claim_cpu:
- msg = _("Unable to claim resources. Free CPU %(free_vcpus)d < "
- "requested CPU %(vcpus)d") % locals()
- LOG.info(msg)
-
- return can_claim_cpu
-
- @lockutils.synchronized(COMPUTE_RESOURCE_SEMAPHORE, 'nova-')
- def finish_resource_claim(self, claim):
- """Indicate that the compute operation that previously claimed the
- resources identified by 'claim' has now completed and the resources
- have been allocated at the virt layer.
-
- :param claim: A claim indicating a set of resources that were
- previously claimed.
- """
- if self.disabled:
- return
-
- if self.claims.pop(claim.claim_id, None):
- LOG.debug(_("Finishing claim: %s") % claim)
+ def abort_instance_claim(self, instance):
+ """Remove usage from the given instance"""
+ # flag the instance as deleted to revert the resource usage
+ # and associated stats:
+ instance['vm_state'] = vm_states.DELETED
+ self._update_usage_from_instance(self.compute_node, instance)
- @lockutils.synchronized(COMPUTE_RESOURCE_SEMAPHORE, 'nova-')
- def abort_resource_claim(self, context, claim):
- """Indicate that the operation that claimed the resources identified by
- 'claim_id' has either failed or been aborted and the resources are no
- longer needed.
-
- :param claim: A claim ticket indicating a set of resources that were
- previously claimed.
- """
- if self.disabled:
- return
-
- if self.claims.pop(claim.claim_id, None):
- LOG.debug(_("Aborting claim: %s") % claim)
- # flag the instance as deleted to revert the resource usage
- # and associated stats:
- claim.instance['vm_state'] = vm_states.DELETED
- self._update_usage_from_instance(self.compute_node, claim.instance)
- self._update(context, self.compute_node)
+ ctxt = context.get_admin_context()
+ self._update(ctxt, self.compute_node)
@lockutils.synchronized(COMPUTE_RESOURCE_SEMAPHORE, 'nova-')
def update_usage(self, context, instance):
@@ -363,15 +163,12 @@ class ResourceTracker(object):
LOG.audit(_("Virt driver does not support "
"'get_available_resource' Compute tracking is disabled."))
self.compute_node = None
- self.claims = {}
return
self._verify_resources(resources)
self._report_hypervisor_resource_view(resources)
- self._purge_claims()
-
# Grab all instances assigned to this host:
instances = db.instance_get_all_by_host(context, self.host)
@@ -405,12 +202,6 @@ class ResourceTracker(object):
self._update(context, resources, prune_stats=True)
LOG.info(_('Compute_service record updated for %s ') % self.host)
- def _purge_claims(self):
- """Purge claims. They are no longer needed once the audit process
- reconciles usage values from the database.
- """
- self.claims.clear()
-
def _create(self, context, values):
"""Create the compute node in the DB"""
# initialize load stats from existing instances:
diff --git a/nova/tests/compute/test_claims.py b/nova/tests/compute/test_claims.py
new file mode 100644
index 000000000..f631c1665
--- /dev/null
+++ b/nova/tests/compute/test_claims.py
@@ -0,0 +1,125 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2012 OpenStack, LLC.
+# All Rights Reserved.
+#
+# 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.
+
+"""Tests for resource tracker claims"""
+
+import uuid
+
+from nova.compute import claims
+from nova.openstack.common import log as logging
+from nova import test
+
+LOG = logging.getLogger(__name__)
+
+
+class ClaimTestCase(test.TestCase):
+
+ def setUp(self):
+ super(ClaimTestCase, self).setUp()
+ self.resources = self._fake_resources()
+
+ def _claim(self, **kwargs):
+ instance = self._fake_instance(**kwargs)
+ return claims.Claim(instance, None)
+
+ def _fake_instance(self, **kwargs):
+ instance = {
+ 'uuid': str(uuid.uuid1()),
+ 'memory_mb': 1024,
+ 'root_gb': 10,
+ 'ephemeral_gb': 5,
+ 'vcpus': 1
+ }
+ instance.update(**kwargs)
+ return instance
+
+ def _fake_resources(self, values=None):
+ 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
+ }
+ if values:
+ resources.update(values)
+ return resources
+
+ 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))
+
+ def test_cpu_insufficient(self):
+ claim = self._claim(vcpus=17)
+ limits = {'vcpu': 16}
+ self.assertFalse(claim.test(self.resources, limits))
+
+ def test_memory_oversubscription(self):
+ claim = self._claim(memory_mb=4096)
+ limits = {'memory_mb': 8192}
+ self.assertTrue(claim.test(self.resources, limits))
+
+ def test_memory_insufficient(self):
+ claim = self._claim(memory_mb=16384)
+ limits = {'memory_mb': 8192}
+ self.assertFalse(claim.test(self.resources, limits))
+
+ 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))
+
+ 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))
+
+ def test_abort(self):
+ instance = self._fake_instance(root_gb=10, ephemeral_gb=40)
+
+ def fake_abort(self):
+ self._called = True
+
+ self.stubs.Set(claims.Claim, 'abort', fake_abort)
+ claim = None
+ try:
+ with claims.Claim(instance, None) as claim:
+ raise test.TestingException("abort")
+ except test.TestingException:
+ pass
+
+ self.assertTrue(claim._called)
diff --git a/nova/tests/compute/test_resource_tracker.py b/nova/tests/compute/test_resource_tracker.py
index dfeebf0d0..64cdb8d53 100644
--- a/nova/tests/compute/test_resource_tracker.py
+++ b/nova/tests/compute/test_resource_tracker.py
@@ -33,8 +33,16 @@ from nova.virt import driver
LOG = logging.getLogger(__name__)
+FAKE_VIRT_MEMORY_MB = 5
+FAKE_VIRT_LOCAL_GB = 6
+FAKE_VIRT_VCPUS = 1
+
+
class UnsupportedVirtDriver(driver.ComputeDriver):
"""Pretend version of a lame virt driver"""
+ def __init__(self):
+ super(UnsupportedVirtDriver, self).__init__(None)
+
def get_available_resource(self):
# no support for getting resource usage info
return {}
@@ -42,10 +50,11 @@ class UnsupportedVirtDriver(driver.ComputeDriver):
class FakeVirtDriver(driver.ComputeDriver):
- def __init__(self, virtapi):
- self.memory_mb = 5
- self.local_gb = 6
- self.vcpus = 1
+ def __init__(self):
+ super(FakeVirtDriver, self).__init__(None)
+ self.memory_mb = FAKE_VIRT_MEMORY_MB
+ self.local_gb = FAKE_VIRT_LOCAL_GB
+ self.vcpus = FAKE_VIRT_VCPUS
self.memory_mb_used = 0
self.local_gb_used = 0
@@ -148,9 +157,9 @@ class BaseTestCase(test.TestCase):
host = "fakehost"
if unsupported:
- driver = UnsupportedVirtDriver(None)
+ driver = UnsupportedVirtDriver()
else:
- driver = FakeVirtDriver(None)
+ driver = FakeVirtDriver()
tracker = resource_tracker.ResourceTracker(host, driver)
return tracker
@@ -166,38 +175,31 @@ class UnsupportedDriverTestCase(BaseTestCase):
# seed tracker with data:
self.tracker.update_available_resource(self.context)
- def testDisabled(self):
+ def test_disabled(self):
# disabled = no compute node stats
self.assertTrue(self.tracker.disabled)
self.assertEqual(None, self.tracker.compute_node)
- def testDisabledClaim(self):
+ def test_disabled_claim(self):
# basic claim:
instance = self._fake_instance()
- claim = self.tracker.begin_resource_claim(self.context, instance)
- self.assertEqual(None, claim)
+ claim = self.tracker.instance_claim(self.context, instance)
+ self.assertEqual(0, claim.memory_mb)
- def testDisabledInstanceClaim(self):
+ def test_disabled_instance_claim(self):
# instance variation:
instance = self._fake_instance()
- claim = self.tracker.begin_resource_claim(self.context, instance)
- self.assertEqual(None, claim)
+ claim = self.tracker.instance_claim(self.context, instance)
+ self.assertEqual(0, claim.memory_mb)
- def testDisabledInstanceContextClaim(self):
+ def test_disabled_instance_context_claim(self):
# instance context manager variation:
instance = self._fake_instance()
- with self.tracker.resource_claim(self.context, instance):
- pass
- self.assertEqual(0, len(self.tracker.claims))
+ claim = self.tracker.instance_claim(self.context, instance)
+ with self.tracker.instance_claim(self.context, instance) as claim:
+ self.assertEqual(0, claim.memory_mb)
- def testDisabledFinishClaim(self):
- self.assertEqual(None, self.tracker.finish_resource_claim(None))
-
- def testDisabledAbortClaim(self):
- self.assertEqual(None, self.tracker.abort_resource_claim(self.context,
- None))
-
- def testDisabledUpdateUsage(self):
+ def test_disabled_updated_usage(self):
instance = self._fake_instance(host='fakehost', memory_mb=5,
root_gb=10)
self.tracker.update_usage(self.context, instance)
@@ -209,8 +211,7 @@ class MissingServiceTestCase(BaseTestCase):
self.context = context.get_admin_context()
self.tracker = self._tracker()
- def testMissingService(self):
- """No service record in DB."""
+ def test_missing_service(self):
self.tracker.update_available_resource(self.context)
self.assertTrue(self.tracker.disabled)
@@ -234,11 +235,11 @@ class MissingComputeNodeTestCase(BaseTestCase):
service = self._create_service()
return [service]
- def testCreatedComputeNode(self):
+ def test_create_compute_node(self):
self.tracker.update_available_resource(self.context)
self.assertTrue(self.created)
- def testEnabled(self):
+ def test_enabled(self):
self.tracker.update_available_resource(self.context)
self.assertFalse(self.tracker.disabled)
@@ -253,6 +254,7 @@ class ResourceTestCase(BaseTestCase):
self._fake_compute_node_update)
self.tracker.update_available_resource(self.context)
+ self.limits = self._basic_limits()
def _fake_service_get_all_compute_by_host(self, ctx, host):
self.compute = self._create_compute_node()
@@ -267,10 +269,15 @@ class ResourceTestCase(BaseTestCase):
self.compute.update(values)
return self.compute
- def testUpdateUseOnlyForTracked(self):
- """Only update usage is a previous claim has added instance to
- list of tracked instances.
- """
+ def _basic_limits(self):
+ """Get basic limits, no oversubscription"""
+ return {
+ 'memory_mb': FAKE_VIRT_MEMORY_MB * 2,
+ 'disk_gb': FAKE_VIRT_LOCAL_GB,
+ 'vcpu': FAKE_VIRT_VCPUS,
+ }
+
+ def test_update_usage_only_for_tracked(self):
instance = self._fake_instance(memory_mb=3, root_gb=1, ephemeral_gb=1,
task_state=None)
self.tracker.update_usage(self.context, instance)
@@ -279,8 +286,9 @@ class ResourceTestCase(BaseTestCase):
self.assertEqual(0, self.tracker.compute_node['local_gb_used'])
self.assertEqual(0, self.tracker.compute_node['current_workload'])
- claim = self.tracker.begin_resource_claim(self.context, instance)
- self.assertNotEqual(None, claim)
+ claim = self.tracker.instance_claim(self.context, instance,
+ self.limits)
+ self.assertNotEqual(0, claim.memory_mb)
self.assertEqual(3, self.tracker.compute_node['memory_mb_used'])
self.assertEqual(2, self.tracker.compute_node['local_gb_used'])
@@ -292,126 +300,21 @@ class ResourceTestCase(BaseTestCase):
self.assertEqual(2, self.tracker.compute_node['local_gb_used'])
self.assertEqual(1, self.tracker.compute_node['current_workload'])
- def testFreeRamResourceValue(self):
- driver = FakeVirtDriver(None)
+ def test_free_ram_resource_value(self):
+ driver = FakeVirtDriver()
mem_free = driver.memory_mb - driver.memory_mb_used
self.assertEqual(mem_free, self.tracker.compute_node['free_ram_mb'])
- def testFreeDiskResourceValue(self):
- driver = FakeVirtDriver(None)
+ def test_free_disk_resource_value(self):
+ driver = FakeVirtDriver()
mem_free = driver.local_gb - driver.local_gb_used
self.assertEqual(mem_free, self.tracker.compute_node['free_disk_gb'])
- def testUpdateComputeNode(self):
+ def test_update_compute_node(self):
self.assertFalse(self.tracker.disabled)
self.assertTrue(self.updated)
- def testCpuUnlimited(self):
- """Test default of unlimited CPU"""
- self.assertEqual(0, self.tracker.compute_node['vcpus_used'])
- instance = self._fake_instance(memory_mb=1, root_gb=1, ephemeral_gb=1,
- vcpus=100000)
- claim = self.tracker.begin_resource_claim(self.context, instance)
- self.assertNotEqual(None, claim)
- self.assertEqual(100000, self.tracker.compute_node['vcpus_used'])
-
- def testCpuOversubscription(self):
- """Test client-supplied oversubscription of CPU"""
- self.assertEqual(1, self.tracker.compute_node['vcpus'])
-
- instance = self._fake_instance(memory_mb=1, root_gb=1, ephemeral_gb=1,
- vcpus=3)
- limits = {'vcpu': 5}
- claim = self.tracker.begin_resource_claim(self.context, instance,
- limits)
- self.assertNotEqual(None, claim)
- self.assertEqual(3, self.tracker.compute_node['vcpus_used'])
-
- def testMemoryOversubscription(self):
- """Test client-supplied oversubscription of memory"""
- instance = self._fake_instance(memory_mb=8, root_gb=1, ephemeral_gb=1)
- limits = {'memory_mb': 8}
- claim = self.tracker.begin_resource_claim(self.context, instance,
- limits)
- self.assertNotEqual(None, claim)
- self.assertEqual(8, self.tracker.compute_node['memory_mb_used'])
- self.assertEqual(2, self.tracker.compute_node['local_gb_used'])
-
- def testDiskOversubscription(self):
- """Test client-supplied oversubscription of disk space"""
- instance = self._fake_instance(memory_mb=1, root_gb=10, ephemeral_gb=1)
- limits = {'disk_gb': 12}
- claim = self.tracker.begin_resource_claim(self.context, instance,
- limits)
- self.assertNotEqual(None, claim)
- self.assertEqual(1, self.tracker.compute_node['memory_mb_used'])
- self.assertEqual(11, self.tracker.compute_node['local_gb_used'])
-
- def testUnlimitedMemoryClaim(self):
- """Test default of unlimited memory"""
- instance = self._fake_instance(memory_mb=200000000000, root_gb=1,
- ephemeral_gb=1)
- claim = self.tracker.begin_resource_claim(self.context, instance)
- self.assertNotEqual(None, claim)
- self.assertEqual(200000000000,
- self.tracker.compute_node['memory_mb_used'])
-
- def testInsufficientMemoryClaimWithOversubscription(self):
- """Exceed oversubscribed memory limit of 10MB"""
- instance = self._fake_instance(memory_mb=10, root_gb=0,
- ephemeral_gb=0)
- limits = {'memory_mb': 10}
- claim = self.tracker.begin_resource_claim(self.context, instance,
- limits)
- self.assertNotEqual(None, claim)
-
- instance = self._fake_instance(memory_mb=1, root_gb=0,
- ephemeral_gb=0)
- limits = {'memory_mb': 10}
- claim = self.tracker.begin_resource_claim(self.context, instance,
- limits)
- self.assertEqual(None, claim)
-
- def testUnlimitDiskClaim(self):
- """Test default of unlimited disk space"""
- instance = self._fake_instance(memory_mb=0, root_gb=200000000,
- ephemeral_gb=0)
- claim = self.tracker.begin_resource_claim(self.context, instance)
- self.assertNotEqual(None, claim)
- self.assertEqual(200000000, self.tracker.compute_node['local_gb_used'])
-
- def testInsufficientDiskClaimWithOversubscription(self):
- """Exceed oversubscribed disk limit of 10GB"""
- instance = self._fake_instance(memory_mb=1, root_gb=4,
- ephemeral_gb=5) # 9 GB
- limits = {'disk_gb': 10}
- claim = self.tracker.begin_resource_claim(self.context, instance,
- limits)
- self.assertNotEqual(None, claim)
-
- instance = self._fake_instance(memory_mb=1, root_gb=1,
- ephemeral_gb=1) # 2 GB
- limits = {'disk_gb': 10}
- claim = self.tracker.begin_resource_claim(self.context, instance,
- limits)
- self.assertEqual(None, claim)
-
- def testInsufficientCpuClaim(self):
- instance = self._fake_instance(memory_mb=0, root_gb=0,
- ephemeral_gb=0, vcpus=1)
- claim = self.tracker.begin_resource_claim(self.context, instance)
- self.assertNotEqual(None, claim)
- self.assertEqual(1, self.tracker.compute_node['vcpus_used'])
-
- instance = self._fake_instance(memory_mb=0, root_gb=0,
- ephemeral_gb=0, vcpus=1)
-
- limits = {'vcpu': 1}
- claim = self.tracker.begin_resource_claim(self.context, instance,
- limits)
- self.assertEqual(None, claim)
-
- def testClaimAndFinish(self):
+ def test_claim_and_audit(self):
self.assertEqual(5, self.tracker.compute_node['memory_mb'])
self.assertEqual(0, self.tracker.compute_node['memory_mb_used'])
@@ -422,7 +325,9 @@ class ResourceTestCase(BaseTestCase):
claim_disk = 2
instance = self._fake_instance(memory_mb=claim_mem, root_gb=claim_disk,
ephemeral_gb=0)
- claim = self.tracker.begin_resource_claim(self.context, instance)
+
+ claim = self.tracker.instance_claim(self.context, instance,
+ self.limits)
self.assertEqual(5, self.compute["memory_mb"])
self.assertEqual(claim_mem, self.compute["memory_mb_used"])
@@ -448,18 +353,7 @@ class ResourceTestCase(BaseTestCase):
self.assertEqual(claim_disk, self.compute['local_gb_used'])
self.assertEqual(6 - claim_disk, self.compute['free_disk_gb'])
- # Finally, finish the claimm and update from the virt layer again.
- # Resource usage will be consistent again:
- self.tracker.finish_resource_claim(claim)
- self.tracker.update_available_resource(self.context)
-
- self.assertEqual(claim_mem, self.compute['memory_mb_used'])
- self.assertEqual(5 - claim_mem, self.compute['free_ram_mb'])
-
- self.assertEqual(claim_disk, self.compute['local_gb_used'])
- self.assertEqual(6 - claim_disk, self.compute['free_disk_gb'])
-
- def testClaimAndAbort(self):
+ def test_claim_and_abort(self):
self.assertEqual(5, self.tracker.compute_node['memory_mb'])
self.assertEqual(0, self.tracker.compute_node['memory_mb_used'])
@@ -470,7 +364,8 @@ class ResourceTestCase(BaseTestCase):
claim_disk = 2
instance = self._fake_instance(memory_mb=claim_mem,
root_gb=claim_disk, ephemeral_gb=0)
- claim = self.tracker.begin_resource_claim(self.context, instance)
+ claim = self.tracker.instance_claim(self.context, instance,
+ self.limits)
self.assertNotEqual(None, claim)
self.assertEqual(5, self.compute["memory_mb"])
@@ -481,7 +376,7 @@ class ResourceTestCase(BaseTestCase):
self.assertEqual(claim_disk, self.compute["local_gb_used"])
self.assertEqual(6 - claim_disk, self.compute["free_disk_gb"])
- self.tracker.abort_resource_claim(self.context, claim)
+ claim.abort()
self.assertEqual(5, self.compute["memory_mb"])
self.assertEqual(0, self.compute["memory_mb_used"])
@@ -491,25 +386,42 @@ class ResourceTestCase(BaseTestCase):
self.assertEqual(0, self.compute["local_gb_used"])
self.assertEqual(6, self.compute["free_disk_gb"])
- def testClaimsPurge(self):
- """Test that claims get get purged when the audit process runs"""
+ def test_instance_claim_with_oversubscription(self):
+ memory_mb = FAKE_VIRT_MEMORY_MB * 2
+ root_gb = ephemeral_gb = FAKE_VIRT_LOCAL_GB
+ vcpus = FAKE_VIRT_VCPUS * 2
- instance = self._fake_instance(memory_mb=2, root_gb=2, ephemeral_gb=0)
- claim = self.tracker.begin_resource_claim(self.context, instance)
+ limits = {'memory_mb': memory_mb, 'disk_gb': root_gb * 2,
+ 'vcpu': vcpus}
+ instance = self._fake_instance(memory_mb=memory_mb,
+ root_gb=root_gb, ephemeral_gb=ephemeral_gb)
- self.tracker.update_available_resource(self.context)
- self.assertEqual({}, self.tracker.claims)
+ self.tracker.instance_claim(self.context, instance, limits)
+ self.assertEqual(memory_mb,
+ self.tracker.compute_node['memory_mb_used'])
+ self.assertEqual(root_gb * 2,
+ self.tracker.compute_node['local_gb_used'])
- def testInstanceClaim(self):
- instance = self._fake_instance(memory_mb=1, root_gb=0, ephemeral_gb=2)
- self.tracker.begin_resource_claim(self.context, instance)
- self.assertEqual(1, self.tracker.compute_node['memory_mb_used'])
- self.assertEqual(2, self.tracker.compute_node['local_gb_used'])
+ def test_additive_claims(self):
+ self.limits['vcpu'] = 2
+
+ instance = self._fake_instance(memory_mb=1, root_gb=1, ephemeral_gb=1,
+ vcpus=1)
+ with self.tracker.instance_claim(self.context, instance, self.limits):
+ pass
+ instance = self._fake_instance(memory_mb=1, root_gb=1, ephemeral_gb=1,
+ vcpus=1)
+ with self.tracker.instance_claim(self.context, instance, self.limits):
+ pass
+
+ self.assertEqual(2, self.tracker.compute_node['memory_mb_used'])
+ self.assertEqual(4, self.tracker.compute_node['local_gb_used'])
+ self.assertEqual(2, self.tracker.compute_node['vcpus_used'])
- def testContextClaimWithException(self):
+ def test_context_claim_with_exception(self):
instance = self._fake_instance(memory_mb=1, root_gb=1, ephemeral_gb=1)
try:
- with self.tracker.resource_claim(self.context, instance):
+ with self.tracker.instance_claim(self.context, instance):
# <insert exciting things that utilize resources>
raise test.TestingException()
except test.TestingException:
@@ -520,9 +432,9 @@ class ResourceTestCase(BaseTestCase):
self.assertEqual(0, self.compute['memory_mb_used'])
self.assertEqual(0, self.compute['local_gb_used'])
- def testInstanceContextClaim(self):
+ def test_instance_context_claim(self):
instance = self._fake_instance(memory_mb=1, root_gb=1, ephemeral_gb=1)
- with self.tracker.resource_claim(self.context, instance):
+ with self.tracker.instance_claim(self.context, instance):
# <insert exciting things that utilize resources>
self.assertEqual(1, self.tracker.compute_node['memory_mb_used'])
self.assertEqual(2, self.tracker.compute_node['local_gb_used'])
@@ -537,12 +449,12 @@ class ResourceTestCase(BaseTestCase):
self.assertEqual(1, self.compute['memory_mb_used'])
self.assertEqual(2, self.compute['local_gb_used'])
- def testUpdateLoadStatsForInstance(self):
+ def test_update_load_stats_for_instance(self):
self.assertFalse(self.tracker.disabled)
self.assertEqual(0, self.tracker.compute_node['current_workload'])
instance = self._fake_instance(task_state=task_states.SCHEDULING)
- with self.tracker.resource_claim(self.context, instance):
+ with self.tracker.instance_claim(self.context, instance):
pass
self.assertEqual(1, self.tracker.compute_node['current_workload'])
@@ -554,7 +466,7 @@ class ResourceTestCase(BaseTestCase):
self.tracker.update_usage(self.context, instance)
self.assertEqual(0, self.tracker.compute_node['current_workload'])
- def testCpuStats(self):
+ def test_cpu_stats(self):
limits = {'disk_gb': 100, 'memory_mb': 100}
self.assertEqual(0, self.tracker.compute_node['vcpus_used'])
@@ -564,7 +476,7 @@ class ResourceTestCase(BaseTestCase):
self.tracker.update_usage(self.context, instance)
self.assertEqual(0, self.tracker.compute_node['vcpus_used'])
- with self.tracker.resource_claim(self.context, instance, limits):
+ with self.tracker.instance_claim(self.context, instance, limits):
pass
self.assertEqual(1, self.tracker.compute_node['vcpus_used'])
@@ -574,7 +486,7 @@ class ResourceTestCase(BaseTestCase):
self.assertEqual(1, self.tracker.compute_node['vcpus_used'])
instance = self._fake_instance(vcpus=10)
- with self.tracker.resource_claim(self.context, instance, limits):
+ with self.tracker.instance_claim(self.context, instance, limits):
pass
self.assertEqual(11, self.tracker.compute_node['vcpus_used'])