Merge "Remove the Glance HTTP client. Use the common Rest Client instead."
diff --git a/HACKING.rst b/HACKING.rst
index d3ac5c6..480650c 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -21,6 +21,7 @@
- [T111] Check that service client names of DELETE should be consistent
- [T112] Check that tempest.lib should not import local tempest code
- [T113] Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
+- [T114] Check that tempest.lib does not use tempest config
- [N322] Method's default argument shouldn't be mutable
Test Data/Configuration
@@ -132,16 +133,18 @@
Set-up is split in a series of steps (setup stages), which can be overwritten
by test classes. Set-up stages are:
- - `skip_checks`
- - `setup_credentials`
- - `setup_clients`
- - `resource_setup`
+
+- `skip_checks`
+- `setup_credentials`
+- `setup_clients`
+- `resource_setup`
Tear-down is also split in a series of steps (teardown stages), which are
stacked for execution only if the corresponding setup stage had been
reached during the setup phase. Tear-down stages are:
- - `clear_credentials` (defined in the base test class)
- - `resource_cleanup`
+
+- `clear_credentials` (defined in the base test class)
+- `resource_cleanup`
Skipping Tests
--------------
diff --git a/README.rst b/README.rst
index 650a1ed..725a890 100644
--- a/README.rst
+++ b/README.rst
@@ -25,8 +25,7 @@
discover features of a cloud incorrectly, and give people an
incorrect assessment of their cloud. Explicit is always better.
- Tempest uses OpenStack public interfaces. Tests in Tempest should
- only touch public interfaces, API calls (native or 3rd party),
- or libraries.
+ only touch public OpenStack APIs.
- Tempest should not touch private or implementation specific
interfaces. This means not directly going to the database, not
directly hitting the hypervisors, not testing extensions not
@@ -163,7 +162,7 @@
Tempest also has a set of unit tests which test the Tempest code itself. These
tests can be run by specifying the test discovery path::
- $> OS_TEST_PATH=./tempest/tests testr run --parallel
+ $ OS_TEST_PATH=./tempest/tests testr run --parallel
By setting OS_TEST_PATH to ./tempest/tests it specifies that test discover
should only be run on the unit test directory. The default value of OS_TEST_PATH
@@ -214,8 +213,8 @@
To start you need to create a configuration file. The easiest way to create a
configuration file is to generate a sample in the ``etc/`` directory ::
- $> cd $TEMPEST_ROOT_DIR
- $> oslo-config-generator --config-file \
+ $ cd $TEMPEST_ROOT_DIR
+ $ oslo-config-generator --config-file \
etc/config-generator.tempest.conf \
--output-file etc/tempest.conf
@@ -237,21 +236,21 @@
After setting up your configuration file, you can execute the set of Tempest
tests by using ``testr`` ::
- $> testr run --parallel
+ $ testr run --parallel
To run one single test serially ::
- $> testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
+ $ testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
Alternatively, you can use the run_tempest.sh script which will create a venv
and run the tests or use tox to do the same. Tox also contains several existing
job configurations. For example::
- $> tox -efull
+ $ tox -efull
which will run the same set of tests as the OpenStack gate. (it's exactly how
the gate invokes Tempest) Or::
- $> tox -esmoke
+ $ tox -esmoke
to run the tests tagged as smoke.
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index d610dc5..9a7ce15 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -348,11 +348,14 @@
service catalog should be in a standard format (which is going to be
standardized at the keystone level).
Tempest expects URLs in the Service catalog in the following format:
- * ``http://example.com:1234/<version-info>``
+
+ * ``http://example.com:1234/<version-info>``
+
Examples:
- * Good - ``http://example.com:1234/v2.0``
- * Wouldn’t work - ``http://example.com:1234/xyz/v2.0/``
- (adding prefix/suffix around version etc)
+
+ * Good - ``http://example.com:1234/v2.0``
+ * Wouldn’t work - ``http://example.com:1234/xyz/v2.0/``
+ (adding prefix/suffix around version etc)
Service Feature Configuration
-----------------------------
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index fc05b12..3568470 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -24,6 +24,7 @@
Those should be defined under respective section of each service.
For example::
+
[compute]
min_microversion = None
max_microversion = latest
@@ -159,7 +160,8 @@
Notes about Compute Microversion Tests
-"""""""""""""""""""""""""""""""""""
+""""""""""""""""""""""""""""""""""""""
+
Some of the compute Microversion tests have been already implemented
with the Microversion testing framework. So for further tests only
step 4 is needed.
diff --git a/releasenotes/notes/remove-integrated-horizon-bb57551c1e5f5be3.yaml b/releasenotes/notes/remove-integrated-horizon-bb57551c1e5f5be3.yaml
new file mode 100644
index 0000000..294f6d9
--- /dev/null
+++ b/releasenotes/notes/remove-integrated-horizon-bb57551c1e5f5be3.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+ - The integrated dashboard scenario test has been
+ removed and is now in a separate tempest plugin
+ tempest-horizon. The removed test coverage can be
+ used by installing tempest-horizon on the server
+ where you run tempest.
diff --git a/requirements.txt b/requirements.txt
index d567082..f41d2a7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,17 +9,17 @@
netaddr!=0.7.16,>=0.7.12 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
pyOpenSSL>=0.14 # Apache-2.0
-oslo.concurrency>=3.5.0 # Apache-2.0
+oslo.concurrency>=3.8.0 # Apache-2.0
oslo.config>=3.9.0 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
oslo.log>=1.14.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.utils>=3.5.0 # Apache-2.0
six>=1.9.0 # MIT
-fixtures<2.0,>=1.3.1 # Apache-2.0/BSD
+fixtures>=3.0.0 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
PyYAML>=3.1.0 # MIT
stevedore>=1.10.0 # Apache-2.0
PrettyTable<0.8,>=0.7 # BSD
-os-testr>=0.4.1 # Apache-2.0
+os-testr>=0.7.0 # Apache-2.0
urllib3>=1.15.1 # MIT
diff --git a/setup.cfg b/setup.cfg
index 0ddb898..24e0214 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
-home-page = http://www.openstack.org/
+home-page = http://docs.openstack.org/developer/tempest/
classifier =
Intended Audience :: Information Technology
Intended Audience :: System Administrators
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index 8986db8..a4ed8dc 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -59,7 +59,9 @@
return True
except e.InvalidHTTPResponseBody:
return False
- test.call_until_true(is_valid, duration, 1)
+ self.assertEqual(test.call_until_true(is_valid, duration, 1), True,
+ "%s not return valid response in %s secs" % (
+ func.__name__, duration))
return self.resp
@test.idempotent_id('062c8ae9-9912-4249-8b51-e38d664e926e')
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index c05045e..07423ff 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -204,10 +204,6 @@
@test.idempotent_id('1678d144-ed74-43f8-8e57-ab10dbf9b3c2')
@testtools.skipUnless(CONF.service_available.neutron,
'Neutron service must be available.')
- # The below skipUnless should be removed once Kilo-eol happens.
- @testtools.skipUnless(CONF.compute_feature_enabled.
- allow_duplicate_networks,
- 'Duplicate networks must be allowed')
def test_verify_duplicate_network_nics(self):
# Verify that server creation does not fail when more than one nic
# is created on the same network.
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index b9765c8..a3b0a82 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -153,6 +153,7 @@
# Create rule for icmp protocol with invalid ports
states = [(1, 256, 'Invalid value for ICMP code'),
+ (-1, 25, 'Invalid value'),
(None, 6, 'ICMP type (port-range-min) is missing'),
(300, 1, 'Invalid value for ICMP type')]
for pmin, pmax, msg in states:
diff --git a/tempest/api/volume/test_qos.py b/tempest/api/volume/admin/test_qos.py
similarity index 100%
rename from tempest/api/volume/test_qos.py
rename to tempest/api/volume/admin/test_qos.py
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index b2e52bb..cf05f5f 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -30,6 +30,11 @@
super(BaseVolumeQuotasAdminV2TestJSON, cls).setup_credentials()
cls.demo_tenant_id = cls.os.credentials.tenant_id
+ def _delete_volume(self, volume_id):
+ # Delete the specified volume using admin credentials
+ self.admin_volume_client.delete_volume(volume_id)
+ self.admin_volume_client.wait_for_resource_deletion(volume_id)
+
@test.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
def test_list_quotas(self):
quotas = (self.quotas_client.show_quota_set(self.demo_tenant_id)
@@ -83,8 +88,7 @@
self.demo_tenant_id)['quota_set']
volume = self.create_volume()
- self.addCleanup(self.admin_volume_client.delete_volume,
- volume['id'])
+ self.addCleanup(self._delete_volume, volume['id'])
new_quota_usage = self.quotas_client.show_quota_usage(
self.demo_tenant_id)['quota_set']
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index b09cd2c..1289297 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -13,11 +13,15 @@
# License for the specific language governing permissions and limitations
# under the License.
+import base64
+import six
+
+from oslo_serialization import jsonutils as json
+
from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
-from tempest.lib import decorators
from tempest import test
CONF = config.CONF
@@ -41,6 +45,20 @@
self.backups_adm_client.delete_backup(backup_id)
self.backups_adm_client.wait_for_backup_deletion(backup_id)
+ def _decode_url(self, backup_url):
+ return json.loads(base64.decodestring(backup_url))
+
+ def _encode_backup(self, backup):
+ retval = json.dumps(backup)
+ if six.PY3:
+ retval = retval.encode('utf-8')
+ return base64.encodestring(retval)
+
+ def _modify_backup_url(self, backup_url, changes):
+ backup = self._decode_url(backup_url)
+ backup.update(changes)
+ return self._encode_backup(backup)
+
@test.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
def test_volume_backup_create_get_detailed_list_restore_delete(self):
# Create backup
@@ -78,9 +96,13 @@
waiters.wait_for_volume_status(self.admin_volume_client,
restore['volume_id'], 'available')
- @decorators.skip_because(bug='1455043')
@test.idempotent_id('a99c54a1-dd80-4724-8a13-13bf58d4068d')
def test_volume_backup_export_import(self):
+ """Test backup export import functionality.
+
+ Cinder allows exporting DB backup information through its API so it can
+ be imported back in case of a DB loss.
+ """
# Create backup
backup_name = data_utils.rand_name('Backup')
backup = (self.backups_adm_client.create_backup(
@@ -99,25 +121,40 @@
'cinder.backup.drivers'))
self.assertIsNotNone(export_backup['backup_url'])
+ # NOTE(geguileo): Backups are imported with the same backup id
+ # (important for incremental backups among other things), so we cannot
+ # import the exported backup information as it is, because that Backup
+ # ID already exists. So we'll fake the data by changing the backup id
+ # in the exported backup DB info we have retrieved before importing it
+ # back.
+ new_id = data_utils.rand_uuid()
+ new_url = self._modify_backup_url(
+ export_backup['backup_url'], {'id': new_id})
+
# Import Backup
import_backup = self.backups_adm_client.import_backup(
backup_service=export_backup['backup_service'],
- backup_url=export_backup['backup_url'])['backup']
- self.addCleanup(self._delete_backup, import_backup['id'])
+ backup_url=new_url)['backup']
+
+ # NOTE(geguileo): We delete both backups, but only one of those
+ # deletions will delete data from the backup back-end because they
+ # were both pointing to the same backend data.
+ self.addCleanup(self._delete_backup, new_id)
self.assertIn("id", import_backup)
+ self.assertEqual(new_id, import_backup['id'])
self.backups_adm_client.wait_for_backup_status(import_backup['id'],
'available')
# Verify Import Backup
backups = self.backups_adm_client.list_backups(detail=True)['backups']
- self.assertIn(import_backup['id'], [b['id'] for b in backups])
+ self.assertIn(new_id, [b['id'] for b in backups])
# Restore backup
- restore = (self.backups_adm_client.restore_backup(import_backup['id'])
- ['restore'])
+ restore = self.backups_adm_client.restore_backup(
+ backup['id'])['restore']
self.addCleanup(self.admin_volume_client.delete_volume,
restore['volume_id'])
- self.assertEqual(import_backup['id'], restore['backup_id'])
+ self.assertEqual(backup['id'], restore['backup_id'])
waiters.wait_for_volume_status(self.admin_volume_client,
restore['volume_id'], 'available')
diff --git a/tempest/common/cred_client.py b/tempest/common/cred_client.py
index 37c9727..b23bc6f 100644
--- a/tempest/common/cred_client.py
+++ b/tempest/common/cred_client.py
@@ -170,6 +170,29 @@
user['id'],
role['id'])
+ def assign_user_role_on_domain(self, user, role_name, domain=None):
+ """Assign the specified role on a domain
+
+ :param user: a user dict
+ :param role_name: name of the role to be assigned
+ :param domain: (optional) The domain to assign the role on. If not
+ specified the default domain of cred_client
+ """
+ # NOTE(andreaf) This method is very specific to the v3 case, and
+ # because of that it's not defined in the parent class.
+ if domain is None:
+ domain = self.creds_domain
+ role = self._check_role_exists(role_name)
+ if not role:
+ msg = 'No "%s" role found' % role_name
+ raise lib_exc.NotFound(msg)
+ try:
+ self.roles_client.assign_user_role_on_domain(
+ domain['id'], user['id'], role['id'])
+ except lib_exc.Conflict:
+ LOG.debug("Role %s already assigned on domain %s for user %s",
+ role['id'], domain['id'], user['id'])
+
def get_creds_client(identity_client,
projects_client,
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 82db9cc..286e01f 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -73,8 +73,8 @@
# the test should be skipped else it would fail.
identity_version = identity_version or CONF.identity.auth_version
if CONF.auth.use_dynamic_credentials or force_tenant_isolation:
- admin_creds = get_configured_credentials(
- 'identity_admin', fill_in=True, identity_version=identity_version)
+ admin_creds = get_configured_admin_credentials(
+ fill_in=True, identity_version=identity_version)
return dynamic_creds.DynamicCredentialProvider(
name=name,
network_resources=network_resources,
@@ -111,8 +111,8 @@
is_admin = False
else:
try:
- get_configured_credentials('identity_admin', fill_in=False,
- identity_version=identity_version)
+ get_configured_admin_credentials(fill_in=False,
+ identity_version=identity_version)
except exceptions.InvalidConfiguration:
is_admin = False
return is_admin
@@ -163,39 +163,32 @@
# Read credentials from configuration, builds a Credentials object
# based on the specified or configured version
-def get_configured_credentials(credential_type, fill_in=True,
- identity_version=None):
+def get_configured_admin_credentials(fill_in=True, identity_version=None):
identity_version = identity_version or CONF.identity.auth_version
if identity_version not in ('v2', 'v3'):
raise exceptions.InvalidConfiguration(
'Unsupported auth version: %s' % identity_version)
- if credential_type not in CREDENTIAL_TYPES:
- raise exceptions.InvalidCredentials()
- conf_attributes = ['username', 'password', 'project_name']
+ conf_attributes = ['username', 'password',
+ 'project_name']
if identity_version == 'v3':
conf_attributes.append('domain_name')
# Read the parts of credentials from config
params = DEFAULT_PARAMS.copy()
- section, prefix = CREDENTIAL_TYPES[credential_type]
for attr in conf_attributes:
- _section = getattr(CONF, section)
- if prefix is None:
- params[attr] = getattr(_section, attr)
- else:
- params[attr] = getattr(_section, prefix + "_" + attr)
+ params[attr] = getattr(CONF.auth, 'admin_' + attr)
# Build and validate credentials. We are reading configured credentials,
# so validate them even if fill_in is False
credentials = get_credentials(fill_in=fill_in,
identity_version=identity_version, **params)
if not fill_in:
if not credentials.is_valid():
- msg = ("The %s credentials are incorrectly set in the config file."
- " Double check that all required values are assigned" %
- credential_type)
- raise exceptions.InvalidConfiguration(msg)
+ msg = ("The admin credentials are incorrectly set in the config "
+ "file for identity version %s. Double check that all "
+ "required values are assigned.")
+ raise exceptions.InvalidConfiguration(msg % identity_version)
return credentials
@@ -223,19 +216,10 @@
# === Credential / client managers
-class ConfiguredUserManager(clients.Manager):
- """Manager that uses user credentials for its managed client objects"""
-
- def __init__(self, service=None):
- super(ConfiguredUserManager, self).__init__(
- credentials=get_configured_credentials('user'),
- service=service)
-
-
class AdminManager(clients.Manager):
"""Manager that uses admin credentials for its managed client objects"""
def __init__(self, service=None):
super(AdminManager, self).__init__(
- credentials=get_configured_credentials('identity_admin'),
+ credentials=get_configured_admin_credentials(),
service=service)
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index a63af38..3a3e3c2 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -122,7 +122,9 @@
project = self.creds_client.create_project(
name=project_name, description=project_desc)
- username = data_utils.rand_name(root) + suffix
+ # NOTE(andreaf) User and project can be distinguished from the context,
+ # having the same ID in both makes it easier to match them and debug.
+ username = project_name
user_password = data_utils.rand_password()
email = data_utils.rand_name(root) + suffix + "@example.com"
user = self.creds_client.create_user(
@@ -134,6 +136,9 @@
self.creds_client.assign_user_role(user, project,
self.admin_role)
role_assigned = True
+ if self.identity_version == 'v3':
+ self.creds_client.assign_user_role_on_domain(
+ user, CONF.identity.admin_role)
# Add roles specified in config file
for conf_role in CONF.auth.tempest_roles:
self.creds_client.assign_user_role(user, project, conf_role)
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index 7350222..42c69d5 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -249,6 +249,9 @@
raise lib_exc.InvalidCredentials(
"Account file %s doesn't exist" % self.test_accounts_file)
useable_hashes = self._get_match_hash_list(roles)
+ if len(useable_hashes) == 0:
+ msg = 'No users configured for type/roles %s' % roles
+ raise lib_exc.InvalidCredentials(msg)
free_hash = self._get_free_hash(useable_hashes)
clean_creds = self._sanitize_creds(
self.hash_dict['creds'][free_hash])
diff --git a/tempest/common/utils/file_utils.py b/tempest/common/utils/file_utils.py
deleted file mode 100644
index 43083f4..0000000
--- a/tempest/common/utils/file_utils.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-def have_effective_read_access(path):
- try:
- fh = open(path, "rb")
- except IOError:
- return False
- fh.close()
- return True
diff --git a/tempest/config.py b/tempest/config.py
index f5125b5..3810ceb 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -370,14 +370,6 @@
'encrypted volume to a running server instance? This may '
'depend on the combination of compute_driver in nova and '
'the volume_driver(s) in cinder.'),
- # TODO(mriedem): Remove allow_duplicate_networks once kilo-eol happens
- # since the option was removed from nova in Liberty and is the default
- # behavior starting in Liberty.
- cfg.BoolOpt('allow_duplicate_networks',
- default=False,
- help='Does the test environment support creating instances '
- 'with multiple ports on the same network? This is only '
- 'valid when using Neutron.'),
cfg.BoolOpt('config_drive',
default=True,
help='Enable special configuration drive with metadata.'),
@@ -849,21 +841,6 @@
help="Value must match heat configuration of the same name."),
]
-
-dashboard_group = cfg.OptGroup(name="dashboard",
- title="Dashboard options")
-
-DashboardGroup = [
- cfg.StrOpt('dashboard_url',
- default='http://localhost/',
- help="Where the dashboard can be found"),
- cfg.StrOpt('login_url',
- default='http://localhost/auth/login/',
- help="Login page for the dashboard",
- deprecated_for_removal=True),
-]
-
-
data_processing_group = cfg.OptGroup(name="data-processing",
title="Data Processing options")
@@ -994,9 +971,6 @@
cfg.BoolOpt('heat',
default=False,
help="Whether or not Heat is expected to be available"),
- cfg.BoolOpt('horizon',
- default=True,
- help="Whether or not Horizon is expected to be available"),
cfg.BoolOpt('sahara',
default=False,
help="Whether or not Sahara is expected to be available"),
@@ -1139,7 +1113,6 @@
(object_storage_feature_group, ObjectStoreFeaturesGroup),
(database_group, DatabaseGroup),
(orchestration_group, OrchestrationGroup),
- (dashboard_group, DashboardGroup),
(data_processing_group, DataProcessingGroup),
(data_processing_feature_group, DataProcessingFeaturesGroup),
(stress_group, StressGroup),
@@ -1207,7 +1180,6 @@
'object-storage-feature-enabled']
self.database = _CONF.database
self.orchestration = _CONF.orchestration
- self.dashboard = _CONF.dashboard
self.data_processing = _CONF['data-processing']
self.data_processing_feature_enabled = _CONF[
'data-processing-feature-enabled']
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index aff9dee..09106d1 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -256,6 +256,23 @@
yield (0, msg)
+def dont_use_config_in_tempest_lib(logical_line, filename):
+ """Check that tempest.lib doesn't use tempest config
+
+ T114
+ """
+
+ if 'tempest/lib/' not in filename:
+ return
+
+ if ('tempest.config' in logical_line
+ or 'from tempest import config' in logical_line
+ or 'oslo_config' in logical_line):
+ msg = ('T114: tempest.lib can not have any dependency on tempest '
+ 'config.')
+ yield(0, msg)
+
+
def factory(register):
register(import_no_clients_in_api_and_scenario_tests)
register(scenario_tests_need_service_tags)
@@ -268,4 +285,5 @@
register(get_resources_on_service_clients)
register(delete_resources_on_service_clients)
register(dont_import_local_tempest_into_lib)
+ register(dont_use_config_in_tempest_lib)
register(use_rand_uuid_instead_of_uuid4)
diff --git a/tempest/lib/api_schema/response/compute/v2_1/images.py b/tempest/lib/api_schema/response/compute/v2_1/images.py
index daab898..b0f1934 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/images.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/images.py
@@ -77,7 +77,7 @@
'properties': {
'id': {'type': 'string'},
'links': image_links,
- 'name': {'type': 'string'}
+ 'name': {'type': ['string', 'null']}
},
'additionalProperties': False,
'required': ['id', 'links', 'name']
diff --git a/tempest/lib/cli/base.py b/tempest/lib/cli/base.py
index 9460bf4..a43d002 100644
--- a/tempest/lib/cli/base.py
+++ b/tempest/lib/cli/base.py
@@ -119,7 +119,7 @@
:param merge_stderr: if True the stderr buffer is merged into stdout
:type merge_stderr: boolean
"""
- flags += ' --endpoint-type %s' % endpoint_type
+ flags += ' --os-endpoint-type %s' % endpoint_type
return self.cmd_with_auth(
'nova', action, flags, params, fail_ok, merge_stderr)
@@ -247,7 +247,7 @@
:param merge_stderr: if True the stderr buffer is merged into stdout
:type merge_stderr: boolean
"""
- flags += ' --os-endpoint-type %s' % endpoint_type
+ flags += ' --endpoint-type %s' % endpoint_type
return self.cmd_with_auth(
'cinder', action, flags, params, fail_ok, merge_stderr)
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 179db17..627143d 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -221,6 +221,10 @@
:raises exceptions.InvalidHttpSuccessCode: if the read code isn't an
expected http success code
"""
+ if not isinstance(read_code, int):
+ raise TypeError("'read_code' must be an int instead of (%s)"
+ % type(read_code))
+
assert_msg = ("This function only allowed to use for HTTP status"
"codes which explicitly defined in the RFC 7231 & 4918."
"{0} is not a defined Success Code!"
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index e78e624..6ed99b4 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -69,12 +69,11 @@
"or False") % attr
def __call__(self, func):
+ @functools.wraps(func)
def _skipper(*args, **kw):
"""Wrapped skipper function."""
testobj = args[0]
if not getattr(testobj, self.attr, False):
raise testtools.TestCase.skipException(self.message)
func(*args, **kw)
- _skipper.__name__ = func.__name__
- _skipper.__doc__ = func.__doc__
return _skipper
diff --git a/tempest/lib/services/compute/base_compute_client.py b/tempest/lib/services/compute/base_compute_client.py
index a387b85..433c94c 100644
--- a/tempest/lib/services/compute/base_compute_client.py
+++ b/tempest/lib/services/compute/base_compute_client.py
@@ -67,11 +67,13 @@
:param schema_versions_info: List of dict which provides schema
information with range of valid versions.
- Example -
- schema_versions_info = [
- {'min': None, 'max': '2.1', 'schema': schemav21},
- {'min': '2.2', 'max': '2.9', 'schema': schemav22},
- {'min': '2.10', 'max': None, 'schema': schemav210}]
+
+ Example::
+
+ schema_versions_info = [
+ {'min': None, 'max': '2.1', 'schema': schemav21},
+ {'min': '2.2', 'max': '2.9', 'schema': schemav22},
+ {'min': '2.10', 'max': None, 'schema': schemav210}]
"""
schema = None
version = api_version_request.APIVersionRequest(COMPUTE_MICROVERSION)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 65677a0..7389722 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -1069,10 +1069,11 @@
security_groups_client=None):
"""Create loginable security group rule
- These rules are intended to permit inbound ssh and icmp
- traffic from all sources, so no group_id is provided.
- Setting a group_id would only permit traffic from ports
- belonging to the same security group.
+ This function will create:
+ 1. egress and ingress tcp port 22 allow rule in order to allow ssh
+ access for ipv4.
+ 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
+ 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
"""
if security_group_rules_client is None:
diff --git a/tempest/scenario/test_dashboard_basic_ops.py b/tempest/scenario/test_dashboard_basic_ops.py
deleted file mode 100644
index 5d4f7b3..0000000
--- a/tempest/scenario/test_dashboard_basic_ops.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from six.moves import html_parser as HTMLParser
-from six.moves.urllib import parse
-from six.moves.urllib import request
-
-from tempest import config
-from tempest.scenario import manager
-from tempest import test
-
-CONF = config.CONF
-
-
-class HorizonHTMLParser(HTMLParser.HTMLParser):
- csrf_token = None
- region = None
- login = None
-
- def _find_name(self, attrs, name):
- for attrpair in attrs:
- if attrpair[0] == 'name' and attrpair[1] == name:
- return True
- return False
-
- def _find_value(self, attrs):
- for attrpair in attrs:
- if attrpair[0] == 'value':
- return attrpair[1]
- return None
-
- def _find_attr_value(self, attrs, attr_name):
- for attrpair in attrs:
- if attrpair[0] == attr_name:
- return attrpair[1]
- return None
-
- def handle_starttag(self, tag, attrs):
- if tag == 'input':
- if self._find_name(attrs, 'csrfmiddlewaretoken'):
- self.csrf_token = self._find_value(attrs)
- if self._find_name(attrs, 'region'):
- self.region = self._find_value(attrs)
- if tag == 'form':
- self.login = self._find_attr_value(attrs, 'action')
-
-
-class TestDashboardBasicOps(manager.ScenarioTest):
-
- """The test suite for dashboard basic operations
-
- This is a basic scenario test:
- * checks that the login page is available
- * logs in as a regular user
- * checks that the user home page loads without error
- """
-
- @classmethod
- def skip_checks(cls):
- super(TestDashboardBasicOps, cls).skip_checks()
- if not CONF.service_available.horizon:
- raise cls.skipException("Horizon support is required")
-
- @classmethod
- def setup_credentials(cls):
- cls.set_network_resources()
- super(TestDashboardBasicOps, cls).setup_credentials()
-
- def check_login_page(self):
- response = request.urlopen(CONF.dashboard.dashboard_url)
- self.assertIn("id_username", response.read())
-
- def user_login(self, username, password):
- self.opener = request.build_opener(request.HTTPCookieProcessor())
- response = self.opener.open(CONF.dashboard.dashboard_url).read()
-
- # Grab the CSRF token and default region
- parser = HorizonHTMLParser()
- parser.feed(response)
-
- # construct login url for dashboard, discovery accommodates non-/ web
- # root for dashboard
- login_url = parse.urljoin(CONF.dashboard.dashboard_url, parser.login)
-
- # Prepare login form request
- req = request.Request(login_url)
- req.add_header('Content-type', 'application/x-www-form-urlencoded')
- req.add_header('Referer', CONF.dashboard.dashboard_url)
-
- # Pass the default domain name regardless of the auth version in order
- # to test the scenario of when horizon is running with keystone v3
- params = {'username': username,
- 'password': password,
- 'region': parser.region,
- 'domain': CONF.auth.default_credentials_domain_name,
- 'csrfmiddlewaretoken': parser.csrf_token}
- self.opener.open(req, parse.urlencode(params))
-
- def check_home_page(self):
- response = self.opener.open(CONF.dashboard.dashboard_url)
- self.assertIn('Overview', response.read())
-
- @test.idempotent_id('4f8851b1-0e69-482b-b63b-84c6e76f6c80')
- @test.services('dashboard')
- def test_basic_scenario(self):
- creds = self.os.credentials
- self.check_login_page()
- self.user_login(creds.username, creds.password)
- self.check_home_page()
diff --git a/tempest/services/baremetal/base.py b/tempest/services/baremetal/base.py
index 6e24801..2bdd092 100644
--- a/tempest/services/baremetal/base.py
+++ b/tempest/services/baremetal/base.py
@@ -111,7 +111,7 @@
uri += "?%s" % urllib.urlencode(kwargs)
resp, body = self.get(uri)
- self.expected_success(200, resp['status'])
+ self.expected_success(200, resp.status)
return resp, self.deserialize(body)
@@ -127,7 +127,7 @@
else:
uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
resp, body = self.get(uri)
- self.expected_success(200, resp['status'])
+ self.expected_success(200, resp.status)
return resp, self.deserialize(body)
@@ -145,7 +145,7 @@
uri = self._get_uri(resource)
resp, body = self.post(uri, body=body)
- self.expected_success(201, resp['status'])
+ self.expected_success(201, resp.status)
return resp, self.deserialize(body)
@@ -160,7 +160,7 @@
uri = self._get_uri(resource, uuid)
resp, body = self.delete(uri)
- self.expected_success(204, resp['status'])
+ self.expected_success(204, resp.status)
return resp, body
def _patch_request(self, resource, uuid, patch_object):
@@ -176,7 +176,7 @@
patch_body = json.dumps(patch_object)
resp, body = self.patch(uri, body=patch_body)
- self.expected_success(200, resp['status'])
+ self.expected_success(200, resp.status)
return resp, self.deserialize(body)
@handle_errors
@@ -201,5 +201,5 @@
put_body = json.dumps(put_object)
resp, body = self.put(uri, body=put_body)
- self.expected_success(202, resp['status'])
+ self.expected_success([202, 204], resp.status)
return resp, body
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index 382b851..2beaaa9 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -135,10 +135,13 @@
break
if skip:
break
+ # TODO(andreaf) This has to be reworked to use the credential
+ # provider interface. For now only tests marked as 'use_admin' will
+ # work.
if test.get('use_admin', False):
manager = admin_manager
else:
- manager = credentials.ConfiguredUserManager()
+ raise NotImplemented('Non admin tests are not supported')
for p_number in moves.xrange(test.get('threads', default_thread_num)):
if test.get('use_isolated_tenants', False):
username = data_utils.rand_name("stress_user")
diff --git a/tempest/test.py b/tempest/test.py
index aefe1a9..d31c509 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -77,7 +77,6 @@
'network': True,
'identity': True,
'object_storage': CONF.service_available.swift,
- 'dashboard': CONF.service_available.horizon,
'data_processing': CONF.service_available.sahara,
'database': CONF.service_available.trove
}
@@ -92,8 +91,8 @@
"""
def decorator(f):
services = ['compute', 'image', 'baremetal', 'volume', 'orchestration',
- 'network', 'identity', 'object_storage', 'dashboard',
- 'data_processing', 'database']
+ 'network', 'identity', 'object_storage', 'data_processing',
+ 'database']
for service in args:
if service not in services:
raise exceptions.InvalidServiceTag('%s is not a valid '
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index 8d4f33b..f025418 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -21,15 +21,19 @@
from tempest import config
from tempest import exceptions
from tempest.lib.common import rest_client
-from tempest.lib.services.identity.v2 import token_client as json_token_client
-from tempest.services.identity.v2.json import identity_client as \
- json_iden_client
-from tempest.services.identity.v2.json import roles_client as \
- json_roles_client
+from tempest.lib.services.identity.v2 import token_client as v2_token_client
+from tempest.lib.services.identity.v3 import token_client as v3_token_client
+from tempest.services.identity.v2.json import identity_client as v2_iden_client
+from tempest.services.identity.v2.json import roles_client as v2_roles_client
from tempest.services.identity.v2.json import tenants_client as \
- json_tenants_client
-from tempest.services.identity.v2.json import users_client as \
- json_users_client
+ v2_tenants_client
+from tempest.services.identity.v2.json import users_client as v2_users_client
+from tempest.services.identity.v3.json import domains_client
+from tempest.services.identity.v3.json import identity_client as v3_iden_client
+from tempest.services.identity.v3.json import projects_client as \
+ v3_projects_client
+from tempest.services.identity.v3.json import roles_client as v3_roles_client
+from tempest.services.identity.v3.json import users_clients as v3_users_client
from tempest.services.network.json import routers_client
from tempest.tests import base
from tempest.tests import fake_config
@@ -43,13 +47,24 @@
'identity_version': 'v2',
'admin_role': 'admin'}
+ token_client = v2_token_client
+ iden_client = v2_iden_client
+ roles_client = v2_roles_client
+ tenants_client = v2_tenants_client
+ users_client = v2_users_client
+ token_client_class = token_client.TokenClient
+ fake_response = fake_identity._fake_v2_response
+ assign_role_on_project = 'assign_user_role'
+ tenants_client_class = tenants_client.TenantsClient
+ delete_tenant = 'delete_tenant'
+
def setUp(self):
super(TestDynamicCredentialProvider, self).setUp()
self.useFixture(fake_config.ConfigFixture())
self.patchobject(config, 'TempestConfigPrivate',
fake_config.FakePrivate)
- self.patchobject(json_token_client.TokenClient, 'raw_request',
- fake_identity._fake_v2_response)
+ self.patchobject(self.token_client_class, 'raw_request',
+ self.fake_response)
cfg.CONF.set_default('operator_role', 'FakeRole',
group='object-storage')
self._mock_list_ec2_credentials('fake_user_id', 'fake_tenant_id')
@@ -59,7 +74,7 @@
def test_tempest_client(self):
creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
self.assertIsInstance(creds.identity_admin_client,
- json_iden_client.IdentityClient)
+ self.iden_client.IdentityClient)
def _get_fake_admin_creds(self):
return credentials.get_credentials(
@@ -70,7 +85,7 @@
def _mock_user_create(self, id, name):
user_fix = self.useFixture(mockpatch.PatchObject(
- json_users_client.UsersClient,
+ self.users_client.UsersClient,
'create_user',
return_value=(rest_client.ResponseBody
(200, {'user': {'id': id, 'name': name}}))))
@@ -78,7 +93,7 @@
def _mock_tenant_create(self, id, name):
tenant_fix = self.useFixture(mockpatch.PatchObject(
- json_tenants_client.TenantsClient,
+ self.tenants_client.TenantsClient,
'create_tenant',
return_value=(rest_client.ResponseBody
(200, {'tenant': {'id': id, 'name': name}}))))
@@ -86,7 +101,7 @@
def _mock_list_roles(self, id, name):
roles_fix = self.useFixture(mockpatch.PatchObject(
- json_roles_client.RolesClient,
+ self.roles_client.RolesClient,
'list_roles',
return_value=(rest_client.ResponseBody
(200,
@@ -97,7 +112,7 @@
def _mock_list_2_roles(self):
roles_fix = self.useFixture(mockpatch.PatchObject(
- json_roles_client.RolesClient,
+ self.roles_client.RolesClient,
'list_roles',
return_value=(rest_client.ResponseBody
(200,
@@ -108,24 +123,25 @@
def _mock_assign_user_role(self):
tenant_fix = self.useFixture(mockpatch.PatchObject(
- json_roles_client.RolesClient,
- 'assign_user_role',
+ self.roles_client.RolesClient,
+ self.assign_role_on_project,
return_value=(rest_client.ResponseBody
(200, {}))))
return tenant_fix
def _mock_list_role(self):
roles_fix = self.useFixture(mockpatch.PatchObject(
- json_roles_client.RolesClient,
+ self.roles_client.RolesClient,
'list_roles',
return_value=(rest_client.ResponseBody
- (200, {'roles': [{'id': '1',
- 'name': 'FakeRole'}]}))))
+ (200, {'roles': [
+ {'id': '1', 'name': 'FakeRole'},
+ {'id': '2', 'name': 'Member'}]}))))
return roles_fix
def _mock_list_ec2_credentials(self, user_id, tenant_id):
ec2_creds_fix = self.useFixture(mockpatch.PatchObject(
- json_users_client.UsersClient,
+ self.users_client.UsersClient,
'list_user_ec2_credentials',
return_value=(rest_client.ResponseBody
(200, {'credentials': [{
@@ -180,12 +196,12 @@
self._mock_user_create('1234', 'fake_admin_user')
self._mock_tenant_create('1234', 'fake_admin_tenant')
- user_mock = mock.patch.object(json_roles_client.RolesClient,
- 'assign_user_role')
+ user_mock = mock.patch.object(self.roles_client.RolesClient,
+ self.assign_role_on_project)
user_mock.start()
self.addCleanup(user_mock.stop)
- with mock.patch.object(json_roles_client.RolesClient,
- 'assign_user_role') as user_mock:
+ with mock.patch.object(self.roles_client.RolesClient,
+ self.assign_role_on_project) as user_mock:
admin_creds = creds.get_admin_creds()
user_mock.assert_has_calls([
mock.call('1234', '1234', '1234')])
@@ -203,12 +219,12 @@
self._mock_user_create('1234', 'fake_role_user')
self._mock_tenant_create('1234', 'fake_role_tenant')
- user_mock = mock.patch.object(json_roles_client.RolesClient,
- 'assign_user_role')
+ user_mock = mock.patch.object(self.roles_client.RolesClient,
+ self.assign_role_on_project)
user_mock.start()
self.addCleanup(user_mock.stop)
- with mock.patch.object(json_roles_client.RolesClient,
- 'assign_user_role') as user_mock:
+ with mock.patch.object(self.roles_client.RolesClient,
+ self.assign_role_on_project) as user_mock:
role_creds = creds.get_creds_by_roles(
roles=['role1', 'role2'])
calls = user_mock.mock_calls
@@ -240,12 +256,10 @@
self._mock_user_create('123456', 'fake_admin_user')
self._mock_list_roles('123456', 'admin')
creds.get_admin_creds()
- user_mock = self.patch(
- 'tempest.services.identity.v2.json.users_client.'
- 'UsersClient.delete_user')
- tenant_mock = self.patch(
- 'tempest.services.identity.v2.json.tenants_client.'
- 'TenantsClient.delete_tenant')
+ user_mock = self.patchobject(self.users_client.UsersClient,
+ 'delete_user')
+ tenant_mock = self.patchobject(self.tenants_client_class,
+ self.delete_tenant)
creds.clear_creds()
# Verify user delete calls
calls = user_mock.mock_calls
@@ -374,18 +388,13 @@
self._mock_router_create('123456', 'fake_admin_router')
self._mock_list_roles('123456', 'admin')
creds.get_admin_creds()
- self.patch('tempest.services.identity.v2.json.users_client.'
- 'UsersClient.delete_user')
- self.patch('tempest.services.identity.v2.json.tenants_client.'
- 'TenantsClient.delete_tenant')
- net = mock.patch.object(creds.networks_admin_client,
- 'delete_network')
+ self.patchobject(self.users_client.UsersClient, 'delete_user')
+ self.patchobject(self.tenants_client_class, self.delete_tenant)
+ net = mock.patch.object(creds.networks_admin_client, 'delete_network')
net_mock = net.start()
- subnet = mock.patch.object(creds.subnets_admin_client,
- 'delete_subnet')
+ subnet = mock.patch.object(creds.subnets_admin_client, 'delete_subnet')
subnet_mock = subnet.start()
- router = mock.patch.object(creds.routers_admin_client,
- 'delete_router')
+ router = mock.patch.object(creds.routers_admin_client, 'delete_router')
router_mock = router.start()
remove_router_interface_mock = self.patch(
'tempest.services.network.json.routers_client.RoutersClient.'
@@ -587,3 +596,42 @@
self._mock_tenant_create('1234', 'fake_prim_tenant')
self.assertRaises(exceptions.InvalidConfiguration,
creds.get_primary_creds)
+
+
+class TestDynamicCredentialProviderV3(TestDynamicCredentialProvider):
+
+ fixed_params = {'name': 'test class',
+ 'identity_version': 'v3',
+ 'admin_role': 'admin'}
+
+ token_client = v3_token_client
+ iden_client = v3_iden_client
+ roles_client = v3_roles_client
+ tenants_client = v3_projects_client
+ users_client = v3_users_client
+ token_client_class = token_client.V3TokenClient
+ fake_response = fake_identity._fake_v3_response
+ assign_role_on_project = 'assign_user_role_on_project'
+ tenants_client_class = tenants_client.ProjectsClient
+ delete_tenant = 'delete_project'
+
+ def setUp(self):
+ super(TestDynamicCredentialProviderV3, self).setUp()
+ self.useFixture(fake_config.ConfigFixture())
+ self.useFixture(mockpatch.PatchObject(
+ domains_client.DomainsClient, 'list_domains',
+ return_value=dict(domains=[dict(id='default',
+ name='Default')])))
+ self.patchobject(self.roles_client.RolesClient,
+ 'assign_user_role_on_domain')
+
+ def _mock_list_ec2_credentials(self, user_id, tenant_id):
+ pass
+
+ def _mock_tenant_create(self, id, name):
+ project_fix = self.useFixture(mockpatch.PatchObject(
+ self.tenants_client.ProjectsClient,
+ 'create_project',
+ return_value=(rest_client.ResponseBody
+ (200, {'project': {'id': id, 'name': name}}))))
+ return project_fix
diff --git a/tempest/tests/common/test_preprov_creds.py b/tempest/tests/common/test_preprov_creds.py
index 22bbdd3..13d4713 100644
--- a/tempest/tests/common/test_preprov_creds.py
+++ b/tempest/tests/common/test_preprov_creds.py
@@ -14,6 +14,7 @@
import hashlib
import os
+import testtools
import mock
from oslo_concurrency.fixture import lockutils as lockutils_fixtures
@@ -69,10 +70,12 @@
'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
{'username': 'test_user10', 'project_name': 'test_tenant10',
'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
- {'username': 'test_user11', 'tenant_name': 'test_tenant11',
+ {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
'password': 'p', 'roles': [admin_role]},
- {'username': 'test_user12', 'project_name': 'test_tenant12',
- 'password': 'p', 'roles': [admin_role]}]
+ {'username': 'test_admin2', 'project_name': 'test_tenant12',
+ 'password': 'p', 'roles': [admin_role]},
+ {'username': 'test_admin3', 'project_name': 'test_tenant13',
+ 'password': 'p', 'types': ['admin']}]
def setUp(self):
super(TestPreProvisionedCredentials, self).setUp()
@@ -262,9 +265,6 @@
self.assertFalse(test_accounts_class.is_multi_user())
def test__get_creds_by_roles_one_role(self):
- self.useFixture(mockpatch.Patch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
- return_value=self.test_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
hashes = test_accounts_class.hash_dict['roles']['role4']
@@ -280,9 +280,6 @@
self.assertIn(i, args)
def test__get_creds_by_roles_list_role(self):
- self.useFixture(mockpatch.Patch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
- return_value=self.test_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
hashes = test_accounts_class.hash_dict['roles']['role4']
@@ -300,9 +297,6 @@
self.assertIn(i, args)
def test__get_creds_by_roles_no_admin(self):
- self.useFixture(mockpatch.Patch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
- return_value=self.test_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
hashes = list(test_accounts_class.hash_dict['creds'].keys())
@@ -346,13 +340,95 @@
self.assertEqual('fake-id', network['id'])
self.assertEqual('network-2', network['name'])
+ def test_get_primary_creds(self):
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ primary_creds = test_accounts_class.get_primary_creds()
+ self.assertNotIn('test_admin', primary_creds.username)
+
+ def test_get_primary_creds_none_available(self):
+ admin_accounts = [x for x in self.test_accounts if 'test_admin'
+ in x['username']]
+ self.useFixture(mockpatch.Patch(
+ 'tempest.common.preprov_creds.read_accounts_yaml',
+ return_value=admin_accounts))
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ with testtools.ExpectedException(lib_exc.InvalidCredentials):
+ # Get one more
+ test_accounts_class.get_primary_creds()
+
+ def test_get_alt_creds(self):
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ alt_creds = test_accounts_class.get_alt_creds()
+ self.assertNotIn('test_admin', alt_creds.username)
+
+ def test_get_alt_creds_none_available(self):
+ admin_accounts = [x for x in self.test_accounts if 'test_admin'
+ in x['username']]
+ self.useFixture(mockpatch.Patch(
+ 'tempest.common.preprov_creds.read_accounts_yaml',
+ return_value=admin_accounts))
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ with testtools.ExpectedException(lib_exc.InvalidCredentials):
+ # Get one more
+ test_accounts_class.get_alt_creds()
+
+ def test_get_admin_creds(self):
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ admin_creds = test_accounts_class.get_admin_creds()
+ self.assertIn('test_admin', admin_creds.username)
+
+ def test_get_admin_creds_by_type(self):
+ test_accounts = [
+ {'username': 'test_user10', 'project_name': 'test_tenant10',
+ 'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
+ {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
+ 'password': 'p', 'types': ['admin']}]
+ self.useFixture(mockpatch.Patch(
+ 'tempest.common.preprov_creds.read_accounts_yaml',
+ return_value=test_accounts))
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ admin_creds = test_accounts_class.get_admin_creds()
+ self.assertIn('test_admin', admin_creds.username)
+
+ def test_get_admin_creds_by_role(self):
+ test_accounts = [
+ {'username': 'test_user10', 'project_name': 'test_tenant10',
+ 'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
+ {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
+ 'password': 'p', 'roles': [cfg.CONF.identity.admin_role]}]
+ self.useFixture(mockpatch.Patch(
+ 'tempest.common.preprov_creds.read_accounts_yaml',
+ return_value=test_accounts))
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ admin_creds = test_accounts_class.get_admin_creds()
+ self.assertIn('test_admin', admin_creds.username)
+
+ def test_get_admin_creds_none_available(self):
+ non_admin_accounts = [x for x in self.test_accounts if 'test_admin'
+ not in x['username']]
+ self.useFixture(mockpatch.Patch(
+ 'tempest.common.preprov_creds.read_accounts_yaml',
+ return_value=non_admin_accounts))
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ with testtools.ExpectedException(lib_exc.InvalidCredentials):
+ # Get one more
+ test_accounts_class.get_admin_creds()
+
class TestPreProvisionedCredentialsV3(TestPreProvisionedCredentials):
fixed_params = {'name': 'test class',
'identity_version': 'v3',
'test_accounts_file': 'fake_accounts_file',
- 'accounts_lock_dir': 'fake_locks_dir',
+ 'accounts_lock_dir': 'fake_locks_dir_v3',
'admin_role': 'admin',
'object_storage_operator_role': 'operator',
'object_storage_reseller_admin_role': 'reseller'}
@@ -389,9 +465,9 @@
{'username': 'test_user10', 'project_name': 'test_project10',
'domain_name': 'domain', 'password': 'p',
'roles': ['role1', 'role2', 'role3', 'role4']},
- {'username': 'test_user11', 'project_name': 'test_project11',
- 'domain_name': 'domain', 'password': 'p',
- 'roles': [admin_role]},
- {'username': 'test_user12', 'project_name': 'test_project12',
- 'domain_name': 'domain', 'password': 'p',
- 'roles': [admin_role]}]
+ {'username': 'test_admin1', 'project_name': 'test_project11',
+ 'domain_name': 'domain', 'password': 'p', 'roles': [admin_role]},
+ {'username': 'test_admin2', 'project_name': 'test_project12',
+ 'domain_name': 'domain', 'password': 'p', 'roles': [admin_role]},
+ {'username': 'test_admin3', 'project_name': 'test_tenant13',
+ 'domain_name': 'domain', 'password': 'p', 'types': ['admin']}]
diff --git a/tempest/tests/common/utils/test_file_utils.py b/tempest/tests/common/utils/test_file_utils.py
deleted file mode 100644
index 937aefa..0000000
--- a/tempest/tests/common/utils/test_file_utils.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 2014 IBM Corp.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import mock
-
-from tempest.common.utils import file_utils
-from tempest.tests import base
-
-
-class TestFileUtils(base.TestCase):
-
- def test_have_effective_read_path(self):
- with mock.patch('six.moves.builtins.open', mock.mock_open(),
- create=True):
- result = file_utils.have_effective_read_access('fake_path')
- self.assertTrue(result)
-
- def test_not_effective_read_path(self):
- result = file_utils.have_effective_read_access('fake_path')
- self.assertFalse(result)
diff --git a/tempest/tests/lib/test_rest_client.py b/tempest/tests/lib/test_rest_client.py
index 2a6fad5..106a1e5 100644
--- a/tempest/tests/lib/test_rest_client.py
+++ b/tempest/tests/lib/test_rest_client.py
@@ -693,6 +693,24 @@
self.assertRaises(AssertionError, self.rest_client.expected_success,
expected_code, read_code)
+ def test_non_success_read_code_as_string(self):
+ expected_code = 202
+ read_code = '202'
+ self.assertRaises(TypeError, self.rest_client.expected_success,
+ expected_code, read_code)
+
+ def test_non_success_read_code_as_list(self):
+ expected_code = 202
+ read_code = [202]
+ self.assertRaises(TypeError, self.rest_client.expected_success,
+ expected_code, read_code)
+
+ def test_non_success_expected_code_as_non_int(self):
+ expected_code = ['201', 202]
+ read_code = 202
+ self.assertRaises(AssertionError, self.rest_client.expected_success,
+ expected_code, read_code)
+
class TestResponseBody(base.TestCase):
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index aba2aab..f005c21 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -167,3 +167,16 @@
self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
"import tempest.exception",
'./tempest/lib/common/compute.py'))))
+
+ def test_dont_use_config_in_tempest_lib(self):
+ self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
+ 'from tempest import config', './tempest/common/compute.py')))
+ self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
+ 'from oslo_concurrency import lockutils',
+ './tempest/lib/auth.py')))
+ self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+ 'from tempest import config', './tempest/lib/auth.py')))
+ self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+ 'from oslo_config import cfg', './tempest/lib/decorators.py')))
+ self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+ 'import tempest.config', './tempest/lib/common/rest_client.py')))
diff --git a/test-requirements.txt b/test-requirements.txt
index 9ef956a..763f0ba 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -7,6 +7,6 @@
python-subunit>=0.0.18 # Apache-2.0/BSD
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
reno>=1.6.2 # Apache2
-mock>=1.2 # BSD
+mock>=2.0 # BSD
coverage>=3.6 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
diff --git a/tools/check_logs.py b/tools/check_logs.py
index e34dec3..caad85c 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -20,8 +20,8 @@
import os
import re
import six
+import six.moves.urllib.request as urlreq
import sys
-import urllib2
import yaml
@@ -67,9 +67,9 @@
logs_with_errors.append(name)
for (name, url) in url_specs:
whitelist = whitelists.get(name, [])
- req = urllib2.Request(url)
+ req = urlreq.Request(url)
req.add_header('Accept-Encoding', 'gzip')
- page = urllib2.urlopen(req)
+ page = urlreq.urlopen(req)
buf = six.StringIO(page.read())
f = gzip.GzipFile(fileobj=buf)
if scan_content(name, f.read().splitlines(), regexp, whitelist):
@@ -95,7 +95,7 @@
def collect_url_logs(url):
- page = urllib2.urlopen(url)
+ page = urlreq.urlopen(url)
content = page.read()
logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
return logs
diff --git a/tools/find_stack_traces.py b/tools/find_stack_traces.py
index 49a42fe..f2da27a 100755
--- a/tools/find_stack_traces.py
+++ b/tools/find_stack_traces.py
@@ -19,8 +19,8 @@
import pprint
import re
import six
+import six.moves.urllib.request as urlreq
import sys
-import urllib2
pp = pprint.PrettyPrinter()
@@ -65,9 +65,9 @@
def hunt_for_stacktrace(url):
"""Return TRACE or ERROR lines out of logs."""
- req = urllib2.Request(url)
+ req = urlreq.Request(url)
req.add_header('Accept-Encoding', 'gzip')
- page = urllib2.urlopen(req)
+ page = urlreq.urlopen(req)
buf = six.StringIO(page.read())
f = gzip.GzipFile(fileobj=buf)
content = f.read()
@@ -105,7 +105,7 @@
def collect_logs(url):
- page = urllib2.urlopen(url)
+ page = urlreq.urlopen(url)
content = page.read()
logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
return logs