tests: Centralize configuration of default flavor, image

Different tests were doing this in different ways. Centralize it all in
the base test class for functional tests. For both flavor and image, the
order of precedence is:

- Environment variables
- clouds.yaml configuration
- Guesswork (pick a cirros, Ubuntu or CentOS image for images, or the
  flavor with the lowest RAM for flavors)

Change-Id: I90fda8ef48008c7fa634edc295c0e83e5f29387f
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane 2021-12-22 14:57:19 +00:00
parent 11d89450c1
commit 3244abd020
10 changed files with 163 additions and 164 deletions

View File

@ -68,8 +68,9 @@ configured for the one cloud. These accounts are:
configurable via the ``OPENSTACKSDK_DEMO_CLOUD_ALT`` environment variable
In addition, you must indicate the names of the flavor and image that should be
used for tests. These can be configured via ``functional.flavor_name`` and
``functional.image_name`` settings in the ``clouds.yaml`` file.
used for tests. These can be configured via ``OPENSTACKSDK_FLAVOR`` and
``OPENSTACKSDK_IMAGE`` environment variables or ``functional.flavor_name`` and
``functional.image_name`` settings in the ``clouds.yaml`` file, respectively.
Finally, you can configure the timeout for tests using the
``OPENSTACKSDK_FUNC_TEST_TIMEOUT`` environment variable (defaults to 300

View File

@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import operator
import os
from keystoneauth1 import discover
@ -28,9 +29,8 @@ TEST_CLOUD_NAME = os.getenv('OS_CLOUD', 'devstack-admin')
TEST_CLOUD_REGION = openstack.config.get_cloud_region(cloud=TEST_CLOUD_NAME)
def _get_resource_value(resource_key, default):
return TEST_CONFIG.get_extra_config(
'functional').get(resource_key, default)
def _get_resource_value(resource_key):
return TEST_CONFIG.get_extra_config('functional').get(resource_key)
def _disable_keep_alive(conn):
@ -38,10 +38,6 @@ def _disable_keep_alive(conn):
sess.keep_alive = False
IMAGE_NAME = _get_resource_value('image_name', 'cirros-0.4.0-x86_64-disk')
FLAVOR_NAME = _get_resource_value('flavor_name', 'm1.small')
class BaseFunctionalTest(base.TestCase):
_wait_for_timeout_key = ''
@ -52,10 +48,12 @@ class BaseFunctionalTest(base.TestCase):
_disable_keep_alive(self.conn)
self._demo_name = os.environ.get('OPENSTACKSDK_DEMO_CLOUD', 'devstack')
self._demo_name_alt = os.environ.get('OPENSTACKSDK_DEMO_CLOUD_ALT',
'devstack-alt')
self._demo_name_alt = os.environ.get(
'OPENSTACKSDK_DEMO_CLOUD_ALT', 'devstack-alt',
)
self._op_name = os.environ.get(
'OPENSTACKSDK_OPERATOR_CLOUD', 'devstack-admin')
'OPENSTACKSDK_OPERATOR_CLOUD', 'devstack-admin',
)
self.config = openstack.config.OpenStackConfig()
self._set_user_cloud()
@ -64,6 +62,9 @@ class BaseFunctionalTest(base.TestCase):
self.identity_version = \
self.operator_cloud.config.get_api_version('identity')
self.flavor = self._pick_flavor()
self.image = self._pick_image()
# Defines default timeout for wait_for methods used
# in the functional tests
self._wait_for_timeout = int(
@ -71,8 +72,7 @@ class BaseFunctionalTest(base.TestCase):
'OPENSTACKSDK_FUNC_TEST_TIMEOUT', 300)))
def _set_user_cloud(self, **kwargs):
user_config = self.config.get_one(
cloud=self._demo_name, **kwargs)
user_config = self.config.get_one(cloud=self._demo_name, **kwargs)
self.user_cloud = connection.Connection(config=user_config)
_disable_keep_alive(self.user_cloud)
@ -84,37 +84,95 @@ class BaseFunctionalTest(base.TestCase):
_disable_keep_alive(self.user_cloud_alt)
def _set_operator_cloud(self, **kwargs):
operator_config = self.config.get_one(
cloud=self._op_name, **kwargs)
operator_config = self.config.get_one(cloud=self._op_name, **kwargs)
self.operator_cloud = connection.Connection(config=operator_config)
_disable_keep_alive(self.operator_cloud)
def pick_image(self):
def _pick_flavor(self):
"""Pick a sensible flavor to run tests with.
This returns None if the compute service is not present (e.g.
ironic-only deployments).
"""
if not self.user_cloud.has_service('compute'):
return None
flavors = self.user_cloud.list_flavors(get_extra=False)
self.add_info_on_exception('flavors', flavors)
flavor_name = os.environ.get('OPENSTACKSDK_FLAVOR')
if not flavor_name:
flavor_name = _get_resource_value('flavor_name')
if flavor_name:
for flavor in flavors:
if flavor.name == flavor_name:
return flavor
raise self.failureException(
"Cloud does not have flavor '%s'", flavor_name,
)
# Enable running functional tests against RAX, which requires
# performance flavors be used for boot from volume
for flavor in sorted(flavors, key=operator.attrgetter('ram')):
if 'performance' in flavor.name:
return flavor
# Otherwise, pick the smallest flavor with a ephemeral disk configured
for flavor in sorted(flavors, key=operator.attrgetter('ram')):
if flavor.disk:
return flavor
raise self.failureException('No sensible flavor found')
def _pick_image(self):
"""Pick a sensible image to run tests with.
This returns None if the image service is not present.
"""
if not self.user_cloud.has_service('image'):
return None
images = self.user_cloud.list_images()
self.add_info_on_exception('images', images)
image_name = os.environ.get('OPENSTACKSDK_IMAGE')
if not image_name:
image_name = _get_resource_value('image_name')
if image_name:
for image in images:
if image.name == image_name:
return image
self.assertFalse(
"Cloud does not have {image}".format(image=image_name))
raise self.failureException(
"Cloud does not have image '%s'", image_name,
)
for image in images:
if image.name.startswith('cirros') and image.name.endswith('-uec'):
return image
for image in images:
if (image.name.startswith('cirros')
and image.disk_format == 'qcow2'):
if (
image.name.startswith('cirros')
and image.disk_format == 'qcow2'
):
return image
for image in images:
if image.name.lower().startswith('ubuntu'):
return image
for image in images:
if image.name.lower().startswith('centos'):
return image
self.assertFalse('no sensible image available')
raise self.failureException('No sensible image found')
def addEmptyCleanup(self, func, *args, **kwargs):
def cleanup():
@ -125,12 +183,12 @@ class BaseFunctionalTest(base.TestCase):
def require_service(self, service_type, min_microversion=None, **kwargs):
"""Method to check whether a service exists
Usage:
class TestMeter(base.BaseFunctionalTest):
...
def setUp(self):
super(TestMeter, self).setUp()
self.require_service('metering')
Usage::
class TestMeter(base.BaseFunctionalTest):
def setUp(self):
super(TestMeter, self).setUp()
self.require_service('metering')
:returns: True if the service exists, otherwise False.
"""
@ -144,16 +202,19 @@ class BaseFunctionalTest(base.TestCase):
data = self.conn.session.get_endpoint_data(
service_type=service_type, **kwargs)
if not (data.min_microversion
and data.max_microversion
and discover.version_between(
data.min_microversion,
data.max_microversion,
min_microversion)):
self.skipTest('Service {service_type} does not provide '
'microversion {ver}'.format(
service_type=service_type,
ver=min_microversion))
if not (
data.min_microversion
and data.max_microversion
and discover.version_between(
data.min_microversion,
data.max_microversion,
min_microversion,
)
):
self.skipTest(
f'Service {service_type} does not provide microversion '
f'{min_microversion}'
)
class KeystoneBaseFunctionalTest(BaseFunctionalTest):

