Skip to content

Commit

Permalink
Fix unit and functional tests
Browse files Browse the repository at this point in the history
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 420dc28)

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

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

Change-Id: I5ecd52abcaf0da1b067e70d7b19297305af19174
  • Loading branch information
andreykurilin committed Jul 16, 2015
1 parent 0ae7a08 commit 1346326
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 68 deletions.
13 changes: 13 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
8 changes: 8 additions & 0 deletions functional_creds.conf.sample
Original file line number Diff line number Diff line change
@@ -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
126 changes: 116 additions & 10 deletions novaclient/tests/functional/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
17 changes: 15 additions & 2 deletions novaclient/tests/functional/hooks/post_test_hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 0 additions & 42 deletions novaclient/tests/functional/test_instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
11 changes: 0 additions & 11 deletions novaclient/tests/functional/test_volumes_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 1346326

Please sign in to comment.