nova/nova/tests/unit/compute/test_flavors.py

295 lines
11 KiB
Python

# Copyright 2014 IBM Corp.
# 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 flavor basic functions"""
from nova.compute import flavors
from nova import context
from nova.db import constants as db_const
from nova import exception
from nova import objects
from nova.objects import base as obj_base
from nova import test
class TestValidateExtraSpecKeys(test.NoDBTestCase):
def test_flavor_validate_extra_spec_keys_invalid_input(self):
for key_name_list in [['', ], ['*', ], ['+', ]]:
self.assertRaises(
exception.InvalidInput,
flavors.validate_extra_spec_keys, key_name_list)
def test_flavor_validate_extra_spec_keys(self):
key_name_list = ['abc', 'ab c', 'a-b-c', 'a_b-c', 'a:bc']
flavors.validate_extra_spec_keys(key_name_list)
class TestGetFlavorByFlavorID(test.TestCase):
"""Test cases for flavor code."""
def test_will_not_get_instance_by_unknown_flavor_id(self):
# Ensure get by flavor raises error with wrong flavorid.
self.assertRaises(exception.FlavorNotFound,
flavors.get_flavor_by_flavor_id,
'unknown_flavor')
def test_will_get_instance_by_flavor_id(self):
default_flavor = objects.Flavor.get_by_name(
context.get_admin_context(), 'm1.small')
flavorid = default_flavor.flavorid
fetched = flavors.get_flavor_by_flavor_id(flavorid)
self.assertIsInstance(fetched, objects.Flavor)
self.assertEqual(default_flavor.flavorid, fetched.flavorid)
class TestExtractFlavor(test.TestCase):
def setUp(self):
super().setUp()
self.context = context.get_admin_context()
def _dict_to_metadata(self, data):
return [{'key': key, 'value': value} for key, value in data.items()]
def _test_extract_flavor(self, prefix):
flavor = objects.Flavor.get_by_name(self.context, 'm1.small')
flavor_p = obj_base.obj_to_primitive(flavor)
metadata = {}
flavors.save_flavor_info(metadata, flavor, prefix)
instance = {'system_metadata': self._dict_to_metadata(metadata)}
_flavor = flavors.extract_flavor(instance, prefix)
_flavor_p = obj_base.obj_to_primitive(_flavor)
props = flavors.system_metadata_flavor_props.keys()
for key in list(flavor_p.keys()):
if key not in props:
del flavor_p[key]
self.assertEqual(flavor_p, _flavor_p)
def test_extract_flavor(self):
self._test_extract_flavor('')
def test_extract_flavor_no_sysmeta(self):
instance = {}
prefix = ''
result = flavors.extract_flavor(instance, prefix)
self.assertIsNone(result)
def test_extract_flavor_prefix(self):
self._test_extract_flavor('foo_')
class TestSaveFlavorInfo(test.TestCase):
def setUp(self):
super().setUp()
self.context = context.get_admin_context()
def test_save_flavor_info(self):
flavor = objects.Flavor.get_by_name(self.context, 'm1.small')
example = {}
example_prefix = {}
for key in flavors.system_metadata_flavor_props.keys():
example['instance_type_%s' % key] = flavor[key]
example_prefix['fooinstance_type_%s' % key] = flavor[key]
metadata = {}
flavors.save_flavor_info(metadata, flavor)
self.assertEqual(example, metadata)
metadata = {}
flavors.save_flavor_info(metadata, flavor, 'foo')
self.assertEqual(example_prefix, metadata)
def test_flavor_numa_extras_are_saved(self):
flavor = objects.Flavor.get_by_name(self.context, 'm1.small')
flavor['extra_specs'] = {
'hw:numa_mem.0': '123',
'hw:numa_cpus.0': '456',
'hw:numa_mem.1': '789',
'hw:numa_cpus.1': 'ABC',
'foo': 'bar',
}
sysmeta = flavors.save_flavor_info({}, flavor)
_flavor = flavors.extract_flavor({'system_metadata': sysmeta})
expected_extra_specs = {
'hw:numa_mem.0': '123',
'hw:numa_cpus.0': '456',
'hw:numa_mem.1': '789',
'hw:numa_cpus.1': 'ABC',
}
self.assertEqual(expected_extra_specs, _flavor['extra_specs'])
class TestCreateFlavor(test.TestCase):
def assertInvalidInput(self, *create_args, **create_kwargs):
self.assertRaises(
exception.InvalidInput, flavors.create,
*create_args, **create_kwargs)
def test_memory_must_be_positive_db_integer(self):
self.assertInvalidInput('flavor1', 'foo', 1, 120)
self.assertInvalidInput('flavor1', -1, 1, 120)
self.assertInvalidInput('flavor1', 0, 1, 120)
self.assertInvalidInput('flavor1', db_const.MAX_INT + 1, 1, 120)
flavors.create('flavor1', 1, 1, 120)
def test_vcpus_must_be_positive_db_integer(self):
self.assertInvalidInput('flavor`', 64, 'foo', 120)
self.assertInvalidInput('flavor1', 64, -1, 120)
self.assertInvalidInput('flavor1', 64, 0, 120)
self.assertInvalidInput('flavor1', 64, db_const.MAX_INT + 1, 120)
flavors.create('flavor1', 64, 1, 120)
def test_root_gb_must_be_nonnegative_db_integer(self):
self.assertInvalidInput('flavor1', 64, 1, 'foo')
self.assertInvalidInput('flavor1', 64, 1, -1)
self.assertInvalidInput('flavor1', 64, 1, db_const.MAX_INT + 1)
flavors.create('flavor1', 64, 1, 0)
flavors.create('flavor2', 64, 1, 120)
def test_ephemeral_gb_must_be_nonnegative_db_integer(self):
self.assertInvalidInput('flavor1', 64, 1, 120, ephemeral_gb='foo')
self.assertInvalidInput('flavor1', 64, 1, 120, ephemeral_gb=-1)
self.assertInvalidInput(
'flavor1', 64, 1, 120, ephemeral_gb=db_const.MAX_INT + 1)
flavors.create('flavor1', 64, 1, 120, ephemeral_gb=0)
flavors.create('flavor2', 64, 1, 120, ephemeral_gb=120)
def test_swap_must_be_nonnegative_db_integer(self):
self.assertInvalidInput('flavor1', 64, 1, 120, swap='foo')
self.assertInvalidInput('flavor1', 64, 1, 120, swap=-1)
self.assertInvalidInput(
'flavor1', 64, 1, 120, swap=db_const.MAX_INT + 1)
flavors.create('flavor1', 64, 1, 120, swap=0)
flavors.create('flavor2', 64, 1, 120, swap=1)
def test_rxtx_factor_must_be_positive_float(self):
self.assertInvalidInput('flavor1', 64, 1, 120, rxtx_factor='foo')
self.assertInvalidInput('flavor1', 64, 1, 120, rxtx_factor=-1.0)
self.assertInvalidInput('flavor1', 64, 1, 120, rxtx_factor=0.0)
flavor = flavors.create('flavor1', 64, 1, 120, rxtx_factor=1.0)
self.assertEqual(1.0, flavor.rxtx_factor)
flavor = flavors.create('flavor2', 64, 1, 120, rxtx_factor=1.1)
self.assertEqual(1.1, flavor.rxtx_factor)
def test_rxtx_factor_must_be_within_sql_float_range(self):
# We do * 10 since this is an approximation and we need to make sure
# the difference is noticeble.
over_rxtx_factor = db_const.SQL_SP_FLOAT_MAX * 10
self.assertInvalidInput('flavor1', 64, 1, 120,
rxtx_factor=over_rxtx_factor)
flavor = flavors.create(
'flavor2', 64, 1, 120, rxtx_factor=db_const.SQL_SP_FLOAT_MAX)
self.assertEqual(db_const.SQL_SP_FLOAT_MAX, flavor.rxtx_factor)
def test_is_public_must_be_valid_bool_string(self):
self.assertInvalidInput('flavor1', 64, 1, 120, is_public='foo')
flavors.create('flavor1', 64, 1, 120, is_public='TRUE')
flavors.create('flavor2', 64, 1, 120, is_public='False')
flavors.create('flavor3', 64, 1, 120, is_public='Yes')
flavors.create('flavor4', 64, 1, 120, is_public='No')
flavors.create('flavor5', 64, 1, 120, is_public='Y')
flavors.create('flavor6', 64, 1, 120, is_public='N')
flavors.create('flavor7', 64, 1, 120, is_public='1')
flavors.create('flavor8', 64, 1, 120, is_public='0')
flavors.create('flavor9', 64, 1, 120, is_public='true')
def test_flavorid_populated(self):
flavor1 = flavors.create('flavor1', 64, 1, 120)
self.assertIsNotNone(flavor1.flavorid)
flavor2 = flavors.create('flavor2', 64, 1, 120, flavorid='')
self.assertIsNotNone(flavor2.flavorid)
flavor3 = flavors.create('flavor3', 64, 1, 120, flavorid='foo')
self.assertEqual('foo', flavor3.flavorid)
def test_default_values(self):
flavor1 = flavors.create('flavor1', 64, 1, 120)
self.assertIsNotNone(flavor1.flavorid)
self.assertEqual(flavor1.ephemeral_gb, 0)
self.assertEqual(flavor1.swap, 0)
self.assertEqual(flavor1.rxtx_factor, 1.0)
def test_basic_create(self):
# Ensure instance types can be created.
ctxt = context.get_admin_context()
original_list = objects.FlavorList.get_all(ctxt)
# Create new type and make sure values stick
flavor = flavors.create('flavor', 64, 1, 120)
self.assertEqual(flavor.name, 'flavor')
self.assertEqual(flavor.memory_mb, 64)
self.assertEqual(flavor.vcpus, 1)
self.assertEqual(flavor.root_gb, 120)
# Ensure new type shows up in list
new_list = objects.FlavorList.get_all(ctxt)
self.assertNotEqual(
len(original_list), len(new_list),
'flavor was not created')
def test_create_then_delete(self):
ctxt = context.get_admin_context()
original_list = objects.FlavorList.get_all(ctxt)
flavor = flavors.create('flavor', 64, 1, 120)
# Ensure new type shows up in list
new_list = objects.FlavorList.get_all(ctxt)
self.assertNotEqual(
len(original_list), len(new_list),
'instance type was not created')
flavor.destroy()
self.assertRaises(
exception.FlavorNotFound,
objects.Flavor.get_by_name, ctxt, flavor.name)
# Deleted instance should not be in list anymore
new_list = objects.FlavorList.get_all(ctxt)
self.assertEqual(len(original_list), len(new_list))
for i, f in enumerate(original_list):
self.assertIsInstance(f, objects.Flavor)
self.assertEqual(f.flavorid, new_list[i].flavorid)
def test_duplicate_names_fail(self):
# Ensures that name duplicates raise FlavorExists
flavors.create('flavor', 256, 1, 120, 200, 'flavor1')
self.assertRaises(
exception.FlavorExists,
flavors.create, 'flavor', 64, 1, 120)
def test_duplicate_flavorids_fail(self):
# Ensures that flavorid duplicates raise FlavorExists
flavors.create('flavor1', 64, 1, 120, flavorid='flavorid')
self.assertRaises(
exception.FlavorIdExists,
flavors.create, 'flavor2', 64, 1, 120, flavorid='flavorid')