refactor functional test base class to no inherit from tempest_lib

Test base classes inheriting outside the existing source tree to
anything higher up the stack than testtools is really an anti
pattern. It makes it *far* less clear what is going on. We really need
to own and understand our base class setup for tests.

This does that unwind, so that we only now call out to tempest_lib in
specific ways (like building clients, using decorators). The timeout
and log capture pieces are pulled inline.

At the end of this we end up with a base test that defines:

 - self.client - a Nova API client
 - self.flavor - a workable flavor for booting guests
 - self.image - a workable image for booting guests
 - self.cli_clients - tempest_lib cli clients

Change-Id: I716be51d7d1825a757934298f06b2f04d64cf0dd
This commit is contained in:
Sean Dague 2015-04-21 09:19:13 -04:00
parent 5eab1430f2
commit 420dc2884a
3 changed files with 93 additions and 59 deletions

View File

@ -12,10 +12,44 @@
import os
from tempest_lib.cli import base
import fixtures
import tempest_lib.cli.base
import testtools
import novaclient.client
class ClientTestBase(base.ClientTestBase):
# The following are simple filter functions that filter our available
# image / flavor list so that they can be used in standard testing.
def pick_flavor(flavors):
"""Given a flavor list pick a reasonable one."""
for flavor in flavors:
if flavor.name == 'm1.tiny':
return flavor
for flavor in flavors:
if flavor.name == 'm1.small':
return flavor
raise NoFlavorException()
def pick_image(images):
for image in images:
if image.name.startswith('cirros') and image.name.endswith('-uec'):
return image
raise NoImageException()
class NoImageException(Exception):
"""We couldn't find an acceptable image."""
pass
class NoFlavorException(Exception):
"""We couldn't find an acceptable flavor."""
pass
class ClientTestBase(testtools.TestCase):
"""
This is a first pass at a simple read only python-novaclient test. This
only exercises client commands that are read only.
@ -27,12 +61,65 @@ class ClientTestBase(base.ClientTestBase):
* initially just check return codes, and later test command outputs
"""
def _get_clients(self):
log_format = ('%(asctime)s %(process)d %(levelname)-8s '
'[%(name)s] %(message)s')
def setUp(self):
super(ClientTestBase, self).setUp()
test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
try:
test_timeout = int(test_timeout)
except ValueError:
test_timeout = 0
if test_timeout > 0:
self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
os.environ.get('OS_STDOUT_CAPTURE') == '1'):
stdout = self.useFixture(fixtures.StringStream('stdout')).stream
self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
os.environ.get('OS_STDERR_CAPTURE') == '1'):
stderr = self.useFixture(fixtures.StringStream('stderr')).stream
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
os.environ.get('OS_LOG_CAPTURE') != '0'):
self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
format=self.log_format,
level=None))
# TODO(sdague): while we collect this information in
# tempest-lib, we do it in a way that's not available for top
# level tests. Long term this probably needs to be in the base
# class.
user = os.environ['OS_USERNAME']
passwd = os.environ['OS_PASSWORD']
tenant = os.environ['OS_TENANT_NAME']
auth_url = os.environ['OS_AUTH_URL']
# TODO(sdague): we made a lot of fun of the glanceclient team
# for version as int in first parameter. I guess we know where
# they copied it from.
self.client = novaclient.client.Client(
2, user, passwd, tenant,
auth_url=auth_url)
# pick some reasonable flavor / image combo
self.flavor = pick_flavor(self.client.flavors.list())
self.image = pick_image(self.client.images.list())
# create a CLI client in case we'd like to do CLI
# testing. tempest_lib does this realy weird thing where it
# builds a giant factory of all the CLIs that it knows
# about. Eventually that should really be unwound into
# something more sensible.
cli_dir = os.environ.get(
'OS_NOVACLIENT_EXEC_DIR',
os.path.join(os.path.abspath('.'), '.tox/functional/bin'))
return base.CLIClient(
self.cli_clients = tempest_lib.cli.base.CLIClient(
username=os.environ.get('OS_USERNAME'),
password=os.environ.get('OS_PASSWORD'),
tenant_name=os.environ.get('OS_TENANT_NAME'),
@ -40,5 +127,5 @@ class ClientTestBase(base.ClientTestBase):
cli_dir=cli_dir)
def nova(self, *args, **kwargs):
return self.clients.nova(*args,
**kwargs)
return self.cli_clients.nova(*args,
**kwargs)

View File

@ -10,33 +10,12 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
import time
import uuid
import novaclient.client
from novaclient.tests.functional import base
# TODO(sdague): content that probably should be in utils, also throw
# Exceptions when they fail.
def pick_flavor(flavors):
"""Given a flavor list pick a reasonable one."""
for flavor in flavors:
if flavor.name == 'm1.tiny':
return flavor
for flavor in flavors:
if flavor.name == 'm1.small':
return flavor
def pick_image(images):
for image in images:
if image.name.startswith('cirros') and image.name.endswith('-uec'):
return image
def volume_id_from_cli_create(output):
"""Scrape the volume id out of the 'volume create' command
@ -67,27 +46,6 @@ def volume_at_status(output, volume_id, status):
class TestInstanceCLI(base.ClientTestBase):
def setUp(self):
super(TestInstanceCLI, self).setUp()
# TODO(sdague): while we collect this information in
# tempest-lib, we do it in a way that's not available for top
# level tests. Long term this probably needs to be in the base
# class.
user = os.environ['OS_USERNAME']
passwd = os.environ['OS_PASSWORD']
tenant = os.environ['OS_TENANT_NAME']
auth_url = os.environ['OS_AUTH_URL']
# TODO(sdague): we made a lot of fun of the glanceclient team
# for version as int in first parameter. I guess we know where
# they copied it from.
self.client = novaclient.client.Client(
2, user, passwd, tenant,
auth_url=auth_url)
# pick some reasonable flavor / image combo
self.flavor = pick_flavor(self.client.flavors.list())
self.image = pick_image(self.client.images.list())
def test_attach_volume(self):
"""Test we can attach a volume via the cli.

View File

@ -10,13 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
import time
import uuid
import six.moves
from novaclient import client
from novaclient import exceptions
from novaclient.tests.functional import base
@ -35,15 +33,6 @@ def wait_for_delete(test, name, thing, get_func):
class TestVolumesAPI(base.ClientTestBase):
def setUp(self):
super(TestVolumesAPI, self).setUp()
user = os.environ['OS_USERNAME']
passwd = os.environ['OS_PASSWORD']
tenant = os.environ['OS_TENANT_NAME']
auth_url = os.environ['OS_AUTH_URL']
self.client = client.Client(2, user, passwd, tenant, auth_url=auth_url)
def test_volumes_snapshots_types_create_get_list_delete(self):
# Create a volume
volume = self.client.volumes.create(1)