summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.rst13
-rw-r--r--functional_creds.conf.sample8
-rw-r--r--novaclient/tests/functional/base.py126
-rwxr-xr-xnovaclient/tests/functional/hooks/post_test_hook.sh17
-rw-r--r--novaclient/tests/functional/test_instances.py42
-rw-r--r--novaclient/tests/functional/test_volumes_api.py11
-rw-r--r--requirements.txt4
-rw-r--r--test-requirements.txt2
8 files changed, 155 insertions, 68 deletions
diff --git a/README.rst b/README.rst
index a7edf870..b28112c9 100644
--- a/README.rst
+++ b/README.rst
@@ -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.
diff --git a/functional_creds.conf.sample b/functional_creds.conf.sample
new file mode 100644
index 00000000..081a7368
--- /dev/null
+++ b/functional_creds.conf.sample
@@ -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
diff --git a/novaclient/tests/functional/base.py b/novaclient/tests/functional/base.py
index 27015bda..ac795325 100644
--- a/novaclient/tests/functional/base.py
+++ b/novaclient/tests/functional/base.py
@@ -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)
diff --git a/novaclient/tests/functional/hooks/post_test_hook.sh b/novaclient/tests/functional/hooks/post_test_hook.sh
index e9e35b24..5878ace9 100755
--- a/novaclient/tests/functional/hooks/post_test_hook.sh
+++ b/novaclient/tests/functional/hooks/post_test_hook.sh
@@ -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
diff --git a/novaclient/tests/functional/test_instances.py b/novaclient/tests/functional/test_instances.py
index c963a6b7..8df28d13 100644
--- a/novaclient/tests/functional/test_instances.py
+++ b/novaclient/tests/functional/test_instances.py
@@ -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.
diff --git a/novaclient/tests/functional/test_volumes_api.py b/novaclient/tests/functional/test_volumes_api.py
index e427c2e2..38c63bbf 100644
--- a/novaclient/tests/functional/test_volumes_api.py
+++ b/novaclient/tests/functional/test_volumes_api.py
@@ -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)
diff --git a/requirements.txt b/requirements.txt
index bc729cf8..324426b3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -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
diff --git a/test-requirements.txt b/test-requirements.txt
index acdbc244..4e98decc 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -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