RT: Decrese usage for offloaded instances

Allow for update_usage to consider SHELVED_OFFLOADED instances as
removed and update the resource usage accordingly. This means we want to
make sure that the stats class does the same.

We can now make sure that RT is updated immediately once the instance
has been shelved offloaded.

Change-Id: Ia22963021995c71758a18b21070dfdf6a950da09
Partial-bug: #1545675
This commit is contained in:
Nikola Dipanov 2016-02-16 19:14:50 +00:00
parent dbdf27021c
commit a54997c62f
6 changed files with 61 additions and 5 deletions

View File

@ -4291,6 +4291,9 @@ class ComputeManager(manager.Manager):
instance.task_state = None
instance.save(expected_task_state=[task_states.SHELVING,
task_states.SHELVING_OFFLOADING])
# NOTE(ndipanov): This frees the resources with the resource_tracker
self._update_resource_tracker(context, instance)
self._delete_scheduler_instance_info(context, instance.uuid)
self._notify_about_instance_usage(context, instance,
'shelve_offload.end')

View File

@ -126,6 +126,9 @@ def _instance_in_resize_state(instance):
return False
_REMOVED_STATES = (vm_states.DELETED, vm_states.SHELVED_OFFLOADED)
class ResourceTracker(object):
"""Compute helper class for keeping track of resource usage as instances
are built and destroyed.
@ -853,20 +856,20 @@ class ResourceTracker(object):
uuid = instance['uuid']
is_new_instance = uuid not in self.tracked_instances
is_deleted_instance = instance['vm_state'] == vm_states.DELETED
is_removed_instance = instance['vm_state'] in _REMOVED_STATES
if is_new_instance:
self.tracked_instances[uuid] = obj_base.obj_to_primitive(instance)
sign = 1
if is_deleted_instance:
if is_removed_instance:
self.tracked_instances.pop(uuid)
sign = -1
self.stats.update_stats_for_instance(instance)
# if it's a new or deleted instance:
if is_new_instance or is_deleted_instance:
if is_new_instance or is_removed_instance:
if self.pci_tracker:
self.pci_tracker.update_pci_for_instance(context,
instance,
@ -905,7 +908,7 @@ class ResourceTracker(object):
self.driver)
for instance in instances:
if instance.vm_state != vm_states.DELETED:
if instance.vm_state not in _REMOVED_STATES:
self._update_usage_from_instance(context, instance)
def _find_orphaned_instances(self):

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from nova.compute import resource_tracker
from nova.compute import task_states
from nova.compute import vm_states
from nova.i18n import _
@ -105,7 +106,7 @@ class Stats(dict):
(vm_state, task_state, os_type, project_id) = \
self._extract_state_from_instance(instance)
if vm_state == vm_states.DELETED:
if vm_state in resource_tracker._REMOVED_STATES:
self._decrement("num_instances")
self.states.pop(uuid)

View File

@ -148,6 +148,7 @@ class ShelveComputeManagerTestCase(test_compute.BaseTestCase):
self.mox.StubOutWithMock(self.compute, '_get_power_state')
self.mox.StubOutWithMock(self.compute.network_api,
'cleanup_instance_network_on_host')
self.mox.StubOutWithMock(self.compute, '_update_resource_tracker')
self.compute._notify_about_instance_usage(self.context, instance,
'shelve_offload.start')
@ -161,6 +162,7 @@ class ShelveComputeManagerTestCase(test_compute.BaseTestCase):
self.context, instance, instance.host)
self.compute._get_power_state(self.context,
instance).AndReturn(123)
self.compute._update_resource_tracker(self.context, instance)
self.compute._notify_about_instance_usage(self.context, instance,
'shelve_offload.end')
self.mox.ReplayAll()

View File

@ -195,6 +195,19 @@ class StatsTestCase(test.NoDBTestCase):
self.assertEqual(0, self.stats.num_os_type("Linux"))
self.assertEqual(0, self.stats["num_vm_" + vm_states.BUILDING])
def test_update_stats_for_instance_offloaded(self):
instance = self._create_instance()
self.stats.update_stats_for_instance(instance)
self.assertEqual(1, self.stats["num_proj_1234"])
instance["vm_state"] = vm_states.SHELVED_OFFLOADED
self.stats.update_stats_for_instance(instance)
self.assertEqual(0, self.stats.num_instances)
self.assertEqual(0, self.stats.num_instances_for_project("1234"))
self.assertEqual(0, self.stats.num_os_type("Linux"))
self.assertEqual(0, self.stats["num_vm_" + vm_states.BUILDING])
def test_io_workload(self):
vms = [vm_states.ACTIVE, vm_states.BUILDING, vm_states.PAUSED]
tasks = [task_states.RESIZE_MIGRATING, task_states.REBUILDING,

View File

@ -1218,6 +1218,40 @@ class TestInstanceClaim(BaseTestCase):
self.assertTrue(obj_base.obj_equal_prims(expected,
self.rt.compute_node))
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance_uuid')
@mock.patch('nova.objects.MigrationList.get_in_progress_by_host_and_node')
def test_update_usage_removed(self, migr_mock, pci_mock):
# Test that RT.update_usage() removes the instance when update is
# called in a removed state
pci_mock.return_value = objects.InstancePCIRequests(requests=[])
expected = copy.deepcopy(_COMPUTE_NODE_FIXTURES[0])
disk_used = self.instance.root_gb + self.instance.ephemeral_gb
expected.update({
'local_gb_used': disk_used,
'memory_mb_used': self.instance.memory_mb,
'free_disk_gb': expected['local_gb'] - disk_used,
"free_ram_mb": expected['memory_mb'] - self.instance.memory_mb,
'running_vms': 1,
'vcpus_used': 1,
'pci_device_pools': objects.PciDevicePoolList(),
})
with mock.patch.object(self.rt, '_update') as update_mock:
with mock.patch.object(self.instance, 'save'):
self.rt.instance_claim(self.ctx, self.instance, None)
update_mock.assert_called_once_with(self.elevated)
self.assertTrue(obj_base.obj_equal_prims(expected,
self.rt.compute_node))
expected_updated = copy.deepcopy(_COMPUTE_NODE_FIXTURES[0])
expected_updated['pci_device_pools'] = objects.PciDevicePoolList()
self.instance.vm_state = vm_states.SHELVED_OFFLOADED
with mock.patch.object(self.rt, '_update') as update_mock:
self.rt.update_usage(self.ctx, self.instance)
self.assertTrue(obj_base.obj_equal_prims(expected_updated,
self.rt.compute_node))
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance_uuid')
@mock.patch('nova.objects.MigrationList.get_in_progress_by_host_and_node')
def test_claim(self, migr_mock, pci_mock):