Fix unit and functional tests

stable/kilo branch is broken due to next reasons:
1) unittests requires capped oslo libraries and mock
2) functional tests cannot access to credentials via environment variables

Both issues block each other, so we need to squach several commits.

Fix for functional tests is done via backporting patches:
* refactor functional test base class to no inherit from tempest_lib
  Conflicts: None
  Change-Id: I716be51d7d1825a757934298f06b2f04d64cf0dd
  (cherry picked from commit 420dc2884a)

* pass credentials via config file instead of magic
  Conflicts: README.rst
  Change-Id: Ifdab38a03c94f51d30449149c0dbd9c6265460a5
  (cherry picked from commit 6379287480)

Unittest doesn't require full sync with global-requrements, so only
oslo.i18n, oslo.serialization and mock are updated.

Change-Id: I5ecd52abcaf0da1b067e70d7b19297305af19174
This commit is contained in:
Andrey Kurilin 2015-04-21 09:19:13 -04:00 committed by Andrey Kurilin
parent 0ae7a08c08
commit 13463268db
8 changed files with 155 additions and 68 deletions

View File

@ -77,3 +77,16 @@ To use with nova, with keystone as the authentication system::
[...]
>>> nt.keypairs.list()
[...]
Testing
-------
There are multiple test targets that can be run to validate the code.
* tox -e pep8 - style guidelines enforcement
* tox -e py27 - traditional unit testing
* tox -e functional - live functional testing against an existing
openstack
Functional testing assumes the existance of a functional_creds.conf in
the root directory. See the .sample for example format.

View File

@ -0,0 +1,8 @@
# Credentials for functional testing
[auth]
uri = http://10.42.0.50:5000/v2.0
[admin]
user = admin
tenant = admin
pass = secrete

View File

@ -10,12 +10,47 @@
# License for the specific language governing permissions and limitations
# under the License.
import ConfigParser
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,18 +62,89 @@ 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))
# Collecting of credentials:
#
# Support the existence of a functional_creds.conf for
# testing. This makes it possible to use a config file.
#
# Those variables can be overridden by environmental variables
# as well to support existing users running these the old
# way. We should deprecate that.
# 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.get('OS_USERNAME')
passwd = os.environ.get('OS_PASSWORD')
tenant = os.environ.get('OS_TENANT_NAME')
auth_url = os.environ.get('OS_AUTH_URL')
config = ConfigParser.RawConfigParser()
if config.read('functional_creds.conf'):
# the OR pattern means the environment is preferred for
# override
user = user or config.get('admin', 'user')
passwd = passwd or config.get('admin', 'pass')
tenant = tenant or config.get('admin', 'tenant')
auth_url = auth_url or config.get('auth', 'uri')
# 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(
username=os.environ.get('OS_USERNAME'),
password=os.environ.get('OS_PASSWORD'),
tenant_name=os.environ.get('OS_TENANT_NAME'),
uri=os.environ.get('OS_AUTH_URL'),
self.cli_clients = tempest_lib.cli.base.CLIClient(
username=user,
password=passwd,
tenant_name=tenant,
uri=auth_url,
cli_dir=cli_dir)
def nova(self, *args, **kwargs):
return self.clients.nova(*args,
**kwargs)
return self.cli_clients.nova(*args,
**kwargs)

View File

@ -28,15 +28,28 @@ function generate_testr_results {
export NOVACLIENT_DIR="$BASE/new/python-novaclient"
sudo chown -R jenkins:stack $NOVACLIENT_DIR
# Get admin credentials
cd $BASE/new/devstack
source openrc admin admin
# pass the appropriate variables via a config file
CREDS_FILE=$NOVACLIENT_DIR/functional_creds.conf
cat <<EOF > $CREDS_FILE
# Credentials for functional testing
[auth]
uri = $OS_AUTH_URL
[admin]
user = $OS_USERNAME
tenant = $OS_TENANT_NAME
pass = $OS_PASSWORD
EOF
# Go to the novaclient dir
cd $NOVACLIENT_DIR
sudo chown -R jenkins:stack $NOVACLIENT_DIR
# Run tests
echo "Running novaclient functional test suite"
set +e

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)

View File

@ -4,8 +4,8 @@
pbr>=0.6,!=0.7,<1.0
argparse
iso8601>=0.1.9
oslo.i18n>=1.3.0 # Apache-2.0
oslo.serialization>=1.2.0 # Apache-2.0
oslo.i18n<1.6.0,>=1.5.0 # Apache-2.0
oslo.serialization<1.5.0,>=1.4.0 # Apache-2.0
oslo.utils>=1.2.0 # Apache-2.0
PrettyTable>=0.7,<0.8
requests>=2.2.0,!=2.4.0

View File

@ -7,7 +7,7 @@ coverage>=3.6
discover
fixtures>=0.3.14
keyring>=2.1,!=3.3
mock>=1.0
mock<1.1.0,>=1.0
requests-mock>=0.5.1 # Apache-2.0
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
oslosphinx>=2.2.0 # Apache-2.0