Merge "Add cleanup in identity/test_users_negative.py"
diff --git a/HACKING.rst b/HACKING.rst
index b66fa24..d3ac5c6 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -132,16 +132,16 @@
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/doc/source/configuration.rst b/doc/source/configuration.rst
index bcb1e3e..d610dc5 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -27,21 +27,13 @@
- Generate test credentials on the fly (see `Dynamic Credentials`_)
Tempest allows for configuring pre-provisioned test credentials as well.
-This can be done in two different ways.
-
-One is to provide credentials is using the accounts.yaml file (see
+This can be done using the accounts.yaml file (see
`Pre-Provisioned Credentials`_). This file is used to specify an arbitrary
number of users available to run tests with.
You can specify the location of the file in the ``auth`` section in the
tempest.conf file. To see the specific format used in the file please refer to
the accounts.yaml.sample file included in Tempest.
-A second way - now deprecated - is a set of configuration options in the
-tempest.conf file (see `Legacy Credentials`_). These options are clearly
-labelled in the ``identity`` section and let you specify a set of credentials
-for a regular user and an alternate user, consisting of a username, password,
-project and domain name.
-
Keystone Connection Info
^^^^^^^^^^^^^^^^^^^^^^^^
In order for Tempest to be able to talk to your OpenStack deployment you need
@@ -134,44 +126,6 @@
Pre-Provisioned Credentials are also know as accounts.yaml or accounts file.
-Legacy Credentials
-""""""""""""""""""
-**Starting in the Liberty release this mechanism was deprecated; it will be
-removed in a future release.**
-
-When Tempest was refactored to allow for locking test accounts, the original
-non-project isolated case was converted to internally work similarly to the
-accounts.yaml file. This mechanism was then called the legacy test accounts
-provider. To use the legacy test accounts provider you can specify the sets of
-credentials in the configuration file as detailed above with following nine
-options in the ``identity`` section:
-
- #. ``username``
- #. ``password``
- #. ``project_name``
- #. ``alt_username``
- #. ``alt_password``
- #. ``alt_project_name``
-
-If using Identity API v3, use the ``domain_name`` option to specify a
-domain other than the default domain. The ``auth_version`` setting is
-used to switch between v2 (``v2``) or v3 (``v3``) versions of the Identity
-API.
-
-And in the ``auth`` section:
-
- #. ``use_dynamic_credentials = False``
- #. Comment out ``test_accounts_file`` or keep it empty.
-
-It only makes sense to use this if parallel execution isn't needed, since
-Tempest won't be able to properly isolate tests using this. Additionally, using
-the traditional config options for credentials is not able to provide
-credentials to tests requiring specific roles on accounts. This is because the
-config options do not give sufficient flexibility to describe the roles assigned
-to a user for running the tests. There are additional limitations with regard to
-network configuration when using this credential provider mechanism - see the
-`Networking`_ section below.
-
Compute
-------
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index ad26741..9640469 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -15,10 +15,22 @@
doing this is that the interfaces exposed by tempest are not considered stable
(with the exception of configuration variables which ever effort goes into
ensuring backwards compatibility). You should not need to import anything from
-tempest itself except where explicitly noted. If there is an interface from
-tempest that you need to rely on in your plugin it likely needs to be migrated
-to tempest.lib. In that situation, file a bug, push a migration patch, etc. to
-expedite providing the interface in a reliable manner.
+tempest itself except where explicitly noted.
+
+Stable Tempest APIs plugins may use
+-----------------------------------
+
+As noted above, several tempest APIs are acceptable to use from plugins, while
+others are not. A list of stable APIs available to plugins is provided below:
+
+* tempest.lib.*
+* tempest.config
+* tempest.test_discover.plugins
+
+If there is an interface from tempest that you need to rely on in your plugin
+which is not listed above, it likely needs to be migrated to tempest.lib. In
+that situation, file a bug, push a migration patch, etc. to expedite providing
+the interface in a reliable manner.
Plugin Cookiecutter
-------------------
diff --git a/etc/javelin-resources.yaml.sample b/etc/javelin-resources.yaml.sample
index fb270a4..1565686 100644
--- a/etc/javelin-resources.yaml.sample
+++ b/etc/javelin-resources.yaml.sample
@@ -61,5 +61,3 @@
owner: javelin
file: /etc/hosts
swift_role: Member
-
-telemetry: true
\ No newline at end of file
diff --git a/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml b/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
new file mode 100644
index 0000000..9baf035
--- /dev/null
+++ b/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
@@ -0,0 +1,12 @@
+---
+prelude: >
+ This release is marking the end of Kilo release support in Tempest
+other:
+ - OpenStack Releases Supported after this release are **Liberty**
+ and **Mitaka**
+
+ The release under current development as of this tag is Newton,
+ meaning that every Tempest commit is also tested against master during
+ the Newton cycle. However, this does not necessarily mean that using
+ Tempest as of this tag will work against a Newton (or future releases)
+ cloud.
diff --git a/releasenotes/notes/remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml b/releasenotes/notes/remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
new file mode 100644
index 0000000..89b3f41
--- /dev/null
+++ b/releasenotes/notes/remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+ - The deprecated legacy credential provider has been removed. The only way to
+ configure credentials in tempest now is to use the dynamic or preprovisioned
+ credential providers
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index b617b22..2c22408 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -5,6 +5,7 @@
.. toctree::
:maxdepth: 1
+ v12.0.0
v11.0.0
v10.0.0
unreleased
diff --git a/releasenotes/source/v12.0.0.rst b/releasenotes/source/v12.0.0.rst
new file mode 100644
index 0000000..0bc8343
--- /dev/null
+++ b/releasenotes/source/v12.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v12.0.0 Release Notes
+=====================
+
+.. release-notes:: 12.0.0 Release Notes
+ :version: 12.0.0
diff --git a/requirements.txt b/requirements.txt
index dd73257..d567082 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,7 @@
cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
testtools>=1.4.0 # MIT
-paramiko>=1.16.0 # LGPL
+paramiko>=2.0 # LGPL
netaddr!=0.7.16,>=0.7.12 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
pyOpenSSL>=0.14 # Apache-2.0
@@ -19,7 +19,7 @@
fixtures<2.0,>=1.3.1 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
PyYAML>=3.1.0 # MIT
-stevedore>=1.9.0 # Apache-2.0
+stevedore>=1.10.0 # Apache-2.0
PrettyTable<0.8,>=0.7 # BSD
os-testr>=0.4.1 # Apache-2.0
-urllib3>=1.8.3 # MIT
+urllib3>=1.15.1 # MIT
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 8201363..fdf55e5 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -13,10 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-import netaddr
import time
from tempest.api.compute import base
+from tempest.common.utils import net_utils
from tempest import config
from tempest import exceptions
from tempest.lib import exceptions as lib_exc
@@ -125,18 +125,21 @@
def _test_create_interface_by_fixed_ips(self, server, ifs):
network_id = ifs[0]['net_id']
- ip_list = [
- ifs[n]['fixed_ips'][0]['ip_address'] for n in range(0, len(ifs))]
- ip = str(netaddr.IPAddress(sorted(ip_list)[-1]) + 1)
+ subnet_id = ifs[0]['fixed_ips'][0]['subnet_id']
+ ip_list = net_utils.get_unused_ip_addresses(self.ports_client,
+ self.subnets_client,
+ network_id,
+ subnet_id,
+ 1)
- fixed_ips = [{'ip_address': ip}]
+ fixed_ips = [{'ip_address': ip_list[0]}]
iface = self.client.create_interface(
server['id'], net_id=network_id,
fixed_ips=fixed_ips)['interfaceAttachment']
self.addCleanup(self.ports_client.delete_port, iface['port_id'])
iface = self.wait_for_interface_status(
server['id'], iface['port_id'], 'ACTIVE')
- self._check_interface(iface, fixed_ip=ip)
+ self._check_interface(iface, fixed_ip=ip_list[0])
return iface
def _test_show_interface(self, server, ifs):
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index f01657b..5ce41c8 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -226,6 +226,35 @@
self.client.start_server(self.server_id)
+ @test.idempotent_id('b68bd8d6-855d-4212-b59b-2e704044dace')
+ @test.services('volume')
+ def test_rebuild_server_with_volume_attached(self):
+ # create a new volume and attach it to the server
+ volume = self.volumes_client.create_volume(
+ size=CONF.volume.volume_size)
+ volume = volume['volume']
+ self.addCleanup(self.volumes_client.delete_volume, volume['id'])
+ waiters.wait_for_volume_status(self.volumes_client, volume['id'],
+ 'available')
+
+ self.client.attach_volume(self.server_id, volumeId=volume['id'])
+ self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
+ volume['id'], 'available')
+ self.addCleanup(self.client.detach_volume,
+ self.server_id, volume['id'])
+ waiters.wait_for_volume_status(self.volumes_client, volume['id'],
+ 'in-use')
+
+ # run general rebuild test
+ self.test_rebuild_server()
+
+ # make sure the volume is attached to the instance after rebuild
+ vol_after_rebuild = self.volumes_client.show_volume(volume['id'])
+ vol_after_rebuild = vol_after_rebuild['volume']
+ self.assertEqual('in-use', vol_after_rebuild['status'])
+ self.assertEqual(self.server_id,
+ vol_after_rebuild['attachments'][0]['server_id'])
+
def _test_resize_server_confirm(self, stop=False):
# The server's RAM and disk space should be modified to that of
# the provided flavor
diff --git a/tempest/api/identity/v2/test_users.py b/tempest/api/identity/v2/test_users.py
index 62ddead..79f2576 100644
--- a/tempest/api/identity/v2/test_users.py
+++ b/tempest/api/identity/v2/test_users.py
@@ -13,13 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-import copy
import time
from tempest.api.identity import base
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
-from tempest import manager
from tempest import test
@@ -35,23 +33,26 @@
@test.idempotent_id('165859c9-277f-4124-9479-a7d1627b0ca7')
def test_user_update_own_password(self):
- self.new_creds = copy.copy(self.creds.credentials)
- self.new_creds.password = data_utils.rand_password()
- # we need new non-admin Identity Client with new credentials, since
- # current non_admin_client token will be revoked after updating
- # password
- self.non_admin_users_client_for_cleanup = copy.copy(
- self.non_admin_users_client)
- self.non_admin_users_client_for_cleanup.auth_provider = (
- manager.get_auth_provider(self.new_creds))
- user_id = self.creds.credentials.user_id
- old_pass = self.creds.credentials.password
- new_pass = self.new_creds.password
+ def _restore_password(client, user_id, old_pass, new_pass):
+ # Reset auth to get a new token with the new password
+ client.auth_provider.clear_auth()
+ client.auth_provider.credentials.password = new_pass
+ client.update_user_own_password(user_id, password=old_pass,
+ original_password=new_pass)
+ # Reset auth again to verify the password restore does work.
+ # Clear auth restores the original credentials and deletes
+ # cached auth data
+ client.auth_provider.clear_auth()
+ client.auth_provider.set_auth()
+
+ old_pass = self.creds.credentials.password
+ new_pass = data_utils.rand_password()
+ user_id = self.creds.credentials.user_id
# to change password back. important for allow_tenant_isolation = false
- self.addCleanup(
- self.non_admin_users_client_for_cleanup.update_user_own_password,
- user_id, original_password=new_pass, password=old_pass)
+ self.addCleanup(_restore_password, self.non_admin_users_client,
+ user_id, old_pass=old_pass, new_pass=new_pass)
+
# user updates own password
self.non_admin_users_client.update_user_own_password(
user_id, password=new_pass, original_password=old_pass)
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index 60fbe12..76b46c0 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -13,13 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-import copy
import time
from tempest.api.identity import base
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
-from tempest import manager
from tempest import test
@@ -35,24 +33,25 @@
@test.idempotent_id('ad71bd23-12ad-426b-bb8b-195d2b635f27')
def test_user_update_own_password(self):
- self.new_creds = copy.copy(self.creds.credentials)
- self.new_creds.password = data_utils.rand_password()
- # we need new non-admin Identity V3 Client with new credentials, since
- # current non_admin_users_client token will be revoked after updating
- # password
- self.non_admin_users_client_for_cleanup = (
- copy.copy(self.non_admin_users_client))
- self.non_admin_users_client_for_cleanup.auth_provider = (
- manager.get_auth_provider(self.new_creds))
- user_id = self.creds.credentials.user_id
+
+ def _restore_password(client, user_id, old_pass, new_pass):
+ # Reset auth to get a new token with the new password
+ client.auth_provider.clear_auth()
+ client.auth_provider.credentials.password = new_pass
+ client.update_user_password(user_id, password=old_pass,
+ original_password=new_pass)
+ # Reset auth again to verify the password restore does work.
+ # Clear auth restores the original credentials and deletes
+ # cached auth data
+ client.auth_provider.clear_auth()
+ client.auth_provider.set_auth()
+
old_pass = self.creds.credentials.password
- new_pass = self.new_creds.password
+ new_pass = data_utils.rand_password()
+ user_id = self.creds.credentials.user_id
# to change password back. important for allow_tenant_isolation = false
- self.addCleanup(
- self.non_admin_users_client_for_cleanup.update_user_password,
- user_id,
- password=old_pass,
- original_password=new_pass)
+ self.addCleanup(_restore_password, self.non_admin_users_client,
+ user_id, old_pass=old_pass, new_pass=new_pass)
# user updates own password
self.non_admin_users_client.update_user_password(
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 1a84d06..b2f12ca 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -32,7 +32,7 @@
if container_format in a_formats and container_format != disk_format:
msg = ("The container format and the disk format don't match. "
- "Contaiter format: %(container)s, Disk format: %(disk)s." %
+ "Container format: %(container)s, Disk format: %(disk)s." %
{'container': container_format, 'disk': disk_format})
raise exceptions.InvalidConfiguration(message=msg)
@@ -305,7 +305,7 @@
@test.idempotent_id('01752c1c-0275-4de3-9e5b-876e44541928')
def test_list_image_metadata(self):
# All metadata key/value pairs for an image should be returned
- resp_metadata = self.client.get_image_meta(self.image_id)
+ resp_metadata = self.client.check_image(self.image_id)
expected = {'key1': 'value1'}
self.assertEqual(expected, resp_metadata['properties'])
@@ -313,12 +313,12 @@
def test_update_image_metadata(self):
# The metadata for the image should match the updated values
req_metadata = {'key1': 'alt1', 'key2': 'value2'}
- metadata = self.client.get_image_meta(self.image_id)
+ metadata = self.client.check_image(self.image_id)
self.assertEqual(metadata['properties'], {'key1': 'value1'})
metadata['properties'].update(req_metadata)
metadata = self.client.update_image(
self.image_id, properties=metadata['properties'])['image']
- resp_metadata = self.client.get_image_meta(self.image_id)
+ resp_metadata = self.client.check_image(self.image_id)
expected = {'key1': 'alt1', 'key2': 'value2'}
self.assertEqual(expected, resp_metadata['properties'])
diff --git a/tempest/api/network/test_floating_ips.py b/tempest/api/network/test_floating_ips.py
index 2156e64..2abbf93 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -13,10 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-import netaddr
-
from tempest.api.network import base
from tempest.common.utils import data_utils
+from tempest.common.utils import net_utils
from tempest import config
from tempest import test
@@ -192,8 +191,12 @@
@test.idempotent_id('45c4c683-ea97-41ef-9c51-5e9802f2f3d7')
def test_create_update_floatingip_with_port_multiple_ip_address(self):
# Find out ips that can be used for tests
- ips = list(netaddr.IPNetwork(self.subnet['cidr']))
- list_ips = [str(ip) for ip in ips[-3:-1]]
+ list_ips = net_utils.get_unused_ip_addresses(
+ self.ports_client,
+ self.subnets_client,
+ self.subnet['network_id'],
+ self.subnet['id'],
+ 2)
fixed_ips = [{'ip_address': list_ips[0]}, {'ip_address': list_ips[1]}]
# Create port
body = self.ports_client.create_port(network_id=self.network['id'],
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index e8b035b..dc926e0 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -179,23 +179,14 @@
@test.idempotent_id('84dafe57-9666-4f6d-84c8-0814d37923b8')
def test_create_object_with_expect_continue(self):
# create object with expect_continue
+
object_name = data_utils.rand_name(name='TestObject')
data = data_utils.arbitrary_string()
- metadata = {'Expect': '100-continue'}
- resp = self.object_client.create_object_continue(
- self.container_name,
- object_name,
- data,
- metadata=metadata)
- self.assertIn('status', resp)
- self.assertEqual(resp['status'], '100')
+ status, _ = self.object_client.create_object_continue(
+ self.container_name, object_name, data)
- self.object_client.create_object_continue(
- self.container_name,
- object_name,
- data,
- metadata=None)
+ self.assertEqual(status, 201)
# check uploaded content
_, body = self.object_client.get_object(self.container_name,
diff --git a/tempest/api/telemetry/base.py b/tempest/api/telemetry/base.py
deleted file mode 100644
index 7238098..0000000
--- a/tempest/api/telemetry/base.py
+++ /dev/null
@@ -1,196 +0,0 @@
-# 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 time
-
-from oslo_utils import timeutils
-
-from tempest.common import compute
-from tempest.common.utils import data_utils
-from tempest.common import waiters
-from tempest import config
-from tempest import exceptions
-from tempest.lib import exceptions as lib_exc
-import tempest.test
-
-CONF = config.CONF
-
-
-class BaseTelemetryTest(tempest.test.BaseTestCase):
-
- """Base test case class for all Telemetry API tests."""
-
- credentials = ['primary']
-
- @classmethod
- def skip_checks(cls):
- super(BaseTelemetryTest, cls).skip_checks()
- if not CONF.service_available.ceilometer:
- raise cls.skipException("Ceilometer support is required")
-
- @classmethod
- def setup_credentials(cls):
- cls.set_network_resources()
- super(BaseTelemetryTest, cls).setup_credentials()
-
- @classmethod
- def setup_clients(cls):
- super(BaseTelemetryTest, cls).setup_clients()
- cls.telemetry_client = cls.os.telemetry_client
- cls.servers_client = cls.os.servers_client
- cls.flavors_client = cls.os.flavors_client
- cls.image_client = cls.os.image_client
- cls.image_client_v2 = cls.os.image_client_v2
-
- @classmethod
- def resource_setup(cls):
- super(BaseTelemetryTest, cls).resource_setup()
- cls.nova_notifications = ['memory', 'vcpus', 'disk.root.size',
- 'disk.ephemeral.size']
-
- cls.glance_notifications = ['image.size']
-
- cls.glance_v2_notifications = ['image.download', 'image.serve']
-
- cls.server_ids = []
- cls.image_ids = []
-
- @classmethod
- def create_server(cls):
- tenant_network = cls.get_tenant_network()
- body, server = compute.create_test_server(
- cls.os,
- tenant_network=tenant_network,
- name=data_utils.rand_name('ceilometer-instance'),
- wait_until='ACTIVE')
- cls.server_ids.append(body['id'])
- return body
-
- @classmethod
- def create_image(cls, client, **kwargs):
- body = client.create_image(name=data_utils.rand_name('image'),
- container_format='bare',
- disk_format='raw',
- **kwargs)
- # TODO(jswarren) Move ['image'] up to initial body value assignment
- # once both v1 and v2 glance clients include the full response
- # object.
- if 'image' in body:
- body = body['image']
- cls.image_ids.append(body['id'])
- return body
-
- @staticmethod
- def cleanup_resources(method, list_of_ids):
- for resource_id in list_of_ids:
- try:
- method(resource_id)
- except lib_exc.NotFound:
- pass
-
- @classmethod
- def wait_for_server_termination(cls, server_id):
- waiters.wait_for_server_termination(cls.servers_client,
- server_id)
-
- @classmethod
- def resource_cleanup(cls):
- cls.cleanup_resources(cls.servers_client.delete_server, cls.server_ids)
- cls.cleanup_resources(cls.wait_for_server_termination, cls.server_ids)
- cls.cleanup_resources(cls.image_client.delete_image, cls.image_ids)
- super(BaseTelemetryTest, cls).resource_cleanup()
-
- def await_samples(self, metric, query):
- """This method is to wait for sample to add it to database.
-
- There are long time delays when using Postgresql (or Mysql)
- database as ceilometer backend
- """
- timeout = CONF.compute.build_timeout
- start = timeutils.utcnow()
- while timeutils.delta_seconds(start, timeutils.utcnow()) < timeout:
- body = self.telemetry_client.list_samples(metric, query)
- if body:
- return body
- time.sleep(CONF.compute.build_interval)
-
- raise exceptions.TimeoutException(
- 'Sample for metric:%s with query:%s has not been added to the '
- 'database within %d seconds' % (metric, query,
- CONF.compute.build_timeout))
-
-
-class BaseTelemetryAdminTest(BaseTelemetryTest):
- """Base test case class for admin Telemetry API tests."""
-
- credentials = ['primary', 'admin']
-
- @classmethod
- def setup_clients(cls):
- super(BaseTelemetryAdminTest, cls).setup_clients()
- cls.telemetry_admin_client = cls.os_adm.telemetry_client
-
- def await_events(self, query):
- timeout = CONF.compute.build_timeout
- start = timeutils.utcnow()
- while timeutils.delta_seconds(start, timeutils.utcnow()) < timeout:
- body = self.telemetry_admin_client.list_events(query)
- if body:
- return body
- time.sleep(CONF.compute.build_interval)
-
- raise exceptions.TimeoutException(
- 'Event with query:%s has not been added to the '
- 'database within %d seconds' % (query, CONF.compute.build_timeout))
-
-
-class BaseAlarmingTest(tempest.test.BaseTestCase):
- """Base test case class for all Alarming API tests."""
-
- credentials = ['primary']
-
- @classmethod
- def skip_checks(cls):
- super(BaseAlarmingTest, cls).skip_checks()
- if not CONF.service_available.aodh:
- raise cls.skipException("Aodh support is required")
-
- @classmethod
- def setup_clients(cls):
- super(BaseAlarmingTest, cls).setup_clients()
- cls.alarming_client = cls.os.alarming_client
-
- @classmethod
- def resource_setup(cls):
- super(BaseAlarmingTest, cls).resource_setup()
- cls.alarm_ids = []
-
- @classmethod
- def create_alarm(cls, **kwargs):
- body = cls.alarming_client.create_alarm(
- name=data_utils.rand_name('telemetry_alarm'),
- type='threshold', **kwargs)
- cls.alarm_ids.append(body['alarm_id'])
- return body
-
- @staticmethod
- def cleanup_resources(method, list_of_ids):
- for resource_id in list_of_ids:
- try:
- method(resource_id)
- except lib_exc.NotFound:
- pass
-
- @classmethod
- def resource_cleanup(cls):
- cls.cleanup_resources(cls.alarming_client.delete_alarm, cls.alarm_ids)
- super(BaseAlarmingTest, cls).resource_cleanup()
diff --git a/tempest/api/telemetry/test_alarming_api.py b/tempest/api/telemetry/test_alarming_api.py
deleted file mode 100644
index 586bb42..0000000
--- a/tempest/api/telemetry/test_alarming_api.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# 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 tempest.api.telemetry import base
-from tempest.common.utils import data_utils
-from tempest.lib import exceptions as lib_exc
-from tempest import test
-
-
-class TelemetryAlarmingAPITestJSON(base.BaseAlarmingTest):
-
- @classmethod
- def resource_setup(cls):
- super(TelemetryAlarmingAPITestJSON, cls).resource_setup()
- cls.rule = {'meter_name': 'cpu_util',
- 'comparison_operator': 'gt',
- 'threshold': 80.0,
- 'period': 70}
- for i in range(2):
- cls.create_alarm(threshold_rule=cls.rule)
-
- @test.idempotent_id('1c918e06-210b-41eb-bd45-14676dd77cd6')
- def test_alarm_list(self):
- # List alarms
- alarm_list = self.alarming_client.list_alarms()
-
- # Verify created alarm in the list
- fetched_ids = [a['alarm_id'] for a in alarm_list]
- missing_alarms = [a for a in self.alarm_ids if a not in fetched_ids]
- self.assertEqual(0, len(missing_alarms),
- "Failed to find the following created alarm(s)"
- " in a fetched list: %s" %
- ', '.join(str(a) for a in missing_alarms))
-
- @test.idempotent_id('1297b095-39c1-4e74-8a1f-4ae998cedd67')
- def test_create_update_get_delete_alarm(self):
- # Create an alarm
- alarm_name = data_utils.rand_name('telemetry_alarm')
- body = self.alarming_client.create_alarm(
- name=alarm_name, type='threshold', threshold_rule=self.rule)
- self.assertEqual(alarm_name, body['name'])
- alarm_id = body['alarm_id']
- self.assertDictContainsSubset(self.rule, body['threshold_rule'])
- # Update alarm with new rule and new name
- new_rule = {'meter_name': 'cpu',
- 'comparison_operator': 'eq',
- 'threshold': 70.0,
- 'period': 60}
- alarm_name_updated = data_utils.rand_name('telemetry-alarm-update')
- body = self.alarming_client.update_alarm(
- alarm_id,
- threshold_rule=new_rule,
- name=alarm_name_updated,
- type='threshold')
- self.assertEqual(alarm_name_updated, body['name'])
- self.assertDictContainsSubset(new_rule, body['threshold_rule'])
- # Get and verify details of an alarm after update
- body = self.alarming_client.show_alarm(alarm_id)
- self.assertEqual(alarm_name_updated, body['name'])
- self.assertDictContainsSubset(new_rule, body['threshold_rule'])
- # Get history for the alarm and verify the same
- body = self.alarming_client.show_alarm_history(alarm_id)
- self.assertEqual("rule change", body[0]['type'])
- self.assertIn(alarm_name_updated, body[0]['detail'])
- self.assertEqual("creation", body[1]['type'])
- self.assertIn(alarm_name, body[1]['detail'])
- # Delete alarm and verify if deleted
- self.alarming_client.delete_alarm(alarm_id)
- self.assertRaises(lib_exc.NotFound,
- self.alarming_client.show_alarm, alarm_id)
-
- @test.idempotent_id('aca49486-70bb-4016-87e0-f6131374f741')
- def test_set_get_alarm_state(self):
- alarm_states = ['ok', 'alarm', 'insufficient data']
- alarm = self.create_alarm(threshold_rule=self.rule)
- # Set alarm state and verify
- new_state =\
- [elem for elem in alarm_states if elem != alarm['state']][0]
- state = self.alarming_client.alarm_set_state(alarm['alarm_id'],
- new_state)
- self.assertEqual(new_state, state.data)
- # Get alarm state and verify
- state = self.alarming_client.show_alarm_state(alarm['alarm_id'])
- self.assertEqual(new_state, state.data)
-
- @test.idempotent_id('08d7e45a-1344-4e5c-ba6f-f6cbb77f55b9')
- def test_create_delete_alarm_with_combination_rule(self):
- rule = {"alarm_ids": self.alarm_ids,
- "operator": "or"}
- # Verifies alarm create
- alarm_name = data_utils.rand_name('combination_alarm')
- body = self.alarming_client.create_alarm(name=alarm_name,
- combination_rule=rule,
- type='combination')
- self.assertEqual(alarm_name, body['name'])
- alarm_id = body['alarm_id']
- self.assertDictContainsSubset(rule, body['combination_rule'])
- # Verify alarm delete
- self.alarming_client.delete_alarm(alarm_id)
- self.assertRaises(lib_exc.NotFound,
- self.alarming_client.show_alarm, alarm_id)
diff --git a/tempest/api/telemetry/test_alarming_api_negative.py b/tempest/api/telemetry/test_alarming_api_negative.py
deleted file mode 100644
index 3e34f8b..0000000
--- a/tempest/api/telemetry/test_alarming_api_negative.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright 2015 GlobalLogic. 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 tempest.api.telemetry import base
-from tempest.common.utils import data_utils
-from tempest.lib import exceptions as lib_exc
-from tempest import test
-
-
-class TelemetryAlarmingNegativeTest(base.BaseAlarmingTest):
- """Negative tests for show_alarm, update_alarm, show_alarm_history tests
-
- ** show non-existent alarm
- ** show the deleted alarm
- ** delete deleted alarm
- ** update deleted alarm
- """
-
- @test.attr(type=['negative'])
- @test.idempotent_id('668743d5-08ad-4480-b2b8-15da34f81e7d')
- def test_get_non_existent_alarm(self):
- # get the non-existent alarm
- non_existent_id = data_utils.rand_uuid()
- self.assertRaises(lib_exc.NotFound, self.alarming_client.show_alarm,
- non_existent_id)
-
- @test.attr(type=['negative'])
- @test.idempotent_id('ef45000d-0a72-4781-866d-4cb7bf2582ad')
- def test_get_update_show_history_delete_deleted_alarm(self):
- # get, update and delete the deleted alarm
- alarm_name = data_utils.rand_name('telemetry_alarm')
- rule = {'meter_name': 'cpu',
- 'comparison_operator': 'eq',
- 'threshold': 100.0,
- 'period': 90}
- body = self.alarming_client.create_alarm(
- name=alarm_name,
- type='threshold',
- threshold_rule=rule)
- alarm_id = body['alarm_id']
- self.alarming_client.delete_alarm(alarm_id)
- # get the deleted alarm
- self.assertRaises(lib_exc.NotFound, self.alarming_client.show_alarm,
- alarm_id)
-
- # update the deleted alarm
- updated_alarm_name = data_utils.rand_name('telemetry_alarm_updated')
- updated_rule = {'meter_name': 'cpu_new',
- 'comparison_operator': 'eq',
- 'threshold': 70,
- 'period': 50}
- self.assertRaises(lib_exc.NotFound, self.alarming_client.update_alarm,
- alarm_id, threshold_rule=updated_rule,
- name=updated_alarm_name,
- type='threshold')
- # delete the deleted alarm
- self.assertRaises(lib_exc.NotFound, self.alarming_client.delete_alarm,
- alarm_id)
diff --git a/tempest/api/telemetry/test_telemetry_notification_api.py b/tempest/api/telemetry/test_telemetry_notification_api.py
deleted file mode 100644
index 53d457f..0000000
--- a/tempest/api/telemetry/test_telemetry_notification_api.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# 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 testtools
-
-from tempest.api.telemetry import base
-from tempest import config
-from tempest.lib import decorators
-from tempest import test
-
-CONF = config.CONF
-
-
-class TelemetryNotificationAPITestJSON(base.BaseTelemetryTest):
-
- @test.idempotent_id('d7f8c1c8-d470-4731-8604-315d3956caad')
- @test.services('compute')
- def test_check_nova_notification(self):
-
- body = self.create_server()
-
- query = ('resource', 'eq', body['id'])
-
- for metric in self.nova_notifications:
- self.await_samples(metric, query)
-
- @test.attr(type="smoke")
- @test.idempotent_id('04b10bfe-a5dc-47af-b22f-0460426bf498')
- @test.services("image")
- @testtools.skipIf(not CONF.image_feature_enabled.api_v1,
- "Glance api v1 is disabled")
- def test_check_glance_v1_notifications(self):
- body = self.create_image(self.image_client, is_public=False)
- self.image_client.update_image(body['id'], data='data')
-
- query = 'resource', 'eq', body['id']
-
- self.image_client.delete_image(body['id'])
-
- for metric in self.glance_notifications:
- self.await_samples(metric, query)
-
- @test.attr(type="smoke")
- @test.idempotent_id('c240457d-d943-439b-8aea-85e26d64fe8e')
- @test.services("image")
- @testtools.skipIf(not CONF.image_feature_enabled.api_v2,
- "Glance api v2 is disabled")
- def test_check_glance_v2_notifications(self):
- body = self.create_image(self.image_client_v2, visibility='private')
-
- self.image_client_v2.store_image_file(body['id'], "file")
- self.image_client_v2.show_image_file(body['id'])
-
- query = 'resource', 'eq', body['id']
-
- for metric in self.glance_v2_notifications:
- self.await_samples(metric, query)
-
-
-class TelemetryNotificationAdminAPITestJSON(base.BaseTelemetryAdminTest):
-
- @test.idempotent_id('29604198-8b45-4fc0-8af8-1cae4f94ebe9')
- @test.services('compute')
- @decorators.skip_because(bug='1480490')
- def test_check_nova_notification_event_and_meter(self):
-
- body = self.create_server()
-
- if CONF.telemetry_feature_enabled.events:
- query = ('instance_id', 'eq', body['id'])
- self.await_events(query)
-
- query = ('resource', 'eq', body['id'])
- for metric in self.nova_notifications:
- self.await_samples(metric, query)
diff --git a/tempest/api/volume/admin/test_volume_pools.py b/tempest/api/volume/admin/test_volume_pools.py
new file mode 100644
index 0000000..c662e8c
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_pools.py
@@ -0,0 +1,43 @@
+# Copyright 2016 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.
+
+from tempest.api.volume import base
+from tempest import test
+
+
+class VolumePoolsAdminV2TestsJSON(base.BaseVolumeAdminTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(VolumePoolsAdminV2TestsJSON, cls).resource_setup()
+ # Create a test shared volume for tests
+ cls.volume = cls.create_volume()
+
+ @test.idempotent_id('0248a46c-e226-4933-be10-ad6fca8227e7')
+ def test_get_pools_without_details(self):
+ volume_info = self.admin_volume_client. \
+ show_volume(self.volume['id'])['volume']
+ cinder_pools = self.admin_volume_client.show_pools()['pools']
+ self.assertIn(volume_info['os-vol-host-attr:host'],
+ [pool['name'] for pool in cinder_pools])
+
+ @test.idempotent_id('d4bb61f7-762d-4437-b8a4-5785759a0ced')
+ def test_get_pools_with_details(self):
+ volume_info = self.admin_volume_client. \
+ show_volume(self.volume['id'])['volume']
+ cinder_pools = self.admin_volume_client.\
+ show_pools(detail=True)['pools']
+ self.assertIn(volume_info['os-vol-host-attr:host'],
+ [pool['name'] for pool in cinder_pools])
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 866e676..6707121 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -14,6 +14,7 @@
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
@@ -34,6 +35,9 @@
cls.name_field = cls.special_fields['name_field']
cls.descrip_field = cls.special_fields['descrip_field']
+ # Create 2 snapshots
+ for _ in xrange(2):
+ cls.create_snapshot(cls.volume_origin['id'])
def _detach(self, volume_id):
"""Detach volume."""
@@ -58,6 +62,14 @@
('details' if with_detail else '', key)
self.assertEqual(params[key], snap[key], msg)
+ def _list_snapshots_by_param_limit(self, limit, expected_elements):
+ """list snapshots by limit param"""
+ # Get snapshots list using limit parameter
+ fetched_snap_list = self.snapshots_client.list_snapshots(
+ limit=limit)['snapshots']
+ # Validating filtered snapshots length equals to expected_elements
+ self.assertEqual(expected_elements, len(fetched_snap_list))
+
@test.idempotent_id('b467b54c-07a4-446d-a1cf-651dedcc3ff1')
@test.services('compute')
def test_snapshot_create_with_volume_in_use(self):
@@ -178,6 +190,25 @@
self.volumes_client.wait_for_resource_deletion(volume['id'])
self.cleanup_snapshot(snapshot)
+ @test.idempotent_id('db4d8e0a-7a2e-41cc-a712-961f6844e896')
+ def test_snapshot_list_param_limit(self):
+ # List returns limited elements
+ self._list_snapshots_by_param_limit(limit=1, expected_elements=1)
+
+ @test.idempotent_id('a1427f61-420e-48a5-b6e3-0b394fa95400')
+ def test_snapshot_list_param_limit_equals_infinite(self):
+ # List returns all elements when request limit exceeded
+ # snapshots number
+ snap_list = self.snapshots_client.list_snapshots()['snapshots']
+ self._list_snapshots_by_param_limit(limit=100000,
+ expected_elements=len(snap_list))
+
+ @decorators.skip_because(bug='1540893')
+ @test.idempotent_id('e3b44b7f-ae87-45b5-8a8c-66110eb24d0a')
+ def test_snapshot_list_param_limit_equals_zero(self):
+ # List returns zero elements
+ self._list_snapshots_by_param_limit(limit=0, expected_elements=0)
+
def cleanup_snapshot(self, snapshot):
# Delete the snapshot
self.snapshots_client.delete_snapshot(snapshot['id'])
diff --git a/tempest/clients.py b/tempest/clients.py
index 0eded8b..2ad1733 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -138,9 +138,6 @@
from tempest.services.object_storage.object_client import ObjectClient
from tempest.services.orchestration.json.orchestration_client import \
OrchestrationClient
-from tempest.services.telemetry.json.alarming_client import AlarmingClient
-from tempest.services.telemetry.json.telemetry_client import \
- TelemetryClient
from tempest.services.volume.v1.json.admin.hosts_client import \
HostsClient as VolumeHostsClient
from tempest.services.volume.v1.json.admin.quotas_client import \
@@ -324,20 +321,6 @@
build_interval=CONF.network.build_interval,
build_timeout=CONF.network.build_timeout,
**self.default_params)
- if CONF.service_available.ceilometer:
- self.telemetry_client = TelemetryClient(
- self.auth_provider,
- CONF.telemetry.catalog_type,
- CONF.identity.region,
- endpoint_type=CONF.telemetry.endpoint_type,
- **self.default_params_with_timeout_values)
- if CONF.service_available.aodh:
- self.alarming_client = AlarmingClient(
- self.auth_provider,
- CONF.alarming.catalog_type,
- CONF.identity.region,
- endpoint_type=CONF.alarming.endpoint_type,
- **self.default_params_with_timeout_values)
if CONF.service_available.glance:
self.image_client = ImagesClient(
self.auth_provider,
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index f51fc53..9e86b48 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -33,7 +33,6 @@
CONF_TENANTS = None
CONF_USERS = None
-IS_AODH = None
IS_CINDER = None
IS_GLANCE = None
IS_HEAT = None
@@ -51,14 +50,12 @@
global CONF_PUB_ROUTER
global CONF_TENANTS
global CONF_USERS
- global IS_AODH
global IS_CINDER
global IS_GLANCE
global IS_HEAT
global IS_NEUTRON
global IS_NOVA
- IS_AODH = CONF.service_available.aodh
IS_CINDER = CONF.service_available.cinder
IS_GLANCE = CONF.service_available.glance
IS_HEAT = CONF.service_available.heat
@@ -706,32 +703,6 @@
self.data['subnets'] = subnets
-# Telemetry services
-class TelemetryAlarmService(BaseService):
- def __init__(self, manager, **kwargs):
- super(TelemetryAlarmService, self).__init__(kwargs)
- self.client = manager.alarming_client
-
- def list(self):
- client = self.client
- alarms = client.list_alarms()
- LOG.debug("List count, %s Alarms" % len(alarms))
- return alarms
-
- def delete(self):
- client = self.client
- alarms = self.list()
- for alarm in alarms:
- try:
- client.delete_alarm(alarm['id'])
- except Exception:
- LOG.exception("Delete Alarms exception.")
-
- def dry_run(self):
- alarms = self.list()
- self.data['alarms'] = alarms
-
-
# begin global services
class FlavorService(BaseService):
def __init__(self, manager, **kwargs):
@@ -976,8 +947,8 @@
def get_tenant_cleanup_services():
tenant_services = []
- if IS_AODH:
- tenant_services.append(TelemetryAlarmService)
+ # TODO(gmann): Tempest should provide some plugin hook for cleanup
+ # script extension to plugin tests also.
if IS_NOVA:
tenant_services.append(ServerService)
tenant_services.append(KeyPairService)
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 2a4e314..9b3cac7 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -113,7 +113,6 @@
import netaddr
from oslo_log import log as logging
-from oslo_utils import timeutils
import six
import yaml
@@ -138,8 +137,6 @@
from tempest.services.network.json import routers_client
from tempest.services.object_storage import container_client
from tempest.services.object_storage import object_client
-from tempest.services.telemetry.json import alarming_client
-from tempest.services.telemetry.json import telemetry_client
from tempest.services.volume.v1.json import volumes_client
CONF = config.CONF
@@ -244,18 +241,6 @@
build_interval=CONF.image.build_interval,
build_timeout=CONF.image.build_timeout,
**default_params)
- self.telemetry = telemetry_client.TelemetryClient(
- _auth,
- CONF.telemetry.catalog_type,
- CONF.identity.region,
- endpoint_type=CONF.telemetry.endpoint_type,
- **default_params_with_timeout_values)
- self.alarming = alarming_client.AlarmingClient(
- _auth,
- CONF.alarm.catalog_type,
- CONF.identity.region,
- endpoint_type=CONF.alarm.endpoint_type,
- **default_params_with_timeout_values)
self.volumes = volumes_client.VolumesClient(
_auth,
CONF.volume.catalog_type,
@@ -461,7 +446,6 @@
self.check_objects()
self.check_servers()
self.check_volumes()
- self.check_telemetry()
self.check_secgroups()
# validate neutron is enabled and ironic disabled:
@@ -563,27 +547,6 @@
found,
"Couldn't find expected secgroup %s" % secgroup['name'])
- def check_telemetry(self):
- """Check that ceilometer provides a sane sample.
-
- Confirm that there is more than one sample and that they have the
- expected metadata.
-
- If in check mode confirm that the oldest sample available is from
- before the upgrade.
- """
- if not self.res.get('telemetry'):
- return
- LOG.info("checking telemetry")
- for server in self.res['servers']:
- client = client_for_user(server['owner'])
- body = client.telemetry.list_samples(
- 'instance',
- query=('metadata.display_name', 'eq', server['name'])
- )
- self.assertTrue(len(body) >= 1, 'expecting at least one sample')
- self._confirm_telemetry_sample(server, body[-1])
-
def check_volumes(self):
"""Check that the volumes are still there and attached."""
if not self.res.get('volumes'):
@@ -602,26 +565,6 @@
self.assertEqual(vol_body['id'], attachment['volume_id'])
self.assertEqual(server_id, attachment['server_id'])
- def _confirm_telemetry_sample(self, server, sample):
- """Check this sample matches the expected resource metadata."""
- # Confirm display_name
- self.assertEqual(server['name'],
- sample['resource_metadata']['display_name'])
- # Confirm instance_type of flavor
- flavor = sample['resource_metadata'].get(
- 'flavor.name',
- sample['resource_metadata'].get('instance_type')
- )
- self.assertEqual(server['flavor'], flavor)
- # Confirm the oldest sample was created before upgrade.
- if OPTS.mode == 'check':
- oldest_timestamp = timeutils.normalize_time(
- timeutils.parse_isotime(sample['timestamp']))
- self.assertTrue(
- oldest_timestamp < JAVELIN_START,
- 'timestamp should come before start of second javelin run'
- )
-
def check_networking(self):
"""Check that the networks are still there."""
for res_type in ('networks', 'subnets', 'routers'):
diff --git a/tempest/cmd/resources.yaml b/tempest/cmd/resources.yaml
index 2d6664c..5c62ee3 100644
--- a/tempest/cmd/resources.yaml
+++ b/tempest/cmd/resources.yaml
@@ -93,4 +93,3 @@
name: javelin1
owner: javelin
file: /etc/hosts
-telemetry: true
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 39fed63..72ea894 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -282,8 +282,6 @@
'object_storage': 'swift',
'compute': 'nova',
'orchestration': 'heat',
- 'metering': 'ceilometer',
- 'telemetry': 'ceilometer',
'data_processing': 'sahara',
'baremetal': 'ironic',
'identity': 'keystone',
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
index a4b2ae8..5cce0bb 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -88,6 +88,11 @@
def __getattr__(self, item):
return getattr(self._credentials, item)
+ def __str__(self):
+ _format = "Credentials: %s, Network: %s, Subnet: %s, Router: %s"
+ return _format % (self._credentials, self.network, self.subnet,
+ self.router)
+
def set_resources(self, **kwargs):
for key in kwargs.keys():
if hasattr(self, key):
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 6cb43f3..7c73ada 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -14,7 +14,6 @@
from oslo_concurrency import lockutils
from tempest import clients
-from tempest.common import cred_provider
from tempest.common import dynamic_creds
from tempest.common import preprov_creds
from tempest import config
@@ -62,89 +61,6 @@
]))
-class LegacyCredentialProvider(cred_provider.CredentialProvider):
-
- def __init__(self, identity_version):
- """Credentials provider which returns credentials from tempest.conf
-
- Credentials provider which always returns the first and second
- configured accounts as primary and alt users.
- Credentials from tempest.conf are deprecated, and this credential
- provider is also accordingly.
-
- This credential provider can be used in case of serial test execution
- to preserve the current behaviour of the serial tempest run.
-
- :param identity_version: Version of the identity API
- :return: CredentialProvider
- """
- super(LegacyCredentialProvider, self).__init__(
- identity_version=identity_version)
- self._creds = {}
-
- def _unique_creds(self, cred_arg=None):
- """Verify that the configured credentials are valid and distinct """
- try:
- user = self.get_primary_creds()
- alt_user = self.get_alt_creds()
- return getattr(user, cred_arg) != getattr(alt_user, cred_arg)
- except exceptions.InvalidCredentials as ic:
- msg = "At least one of the configured credentials is " \
- "not valid: %s" % ic.message
- raise exceptions.InvalidConfiguration(msg)
-
- def is_multi_user(self):
- return self._unique_creds('username')
-
- def is_multi_tenant(self):
- return self._unique_creds('tenant_id')
-
- def get_primary_creds(self):
- if self._creds.get('primary'):
- return self._creds.get('primary')
- primary_credential = get_configured_credentials(
- credential_type='user', fill_in=False,
- identity_version=self.identity_version)
- self._creds['primary'] = cred_provider.TestResources(
- primary_credential)
- return self._creds['primary']
-
- def get_alt_creds(self):
- if self._creds.get('alt'):
- return self._creds.get('alt')
- alt_credential = get_configured_credentials(
- credential_type='alt_user', fill_in=False,
- identity_version=self.identity_version)
- self._creds['alt'] = cred_provider.TestResources(
- alt_credential)
- return self._creds['alt']
-
- def clear_creds(self):
- self._creds = {}
-
- def get_admin_creds(self):
- if self._creds.get('admin'):
- return self._creds.get('admin')
- creds = get_configured_credentials(
- "identity_admin", fill_in=False)
- self._creds['admin'] = cred_provider.TestResources(creds)
- return self._creds['admin']
-
- def get_creds_by_roles(self, roles, force_new=False):
- msg = "Credentials being specified through the config file can not be"\
- " used with tests that specify using credentials by roles. "\
- "Either exclude/skip the tests doing this or use either a "\
- "test_accounts_file or dynamic credentials."
- raise exceptions.InvalidConfiguration(msg)
-
- def is_role_available(self, role):
- # NOTE(andreaf) LegacyCredentialProvider does not support credentials
- # by role, so returning always False.
- # Test that rely on credentials by role should use this to skip
- # when this is credential provider is used
- return False
-
-
# Return the right implementation of CredentialProvider based on config
# Dropping interface and password, as they are never used anyways
# TODO(andreaf) Drop them from the CredentialsProvider interface completely
@@ -172,9 +88,8 @@
name=name, identity_version=identity_version,
**_get_preprov_provider_params())
else:
- # Dynamic credentials are disabled, and the account file is not
- # defined - we fall back on credentials configured in tempest.conf
- return LegacyCredentialProvider(identity_version=identity_version)
+ raise exceptions.InvalidConfiguration(
+ 'A valid credential provider is needed')
# We want a helper function here to check and see if admin credentials
@@ -218,7 +133,9 @@
identity_version=identity_version, name='check_alt',
**_get_preprov_provider_params())
else:
- check_accounts = LegacyCredentialProvider(identity_version)
+ raise exceptions.InvalidConfiguration(
+ 'A valid credential provider is needed')
+
try:
if not check_accounts.is_multi_user():
return False
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index d374be4..a63af38 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -154,6 +154,20 @@
return cred_provider.TestResources(creds)
def _create_network_resources(self, tenant_id):
+ """The function creates network resources in the given tenant.
+
+ The function checks if network_resources class member is empty,
+ In case it is, it will create a network, a subnet and a router for
+ the tenant according to the given tenant id parameter.
+ Otherwise it will create a network resource according
+ to the values from network_resources dict.
+
+ :param tenant_id: The tenant id to create resources for.
+ :type tenant_id: str
+ :raises: InvalidConfiguration, Exception
+ :returns: network resources(network,subnet,router)
+ :rtype: tuple
+ """
network = None
subnet = None
router = None
diff --git a/tempest/common/generator/base_generator.py b/tempest/common/generator/base_generator.py
index 3a51f2e..0647edb 100644
--- a/tempest/common/generator/base_generator.py
+++ b/tempest/common/generator/base_generator.py
@@ -169,7 +169,7 @@
expected_result = generator_result[2]
element = path.pop()
if len(path) > 0:
- schema_snip = reduce(dict.get, path, schema)
+ schema_snip = six.moves.reduce(dict.get, path, schema)
schema_snip[element] = invalid_snippet
else:
schema[element] = invalid_snippet
diff --git a/tempest/common/utils/net_utils.py b/tempest/common/utils/net_utils.py
new file mode 100644
index 0000000..d98fb32
--- /dev/null
+++ b/tempest/common/utils/net_utils.py
@@ -0,0 +1,48 @@
+# Copyright 2016 Hewlett Packard Enterprise Development Company
+#
+# 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 itertools
+import netaddr
+
+from tempest.lib import exceptions as lib_exc
+
+
+def get_unused_ip_addresses(ports_client, subnets_client,
+ network_id, subnet_id, count):
+
+ """Return a list with the specified number of unused IP addresses
+
+ This method uses the given ports_client to find the specified number of
+ unused IP addresses on the given subnet using the supplied subnets_client
+ """
+
+ ports = ports_client.list_ports(network_id=network_id)['ports']
+ subnet = subnets_client.show_subnet(subnet_id)
+ ip_net = netaddr.IPNetwork(subnet['subnet']['cidr'])
+ subnet_set = netaddr.IPSet(ip_net.iter_hosts())
+ alloc_set = netaddr.IPSet()
+
+ # prune out any addresses already allocated to existing ports
+ for port in ports:
+ for fixed_ip in port.get('fixed_ips'):
+ alloc_set.add(fixed_ip['ip_address'])
+
+ av_set = subnet_set - alloc_set
+ ip_list = [str(ip) for ip in itertools.islice(av_set, count)]
+
+ if len(ip_list) != count:
+ msg = "Insufficient IP addresses available"
+ raise lib_exc.BadRequest(message=msg)
+
+ return ip_list
diff --git a/tempest/config.py b/tempest/config.py
index db94e6c..f5125b5 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -160,41 +160,9 @@
'publicURL', 'adminURL', 'internalURL'],
help="The endpoint type to use for OpenStack Identity "
"(Keystone) API v3"),
- cfg.StrOpt('username',
- help="Username to use for Nova API requests.",
- deprecated_for_removal=True),
- cfg.StrOpt('project_name',
- deprecated_name='tenant_name',
- help="Project name to use for Nova API requests.",
- deprecated_for_removal=True),
cfg.StrOpt('admin_role',
default='admin',
help="Role required to administrate keystone."),
- cfg.StrOpt('password',
- help="API key to use when authenticating.",
- secret=True,
- deprecated_for_removal=True),
- cfg.StrOpt('domain_name',
- help="Domain name for authentication (Keystone V3)."
- "The same domain applies to user and project",
- deprecated_for_removal=True),
- cfg.StrOpt('alt_username',
- help="Username of alternate user to use for Nova API "
- "requests.",
- deprecated_for_removal=True),
- cfg.StrOpt('alt_project_name',
- deprecated_name='alt_tenant_name',
- help="Alternate user's Project name to use for Nova API "
- "requests.",
- deprecated_for_removal=True),
- cfg.StrOpt('alt_password',
- help="API key to use when authenticating as alternate user.",
- secret=True,
- deprecated_for_removal=True),
- cfg.StrOpt('alt_domain_name',
- help="Alternate domain name for authentication (Keystone V3)."
- "The same domain applies to user and project",
- deprecated_for_removal=True),
cfg.StrOpt('default_domain_id',
default='default',
help="ID of the default domain"),
@@ -672,7 +640,7 @@
cfg.StrOpt('network_for_ssh',
default='public',
help="Network used for SSH connections. Ignored if "
- "use_floatingip_for_ssh=true or run_validation=false.",
+ "connect_method=floating or run_validation=false.",
deprecated_opts=[cfg.DeprecatedOpt('network_for_ssh',
group='compute')]),
]
@@ -882,50 +850,6 @@
]
-telemetry_group = cfg.OptGroup(name='telemetry',
- title='Telemetry Service Options')
-
-TelemetryGroup = [
- cfg.StrOpt('catalog_type',
- default='metering',
- help="Catalog type of the Telemetry service."),
- cfg.StrOpt('endpoint_type',
- default='publicURL',
- choices=['public', 'admin', 'internal',
- 'publicURL', 'adminURL', 'internalURL'],
- help="The endpoint type to use for the telemetry service."),
- cfg.BoolOpt('too_slow_to_test',
- default=True,
- deprecated_for_removal=True,
- help="This variable is used as flag to enable "
- "notification tests")
-]
-
-alarming_group = cfg.OptGroup(name='alarming',
- title='Alarming Service Options')
-
-AlarmingGroup = [
- cfg.StrOpt('catalog_type',
- default='alarming',
- help="Catalog type of the Alarming service."),
- cfg.StrOpt('endpoint_type',
- default='publicURL',
- choices=['public', 'admin', 'internal',
- 'publicURL', 'adminURL', 'internalURL'],
- help="The endpoint type to use for the alarming service."),
-]
-
-
-telemetry_feature_group = cfg.OptGroup(name='telemetry-feature-enabled',
- title='Enabled Ceilometer Features')
-
-TelemetryFeaturesGroup = [
- cfg.BoolOpt('events',
- default=False,
- help="Runs Ceilometer event-related tests"),
-]
-
-
dashboard_group = cfg.OptGroup(name="dashboard",
title="Dashboard options")
@@ -1070,12 +994,6 @@
cfg.BoolOpt('heat',
default=False,
help="Whether or not Heat is expected to be available"),
- cfg.BoolOpt('ceilometer',
- default=True,
- help="Whether or not Ceilometer is expected to be available"),
- cfg.BoolOpt('aodh',
- default=False,
- help="Whether or not Aodh is expected to be available"),
cfg.BoolOpt('horizon',
default=True,
help="Whether or not Horizon is expected to be available"),
@@ -1221,9 +1139,6 @@
(object_storage_feature_group, ObjectStoreFeaturesGroup),
(database_group, DatabaseGroup),
(orchestration_group, OrchestrationGroup),
- (telemetry_group, TelemetryGroup),
- (telemetry_feature_group, TelemetryFeaturesGroup),
- (alarming_group, AlarmingGroup),
(dashboard_group, DashboardGroup),
(data_processing_group, DataProcessingGroup),
(data_processing_feature_group, DataProcessingFeaturesGroup),
@@ -1292,8 +1207,6 @@
'object-storage-feature-enabled']
self.database = _CONF.database
self.orchestration = _CONF.orchestration
- self.telemetry = _CONF.telemetry
- self.telemetry_feature_enabled = _CONF['telemetry-feature-enabled']
self.dashboard = _CONF.dashboard
self.data_processing = _CONF['data-processing']
self.data_processing_feature_enabled = _CONF[
@@ -1305,12 +1218,6 @@
self.baremetal = _CONF.baremetal
self.input_scenario = _CONF['input-scenario']
self.negative = _CONF.negative
- _CONF.set_default('domain_name',
- self.auth.default_credentials_domain_name,
- group='identity')
- _CONF.set_default('alt_domain_name',
- self.auth.default_credentials_domain_name,
- group='identity')
logging.tempest_set_log_file('tempest.log')
def __init__(self, parse_conf=True, config_path=None):
@@ -1390,7 +1297,7 @@
def skip_unless_config(*args):
- """Decorator to raise a skip if a config opt doesn't exist and is False
+ """Decorator to raise a skip if a config opt doesn't exist or is False
:param str group: The first arg, the option group to check
:param str name: The second arg, the option name to check
@@ -1407,21 +1314,21 @@
if not hasattr(CONF, group):
msg = "Config group %s doesn't exist" % group
raise testtools.TestCase.skipException(msg)
- else:
- conf_group = getattr(CONF, group)
- if not hasattr(conf_group, name):
- msg = "Config option %s.%s doesn't exist" % (group,
- name)
- raise testtools.TestCase.skipException(msg)
+
+ conf_group = getattr(CONF, group)
+ if not hasattr(conf_group, name):
+ msg = "Config option %s.%s doesn't exist" % (group,
+ name)
+ raise testtools.TestCase.skipException(msg)
+
+ value = getattr(conf_group, name)
+ if not value:
+ if len(args) == 3:
+ msg = args[2]
else:
- value = getattr(conf_group, name)
- if not value:
- if len(args) == 3:
- msg = args[2]
- else:
- msg = "Config option %s.%s is false" % (group,
- name)
- raise testtools.TestCase.skipException(msg)
+ msg = "Config option %s.%s is false" % (group,
+ name)
+ raise testtools.TestCase.skipException(msg)
return f(self, *func_args, **func_kwargs)
return wrapper
return decorator
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 5943adf..aff9dee 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -19,8 +19,7 @@
PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
- 'trove', 'ironic', 'savanna', 'heat', 'ceilometer',
- 'sahara']
+ 'trove', 'ironic', 'savanna', 'heat', 'sahara']
PYTHON_CLIENT_RE = re.compile('import (%s)client' % '|'.join(PYTHON_CLIENTS))
TEST_DEFINITION = re.compile(r'^\s*def test.*')
diff --git a/tempest/hacking/ignored_list_T110.txt b/tempest/hacking/ignored_list_T110.txt
index 380c173..4ef9012 100644
--- a/tempest/hacking/ignored_list_T110.txt
+++ b/tempest/hacking/ignored_list_T110.txt
@@ -1,6 +1,4 @@
./tempest/services/object_storage/object_client.py
-./tempest/services/telemetry/json/alarming_client.py
-./tempest/services/telemetry/json/telemetry_client.py
./tempest/services/volume/base/base_qos_client.py
./tempest/services/volume/base/base_backups_client.py
./tempest/services/baremetal/base.py
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index d001d27..30750de 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -15,6 +15,7 @@
# under the License.
import collections
+import email.utils
import logging as real_logging
import re
import time
@@ -400,6 +401,10 @@
caller_name=None, extra=None):
if 'X-Auth-Token' in req_headers:
req_headers['X-Auth-Token'] = '<omitted>'
+ # A shallow copy is sufficient
+ resp_log = resp.copy()
+ if 'x-subject-token' in resp_log:
+ resp_log['x-subject-token'] = '<omitted>'
log_fmt = """Request - Headers: %s
Body: %s
Response - Headers: %s
@@ -409,7 +414,7 @@
log_fmt % (
str(req_headers),
self._safe_body(req_body),
- str(resp),
+ str(resp_log),
self._safe_body(resp_body)),
extra=extra)
@@ -633,7 +638,10 @@
resp, self._parse_resp(resp_body)) and
retry < MAX_RECURSION_DEPTH):
retry += 1
- delay = int(resp['retry-after'])
+ delay = self._get_retry_after_delay(resp)
+ self.LOG.debug(
+ "Sleeping %s seconds based on retry-after header", delay
+ )
time.sleep(delay)
resp, resp_body = self._request(method, url,
headers=headers, body=body)
@@ -641,6 +649,51 @@
resp, resp_body)
return resp, resp_body
+ def _get_retry_after_delay(self, resp):
+ """Extract the delay from the retry-after header.
+
+ This supports both integer and HTTP date formatted retry-after headers
+ per RFC 2616.
+
+ :param resp: The response containing the retry-after headers
+ :rtype: int
+ :return: The delay in seconds, clamped to be at least 1 second
+ :raises ValueError: On failing to parse the delay
+ """
+ delay = None
+ try:
+ delay = int(resp['retry-after'])
+ except (ValueError, KeyError):
+ pass
+
+ try:
+ retry_timestamp = self._parse_http_date(resp['retry-after'])
+ date_timestamp = self._parse_http_date(resp['date'])
+ delay = int(retry_timestamp - date_timestamp)
+ except (ValueError, OverflowError, KeyError):
+ pass
+
+ if delay is None:
+ raise ValueError(
+ "Failed to parse retry-after header %r as either int or "
+ "HTTP-date." % resp.get('retry-after')
+ )
+
+ # Retry-after headers do not have sub-second precision. Clients may
+ # receive a delay of 0. After sleeping 0 seconds, we would (likely) hit
+ # another 413. To avoid this, always sleep at least 1 second.
+ return max(1, delay)
+
+ def _parse_http_date(self, val):
+ """Parse an HTTP date, like 'Fri, 31 Dec 1999 23:59:59 GMT'.
+
+ Return an epoch timestamp (float), as returned by time.mktime().
+ """
+ parts = email.utils.parsedate(val)
+ if not parts:
+ raise ValueError("Failed to parse date %s" % val)
+ return time.mktime(parts)
+
def _error_checker(self, method, url,
headers, body, resp, resp_body):
@@ -767,10 +820,7 @@
if (not isinstance(resp_body, collections.Mapping) or
'retry-after' not in resp):
return True
- over_limit = resp_body.get('overLimit', None)
- if not over_limit:
- return True
- return 'exceed' in over_limit.get('message', 'blabla')
+ return 'exceed' in resp_body.get('message', 'blabla')
def wait_for_resource_deletion(self, id):
"""Waits for a resource to be deleted
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 0472eda..8e4eca1 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -61,7 +61,7 @@
post_body = {'server': body}
if hints:
- post_body = dict(post_body.items() + hints.items())
+ post_body.update(hints)
post_body = json.dumps(post_body)
resp, body = self.post('servers', post_body)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 956fe88..262ec50 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -465,7 +465,7 @@
thing_id=image_id, thing_id_param='id',
cleanup_callable=self.delete_wrapper,
cleanup_args=[_image_client.delete_image, image_id])
- snapshot_image = _image_client.get_image_meta(image_id)
+ snapshot_image = _image_client.check_image(image_id)
bdm = snapshot_image.get('properties', {}).get('block_device_mapping')
if bdm:
diff --git a/tempest/scenario/test_object_storage_telemetry_middleware.py b/tempest/scenario/test_object_storage_telemetry_middleware.py
deleted file mode 100644
index eee4d3d..0000000
--- a/tempest/scenario/test_object_storage_telemetry_middleware.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# Copyright 2014 Red Hat
-#
-# 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 oslo_log import log as logging
-
-from tempest import config
-from tempest.scenario import manager
-from tempest import test
-
-CONF = config.CONF
-
-LOG = logging.getLogger(__name__)
-
-# Loop for up to 120 seconds waiting on notifications
-# NOTE(chdent): The choice of 120 seconds is fairly
-# arbitrary: Long enough to give the notifications the
-# chance to travel across a highly latent bus but not
-# so long as to allow excessive latency to never be visible.
-# TODO(chdent): Ideally this value would come from configuration.
-NOTIFICATIONS_WAIT = 120
-NOTIFICATIONS_SLEEP = 1
-
-
-class TestObjectStorageTelemetry(manager.ObjectStorageScenarioTest):
- """Test that swift uses the ceilometer middleware.
-
- * create container.
- * upload a file to the created container.
- * retrieve the file from the created container.
- * wait for notifications from ceilometer.
- """
-
- @classmethod
- def skip_checks(cls):
- super(TestObjectStorageTelemetry, cls).skip_checks()
- if not CONF.service_available.ceilometer:
- skip_msg = ("%s skipped as ceilometer is not available" %
- cls.__name__)
- raise cls.skipException(skip_msg)
-
- @classmethod
- def setup_clients(cls):
- super(TestObjectStorageTelemetry, cls).setup_clients()
- cls.telemetry_client = cls.os_operator.telemetry_client
-
- def _confirm_notifications(self, container_name, obj_name):
- # NOTE: Loop seeking for appropriate notifications about the containers
- # and objects sent to swift.
-
- def _check_samples():
- # NOTE: Return True only if we have notifications about some
- # containers and some objects and the notifications are about
- # the expected containers and objects.
- # Otherwise returning False will case _check_samples to be
- # called again.
- results = self.telemetry_client.list_samples(
- 'storage.objects.incoming.bytes')
- LOG.debug('got samples %s', results)
-
- # Extract container info from samples.
- containers, objects = [], []
- for sample in results:
- meta = sample['resource_metadata']
- if meta.get('container') and meta['container'] != 'None':
- containers.append(meta['container'])
- elif (meta.get('target.metadata:container') and
- meta['target.metadata:container'] != 'None'):
- containers.append(meta['target.metadata:container'])
-
- if meta.get('object') and meta['object'] != 'None':
- objects.append(meta['object'])
- elif (meta.get('target.metadata:object') and
- meta['target.metadata:object'] != 'None'):
- objects.append(meta['target.metadata:object'])
-
- return (container_name in containers and obj_name in objects)
-
- self.assertTrue(test.call_until_true(_check_samples,
- NOTIFICATIONS_WAIT,
- NOTIFICATIONS_SLEEP),
- 'Correct notifications were not received after '
- '%s seconds.' % NOTIFICATIONS_WAIT)
-
- @test.idempotent_id('6d6b88e5-3e38-41bc-b34a-79f713a6cb84')
- @test.services('object_storage', 'telemetry')
- def test_swift_middleware_notifies(self):
- container_name = self.create_container()
- obj_name, _ = self.upload_object_to_container(container_name)
- self._confirm_notifications(container_name, obj_name)
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 4b932ce..504d72b 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -31,7 +31,7 @@
"""The test suite for server advanced operations
This test case stresses some advanced server instance operations:
- * Resizing an instance
+ * Resizing a volume-backed instance
* Sequence suspend resume
"""
@@ -50,10 +50,10 @@
@test.idempotent_id('e6c28180-7454-4b59-b188-0257af08a63b')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize is not available.')
- @test.services('compute')
- def test_resize_server_confirm(self):
+ @test.services('compute', 'volume')
+ def test_resize_volume_backed_server_confirm(self):
# We create an instance for use in this test
- instance = self.create_server(wait_until='ACTIVE')
+ instance = self.create_server(wait_until='ACTIVE', volume_backed=True)
instance_id = instance['id']
resize_flavor = CONF.compute.flavor_ref_alt
LOG.debug("Resizing instance %s from flavor %s to flavor %s",
diff --git a/tempest/services/identity/v3/json/projects_client.py b/tempest/services/identity/v3/json/projects_client.py
index dc553d0..97e43df 100644
--- a/tempest/services/identity/v3/json/projects_client.py
+++ b/tempest/services/identity/v3/json/projects_client.py
@@ -23,17 +23,15 @@
api_version = "v3"
def create_project(self, name, **kwargs):
- """Creates a project."""
- description = kwargs.get('description', None)
- en = kwargs.get('enabled', True)
- domain_id = kwargs.get('domain_id', 'default')
- post_body = {
- 'description': description,
- 'domain_id': domain_id,
- 'enabled': en,
- 'name': name
- }
- post_body = json.dumps({'project': post_body})
+ """Create a Project.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v3.html#createProject
+
+ """
+ # Include the project name to the kwargs parameters
+ kwargs['name'] = name
+ post_body = json.dumps({'project': kwargs})
resp, body = self.post('projects', post_body)
self.expected_success(201, resp.status)
body = json.loads(body)
@@ -49,19 +47,13 @@
return rest_client.ResponseBody(resp, body)
def update_project(self, project_id, **kwargs):
- body = self.show_project(project_id)['project']
- name = kwargs.get('name', body['name'])
- desc = kwargs.get('description', body['description'])
- en = kwargs.get('enabled', body['enabled'])
- domain_id = kwargs.get('domain_id', body['domain_id'])
- post_body = {
- 'id': project_id,
- 'name': name,
- 'description': desc,
- 'enabled': en,
- 'domain_id': domain_id,
- }
- post_body = json.dumps({'project': post_body})
+ """Update a Project.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v3.html#updateProject
+
+ """
+ post_body = json.dumps({'project': kwargs})
resp, body = self.patch('projects/%s' % project_id, post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
diff --git a/tempest/services/image/v1/json/images_client.py b/tempest/services/image/v1/json/images_client.py
index e29ff89..7274e5f 100644
--- a/tempest/services/image/v1/json/images_client.py
+++ b/tempest/services/image/v1/json/images_client.py
@@ -198,7 +198,8 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def get_image_meta(self, image_id):
+ def check_image(self, image_id):
+ """Check image metadata."""
url = 'v1/images/%s' % image_id
resp, __ = self.head(url)
self.expected_success(200, resp.status)
@@ -206,6 +207,7 @@
return rest_client.ResponseBody(resp, body)
def show_image(self, image_id):
+ """Get image details plus the image itself."""
url = 'v1/images/%s' % image_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
@@ -213,7 +215,7 @@
def is_resource_deleted(self, id):
try:
- if self.get_image_meta(id)['status'] == 'deleted':
+ if self.check_image(id)['status'] == 'deleted':
return True
except lib_exc.NotFound:
return True
@@ -257,17 +259,11 @@
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp)
- # NOTE(afazekas): just for the wait function
- def _get_image_status(self, image_id):
- meta = self.get_image_meta(image_id)
- status = meta['status']
- return status
-
# NOTE(afazkas): Wait reinvented again. It is not in the correct layer
def wait_for_image_status(self, image_id, status):
"""Waits for a Image to reach a given status."""
start_time = time.time()
- old_value = value = self._get_image_status(image_id)
+ old_value = value = self.check_image(image_id)['status']
while True:
dtime = time.time() - start_time
time.sleep(self.build_interval)
@@ -292,4 +288,4 @@
raise exceptions.TimeoutException(message)
time.sleep(self.build_interval)
old_value = value
- value = self._get_image_status(image_id)
+ value = self.check_image(image_id)['status']
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 0acd4ad..fa43d94 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -18,6 +18,7 @@
from six.moves.urllib import parse as urlparse
from tempest.lib.common import rest_client
+from tempest.lib import exceptions
class ObjectClient(rest_client.RestClient):
@@ -170,29 +171,67 @@
def create_object_continue(self, container, object_name,
data, metadata=None):
- """Create storage object."""
+ """Put an object using Expect:100-continue"""
headers = {}
if metadata:
for key in metadata:
headers[str(key)] = metadata[key]
- if not data:
- headers['content-length'] = '0'
-
headers['X-Auth-Token'] = self.token
+ headers['content-length'] = 0 if data is None else len(data)
+ headers['Expect'] = '100-continue'
- conn = put_object_connection(self.base_url, str(container),
- str(object_name), data, None, headers)
+ parsed = urlparse.urlparse(self.base_url)
+ path = str(parsed.path) + "/"
+ path += "%s/%s" % (str(container), str(object_name))
+ conn = create_connection(parsed)
+
+ # Send the PUT request and the headers including the "Expect" header
+ conn.putrequest('PUT', path)
+
+ for header, value in six.iteritems(headers):
+ conn.putheader(header, value)
+ conn.endheaders()
+
+ # Read the 100 status prior to sending the data
response = conn.response_class(conn.sock,
strict=conn.strict,
method=conn._method)
- version, status, reason = response._read_status()
- resp = {'version': version,
- 'status': str(status),
- 'reason': reason}
+ _, status, _ = response._read_status()
- return resp
+ # toss the CRLF at the end of the response
+ response._safe_read(2)
+
+ # Expecting a 100 here, if not close and throw an exception
+ if status != 100:
+ conn.close()
+ pattern = "%s %s" % (
+ """Unexpected http success status code {0}.""",
+ """The expected status code is {1}""")
+ details = pattern.format(status, 100)
+ raise exceptions.UnexpectedResponseCode(details)
+
+ # If a continue was received go ahead and send the data
+ # and get the final response
+ conn.send(data)
+
+ resp = conn.getresponse()
+
+ return resp.status, resp.reason
+
+
+def create_connection(parsed_url):
+ """Helper function to create connection with httplib
+
+ :param parsed_url: parsed url of the remote location
+ """
+ if parsed_url.scheme == 'https':
+ conn = httplib.HTTPSConnection(parsed_url.netloc)
+ else:
+ conn = httplib.HTTPConnection(parsed_url.netloc)
+
+ return conn
def put_object_connection(base_url, container, name, contents=None,
@@ -211,13 +250,12 @@
:param query_string: if set will be appended with '?' to generated path
"""
parsed = urlparse.urlparse(base_url)
- if parsed.scheme == 'https':
- conn = httplib.HTTPSConnection(parsed.netloc)
- else:
- conn = httplib.HTTPConnection(parsed.netloc)
+
path = str(parsed.path) + "/"
path += "%s/%s" % (str(container), str(name))
+ conn = create_connection(parsed)
+
if query_string:
path += '?' + query_string
if headers:
diff --git a/tempest/services/telemetry/__init__.py b/tempest/services/telemetry/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/telemetry/__init__.py
+++ /dev/null
diff --git a/tempest/services/telemetry/json/__init__.py b/tempest/services/telemetry/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/telemetry/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/telemetry/json/alarming_client.py b/tempest/services/telemetry/json/alarming_client.py
deleted file mode 100644
index 703efdf..0000000
--- a/tempest/services/telemetry/json/alarming_client.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# Copyright 2014 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.
-
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-
-
-class AlarmingClient(rest_client.RestClient):
-
- version = '2'
- uri_prefix = "v2"
-
- def deserialize(self, body):
- return json.loads(body.replace("\n", ""))
-
- def serialize(self, body):
- return json.dumps(body)
-
- def list_alarms(self, query=None):
- uri = '%s/alarms' % self.uri_prefix
- uri_dict = {}
- if query:
- uri_dict = {'q.field': query[0],
- 'q.op': query[1],
- 'q.value': query[2]}
- if uri_dict:
- uri += "?%s" % urllib.urlencode(uri_dict)
- resp, body = self.get(uri)
- self.expected_success(200, resp.status)
- body = self.deserialize(body)
- return rest_client.ResponseBodyList(resp, body)
-
- def show_alarm(self, alarm_id):
- uri = '%s/alarms/%s' % (self.uri_prefix, alarm_id)
- resp, body = self.get(uri)
- self.expected_success(200, resp.status)
- body = self.deserialize(body)
- return rest_client.ResponseBody(resp, body)
-
- def show_alarm_history(self, alarm_id):
- uri = "%s/alarms/%s/history" % (self.uri_prefix, alarm_id)
- resp, body = self.get(uri)
- self.expected_success(200, resp.status)
- body = self.deserialize(body)
- return rest_client.ResponseBodyList(resp, body)
-
- def delete_alarm(self, alarm_id):
- uri = "%s/alarms/%s" % (self.uri_prefix, alarm_id)
- resp, body = self.delete(uri)
- self.expected_success(204, resp.status)
- if body:
- body = self.deserialize(body)
- return rest_client.ResponseBody(resp, body)
-
- def create_alarm(self, **kwargs):
- uri = "%s/alarms" % self.uri_prefix
- body = self.serialize(kwargs)
- resp, body = self.post(uri, body)
- self.expected_success(201, resp.status)
- body = self.deserialize(body)
- return rest_client.ResponseBody(resp, body)
-
- def update_alarm(self, alarm_id, **kwargs):
- uri = "%s/alarms/%s" % (self.uri_prefix, alarm_id)
- body = self.serialize(kwargs)
- resp, body = self.put(uri, body)
- self.expected_success(200, resp.status)
- body = self.deserialize(body)
- return rest_client.ResponseBody(resp, body)
-
- def show_alarm_state(self, alarm_id):
- uri = "%s/alarms/%s/state" % (self.uri_prefix, alarm_id)
- resp, body = self.get(uri)
- self.expected_success(200, resp.status)
- body = self.deserialize(body)
- return rest_client.ResponseBodyData(resp, body)
-
- def alarm_set_state(self, alarm_id, state):
- uri = "%s/alarms/%s/state" % (self.uri_prefix, alarm_id)
- body = self.serialize(state)
- resp, body = self.put(uri, body)
- self.expected_success(200, resp.status)
- body = self.deserialize(body)
- return rest_client.ResponseBodyData(resp, body)
diff --git a/tempest/services/telemetry/json/telemetry_client.py b/tempest/services/telemetry/json/telemetry_client.py
deleted file mode 100644
index df7d916..0000000
--- a/tempest/services/telemetry/json/telemetry_client.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright 2014 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.
-
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-
-
-class TelemetryClient(rest_client.RestClient):
-
- version = '2'
- uri_prefix = "v2"
-
- def deserialize(self, body):
- return json.loads(body.replace("\n", ""))
-
- def serialize(self, body):
- return json.dumps(body)
-
- def create_sample(self, meter_name, sample_list):
- uri = "%s/meters/%s" % (self.uri_prefix, meter_name)
- body = self.serialize(sample_list)
- resp, body = self.post(uri, body)
- self.expected_success(200, resp.status)
- body = self.deserialize(body)
- return rest_client.ResponseBody(resp, body)
-
- def _helper_list(self, uri, query=None, period=None):
- uri_dict = {}
- if query:
- uri_dict = {'q.field': query[0],
- 'q.op': query[1],
- 'q.value': query[2]}
- if period:
- uri_dict['period'] = period
- if uri_dict:
- uri += "?%s" % urllib.urlencode(uri_dict)
- resp, body = self.get(uri)
- self.expected_success(200, resp.status)
- body = self.deserialize(body)
- return rest_client.ResponseBodyList(resp, body)
-
- def list_resources(self, query=None):
- uri = '%s/resources' % self.uri_prefix
- return self._helper_list(uri, query)
-
- def list_meters(self, query=None):
- uri = '%s/meters' % self.uri_prefix
- return self._helper_list(uri, query)
-
- def list_statistics(self, meter, period=None, query=None):
- uri = "%s/meters/%s/statistics" % (self.uri_prefix, meter)
- return self._helper_list(uri, query, period)
-
- def list_samples(self, meter_id, query=None):
- uri = '%s/meters/%s' % (self.uri_prefix, meter_id)
- return self._helper_list(uri, query)
-
- def list_events(self, query=None):
- uri = '%s/events' % self.uri_prefix
- return self._helper_list(uri, query)
-
- def show_resource(self, resource_id):
- uri = '%s/resources/%s' % (self.uri_prefix, resource_id)
- resp, body = self.get(uri)
- self.expected_success(200, resp.status)
- body = self.deserialize(body)
- return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
index 4344802..6237745 100644
--- a/tempest/services/volume/base/base_volumes_client.py
+++ b/tempest/services/volume/base/base_volumes_client.py
@@ -62,6 +62,17 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
+ def show_pools(self, detail=False):
+ # List all the volumes pools (hosts)
+ url = 'scheduler-stats/get_pools'
+ if detail:
+ url += '?detail=True'
+
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
def show_volume(self, volume_id):
"""Returns the details of a single volume."""
url = "volumes/%s" % str(volume_id)
diff --git a/tempest/test.py b/tempest/test.py
index b32beaa..aefe1a9 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -78,7 +78,6 @@
'identity': True,
'object_storage': CONF.service_available.swift,
'dashboard': CONF.service_available.horizon,
- 'telemetry': CONF.service_available.ceilometer,
'data_processing': CONF.service_available.sahara,
'database': CONF.service_available.trove
}
@@ -94,7 +93,7 @@
def decorator(f):
services = ['compute', 'image', 'baremetal', 'volume', 'orchestration',
'network', 'identity', 'object_storage', 'dashboard',
- 'telemetry', 'data_processing', 'database']
+ 'data_processing', 'database']
for service in args:
if service not in services:
raise exceptions.InvalidServiceTag('%s is not a valid '
@@ -350,11 +349,14 @@
@classmethod
def setup_credentials(cls):
- """Allocate credentials and the client managers from them.
+ """Allocate credentials and create the client managers from them.
- A test class that requires network resources must override
- setup_credentials and defined the required resources before super
- is invoked.
+ For every element of credentials param function creates tenant/user,
+ Then it creates client manager for that credential.
+
+ Network related tests must override this function with
+ set_network_resources() method, otherwise it will create
+ network resources(network resources are created in a later step).
"""
for credentials_type in cls.credentials:
# This may raise an exception in case credentials are not available
diff --git a/tempest/tests/common/test_alt_available.py b/tempest/tests/common/test_alt_available.py
index d4cfab6..27db95c 100644
--- a/tempest/tests/common/test_alt_available.py
+++ b/tempest/tests/common/test_alt_available.py
@@ -49,28 +49,6 @@
else:
self.useFixture(mockpatch.Patch('os.path.isfile',
return_value=False))
- cred_prefix = ['', 'alt_']
- for ii in range(0, 2):
- if len(creds) > ii:
- username = 'u%s' % creds[ii]
- project = 't%s' % creds[ii]
- password = 'p'
- domain = 'd'
- else:
- username = None
- project = None
- password = None
- domain = None
-
- cfg.CONF.set_default('%susername' % cred_prefix[ii], username,
- group='identity')
- cfg.CONF.set_default('%sproject_name' % cred_prefix[ii],
- project, group='identity')
- cfg.CONF.set_default('%spassword' % cred_prefix[ii], password,
- group='identity')
- cfg.CONF.set_default('%sdomain_name' % cred_prefix[ii], domain,
- group='identity')
-
expected = len(set(creds)) > 1 or dynamic_creds
observed = credentials.is_alt_available(
identity_version=self.identity_version)
@@ -97,21 +75,6 @@
use_accounts_file=True,
creds=['1', '1'])
- def test__no_dynamic_creds__no_accounts_file__one_user(self):
- self.run_test(dynamic_creds=False,
- use_accounts_file=False,
- creds=['1'])
-
- def test__no_dynamic_creds__no_accounts_file__two_users(self):
- self.run_test(dynamic_creds=False,
- use_accounts_file=False,
- creds=['1', '2'])
-
- def test__no_dynamic_creds__no_accounts_file__two_users_identical(self):
- self.run_test(dynamic_creds=False,
- use_accounts_file=False,
- creds=['1', '1'])
-
class TestAltAvailableV3(TestAltAvailable):
diff --git a/tempest/tests/common/test_configured_creds.py b/tempest/tests/common/test_configured_creds.py
deleted file mode 100644
index 3c242b3..0000000
--- a/tempest/tests/common/test_configured_creds.py
+++ /dev/null
@@ -1,131 +0,0 @@
-# Copyright 2015 Hewlett-Packard Development Company, L.P.
-#
-# 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 oslo_config import cfg
-
-from tempest.common import credentials_factory as common_creds
-from tempest.common import tempest_fixtures as fixtures
-from tempest import config
-from tempest.lib import auth
-from tempest.lib import exceptions as lib_exc
-from tempest.lib.services.identity.v2 import token_client as v2_client
-from tempest.lib.services.identity.v3 import token_client as v3_client
-from tempest.tests import base
-from tempest.tests import fake_config
-from tempest.tests.lib import fake_identity
-
-
-class ConfiguredV2CredentialsTests(base.TestCase):
- attributes = {
- 'username': 'fake_username',
- 'password': 'fake_password',
- 'tenant_name': 'fake_tenant_name'
- }
-
- identity_response = fake_identity._fake_v2_response
- credentials_class = auth.KeystoneV2Credentials
- tokenclient_class = v2_client.TokenClient
- identity_version = 'v2'
-
- def setUp(self):
- super(ConfiguredV2CredentialsTests, self).setUp()
- self.useFixture(fake_config.ConfigFixture())
- self.patchobject(config, 'TempestConfigPrivate',
- fake_config.FakePrivate)
- self.patchobject(self.tokenclient_class, 'raw_request',
- self.identity_response)
-
- def _get_credentials(self, attributes=None):
- if attributes is None:
- attributes = self.attributes
- return self.credentials_class(**attributes)
-
- def _check(self, credentials, credentials_class, filled):
- # Check the right version of credentials has been returned
- self.assertIsInstance(credentials, credentials_class)
- # Check the id attributes are filled in
- attributes = [x for x in credentials.ATTRIBUTES if (
- '_id' in x and x != 'domain_id')]
- for attr in attributes:
- if filled:
- self.assertIsNotNone(getattr(credentials, attr))
- else:
- self.assertIsNone(getattr(credentials, attr))
-
- def _verify_credentials(self, credentials_class, filled=True,
- identity_version=None):
- for ctype in common_creds.CREDENTIAL_TYPES:
- if identity_version is None:
- creds = common_creds.get_configured_credentials(
- credential_type=ctype, fill_in=filled)
- else:
- creds = common_creds.get_configured_credentials(
- credential_type=ctype, fill_in=filled,
- identity_version=identity_version)
- self._check(creds, credentials_class, filled)
-
- def test_create(self):
- creds = self._get_credentials()
- self.assertEqual(self.attributes, creds._initial)
-
- def test_create_invalid_attr(self):
- self.assertRaises(lib_exc.InvalidCredentials,
- self._get_credentials,
- attributes=dict(invalid='fake'))
-
- def test_get_configured_credentials(self):
- self.useFixture(fixtures.LockFixture('auth_version'))
- self._verify_credentials(credentials_class=self.credentials_class)
-
- def test_get_configured_credentials_unfilled(self):
- self.useFixture(fixtures.LockFixture('auth_version'))
- self._verify_credentials(credentials_class=self.credentials_class,
- filled=False)
-
- def test_get_configured_credentials_version(self):
- # version specified and not loaded from config
- self.useFixture(fixtures.LockFixture('auth_version'))
- self._verify_credentials(credentials_class=self.credentials_class,
- identity_version=self.identity_version)
-
- def test_is_valid(self):
- creds = self._get_credentials()
- self.assertTrue(creds.is_valid())
-
-
-class ConfiguredV3CredentialsTests(ConfiguredV2CredentialsTests):
- attributes = {
- 'username': 'fake_username',
- 'password': 'fake_password',
- 'project_name': 'fake_project_name',
- 'user_domain_name': 'fake_domain_name'
- }
-
- credentials_class = auth.KeystoneV3Credentials
- identity_response = fake_identity._fake_v3_response
- tokenclient_class = v3_client.V3TokenClient
- identity_version = 'v3'
-
- def setUp(self):
- super(ConfiguredV3CredentialsTests, self).setUp()
- # Additional config items reset by cfg fixture after each test
- cfg.CONF.set_default('auth_version', 'v3', group='identity')
- # Identity group items
- for prefix in ['', 'alt_', 'admin_']:
- if prefix == 'admin_':
- group = 'auth'
- else:
- group = 'identity'
- cfg.CONF.set_default(prefix + 'domain_name', 'fake_domain_name',
- group=group)
diff --git a/tempest/tests/common/test_credentials.py b/tempest/tests/common/test_credentials.py
deleted file mode 100644
index 00f2d39..0000000
--- a/tempest/tests/common/test_credentials.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2015 Hewlett-Packard Development Company, L.P.
-#
-# 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 tempest.common import credentials_factory as credentials
-from tempest import config
-from tempest import exceptions
-from tempest.tests import base
-from tempest.tests import fake_config
-
-
-class TestLegacyCredentialsProvider(base.TestCase):
-
- fixed_params = {'identity_version': 'v2'}
-
- def setUp(self):
- super(TestLegacyCredentialsProvider, self).setUp()
- self.useFixture(fake_config.ConfigFixture())
- self.patchobject(config, 'TempestConfigPrivate',
- fake_config.FakePrivate)
-
- def test_get_creds_roles_legacy_invalid(self):
- test_accounts_class = credentials.LegacyCredentialProvider(
- **self.fixed_params)
- self.assertRaises(exceptions.InvalidConfiguration,
- test_accounts_class.get_creds_by_roles,
- ['fake_role'])
diff --git a/tempest/tests/fake_auth_provider.py b/tempest/tests/fake_auth_provider.py
index bc68d26..769f6a6 100644
--- a/tempest/tests/fake_auth_provider.py
+++ b/tempest/tests/fake_auth_provider.py
@@ -18,3 +18,9 @@
def auth_request(self, method, url, headers=None, body=None, filters=None):
return url, headers, body
+
+ def get_token(self):
+ return "faketoken"
+
+ def base_url(self, filters, auth_data=None):
+ return "https://example.com"
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index edd7186..65164a0 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -48,14 +48,9 @@
self.conf.set_default('auth_version', 'v2', group='identity')
for config_option in ['username', 'password', 'project_name']:
# Identity group items
- for prefix in ['', 'alt_', 'admin_']:
- if prefix == 'admin_':
- group = 'auth'
- else:
- group = 'identity'
- self.conf.set_default(prefix + config_option,
- 'fake_' + config_option,
- group=group)
+ self.conf.set_default('admin_' + config_option,
+ 'fake_' + config_option,
+ group='auth')
class FakePrivate(config.TempestConfigPrivate):
diff --git a/tempest/tests/lib/cli/test_execute.py b/tempest/tests/lib/cli/test_execute.py
index b846c46..cc9c94c 100644
--- a/tempest/tests/lib/cli/test_execute.py
+++ b/tempest/tests/lib/cli/test_execute.py
@@ -12,26 +12,65 @@
# under the License.
+import mock
+import subprocess
+
from tempest.lib.cli import base as cli_base
from tempest.lib import exceptions
from tempest.tests import base
class TestExecute(base.TestCase):
- def test_execute_success(self):
+
+ @mock.patch('subprocess.Popen', autospec=True)
+ def test_execute_success(self, mock_popen):
+ mock_popen.return_value.returncode = 0
+ mock_popen.return_value.communicate.return_value = (
+ "__init__.py", "")
result = cli_base.execute("/bin/ls", action="tempest",
flags="-l -a")
+ args, kwargs = mock_popen.call_args
+ # Check merge_stderr == False
+ self.assertEqual(subprocess.PIPE, kwargs['stderr'])
+ # Check action and flags are passed
+ args = args[0]
+ # We just tests that all pieces are passed through, we cannot make
+ # assumptions about the order
+ self.assertIn("/bin/ls", args)
+ self.assertIn("-l", args)
+ self.assertIn("-a", args)
+ self.assertIn("tempest", args)
+ # The result is mocked - checking that the mock was invoked correctly
self.assertIsInstance(result, str)
self.assertIn("__init__.py", result)
- def test_execute_failure(self):
+ @mock.patch('subprocess.Popen', autospec=True)
+ def test_execute_failure(self, mock_popen):
+ mock_popen.return_value.returncode = 1
+ mock_popen.return_value.communicate.return_value = (
+ "No such option --foobar", "")
result = cli_base.execute("/bin/ls", action="tempest.lib",
flags="--foobar", merge_stderr=True,
fail_ok=True)
+ args, kwargs = mock_popen.call_args
+ # Check the merge_stderr
+ self.assertEqual(subprocess.STDOUT, kwargs['stderr'])
+ # Check action and flags are passed
+ args = args[0]
+ # We just tests that all pieces are passed through, we cannot make
+ # assumptions about the order
+ self.assertIn("/bin/ls", args)
+ self.assertIn("--foobar", args)
+ self.assertIn("tempest.lib", args)
+ # The result is mocked - checking that the mock was invoked correctly
self.assertIsInstance(result, str)
self.assertIn("--foobar", result)
- def test_execute_failure_raise_exception(self):
+ @mock.patch('subprocess.Popen', autospec=True)
+ def test_execute_failure_raise_exception(self, mock_popen):
+ mock_popen.return_value.returncode = 1
+ mock_popen.return_value.communicate.return_value = (
+ "No such option --foobar", "")
self.assertRaises(exceptions.CommandFailed, cli_base.execute,
"/bin/ls", action="tempest", flags="--foobar",
merge_stderr=True)
diff --git a/tempest/tests/lib/test_rest_client.py b/tempest/tests/lib/test_rest_client.py
index 2959294..2a6fad5 100644
--- a/tempest/tests/lib/test_rest_client.py
+++ b/tempest/tests/lib/test_rest_client.py
@@ -547,6 +547,65 @@
self.assertIsNotNone(str(self.rest_client))
+class TestRateLimiting(BaseRestClientTestClass):
+
+ def setUp(self):
+ self.fake_http = fake_http.fake_httplib2()
+ super(TestRateLimiting, self).setUp()
+
+ def test__get_retry_after_delay_with_integer(self):
+ resp = {'retry-after': '123'}
+ self.assertEqual(123, self.rest_client._get_retry_after_delay(resp))
+
+ def test__get_retry_after_delay_with_http_date(self):
+ resp = {
+ 'date': 'Mon, 4 Apr 2016 21:56:23 GMT',
+ 'retry-after': 'Mon, 4 Apr 2016 21:58:26 GMT',
+ }
+ self.assertEqual(123, self.rest_client._get_retry_after_delay(resp))
+
+ def test__get_retry_after_delay_of_zero_with_integer(self):
+ resp = {'retry-after': '0'}
+ self.assertEqual(1, self.rest_client._get_retry_after_delay(resp))
+
+ def test__get_retry_after_delay_of_zero_with_http_date(self):
+ resp = {
+ 'date': 'Mon, 4 Apr 2016 21:56:23 GMT',
+ 'retry-after': 'Mon, 4 Apr 2016 21:56:23 GMT',
+ }
+ self.assertEqual(1, self.rest_client._get_retry_after_delay(resp))
+
+ def test__get_retry_after_delay_with_missing_date_header(self):
+ resp = {
+ 'retry-after': 'Mon, 4 Apr 2016 21:58:26 GMT',
+ }
+ self.assertRaises(ValueError, self.rest_client._get_retry_after_delay,
+ resp)
+
+ def test__get_retry_after_delay_with_invalid_http_date(self):
+ resp = {
+ 'retry-after': 'Mon, 4 AAA 2016 21:58:26 GMT',
+ 'date': 'Mon, 4 Apr 2016 21:56:23 GMT',
+ }
+ self.assertRaises(ValueError, self.rest_client._get_retry_after_delay,
+ resp)
+
+ def test__get_retry_after_delay_with_missing_retry_after_header(self):
+ self.assertRaises(ValueError, self.rest_client._get_retry_after_delay,
+ {})
+
+ def test_is_absolute_limit_gives_false_with_retry_after(self):
+ resp = {'retry-after': 123}
+
+ # is_absolute_limit() requires the overLimit body to be unwrapped
+ resp_body = self.rest_client._parse_resp("""{
+ "overLimit": {
+ "message": ""
+ }
+ }""")
+ self.assertFalse(self.rest_client.is_absolute_limit(resp, resp_body))
+
+
class TestProperties(BaseRestClientTestClass):
def setUp(self):
diff --git a/tempest/api/telemetry/__init__.py b/tempest/tests/services/object_storage/__init__.py
similarity index 100%
rename from tempest/api/telemetry/__init__.py
rename to tempest/tests/services/object_storage/__init__.py
diff --git a/tempest/tests/services/object_storage/test_object_client.py b/tempest/tests/services/object_storage/test_object_client.py
new file mode 100644
index 0000000..cd8c8f1
--- /dev/null
+++ b/tempest/tests/services/object_storage/test_object_client.py
@@ -0,0 +1,109 @@
+# Copyright 2016 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
+import six
+
+from tempest.lib import exceptions
+from tempest.services.object_storage import object_client
+from tempest.tests import base
+from tempest.tests import fake_auth_provider
+
+
+class TestObjectClient(base.TestCase):
+
+ def setUp(self):
+ super(TestObjectClient, self).setUp()
+ self.fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.url = self.fake_auth.base_url(None)
+ self.object_client = object_client.ObjectClient(self.fake_auth,
+ 'swift', 'region1')
+
+ @mock.patch.object(object_client, 'create_connection')
+ def test_create_object_continue_no_data(self, mock_poc):
+ self._validate_create_object_continue(None, mock_poc)
+
+ @mock.patch.object(object_client, 'create_connection')
+ def test_create_object_continue_with_data(self, mock_poc):
+ self._validate_create_object_continue('hello', mock_poc)
+
+ @mock.patch.object(object_client, 'create_connection')
+ def test_create_continue_with_no_continue_received(self, mock_poc):
+ self._validate_create_object_continue('hello', mock_poc,
+ initial_status=201)
+
+ def _validate_create_object_continue(self, req_data,
+ mock_poc, initial_status=100):
+
+ expected_hdrs = {
+ 'X-Auth-Token': self.fake_auth.get_token(),
+ 'content-length': 0 if req_data is None else len(req_data),
+ 'Expect': '100-continue'}
+
+ # Setup the Mocks prior to invoking the object creation
+ mock_resp_cls = mock.Mock()
+ mock_resp_cls._read_status.return_value = ("1", initial_status, "OK")
+
+ mock_poc.return_value.response_class.return_value = mock_resp_cls
+
+ # This is the final expected return value
+ mock_poc.return_value.getresponse.return_value.status = 201
+ mock_poc.return_value.getresponse.return_value.reason = 'OK'
+
+ # Call method to PUT object using expect:100-continue
+ cnt = "container1"
+ obj = "object1"
+ path = "/%s/%s" % (cnt, obj)
+
+ # If the expected initial status is not 100, then an exception
+ # should be thrown and the connection closed
+ if initial_status is 100:
+ status, reason = \
+ self.object_client.create_object_continue(cnt, obj, req_data)
+ else:
+ self.assertRaises(exceptions.UnexpectedResponseCode,
+ self.object_client.create_object_continue, cnt,
+ obj, req_data)
+ mock_poc.return_value.close.assert_called_once_with()
+
+ # Verify that putrequest is called 1 time with the appropriate values
+ mock_poc.return_value.putrequest.assert_called_once_with('PUT', path)
+
+ # Verify that headers were written, including "Expect:100-continue"
+ calls = []
+
+ for header, value in six.iteritems(expected_hdrs):
+ calls.append(mock.call(header, value))
+
+ mock_poc.return_value.putheader.assert_has_calls(calls, False)
+ mock_poc.return_value.endheaders.assert_called_once_with()
+
+ # The following steps are only taken if the initial status is 100
+ if initial_status is 100:
+ # Verify that the method returned what it was supposed to
+ self.assertEqual(status, 201)
+
+ # Verify that _safe_read was called once to remove the CRLF
+ # after the 100 response
+ mock_rc = mock_poc.return_value.response_class.return_value
+ mock_rc._safe_read.assert_called_once_with(2)
+
+ # Verify the actual data was written via send
+ mock_poc.return_value.send.assert_called_once_with(req_data)
+
+ # Verify that the getresponse method was called to receive
+ # the final
+ mock_poc.return_value.getresponse.assert_called_once_with()
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index af5fc09..8c5d861 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -254,6 +254,13 @@
cfg.CONF.set_default('nova', True, 'service_available')
cfg.CONF.set_default('glance', False, 'service_available')
+ def _assert_skip_message(self, func, skip_msg):
+ try:
+ func()
+ self.fail()
+ except testtools.TestCase.skipException as skip_exc:
+ self.assertEqual(skip_exc.args[0], skip_msg)
+
def _test_skip_unless_config(self, expected_to_skip=True, *decorator_args):
class TestFoo(test.BaseTestCase):
@@ -264,6 +271,9 @@
t = TestFoo('test_bar')
if expected_to_skip:
self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ if (len(decorator_args) >= 3):
+ # decorator_args[2]: skip message specified
+ self._assert_skip_message(t.test_bar, decorator_args[2])
else:
try:
self.assertEqual(t.test_bar(), 0)
@@ -284,6 +294,9 @@
t = TestFoo('test_bar')
if expected_to_skip:
self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ if (len(decorator_args) >= 3):
+ # decorator_args[2]: skip message specified
+ self._assert_skip_message(t.test_bar, decorator_args[2])
else:
try:
self.assertEqual(t.test_bar(), 0)
@@ -303,6 +316,10 @@
def test_skip_unless_false_option(self):
self._test_skip_unless_config(True, 'service_available', 'glance')
+ def test_skip_unless_false_option_msg(self):
+ self._test_skip_unless_config(True, 'service_available', 'glance',
+ 'skip message')
+
def test_skip_unless_true_option(self):
self._test_skip_unless_config(False,
'service_available', 'nova')
@@ -318,3 +335,7 @@
def test_skip_if_true_option(self):
self._test_skip_if_config(True, 'service_available', 'nova')
+
+ def test_skip_if_true_option_msg(self):
+ self._test_skip_if_config(True, 'service_available', 'nova',
+ 'skip message')
diff --git a/tools/check_logs.py b/tools/check_logs.py
index fa7129d..e34dec3 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -54,7 +54,6 @@
'q-meta',
'q-metering',
'q-svc',
- 'q-vpn',
's-proxy'])