Fixes LP Bug#899383 - Cleanup config file search
Cleans up a bunch of configuration-related errors when trying to run tempest out of the box with a simple call to: $> nosetests storm * Raises a sensible error if the config file cannot be found * Makes it possible to set the config file directory and config file name via environment variables * Removes unnecessary calls to create storm.config.StormConfig() and share a configuration object by passing the openstack.Manager's config object with the various rest client objects * Updates the README to show how to make a config file and run the tests in tempest Change-Id: I60e33595b88df596cc9585bcaf18d37ae77d6f2b
This commit is contained in:
parent
e1b050d01c
commit
7f75763f54
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,7 +1,10 @@
|
|||||||
.kong-venv
|
.kong-venv
|
||||||
*.pyc
|
*.pyc
|
||||||
etc/config.ini
|
etc/config.ini
|
||||||
|
etc/storm.conf
|
||||||
|
etc/tempest.conf
|
||||||
include/swift_objects/swift_small
|
include/swift_objects/swift_small
|
||||||
include/swift_objects/swift_medium
|
include/swift_objects/swift_medium
|
||||||
include/swift_objects/swift_large
|
include/swift_objects/swift_large
|
||||||
*.log
|
*.log
|
||||||
|
*.swp
|
||||||
|
36
README.rst
36
README.rst
@ -1,25 +1,37 @@
|
|||||||
::
|
::
|
||||||
|
|
||||||
OpenStack integration test suite
|
Tempest - The OpenStack Integration Test Suite
|
||||||
================================
|
==============================================
|
||||||
|
|
||||||
This is a set of integration tests to be run against a live cluster.
|
This is a set of integration tests to be run against a live cluster.
|
||||||
|
|
||||||
Quickstart
|
Quickstart
|
||||||
----------
|
----------
|
||||||
|
|
||||||
You're going to want to make your own config.ini file in the /etc/ directory,
|
To run Tempest, you first need to create a configuration file that
|
||||||
it needs to point at your running cluster.
|
will tell Tempest where to find the various OpenStack services and
|
||||||
|
other testing behaviour switches.
|
||||||
|
|
||||||
After that try commands such as::
|
The easiest way to create a configuration file is to copy the sample
|
||||||
|
one in the ``etc/`` directory ::
|
||||||
|
|
||||||
run_tests.sh --nova
|
$> cd $TEMPEST_ROOT_DIR
|
||||||
run_tests.sh --glance
|
$> cp etc/storm.conf.sample etc/storm.conf
|
||||||
run_tests.sh --swift
|
|
||||||
run_tests.sh --auth
|
|
||||||
|
|
||||||
|
After that, open up the ``etc/storm.conf`` file and edit the
|
||||||
|
variables to fit your test environment.
|
||||||
|
|
||||||
Additional Info
|
.. note::
|
||||||
---------------
|
|
||||||
|
|
||||||
There are additional README files in the various subdirectories of this project.
|
If you have a running devstack environment, look at the
|
||||||
|
environment variables in your ``devstack/localrc`` file.
|
||||||
|
The ADMIN_PASSWORD variable should match the api_key value
|
||||||
|
in the storm.conf [nova] configuration section. In addition,
|
||||||
|
you will need to get the UUID identifier of the image that
|
||||||
|
devstack uploaded and set the image_ref value in the [environment]
|
||||||
|
section in the storm.conf to that image UUID.
|
||||||
|
|
||||||
|
After setting up your configuration file, you can execute the set of
|
||||||
|
Tempest tests by using ``nosetests`` ::
|
||||||
|
|
||||||
|
$> nosetests storm
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
from storm import exceptions
|
|
||||||
import httplib2
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
import httplib2
|
||||||
|
|
||||||
|
from storm import exceptions
|
||||||
import storm.config
|
import storm.config
|
||||||
|
|
||||||
|
|
||||||
class RestClient(object):
|
class RestClient(object):
|
||||||
|
|
||||||
def __init__(self, user, key, auth_url, tenant_name=None):
|
def __init__(self, config, user, key, auth_url, tenant_name=None):
|
||||||
self.config = storm.config.StormConfig()
|
self.config = config
|
||||||
|
|
||||||
if self.config.env.authentication == 'keystone_v2':
|
if self.config.env.authentication == 'keystone_v2':
|
||||||
self.token, self.base_url = self.keystone_v2_auth(user,
|
self.token, self.base_url = self.keystone_v2_auth(user,
|
||||||
key,
|
key,
|
||||||
@ -55,21 +56,24 @@ class RestClient(object):
|
|||||||
resp, body = self.http_obj.request(auth_url, 'POST',
|
resp, body = self.http_obj.request(auth_url, 'POST',
|
||||||
headers=headers, body=body)
|
headers=headers, body=body)
|
||||||
|
|
||||||
try:
|
if resp.status == 200:
|
||||||
auth_data = json.loads(body)['access']
|
try:
|
||||||
token = auth_data['token']['id']
|
auth_data = json.loads(body)['access']
|
||||||
endpoints = auth_data['serviceCatalog'][0]['endpoints']
|
token = auth_data['token']['id']
|
||||||
mgmt_url = endpoints[0]['publicURL']
|
endpoints = auth_data['serviceCatalog'][0]['endpoints']
|
||||||
|
mgmt_url = endpoints[0]['publicURL']
|
||||||
|
|
||||||
#TODO (dwalleck): This is a horrible stopgap.
|
#TODO (dwalleck): This is a horrible stopgap.
|
||||||
#Need to join strings more cleanly
|
#Need to join strings more cleanly
|
||||||
temp = mgmt_url.rsplit('/')
|
temp = mgmt_url.rsplit('/')
|
||||||
service_url = temp[0] + '//' + temp[2] + '/' + temp[3] + '/'
|
service_url = temp[0] + '//' + temp[2] + '/' + temp[3] + '/'
|
||||||
management_url = service_url + tenant_name
|
management_url = service_url + tenant_name
|
||||||
return token, management_url
|
return token, management_url
|
||||||
except KeyError:
|
except Exception, e:
|
||||||
print "Failed to authenticate user"
|
print "Failed to authenticate user: %s" % e
|
||||||
raise
|
raise
|
||||||
|
elif resp.status == 401:
|
||||||
|
raise exceptions.AuthenticationFailure(user=user, password=api_key)
|
||||||
|
|
||||||
def post(self, url, body, headers):
|
def post(self, url, body, headers):
|
||||||
return self.request('POST', url, headers, body)
|
return self.request('POST', url, headers, body)
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NovaConfig(object):
|
class NovaConfig(object):
|
||||||
@ -119,15 +123,24 @@ class EnvironmentConfig(object):
|
|||||||
class StormConfig(object):
|
class StormConfig(object):
|
||||||
"""Provides OpenStack configuration information."""
|
"""Provides OpenStack configuration information."""
|
||||||
|
|
||||||
_path = "etc/storm.conf"
|
def __init__(self, conf_dir, conf_file):
|
||||||
|
"""
|
||||||
|
Initialize a configuration from a conf directory and conf file.
|
||||||
|
|
||||||
def __init__(self, path=None):
|
:param conf_dir: Directory to look for config files
|
||||||
"""Initialize a configuration from a path."""
|
:param conf_file: Name of config file to use
|
||||||
self._conf = self.load_config(self._path)
|
"""
|
||||||
|
path = os.path.join(conf_dir, conf_file)
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
msg = "Config file %(path)s not found" % locals()
|
||||||
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
|
self._conf = self.load_config(path)
|
||||||
self.nova = NovaConfig(self._conf)
|
self.nova = NovaConfig(self._conf)
|
||||||
self.env = EnvironmentConfig(self._conf)
|
self.env = EnvironmentConfig(self._conf)
|
||||||
|
|
||||||
def load_config(self, path=None):
|
def load_config(self, path):
|
||||||
"""Read configuration from given path and return a config object."""
|
"""Read configuration from given path and return a config object."""
|
||||||
config = ConfigParser.SafeConfigParser()
|
config = ConfigParser.SafeConfigParser()
|
||||||
config.read(path)
|
config.read(path)
|
||||||
|
@ -16,3 +16,10 @@ class BadRequest(Exception):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self.message)
|
return repr(self.message)
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticationFailure(Exception):
|
||||||
|
msg = ("Authentication with user %(user)s and password "
|
||||||
|
"%(password)s failed.")
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.message = self.msg % kwargs
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
from storm.services.nova.json.images_client import ImagesClient
|
from storm.services.nova.json.images_client import ImagesClient
|
||||||
from storm.services.nova.json.flavors_client import FlavorsClient
|
from storm.services.nova.json.flavors_client import FlavorsClient
|
||||||
from storm.services.nova.json.servers_client import ServersClient
|
from storm.services.nova.json.servers_client import ServersClient
|
||||||
@ -7,38 +9,57 @@ import storm.config
|
|||||||
|
|
||||||
class Manager(object):
|
class Manager(object):
|
||||||
|
|
||||||
|
DEFAULT_CONFIG_DIR = os.path.join(
|
||||||
|
os.path.abspath(
|
||||||
|
os.path.dirname(
|
||||||
|
os.path.dirname(__file__))),
|
||||||
|
"etc")
|
||||||
|
|
||||||
|
DEFAULT_CONFIG_FILE = "storm.conf"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
Top level manager for all Openstack APIs
|
Top level manager for all Openstack APIs
|
||||||
"""
|
"""
|
||||||
|
# Environment variables override defaults...
|
||||||
self.config = storm.config.StormConfig()
|
config_dir = os.environ.get('TEMPEST_CONFIG_DIR',
|
||||||
|
self.DEFAULT_CONFIG_DIR)
|
||||||
|
config_file = os.environ.get('TEMPEST_CONFIG',
|
||||||
|
self.DEFAULT_CONFIG_FILE)
|
||||||
|
self.config = storm.config.StormConfig(config_dir, config_file)
|
||||||
self.auth_url = data_utils.build_url(self.config.nova.host,
|
self.auth_url = data_utils.build_url(self.config.nova.host,
|
||||||
self.config.nova.port,
|
self.config.nova.port,
|
||||||
self.config.nova.apiVer,
|
self.config.nova.apiVer,
|
||||||
self.config.nova.path)
|
self.config.nova.path)
|
||||||
|
|
||||||
if self.config.env.authentication == 'keystone_v2':
|
if self.config.env.authentication == 'keystone_v2':
|
||||||
self.servers_client = ServersClient(self.config.nova.username,
|
self.servers_client = ServersClient(self.config,
|
||||||
|
self.config.nova.username,
|
||||||
self.config.nova.api_key,
|
self.config.nova.api_key,
|
||||||
self.auth_url,
|
self.auth_url,
|
||||||
self.config.nova.tenant_name)
|
self.config.nova.tenant_name)
|
||||||
self.flavors_client = FlavorsClient(self.config.nova.username,
|
self.flavors_client = FlavorsClient(self.config,
|
||||||
|
self.config.nova.username,
|
||||||
self.config.nova.api_key,
|
self.config.nova.api_key,
|
||||||
self.auth_url,
|
self.auth_url,
|
||||||
self.config.nova.tenant_name)
|
self.config.nova.tenant_name)
|
||||||
self.images_client = ImagesClient(self.config.nova.username,
|
self.images_client = ImagesClient(self.config,
|
||||||
|
self.config.nova.username,
|
||||||
self.config.nova.api_key,
|
self.config.nova.api_key,
|
||||||
self.auth_url,
|
self.auth_url,
|
||||||
self.config.nova.tenant_name)
|
self.config.nova.tenant_name)
|
||||||
else:
|
else:
|
||||||
#Assuming basic/native authentication
|
#Assuming basic/native authentication
|
||||||
self.servers_client = ServersClient(self.config.nova.username,
|
self.servers_client = ServersClient(self.config,
|
||||||
|
self.config.nova.username,
|
||||||
self.config.nova.api_key,
|
self.config.nova.api_key,
|
||||||
self.auth_url)
|
self.auth_url)
|
||||||
self.flavors_client = FlavorsClient(self.config.nova.username,
|
self.flavors_client = FlavorsClient(self.config,
|
||||||
|
self.config.nova.username,
|
||||||
self.config.nova.api_key,
|
self.config.nova.api_key,
|
||||||
self.auth_url)
|
self.auth_url)
|
||||||
self.images_client = ImagesClient(self.config.nova.username,
|
self.images_client = ImagesClient(self.config,
|
||||||
|
self.config.nova.username,
|
||||||
|
self.config.nova.auth_url,
|
||||||
self.config.nova.api_key,
|
self.config.nova.api_key,
|
||||||
self.auth_url)
|
self.auth_url)
|
||||||
|
@ -5,8 +5,9 @@ import time
|
|||||||
|
|
||||||
class FlavorsClient(object):
|
class FlavorsClient(object):
|
||||||
|
|
||||||
def __init__(self, username, key, auth_url, tenant_name=None):
|
def __init__(self, config, username, key, auth_url, tenant_name=None):
|
||||||
self.client = rest_client.RestClient(username, key,
|
self.config = config
|
||||||
|
self.client = rest_client.RestClient(config, username, key,
|
||||||
auth_url, tenant_name)
|
auth_url, tenant_name)
|
||||||
|
|
||||||
def list_flavors(self, params=None):
|
def list_flavors(self, params=None):
|
||||||
|
@ -6,10 +6,10 @@ import time
|
|||||||
|
|
||||||
class ImagesClient(object):
|
class ImagesClient(object):
|
||||||
|
|
||||||
def __init__(self, username, key, auth_url, tenant_name=None):
|
def __init__(self, config, username, key, auth_url, tenant_name=None):
|
||||||
self.client = rest_client.RestClient(username, key,
|
self.config = config
|
||||||
|
self.client = rest_client.RestClient(config, username, key,
|
||||||
auth_url, tenant_name)
|
auth_url, tenant_name)
|
||||||
self.config = storm.config.StormConfig()
|
|
||||||
self.build_interval = self.config.nova.build_interval
|
self.build_interval = self.config.nova.build_interval
|
||||||
self.build_timeout = self.config.nova.build_timeout
|
self.build_timeout = self.config.nova.build_timeout
|
||||||
self.headers = {'Content-Type': 'application/json',
|
self.headers = {'Content-Type': 'application/json',
|
||||||
|
@ -7,10 +7,10 @@ import time
|
|||||||
|
|
||||||
class ServersClient(object):
|
class ServersClient(object):
|
||||||
|
|
||||||
def __init__(self, username, key, auth_url, tenant_name=None):
|
def __init__(self, config, username, key, auth_url, tenant_name=None):
|
||||||
self.client = rest_client.RestClient(username, key,
|
self.config = config
|
||||||
|
self.client = rest_client.RestClient(config, username, key,
|
||||||
auth_url, tenant_name)
|
auth_url, tenant_name)
|
||||||
self.config = storm.config.StormConfig()
|
|
||||||
self.build_interval = self.config.nova.build_interval
|
self.build_interval = self.config.nova.build_interval
|
||||||
self.build_timeout = self.config.nova.build_timeout
|
self.build_timeout = self.config.nova.build_timeout
|
||||||
self.headers = {'Content-Type': 'application/json',
|
self.headers = {'Content-Type': 'application/json',
|
||||||
|
@ -10,7 +10,7 @@ class FlavorsTest(unittest.TestCase):
|
|||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.os = openstack.Manager()
|
cls.os = openstack.Manager()
|
||||||
cls.client = cls.os.flavors_client
|
cls.client = cls.os.flavors_client
|
||||||
cls.config = storm.config.StormConfig()
|
cls.config = cls.os.config
|
||||||
cls.flavor_id = cls.config.env.flavor_ref
|
cls.flavor_id = cls.config.env.flavor_ref
|
||||||
|
|
||||||
@attr(type='smoke')
|
@attr(type='smoke')
|
||||||
|
@ -12,7 +12,7 @@ class ImagesMetadataTest(unittest.TestCase):
|
|||||||
cls.os = openstack.Manager()
|
cls.os = openstack.Manager()
|
||||||
cls.servers_client = cls.os.servers_client
|
cls.servers_client = cls.os.servers_client
|
||||||
cls.client = cls.os.images_client
|
cls.client = cls.os.images_client
|
||||||
cls.config = storm.config.StormConfig()
|
cls.config = cls.os.config
|
||||||
cls.image_ref = cls.config.env.image_ref
|
cls.image_ref = cls.config.env.image_ref
|
||||||
cls.flavor_ref = cls.config.env.flavor_ref
|
cls.flavor_ref = cls.config.env.flavor_ref
|
||||||
cls.ssh_timeout = cls.config.nova.ssh_timeout
|
cls.ssh_timeout = cls.config.nova.ssh_timeout
|
||||||
|
@ -4,18 +4,21 @@ from storm.common.utils.data_utils import rand_name
|
|||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
import storm.config
|
import storm.config
|
||||||
|
|
||||||
|
# Some module-level skip conditions
|
||||||
|
create_image_enabled = False
|
||||||
|
|
||||||
|
|
||||||
class ImagesTest(unittest.TestCase):
|
class ImagesTest(unittest.TestCase):
|
||||||
create_image_enabled = storm.config.StormConfig().env.create_image_enabled
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.os = openstack.Manager()
|
cls.os = openstack.Manager()
|
||||||
cls.client = cls.os.images_client
|
cls.client = cls.os.images_client
|
||||||
cls.servers_client = cls.os.servers_client
|
cls.servers_client = cls.os.servers_client
|
||||||
cls.config = storm.config.StormConfig()
|
cls.config = cls.os.config
|
||||||
cls.image_ref = cls.config.env.image_ref
|
cls.image_ref = cls.config.env.image_ref
|
||||||
cls.flavor_ref = cls.config.env.flavor_ref
|
cls.flavor_ref = cls.config.env.flavor_ref
|
||||||
|
create_image_enabled = cls.config.env.create_image_enabled
|
||||||
|
|
||||||
def _parse_image_id(self, image_ref):
|
def _parse_image_id(self, image_ref):
|
||||||
temp = image_ref.rsplit('/')
|
temp = image_ref.rsplit('/')
|
||||||
|
@ -4,19 +4,21 @@ import unittest2 as unittest
|
|||||||
import storm.config
|
import storm.config
|
||||||
from storm.common.utils.data_utils import rand_name
|
from storm.common.utils.data_utils import rand_name
|
||||||
|
|
||||||
|
# Some module-level skip conditions
|
||||||
|
resize_available = False
|
||||||
|
|
||||||
class ServerActionsTest(unittest.TestCase):
|
class ServerActionsTest(unittest.TestCase):
|
||||||
resize_available = storm.config.StormConfig().env.resize_available
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.os = openstack.Manager()
|
cls.os = openstack.Manager()
|
||||||
cls.client = cls.os.servers_client
|
cls.client = cls.os.servers_client
|
||||||
cls.config = storm.config.StormConfig()
|
cls.config = cls.os.config
|
||||||
cls.image_ref = cls.config.env.image_ref
|
cls.image_ref = cls.config.env.image_ref
|
||||||
cls.image_ref_alt = cls.config.env.image_ref_alt
|
cls.image_ref_alt = cls.config.env.image_ref_alt
|
||||||
cls.flavor_ref = cls.config.env.flavor_ref
|
cls.flavor_ref = cls.config.env.flavor_ref
|
||||||
cls.flavor_ref_alt = cls.config.env.flavor_ref_alt
|
cls.flavor_ref_alt = cls.config.env.flavor_ref_alt
|
||||||
|
resize_available = cls.config.env.resize_available
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.name = rand_name('server')
|
self.name = rand_name('server')
|
||||||
|
@ -11,7 +11,7 @@ class ServerDetailsTest(unittest.TestCase):
|
|||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.os = openstack.Manager()
|
cls.os = openstack.Manager()
|
||||||
cls.client = cls.os.servers_client
|
cls.client = cls.os.servers_client
|
||||||
cls.config = storm.config.StormConfig()
|
cls.config = cls.os.config
|
||||||
cls.image_ref = cls.config.env.image_ref
|
cls.image_ref = cls.config.env.image_ref
|
||||||
cls.flavor_ref = cls.config.env.flavor_ref
|
cls.flavor_ref = cls.config.env.flavor_ref
|
||||||
cls.image_ref_alt = cls.config.env.image_ref_alt
|
cls.image_ref_alt = cls.config.env.image_ref_alt
|
||||||
|
@ -11,7 +11,7 @@ class ServerMetadataTest(unittest.TestCase):
|
|||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.os = openstack.Manager()
|
cls.os = openstack.Manager()
|
||||||
cls.client = cls.os.servers_client
|
cls.client = cls.os.servers_client
|
||||||
cls.config = storm.config.StormConfig()
|
cls.config = cls.os.config
|
||||||
cls.image_ref = cls.config.env.image_ref
|
cls.image_ref = cls.config.env.image_ref
|
||||||
cls.flavor_ref = cls.config.env.flavor_ref
|
cls.flavor_ref = cls.config.env.flavor_ref
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ class ServersTest(unittest.TestCase):
|
|||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.os = openstack.Manager()
|
cls.os = openstack.Manager()
|
||||||
cls.client = cls.os.servers_client
|
cls.client = cls.os.servers_client
|
||||||
cls.config = storm.config.StormConfig()
|
cls.config = cls.os.config
|
||||||
cls.image_ref = cls.config.env.image_ref
|
cls.image_ref = cls.config.env.image_ref
|
||||||
cls.flavor_ref = cls.config.env.flavor_ref
|
cls.flavor_ref = cls.config.env.flavor_ref
|
||||||
cls.ssh_timeout = cls.config.nova.ssh_timeout
|
cls.ssh_timeout = cls.config.nova.ssh_timeout
|
||||||
|
@ -14,7 +14,7 @@ class ServersNegativeTest(unittest.TestCase):
|
|||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.os = openstack.Manager()
|
cls.os = openstack.Manager()
|
||||||
cls.client = cls.os.servers_client
|
cls.client = cls.os.servers_client
|
||||||
cls.config = storm.config.StormConfig()
|
cls.config = cls.os.config
|
||||||
cls.image_ref = cls.config.env.image_ref
|
cls.image_ref = cls.config.env.image_ref
|
||||||
cls.flavor_ref = cls.config.env.flavor_ref
|
cls.flavor_ref = cls.config.env.flavor_ref
|
||||||
cls.ssh_timeout = cls.config.nova.ssh_timeout
|
cls.ssh_timeout = cls.config.nova.ssh_timeout
|
||||||
|
Loading…
x
Reference in New Issue
Block a user