Remove vcpu resource from extensible resource tracker
This patch makes vcpu resource tracking explicit in the resource tracker and removes the corresponding VCPU resource plugin from the extensible resource tracker. This is a first step towards removing the extensible resource tracker in a future release. A follow-on patch will add deprecation notices for the extensible resource tracker. DocImpact: default value of compute_resources has changed to an empty list. blueprint resource-objects Change-Id: If4318ce1c942292ef041a0b5c800304544207cb4
This commit is contained in:
parent
63347382d5
commit
d3bece175b
@ -45,6 +45,10 @@ class NopClaim(object):
|
|||||||
def memory_mb(self):
|
def memory_mb(self):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vcpus(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@ -97,6 +101,10 @@ class Claim(NopClaim):
|
|||||||
def memory_mb(self):
|
def memory_mb(self):
|
||||||
return self.instance.memory_mb + self.overhead['memory_mb']
|
return self.instance.memory_mb + self.overhead['memory_mb']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vcpus(self):
|
||||||
|
return self.instance.vcpus
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def numa_topology(self):
|
def numa_topology(self):
|
||||||
if self._numa_topology_loaded:
|
if self._numa_topology_loaded:
|
||||||
@ -129,15 +137,18 @@ class Claim(NopClaim):
|
|||||||
# unlimited:
|
# unlimited:
|
||||||
memory_mb_limit = limits.get('memory_mb')
|
memory_mb_limit = limits.get('memory_mb')
|
||||||
disk_gb_limit = limits.get('disk_gb')
|
disk_gb_limit = limits.get('disk_gb')
|
||||||
|
vcpus_limit = limits.get('vcpu')
|
||||||
numa_topology_limit = limits.get('numa_topology')
|
numa_topology_limit = limits.get('numa_topology')
|
||||||
|
|
||||||
msg = _("Attempting claim: memory %(memory_mb)d MB, disk %(disk_gb)d "
|
msg = _("Attempting claim: memory %(memory_mb)d MB, "
|
||||||
"GB")
|
"disk %(disk_gb)d GB, vcpus %(vcpus)d CPU")
|
||||||
params = {'memory_mb': self.memory_mb, 'disk_gb': self.disk_gb}
|
params = {'memory_mb': self.memory_mb, 'disk_gb': self.disk_gb,
|
||||||
|
'vcpus': self.vcpus}
|
||||||
LOG.info(msg % params, instance=self.instance)
|
LOG.info(msg % params, instance=self.instance)
|
||||||
|
|
||||||
reasons = [self._test_memory(resources, memory_mb_limit),
|
reasons = [self._test_memory(resources, memory_mb_limit),
|
||||||
self._test_disk(resources, disk_gb_limit),
|
self._test_disk(resources, disk_gb_limit),
|
||||||
|
self._test_vcpus(resources, vcpus_limit),
|
||||||
self._test_numa_topology(resources, numa_topology_limit),
|
self._test_numa_topology(resources, numa_topology_limit),
|
||||||
self._test_pci()]
|
self._test_pci()]
|
||||||
reasons = reasons + self._test_ext_resources(limits)
|
reasons = reasons + self._test_ext_resources(limits)
|
||||||
@ -166,6 +177,15 @@ class Claim(NopClaim):
|
|||||||
|
|
||||||
return self._test(type_, unit, total, used, requested, limit)
|
return self._test(type_, unit, total, used, requested, limit)
|
||||||
|
|
||||||
|
def _test_vcpus(self, resources, limit):
|
||||||
|
type_ = _("vcpu")
|
||||||
|
unit = "VCPU"
|
||||||
|
total = resources['vcpus']
|
||||||
|
used = resources['vcpus_used']
|
||||||
|
requested = self.vcpus
|
||||||
|
|
||||||
|
return self._test(type_, unit, total, used, requested, limit)
|
||||||
|
|
||||||
def _test_pci(self):
|
def _test_pci(self):
|
||||||
pci_requests = objects.InstancePCIRequests.get_by_instance_uuid(
|
pci_requests = objects.InstancePCIRequests.get_by_instance_uuid(
|
||||||
self.context, self.instance.uuid)
|
self.context, self.instance.uuid)
|
||||||
@ -265,6 +285,10 @@ class MoveClaim(Claim):
|
|||||||
def memory_mb(self):
|
def memory_mb(self):
|
||||||
return self.instance_type.memory_mb + self.overhead['memory_mb']
|
return self.instance_type.memory_mb + self.overhead['memory_mb']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vcpus(self):
|
||||||
|
return self.instance_type.vcpus
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def numa_topology(self):
|
def numa_topology(self):
|
||||||
image_meta = objects.ImageMeta.from_dict(self.image_meta)
|
image_meta = objects.ImageMeta.from_dict(self.image_meta)
|
||||||
|
@ -50,7 +50,7 @@ resource_tracker_opts = [
|
|||||||
default='nova.compute.stats.Stats',
|
default='nova.compute.stats.Stats',
|
||||||
help='Class that will manage stats for the local compute host'),
|
help='Class that will manage stats for the local compute host'),
|
||||||
cfg.ListOpt('compute_resources',
|
cfg.ListOpt('compute_resources',
|
||||||
default=['vcpu'],
|
default=[],
|
||||||
help='The names of the extra resources to track.'),
|
help='The names of the extra resources to track.'),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -663,6 +663,7 @@ class ResourceTracker(object):
|
|||||||
self.compute_node.memory_mb_used += sign * mem_usage
|
self.compute_node.memory_mb_used += sign * mem_usage
|
||||||
self.compute_node.local_gb_used += sign * usage.get('root_gb', 0)
|
self.compute_node.local_gb_used += sign * usage.get('root_gb', 0)
|
||||||
self.compute_node.local_gb_used += sign * usage.get('ephemeral_gb', 0)
|
self.compute_node.local_gb_used += sign * usage.get('ephemeral_gb', 0)
|
||||||
|
self.compute_node.vcpus_used += sign * usage.get('vcpus', 0)
|
||||||
|
|
||||||
# free ram and disk may be negative, depending on policy:
|
# free ram and disk may be negative, depending on policy:
|
||||||
self.compute_node.free_ram_mb = (self.compute_node.memory_mb -
|
self.compute_node.free_ram_mb = (self.compute_node.memory_mb -
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
|
|
||||||
from nova.compute.resources import base
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class VCPU(base.Resource):
|
|
||||||
"""VCPU compute resource plugin.
|
|
||||||
|
|
||||||
This is effectively a simple counter based on the vcpu requirement of each
|
|
||||||
instance.
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
# initialize to a 'zero' resource.
|
|
||||||
# reset will be called to set real resource values
|
|
||||||
self._total = 0
|
|
||||||
self._used = 0
|
|
||||||
|
|
||||||
def reset(self, resources, driver):
|
|
||||||
# total vcpu is reset to the value taken from resources.
|
|
||||||
self._total = int(resources['vcpus'])
|
|
||||||
self._used = 0
|
|
||||||
|
|
||||||
def _get_requested(self, usage):
|
|
||||||
return int(usage.get('vcpus', 0))
|
|
||||||
|
|
||||||
def _get_limit(self, limits):
|
|
||||||
if limits and 'vcpu' in limits:
|
|
||||||
return int(limits.get('vcpu'))
|
|
||||||
|
|
||||||
def test(self, usage, limits):
|
|
||||||
requested = self._get_requested(usage)
|
|
||||||
limit = self._get_limit(limits)
|
|
||||||
|
|
||||||
LOG.debug('Total CPUs: %(total)d VCPUs, used: %(used).02f VCPUs' %
|
|
||||||
{'total': self._total, 'used': self._used})
|
|
||||||
|
|
||||||
if limit is None:
|
|
||||||
# treat resource as unlimited:
|
|
||||||
LOG.debug('CPUs limit not specified, defaulting to unlimited')
|
|
||||||
return
|
|
||||||
|
|
||||||
free = limit - self._used
|
|
||||||
|
|
||||||
# Oversubscribed resource policy info:
|
|
||||||
LOG.debug('CPUs limit: %(limit).02f VCPUs, free: %(free).02f VCPUs' %
|
|
||||||
{'limit': limit, 'free': free})
|
|
||||||
|
|
||||||
if requested > free:
|
|
||||||
return ('Free CPUs %(free).02f VCPUs < '
|
|
||||||
'requested %(requested)d VCPUs' %
|
|
||||||
{'free': free, 'requested': requested})
|
|
||||||
|
|
||||||
def add_instance(self, usage):
|
|
||||||
requested = int(usage.get('vcpus', 0))
|
|
||||||
self._used += requested
|
|
||||||
|
|
||||||
def remove_instance(self, usage):
|
|
||||||
requested = int(usage.get('vcpus', 0))
|
|
||||||
self._used -= requested
|
|
||||||
|
|
||||||
def write(self, resources):
|
|
||||||
resources['vcpus'] = self._total
|
|
||||||
resources['vcpus_used'] = self._used
|
|
||||||
|
|
||||||
def report_free(self):
|
|
||||||
free_vcpus = self._total - self._used
|
|
||||||
LOG.debug('Free VCPUs: %s' % free_vcpus)
|
|
@ -22,11 +22,9 @@ from stevedore import named
|
|||||||
|
|
||||||
from nova.compute import resources
|
from nova.compute import resources
|
||||||
from nova.compute.resources import base
|
from nova.compute.resources import base
|
||||||
from nova.compute.resources import vcpu
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova.objects import flavor as flavor_obj
|
from nova.objects import flavor as flavor_obj
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit import fake_instance
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
@ -271,73 +269,3 @@ class BaseTestCase(test.NoDBTestCase):
|
|||||||
self.assertEqual(expected_extra_resources, extra_resources)
|
self.assertEqual(expected_extra_resources, extra_resources)
|
||||||
|
|
||||||
empty_r_handler.report_free_resources()
|
empty_r_handler.report_free_resources()
|
||||||
|
|
||||||
def test_vcpu_resource_load(self):
|
|
||||||
# load the vcpu example
|
|
||||||
names = ['vcpu']
|
|
||||||
real_r_handler = resources.ResourceHandler(names)
|
|
||||||
ext_names = real_r_handler._mgr.names()
|
|
||||||
self.assertEqual(names, ext_names)
|
|
||||||
|
|
||||||
# check the extension loaded is the one we expect
|
|
||||||
# and an instance of the object has been created
|
|
||||||
ext = real_r_handler._mgr['vcpu']
|
|
||||||
self.assertIsInstance(ext.obj, vcpu.VCPU)
|
|
||||||
|
|
||||||
|
|
||||||
class TestVCPU(test.NoDBTestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestVCPU, self).setUp()
|
|
||||||
self._vcpu = vcpu.VCPU()
|
|
||||||
self._vcpu._total = 10
|
|
||||||
self._vcpu._used = 0
|
|
||||||
self._flavor = fake_flavor_obj(vcpus=5)
|
|
||||||
self._big_flavor = fake_flavor_obj(vcpus=20)
|
|
||||||
self._instance = fake_instance.fake_instance_obj(None)
|
|
||||||
|
|
||||||
def test_reset(self):
|
|
||||||
# set vcpu values to something different to test reset
|
|
||||||
self._vcpu._total = 10
|
|
||||||
self._vcpu._used = 5
|
|
||||||
|
|
||||||
driver_resources = {'vcpus': 20}
|
|
||||||
self._vcpu.reset(driver_resources, None)
|
|
||||||
self.assertEqual(20, self._vcpu._total)
|
|
||||||
self.assertEqual(0, self._vcpu._used)
|
|
||||||
|
|
||||||
def test_add_and_remove_instance(self):
|
|
||||||
self._vcpu.add_instance(self._flavor)
|
|
||||||
self.assertEqual(10, self._vcpu._total)
|
|
||||||
self.assertEqual(5, self._vcpu._used)
|
|
||||||
|
|
||||||
self._vcpu.remove_instance(self._flavor)
|
|
||||||
self.assertEqual(10, self._vcpu._total)
|
|
||||||
self.assertEqual(0, self._vcpu._used)
|
|
||||||
|
|
||||||
def test_test_pass_limited(self):
|
|
||||||
result = self._vcpu.test(self._flavor, {'vcpu': 10})
|
|
||||||
self.assertIsNone(result, 'vcpu test failed when it should pass')
|
|
||||||
|
|
||||||
def test_test_pass_unlimited(self):
|
|
||||||
result = self._vcpu.test(self._big_flavor, {})
|
|
||||||
self.assertIsNone(result, 'vcpu test failed when it should pass')
|
|
||||||
|
|
||||||
def test_test_fail(self):
|
|
||||||
result = self._vcpu.test(self._flavor, {'vcpu': 2})
|
|
||||||
expected = 'Free CPUs 2.00 VCPUs < requested 5 VCPUs'
|
|
||||||
self.assertEqual(expected, result)
|
|
||||||
|
|
||||||
def test_write(self):
|
|
||||||
resources = {'stats': {}}
|
|
||||||
self._vcpu.write(resources)
|
|
||||||
expected = {
|
|
||||||
'vcpus': 10,
|
|
||||||
'vcpus_used': 0,
|
|
||||||
'stats': {
|
|
||||||
'num_vcpus': 10,
|
|
||||||
'num_vcpus_used': 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.assertEqual(sorted(expected),
|
|
||||||
sorted(resources))
|
|
||||||
|
@ -564,18 +564,7 @@ class TestUpdateAvailableResources(BaseTestCase):
|
|||||||
'free_ram_mb': 384, # 512 - 128 used
|
'free_ram_mb': 384, # 512 - 128 used
|
||||||
'memory_mb_used': 128,
|
'memory_mb_used': 128,
|
||||||
'pci_device_pools': objects.PciDevicePoolList(),
|
'pci_device_pools': objects.PciDevicePoolList(),
|
||||||
# NOTE(jaypipes): Due to the design of the ERT, which now is used
|
'vcpus_used': 1,
|
||||||
# track VCPUs, the actual used VCPUs isn't
|
|
||||||
# "written" to the resources dictionary that is
|
|
||||||
# passed to _update() like all the other
|
|
||||||
# resources are. Instead, _update()
|
|
||||||
# calls the ERT's write_resources() method, which
|
|
||||||
# then queries each resource handler plugin for the
|
|
||||||
# changes in its resource usage and the plugin
|
|
||||||
# writes changes to the supplied "values" dict. For
|
|
||||||
# this reason, all other resources except VCPUs
|
|
||||||
# are accurate here. :(
|
|
||||||
'vcpus_used': 0,
|
|
||||||
'hypervisor_type': 'fake',
|
'hypervisor_type': 'fake',
|
||||||
'local_gb_used': 1,
|
'local_gb_used': 1,
|
||||||
'memory_mb': 512,
|
'memory_mb': 512,
|
||||||
@ -705,7 +694,7 @@ class TestUpdateAvailableResources(BaseTestCase):
|
|||||||
'free_ram_mb': 384, # 512 total - 128 for possible revert of orig
|
'free_ram_mb': 384, # 512 total - 128 for possible revert of orig
|
||||||
'memory_mb_used': 128, # 128 possible revert amount
|
'memory_mb_used': 128, # 128 possible revert amount
|
||||||
'pci_device_pools': objects.PciDevicePoolList(),
|
'pci_device_pools': objects.PciDevicePoolList(),
|
||||||
'vcpus_used': 0,
|
'vcpus_used': 1,
|
||||||
'hypervisor_type': 'fake',
|
'hypervisor_type': 'fake',
|
||||||
'local_gb_used': 1,
|
'local_gb_used': 1,
|
||||||
'memory_mb': 512,
|
'memory_mb': 512,
|
||||||
@ -765,7 +754,7 @@ class TestUpdateAvailableResources(BaseTestCase):
|
|||||||
'free_ram_mb': 256, # 512 total - 256 for possible confirm of new
|
'free_ram_mb': 256, # 512 total - 256 for possible confirm of new
|
||||||
'memory_mb_used': 256, # 256 possible confirmed amount
|
'memory_mb_used': 256, # 256 possible confirmed amount
|
||||||
'pci_device_pools': objects.PciDevicePoolList(),
|
'pci_device_pools': objects.PciDevicePoolList(),
|
||||||
'vcpus_used': 0, # See NOTE(jaypipes) above about why this is 0
|
'vcpus_used': 2,
|
||||||
'hypervisor_type': 'fake',
|
'hypervisor_type': 'fake',
|
||||||
'local_gb_used': 5,
|
'local_gb_used': 5,
|
||||||
'memory_mb': 512,
|
'memory_mb': 512,
|
||||||
@ -822,7 +811,7 @@ class TestUpdateAvailableResources(BaseTestCase):
|
|||||||
'free_ram_mb': 256, # 512 total - 256 for possible confirm of new
|
'free_ram_mb': 256, # 512 total - 256 for possible confirm of new
|
||||||
'memory_mb_used': 256, # 256 possible confirmed amount
|
'memory_mb_used': 256, # 256 possible confirmed amount
|
||||||
'pci_device_pools': objects.PciDevicePoolList(),
|
'pci_device_pools': objects.PciDevicePoolList(),
|
||||||
'vcpus_used': 0, # See NOTE(jaypipes) above about why this is 0
|
'vcpus_used': 2,
|
||||||
'hypervisor_type': 'fake',
|
'hypervisor_type': 'fake',
|
||||||
'local_gb_used': 5,
|
'local_gb_used': 5,
|
||||||
'memory_mb': 512,
|
'memory_mb': 512,
|
||||||
@ -890,9 +879,7 @@ class TestUpdateAvailableResources(BaseTestCase):
|
|||||||
'free_ram_mb': 0,
|
'free_ram_mb': 0,
|
||||||
'memory_mb_used': 512, # 128 exist + 256 new flav + 128 old flav
|
'memory_mb_used': 512, # 128 exist + 256 new flav + 128 old flav
|
||||||
'pci_device_pools': objects.PciDevicePoolList(),
|
'pci_device_pools': objects.PciDevicePoolList(),
|
||||||
# See NOTE(jaypipes) above for reason why this isn't accurate until
|
'vcpus_used': 4,
|
||||||
# _update() is called.
|
|
||||||
'vcpus_used': 0,
|
|
||||||
'hypervisor_type': 'fake',
|
'hypervisor_type': 'fake',
|
||||||
'local_gb_used': 7, # 1G existing, 5G new flav + 1 old flav
|
'local_gb_used': 7, # 1G existing, 5G new flav + 1 old flav
|
||||||
'memory_mb': 512,
|
'memory_mb': 512,
|
||||||
@ -1100,18 +1087,7 @@ class TestUpdateComputeNode(BaseTestCase):
|
|||||||
cpu_allocation_ratio=16.0,
|
cpu_allocation_ratio=16.0,
|
||||||
ram_allocation_ratio=1.5,
|
ram_allocation_ratio=1.5,
|
||||||
)
|
)
|
||||||
expected_resources = copy.deepcopy(compute)
|
|
||||||
expected_resources.stats = {}
|
|
||||||
expected_resources.vcpus = 4
|
|
||||||
expected_resources.vcpus_used = 2
|
|
||||||
|
|
||||||
self.rt.compute_node = compute
|
self.rt.compute_node = compute
|
||||||
self.rt.ext_resources_handler.reset_resources(self.rt.compute_node,
|
|
||||||
self.rt.driver)
|
|
||||||
# This emulates the behavior that occurs in the
|
|
||||||
# RT.update_available_resource() method, which updates resource
|
|
||||||
# information in the ERT differently than all other resources.
|
|
||||||
self.rt.ext_resources_handler.update_from_instance(dict(vcpus=2))
|
|
||||||
self.rt._update(mock.sentinel.ctx)
|
self.rt._update(mock.sentinel.ctx)
|
||||||
|
|
||||||
self.assertFalse(self.rt.disabled)
|
self.assertFalse(self.rt.disabled)
|
||||||
@ -1190,9 +1166,7 @@ class TestInstanceClaim(BaseTestCase):
|
|||||||
'free_disk_gb': expected['local_gb'] - disk_used,
|
'free_disk_gb': expected['local_gb'] - disk_used,
|
||||||
"free_ram_mb": expected['memory_mb'] - self.instance.memory_mb,
|
"free_ram_mb": expected['memory_mb'] - self.instance.memory_mb,
|
||||||
'running_vms': 1,
|
'running_vms': 1,
|
||||||
# vcpus are claimed by the ERT in RT._update(), which is mocked
|
'vcpus_used': 1,
|
||||||
# out below...
|
|
||||||
'vcpus_used': 0,
|
|
||||||
'pci_device_pools': objects.PciDevicePoolList(),
|
'pci_device_pools': objects.PciDevicePoolList(),
|
||||||
})
|
})
|
||||||
with mock.patch.object(self.rt, '_update') as update_mock:
|
with mock.patch.object(self.rt, '_update') as update_mock:
|
||||||
@ -1217,9 +1191,7 @@ class TestInstanceClaim(BaseTestCase):
|
|||||||
'free_disk_gb': expected['local_gb'] - disk_used,
|
'free_disk_gb': expected['local_gb'] - disk_used,
|
||||||
"free_ram_mb": expected['memory_mb'] - self.instance.memory_mb,
|
"free_ram_mb": expected['memory_mb'] - self.instance.memory_mb,
|
||||||
'running_vms': 1,
|
'running_vms': 1,
|
||||||
# vcpus are claimed by the ERT in RT._update(), which is mocked
|
'vcpus_used': 1,
|
||||||
# out below...
|
|
||||||
'vcpus_used': 0,
|
|
||||||
'pci_device_pools': objects.PciDevicePoolList(),
|
'pci_device_pools': objects.PciDevicePoolList(),
|
||||||
})
|
})
|
||||||
with mock.patch.object(self.rt, '_update') as update_mock:
|
with mock.patch.object(self.rt, '_update') as update_mock:
|
||||||
|
@ -42,7 +42,7 @@ oslo.config.opts =
|
|||||||
nova.compute.monitors.cpu =
|
nova.compute.monitors.cpu =
|
||||||
virt_driver = nova.compute.monitors.cpu.virt_driver:Monitor
|
virt_driver = nova.compute.monitors.cpu.virt_driver:Monitor
|
||||||
nova.compute.resources =
|
nova.compute.resources =
|
||||||
vcpu = nova.compute.resources.vcpu:VCPU
|
|
||||||
nova.image.download.modules =
|
nova.image.download.modules =
|
||||||
file = nova.image.download.file
|
file = nova.image.download.file
|
||||||
console_scripts =
|
console_scripts =
|
||||||
|
Loading…
Reference in New Issue
Block a user