e5269b3a8f
Partially implements blueprint: nova-python3-mitaka Change-Id: I603ac853505d86afcbce42938f664e402f1e343b
272 lines
8.8 KiB
Python
272 lines
8.8 KiB
Python
# 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.
|
|
|
|
"""Tests for the compute extra resources framework."""
|
|
|
|
|
|
from oslo_config import cfg
|
|
from stevedore import extension
|
|
from stevedore import named
|
|
|
|
from nova.compute import resources
|
|
from nova.compute.resources import base
|
|
from nova import context
|
|
from nova.objects import flavor as flavor_obj
|
|
from nova import test
|
|
|
|
CONF = cfg.CONF
|
|
|
|
|
|
class FakeResourceHandler(resources.ResourceHandler):
|
|
def __init__(self, extensions):
|
|
self._mgr = \
|
|
named.NamedExtensionManager.make_test_instance(extensions)
|
|
|
|
|
|
class FakeResource(base.Resource):
|
|
|
|
def __init__(self):
|
|
self.total_res = 0
|
|
self.used_res = 0
|
|
|
|
def _get_requested(self, usage):
|
|
if 'extra_specs' not in usage:
|
|
return
|
|
if self.resource_name not in usage['extra_specs']:
|
|
return
|
|
req = usage['extra_specs'][self.resource_name]
|
|
return int(req)
|
|
|
|
def _get_limit(self, limits):
|
|
if self.resource_name not in limits:
|
|
return
|
|
limit = limits[self.resource_name]
|
|
return int(limit)
|
|
|
|
def reset(self, resources, driver):
|
|
self.total_res = 0
|
|
self.used_res = 0
|
|
|
|
def test(self, usage, limits):
|
|
requested = self._get_requested(usage)
|
|
if not requested:
|
|
return
|
|
|
|
limit = self._get_limit(limits)
|
|
if not limit:
|
|
return
|
|
|
|
free = limit - self.used_res
|
|
if requested <= free:
|
|
return
|
|
else:
|
|
return ('Free %(free)d < requested %(requested)d ' %
|
|
{'free': free, 'requested': requested})
|
|
|
|
def add_instance(self, usage):
|
|
requested = self._get_requested(usage)
|
|
if requested:
|
|
self.used_res += requested
|
|
|
|
def remove_instance(self, usage):
|
|
requested = self._get_requested(usage)
|
|
if requested:
|
|
self.used_res -= requested
|
|
|
|
def write(self, resources):
|
|
pass
|
|
|
|
def report_free(self):
|
|
return "Free %s" % (self.total_res - self.used_res)
|
|
|
|
|
|
class ResourceA(FakeResource):
|
|
|
|
def reset(self, resources, driver):
|
|
# ResourceA uses a configuration option
|
|
self.total_res = int(CONF.resA)
|
|
self.used_res = 0
|
|
self.resource_name = 'resource:resA'
|
|
|
|
def write(self, resources):
|
|
resources['resA'] = self.total_res
|
|
resources['used_resA'] = self.used_res
|
|
|
|
|
|
class ResourceB(FakeResource):
|
|
|
|
def reset(self, resources, driver):
|
|
# ResourceB uses resource details passed in parameter resources
|
|
self.total_res = resources['resB']
|
|
self.used_res = 0
|
|
self.resource_name = 'resource:resB'
|
|
|
|
def write(self, resources):
|
|
resources['resB'] = self.total_res
|
|
resources['used_resB'] = self.used_res
|
|
|
|
|
|
def fake_flavor_obj(**updates):
|
|
flavor = flavor_obj.Flavor()
|
|
flavor.id = 1
|
|
flavor.name = 'fakeflavor'
|
|
flavor.memory_mb = 8000
|
|
flavor.vcpus = 3
|
|
flavor.root_gb = 11
|
|
flavor.ephemeral_gb = 4
|
|
flavor.swap = 0
|
|
flavor.rxtx_factor = 1.0
|
|
flavor.vcpu_weight = 1
|
|
if updates:
|
|
flavor.update(updates)
|
|
return flavor
|
|
|
|
|
|
class BaseTestCase(test.NoDBTestCase):
|
|
|
|
def _initialize_used_res_counter(self):
|
|
# Initialize the value for the used resource
|
|
for ext in self.r_handler._mgr.extensions:
|
|
ext.obj.used_res = 0
|
|
|
|
def setUp(self):
|
|
super(BaseTestCase, self).setUp()
|
|
|
|
# initialize flavors and stub get_by_id to
|
|
# get flavors from here
|
|
self._flavors = {}
|
|
self.ctxt = context.get_admin_context()
|
|
|
|
# Create a flavor without extra_specs defined
|
|
_flavor_id = 1
|
|
_flavor = fake_flavor_obj(id=_flavor_id)
|
|
self._flavors[_flavor_id] = _flavor
|
|
|
|
# Create a flavor with extra_specs defined
|
|
_flavor_id = 2
|
|
requested_resA = 5
|
|
requested_resB = 7
|
|
requested_resC = 7
|
|
_extra_specs = {'resource:resA': requested_resA,
|
|
'resource:resB': requested_resB,
|
|
'resource:resC': requested_resC}
|
|
_flavor = fake_flavor_obj(id=_flavor_id,
|
|
extra_specs=_extra_specs)
|
|
self._flavors[_flavor_id] = _flavor
|
|
|
|
# create fake resource extensions and resource handler
|
|
_extensions = [
|
|
extension.Extension('resA', None, ResourceA, ResourceA()),
|
|
extension.Extension('resB', None, ResourceB, ResourceB()),
|
|
]
|
|
self.r_handler = FakeResourceHandler(_extensions)
|
|
|
|
# Resources details can be passed to each plugin or can be specified as
|
|
# configuration options
|
|
driver_resources = {'resB': 5}
|
|
CONF.resA = '10'
|
|
|
|
# initialise the resources
|
|
self.r_handler.reset_resources(driver_resources, None)
|
|
|
|
def test_update_from_instance_with_extra_specs(self):
|
|
# Flavor with extra_specs
|
|
_flavor_id = 2
|
|
sign = 1
|
|
self.r_handler.update_from_instance(self._flavors[_flavor_id], sign)
|
|
|
|
expected_resA = self._flavors[_flavor_id].extra_specs['resource:resA']
|
|
expected_resB = self._flavors[_flavor_id].extra_specs['resource:resB']
|
|
self.assertEqual(int(expected_resA),
|
|
self.r_handler._mgr['resA'].obj.used_res)
|
|
self.assertEqual(int(expected_resB),
|
|
self.r_handler._mgr['resB'].obj.used_res)
|
|
|
|
def test_update_from_instance_without_extra_specs(self):
|
|
# Flavor id without extra spec
|
|
_flavor_id = 1
|
|
self._initialize_used_res_counter()
|
|
self.r_handler.resource_list = []
|
|
sign = 1
|
|
self.r_handler.update_from_instance(self._flavors[_flavor_id], sign)
|
|
self.assertEqual(0, self.r_handler._mgr['resA'].obj.used_res)
|
|
self.assertEqual(0, self.r_handler._mgr['resB'].obj.used_res)
|
|
|
|
def test_write_resources(self):
|
|
self._initialize_used_res_counter()
|
|
extra_resources = {}
|
|
expected = {'resA': 10, 'used_resA': 0, 'resB': 5, 'used_resB': 0}
|
|
self.r_handler.write_resources(extra_resources)
|
|
self.assertEqual(expected, extra_resources)
|
|
|
|
def test_test_resources_without_extra_specs(self):
|
|
limits = {}
|
|
# Flavor id without extra_specs
|
|
flavor = self._flavors[1]
|
|
result = self.r_handler.test_resources(flavor, limits)
|
|
self.assertEqual([None, None], result)
|
|
|
|
def test_test_resources_with_limits_for_different_resource(self):
|
|
limits = {'resource:resC': 20}
|
|
# Flavor id with extra_specs
|
|
flavor = self._flavors[2]
|
|
result = self.r_handler.test_resources(flavor, limits)
|
|
self.assertEqual([None, None], result)
|
|
|
|
def test_passing_test_resources(self):
|
|
limits = {'resource:resA': 10, 'resource:resB': 20}
|
|
# Flavor id with extra_specs
|
|
flavor = self._flavors[2]
|
|
self._initialize_used_res_counter()
|
|
result = self.r_handler.test_resources(flavor, limits)
|
|
self.assertEqual([None, None], result)
|
|
|
|
def test_failing_test_resources_for_single_resource(self):
|
|
limits = {'resource:resA': 4, 'resource:resB': 20}
|
|
# Flavor id with extra_specs
|
|
flavor = self._flavors[2]
|
|
self._initialize_used_res_counter()
|
|
result = self.r_handler.test_resources(flavor, limits)
|
|
expected = ['Free 4 < requested 5 ', None]
|
|
self.assertEqual(sorted(expected, key=str),
|
|
sorted(result, key=str))
|
|
|
|
def test_empty_resource_handler(self):
|
|
"""An empty resource handler has no resource extensions,
|
|
should have no effect, and should raise no exceptions.
|
|
"""
|
|
empty_r_handler = FakeResourceHandler([])
|
|
|
|
resources = {}
|
|
empty_r_handler.reset_resources(resources, None)
|
|
|
|
flavor = self._flavors[1]
|
|
sign = 1
|
|
empty_r_handler.update_from_instance(flavor, sign)
|
|
|
|
limits = {}
|
|
test_result = empty_r_handler.test_resources(flavor, limits)
|
|
self.assertEqual([], test_result)
|
|
|
|
sign = -1
|
|
empty_r_handler.update_from_instance(flavor, sign)
|
|
|
|
extra_resources = {}
|
|
expected_extra_resources = extra_resources
|
|
empty_r_handler.write_resources(extra_resources)
|
|
self.assertEqual(expected_extra_resources, extra_resources)
|
|
|
|
empty_r_handler.report_free_resources()
|