View File

@ -115,8 +115,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -145,8 +145,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -189,8 +189,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -232,8 +232,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -318,8 +318,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -392,8 +392,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -474,8 +474,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -567,8 +567,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -656,8 +656,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -713,8 +713,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -775,8 +775,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -863,8 +863,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -914,8 +914,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -1018,8 +1018,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -1056,8 +1056,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -1094,8 +1094,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -1130,8 +1130,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -1297,8 +1297,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"
@ -1356,8 +1356,8 @@ class TestClustering(base.BaseFunctionalTest):
profile_name = "test_profile"
spec = {
"properties": {
"flavor": "m1.tiny",
"image": base.IMAGE_NAME,
"flavor": self.flavor.name,
"image": self.image.name,
"networks": [
{
"network": "private"

View File

@ -23,7 +23,6 @@ from fixtures import TimeoutException
from openstack.cloud import exc
from openstack.tests.functional import base
from openstack.tests.functional.cloud.util import pick_flavor
from openstack import utils
@ -34,11 +33,6 @@ class TestCompute(base.BaseFunctionalTest):
self.TIMEOUT_SCALING_FACTOR = 1.5
super(TestCompute, self).setUp()
self.flavor = pick_flavor(
self.user_cloud.list_flavors(get_extra=False))
if self.flavor is None:
self.assertFalse('no sensible flavor available')
self.image = self.pick_image()
self.server_name = self.getUniqueString()
def _cleanup_servers_and_volumes(self, server_name):

View File

@ -28,7 +28,6 @@ from openstack.cloud.exc import OpenStackCloudException
from openstack.cloud import meta
from openstack import proxy
from openstack.tests.functional import base
from openstack.tests.functional.cloud.util import pick_flavor
from openstack import utils
@ -36,12 +35,7 @@ class TestFloatingIP(base.BaseFunctionalTest):
timeout = 60
def setUp(self):
super(TestFloatingIP, self).setUp()
self.flavor = pick_flavor(
self.user_cloud.list_flavors(get_extra=False))
if self.flavor is None:
self.assertFalse('no sensible flavor available')
self.image = self.pick_image()
super().setUp()
# Generate a random name for these tests
self.new_item_name = self.getUniqueString()

View File

@ -25,9 +25,6 @@ from openstack.tests.functional import base
class TestImage(base.BaseFunctionalTest):
def setUp(self):
super(TestImage, self).setUp()
self.image = self.pick_image()
def test_create_image(self):
test_image = tempfile.NamedTemporaryFile(delete=False)

View File

@ -21,21 +21,15 @@ Functional tests for `shade` inventory methods.
from openstack.cloud import inventory
from openstack.tests.functional import base
from openstack.tests.functional.cloud.util import pick_flavor
class TestInventory(base.BaseFunctionalTest):
def setUp(self):
super(TestInventory, self).setUp()
super().setUp()
# This needs to use an admin account, otherwise a public IP
# is not allocated from devstack.
self.inventory = inventory.OpenStackInventory(cloud='devstack-admin')
self.server_name = self.getUniqueString('inventory')
self.flavor = pick_flavor(
self.user_cloud.list_flavors(get_extra=False))
if self.flavor is None:
self.assertTrue(False, 'no sensible flavor available')
self.image = self.pick_image()
self.addCleanup(self._cleanup_server)
server = self.operator_cloud.create_server(
name=self.server_name, image=self.image, flavor=self.flavor,

View File

@ -1,43 +0,0 @@
# 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.
"""
util
--------------------------------
Util methods for functional tests
"""
import operator
import os
def pick_flavor(flavors):
"""Given a flavor list pick the smallest one."""
# Enable running functional tests against rax - which requires
# performance flavors be used for boot from volume
flavor_name = os.environ.get('OPENSTACKSDK_FLAVOR')
if flavor_name:
for flavor in flavors:
if flavor.name == flavor_name:
return flavor
return None
for flavor in sorted(
flavors,
key=operator.attrgetter('ram')):
if 'performance' in flavor.name:
return flavor
for flavor in sorted(
flavors,
key=operator.attrgetter('ram')):
if flavor.disk:
return flavor

View File

@ -40,8 +40,8 @@ class TestCluster(base.BaseFunctionalTest):
'version': 1.0,
'properties': {
'name': self.getUniqueString(),
'flavor': base.FLAVOR_NAME,
'image': base.IMAGE_NAME,
'flavor': self.flavor.name,
'image': self.image.name,
'networks': [{'network': self.network.id}]
}}}

View File

@ -11,7 +11,6 @@
# under the License.
from openstack.compute.v2 import server
from openstack.tests.functional import base
from openstack.tests.functional.compute import base as ft_base
from openstack.tests.functional.network.v2 import test_network
@ -23,21 +22,24 @@ class TestServerAdmin(ft_base.BaseComputeTest):
self._set_operator_cloud(interface='admin')
self.NAME = 'needstobeshortandlowercase'
self.USERDATA = 'SSdtIGFjdHVhbGx5IGEgZ29hdC4='
flavor = self.conn.compute.find_flavor(base.FLAVOR_NAME,
ignore_missing=False)
image = self.conn.compute.find_image(base.IMAGE_NAME,
ignore_missing=False)
volume = self.conn.create_volume(1)
sot = self.conn.compute.create_server(
name=self.NAME, flavor_id=flavor.id, image_id=image.id,
networks='none', user_data=self.USERDATA,
block_device_mapping=[{
'uuid': volume.id,
'source_type': 'volume',
'boot_index': 0,
'destination_type': 'volume',
'delete_on_termination': True,
'volume_size': 1}])
name=self.NAME,
flavor_id=self.flavor.id,
image_id=self.image.id,
networks='none',
user_data=self.USERDATA,
block_device_mapping=[
{
'uuid': volume.id,
'source_type': 'volume',
'boot_index': 0,
'destination_type': 'volume',
'delete_on_termination': True,
'volume_size': 1,
},
],
)
self.conn.compute.wait_for_server(sot, wait=self._wait_for_timeout)
assert isinstance(sot, server.Server)
self.assertEqual(self.NAME, sot.name)
@ -72,10 +74,6 @@ class TestServer(ft_base.BaseComputeTest):
self.subnet = None
self.cidr = '10.99.99.0/16'
flavor = self.conn.compute.find_flavor(base.FLAVOR_NAME,
ignore_missing=False)
image = self.conn.compute.find_image(base.IMAGE_NAME,
ignore_missing=False)
self.network, self.subnet = test_network.create_network(
self.conn,
self.NAME,
@ -83,8 +81,11 @@ class TestServer(ft_base.BaseComputeTest):
self.assertIsNotNone(self.network)
sot = self.conn.compute.create_server(
name=self.NAME, flavor_id=flavor.id, image_id=image.id,
networks=[{"uuid": self.network.id}])
name=self.NAME,
flavor_id=self.flavor.id,
image_id=self.image.id,
networks=[{"uuid": self.network.id}],
)
self.conn.compute.wait_for_server(sot, wait=self._wait_for_timeout)
assert isinstance(sot, server.Server)
self.assertEqual(self.NAME, sot.name)