Merge "Add ironic instance rebuild test"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 4dcf460..9ace4ea 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -397,6 +397,9 @@
# If false, skip all nova v3 tests. (boolean value)
#api_v3=false
+# If false skip all v2 api tests with xml (boolean value)
+#xml_api_v2=true
+
# If false, skip disk config tests (boolean value)
#disk_config=true
@@ -416,6 +419,10 @@
# password? (boolean value)
#change_password=false
+# Does the test environment support obtaining instance serial
+# console output? (boolean value)
+#console_output=true
+
# Does the test environment support resizing? (boolean value)
#resize=false
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index c2376c9..3485943 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -145,7 +145,7 @@
self.assertEqual(200, resp.status)
self.assertIn((aggregate_id, new_aggregate_name, new_az_name),
map(lambda x:
- (x['id'], x['name'], x['availability_zone']),
+ (x['id'], x['name'], x['availability_zone']),
aggregates))
@test.attr(type='gate')
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index a3295eb..343a39a 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -37,6 +37,9 @@
def setUpClass(cls):
cls.set_network_resources()
super(BaseComputeTest, cls).setUpClass()
+ if getattr(cls, '_interface', None) == 'xml' and cls._api_version == 2:
+ if not CONF.compute_feature_enabled.xml_api_v2:
+ raise cls.skipException('XML API is not enabled')
# TODO(andreaf) WE should care also for the alt_manager here
# but only once client lazy load in the manager is done
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index ee525e7..005f38a 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -15,9 +15,9 @@
import base64
import logging
+import urlparse
import testtools
-import urlparse
from tempest.api.compute import base
from tempest.common.utils import data_utils
@@ -348,6 +348,8 @@
lines = len(output.split('\n'))
self.assertEqual(lines, 10)
+ @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
+ 'Console output not supported.')
@test.attr(type='gate')
def test_get_console_output(self):
# Positive test:Should be able to GET the console output
@@ -364,6 +366,8 @@
self.wait_for(self._get_output)
+ @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
+ 'Console output not supported.')
@test.attr(type='gate')
def test_get_console_output_server_id_in_shutoff_status(self):
# Positive test:Should be able to GET the console output
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
index fb8ded3..3fa4a89 100644
--- a/tempest/api/compute/test_authorization.py
+++ b/tempest/api/compute/test_authorization.py
@@ -62,9 +62,9 @@
name = data_utils.rand_name('image')
resp, body = cls.glance_client.create_image(name=name,
- container_format='bare',
- disk_format='raw',
- is_public=False)
+ container_format='bare',
+ disk_format='raw',
+ is_public=False)
image_id = body['id']
image_file = StringIO.StringIO(('*' * 1024))
resp, body = cls.glance_client.update_image(image_id, data=image_file)
diff --git a/tempest/api/compute/v3/admin/test_aggregates.py b/tempest/api/compute/v3/admin/test_aggregates.py
index e5ec08b..886b6a7 100644
--- a/tempest/api/compute/v3/admin/test_aggregates.py
+++ b/tempest/api/compute/v3/admin/test_aggregates.py
@@ -135,7 +135,7 @@
self.assertEqual(200, resp.status)
self.assertIn((aggregate_id, new_aggregate_name, new_az_name),
map(lambda x:
- (x['id'], x['name'], x['availability_zone']),
+ (x['id'], x['name'], x['availability_zone']),
aggregates))
@test.attr(type='gate')
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index 4404043..d05e158 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -14,9 +14,9 @@
# under the License.
import logging
+import urlparse
import testtools
-import urlparse
from tempest.api.compute import base
from tempest.common.utils import data_utils
@@ -339,6 +339,8 @@
lines = len(output.split('\n'))
self.assertEqual(lines, 10)
+ @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
+ 'Console output not supported.')
@test.attr(type='gate')
def test_get_console_output(self):
# Positive test:Should be able to GET the console output
@@ -355,6 +357,8 @@
self.wait_for(self._get_output)
+ @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
+ 'Console output not supported.')
@test.attr(type='gate')
def test_get_console_output_server_id_in_shutoff_status(self):
# Positive test:Should be able to GET the console output
diff --git a/tempest/api/compute/v3/servers/test_server_rescue_negative.py b/tempest/api/compute/v3/servers/test_server_rescue_negative.py
index 83fe128..6d192a3 100644
--- a/tempest/api/compute/v3/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/v3/servers/test_server_rescue_negative.py
@@ -56,7 +56,8 @@
@classmethod
def tearDownClass(cls):
- cls.delete_volume(cls.volume['id'])
+ if hasattr(cls, 'volume'):
+ cls.delete_volume(cls.volume['id'])
super(ServerRescueNegativeV3Test, cls).tearDownClass()
def _detach(self, server_id, volume_id):
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index c3d6ba6..708524c 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -13,11 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+from testtools import matchers
+
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest import config
from tempest import test
-from testtools import matchers
+
CONF = config.CONF
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 1561a6e..886c808 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -12,6 +12,7 @@
import datetime
import re
+
from tempest.api.identity import base
from tempest import auth
from tempest import clients
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 8eb7d33..3996cc1 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -18,9 +18,12 @@
from tempest import clients
from tempest.common.utils import data_utils
from tempest import config
+from tempest import exceptions
+from tempest.openstack.common import log as logging
import tempest.test
CONF = config.CONF
+LOG = logging.getLogger(__name__)
class BaseIdentityAdminTest(tempest.test.BaseTestCase):
@@ -195,19 +198,36 @@
description=self.test_description)
self.domains.append(self.domain)
+ @staticmethod
+ def _try_wrapper(func, item, **kwargs):
+ try:
+ if kwargs:
+ func(item['id'], kwargs)
+ else:
+ func(item['id'])
+ except exceptions.NotFound:
+ pass
+ except Exception:
+ LOG.exception("Unexpected exception occurred in %s deletion."
+ " But ignored here." % item['id'])
+
def teardown_all(self):
+ # NOTE(masayukig): v3 client doesn't have v2 method.
+ # (e.g. delete_tenant) So we need to check resources existence
+ # before using client methods.
for user in self.users:
- self.client.delete_user(user['id'])
+ self._try_wrapper(self.client.delete_user, user)
for tenant in self.tenants:
- self.client.delete_tenant(tenant['id'])
+ self._try_wrapper(self.client.delete_tenant, tenant)
for role in self.roles:
- self.client.delete_role(role['id'])
+ self._try_wrapper(self.client.delete_role, role)
for v3_user in self.v3_users:
- self.client.delete_user(v3_user['id'])
+ self._try_wrapper(self.client.delete_user, v3_user)
for v3_project in self.projects:
- self.client.delete_project(v3_project['id'])
+ self._try_wrapper(self.client.delete_project, v3_project)
for v3_role in self.v3_roles:
- self.client.delete_role(v3_role['id'])
+ self._try_wrapper(self.client.delete_role, v3_role)
for domain in self.domains:
- self.client.update_domain(domain['id'], enabled=False)
- self.client.delete_domain(domain['id'])
+ self._try_wrapper(self.client.update_domain, domain,
+ enabled=False)
+ self._try_wrapper(self.client.delete_domain, domain)
diff --git a/tempest/api/network/test_allowed_address_pair.py b/tempest/api/network/test_allowed_address_pair.py
index c897716..8d984d1 100644
--- a/tempest/api/network/test_allowed_address_pair.py
+++ b/tempest/api/network/test_allowed_address_pair.py
@@ -85,8 +85,8 @@
# Update allowed address pair attribute of port
allowed_address_pairs = [{'ip_address': self.ip_address,
'mac_address': self.mac_address}]
- resp, body = self.client.update_port(port_id,
- allowed_address_pairs=allowed_address_pairs)
+ resp, body = self.client.update_port(
+ port_id, allowed_address_pairs=allowed_address_pairs)
self.assertEqual('200', resp['status'])
newport = body['port']
self._confirm_allowed_address_pair(newport, self.ip_address)
diff --git a/tempest/api/network/test_load_balancer.py b/tempest/api/network/test_load_balancer.py
index db24e0d..7a12ef6 100644
--- a/tempest/api/network/test_load_balancer.py
+++ b/tempest/api/network/test_load_balancer.py
@@ -290,8 +290,8 @@
health_monitor = body['health_monitor']
# Verification of health_monitor update
resp, body = (self.client.update_health_monitor
- (health_monitor['id'],
- admin_state_up=False))
+ (health_monitor['id'],
+ admin_state_up=False))
self.assertEqual('200', resp['status'])
updated_health_monitor = body['health_monitor']
self.assertFalse(updated_health_monitor['admin_state_up'])
@@ -323,10 +323,10 @@
self.addCleanup(self.client.delete_health_monitor,
health_monitor['id'])
resp, body = (self.client.update_health_monitor
- (health_monitor['id'],
- http_method="POST",
- url_path="/home/user",
- expected_codes="290"))
+ (health_monitor['id'],
+ http_method="POST",
+ url_path="/home/user",
+ expected_codes="290"))
self.assertEqual('200', resp['status'])
updated_health_monitor = body['health_monitor']
self.assertEqual("POST", updated_health_monitor['http_method'])
@@ -348,7 +348,7 @@
def test_associate_disassociate_health_monitor_with_pool(self):
# Verify that a health monitor can be associated with a pool
resp, body = (self.client.associate_health_monitor_with_pool
- (self.health_monitor['id'], self.pool['id']))
+ (self.health_monitor['id'], self.pool['id']))
self.assertEqual('201', resp['status'])
resp, body = self.client.show_health_monitor(
self.health_monitor['id'])
@@ -360,7 +360,7 @@
self.assertIn(health_monitor['id'], pool['health_monitors'])
# Verify that a health monitor can be disassociated from a pool
resp, body = (self.client.disassociate_health_monitor_with_pool
- (self.health_monitor['id'], self.pool['id']))
+ (self.health_monitor['id'], self.pool['id']))
self.assertEqual('204', resp['status'])
resp, body = self.client.show_pool(self.pool['id'])
pool = body['pool']
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index dca4c17..878335d 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -115,11 +115,11 @@
def test_create_router_with_default_snat_value(self):
# Create a router with default snat rule
name = data_utils.rand_name('router')
- router = self._create_router(name,
- external_network_id=CONF.network.public_network_id)
- self._verify_router_gateway(router['id'],
- {'network_id': CONF.network.public_network_id,
- 'enable_snat': True})
+ router = self._create_router(
+ name, external_network_id=CONF.network.public_network_id)
+ self._verify_router_gateway(
+ router['id'], {'network_id': CONF.network.public_network_id,
+ 'enable_snat': True})
@test.requires_ext(extension='ext-gw-mode', service='network')
@test.attr(type='smoke')
@@ -135,10 +135,10 @@
name, external_gateway_info=external_gateway_info)
self.assertEqual('201', resp['status'])
self.addCleanup(self.admin_client.delete_router,
- create_body['router']['id'])
+ create_body['router']['id'])
# Verify snat attributes after router creation
self._verify_router_gateway(create_body['router']['id'],
- exp_ext_gw_info=external_gateway_info)
+ exp_ext_gw_info=external_gateway_info)
@test.attr(type='smoke')
def test_add_remove_router_interface_with_subnet_id(self):
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index b21aa44..8b74b7e 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -17,10 +17,11 @@
import hashlib
import random
import re
-import six
import time
import zlib
+import six
+
from tempest.api.object_storage import base
from tempest.common import custom_matchers
from tempest.common.utils import data_utils
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 531df2d..cfebc2c 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -11,6 +11,7 @@
# under the License.
import os.path
+
import yaml
from tempest import clients
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index 27c6196..26e3ac6 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -12,6 +12,7 @@
import logging
+
import netaddr
from tempest.api.orchestration import base
@@ -66,16 +67,17 @@
cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
_, resources = cls.client.list_resources(cls.stack_identifier)
except exceptions.TimeoutException as e:
- # attempt to log the server console to help with debugging
- # the cause of the server not signalling the waitcondition
- # to heat.
- resp, body = cls.client.get_resource(cls.stack_identifier,
- 'Server')
- server_id = body['physical_resource_id']
- LOG.debug('Console output for %s', server_id)
- resp, output = cls.servers_client.get_console_output(
- server_id, None)
- LOG.debug(output)
+ if CONF.compute_feature_enabled.console_output:
+ # attempt to log the server console to help with debugging
+ # the cause of the server not signalling the waitcondition
+ # to heat.
+ resp, body = cls.client.get_resource(cls.stack_identifier,
+ 'Server')
+ server_id = body['physical_resource_id']
+ LOG.debug('Console output for %s', server_id)
+ resp, output = cls.servers_client.get_console_output(
+ server_id, None)
+ LOG.debug(output)
raise e
cls.test_resources = {}
diff --git a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
index e3ffdaf..c6f880b 100644
--- a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
+++ b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
@@ -56,10 +56,10 @@
ext=self._tpl_type)
resources = [('KeyPairSavePrivate',
nova_keypair_template[self._resource][
- 'KeyPairSavePrivate'][self._type]),
+ 'KeyPairSavePrivate'][self._type]),
('KeyPairDontSavePrivate',
nova_keypair_template[self._resource][
- 'KeyPairDontSavePrivate'][self._type])]
+ 'KeyPairDontSavePrivate'][self._type])]
for resource_name, resource_type in resources:
resource = self.test_resources.get(resource_name, None)
diff --git a/tempest/api/queuing/test_queues.py b/tempest/api/queuing/test_queues.py
index e43178a..b340b60 100644
--- a/tempest/api/queuing/test_queues.py
+++ b/tempest/api/queuing/test_queues.py
@@ -14,6 +14,7 @@
# limitations under the License.
import logging
+
from six import moves
from testtools import matchers
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index b8a2faa..ff225a2 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -15,11 +15,12 @@
# under the License.
import operator
+from testtools import matchers
+
from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest.openstack.common import log as logging
from tempest import test
-from testtools import matchers
LOG = logging.getLogger(__name__)
diff --git a/tempest/api_schema/response/compute/v2/hypervisors.py b/tempest/api_schema/response/compute/v2/hypervisors.py
index 7ad81c0..1878881 100644
--- a/tempest/api_schema/response/compute/v2/hypervisors.py
+++ b/tempest/api_schema/response/compute/v2/hypervisors.py
@@ -13,8 +13,10 @@
# under the License.
import copy
+
from tempest.api_schema.response.compute import hypervisors
+
hypervisors_servers = copy.deepcopy(hypervisors.common_hypervisors_detail)
# Defining extra attributes for V3 show hypervisor schema
diff --git a/tempest/api_schema/response/compute/v3/hypervisors.py b/tempest/api_schema/response/compute/v3/hypervisors.py
index 9a9a9f7..b36ae7e 100644
--- a/tempest/api_schema/response/compute/v3/hypervisors.py
+++ b/tempest/api_schema/response/compute/v3/hypervisors.py
@@ -13,8 +13,10 @@
# under the License.
import copy
+
from tempest.api_schema.response.compute import hypervisors
+
list_hypervisors_detail = copy.deepcopy(
hypervisors.common_list_hypervisors_detail)
# Defining extra attributes for V3 show hypervisor schema
diff --git a/tempest/api_schema/response/compute/v3/servers.py b/tempest/api_schema/response/compute/v3/servers.py
index 230de5f..d0edd44 100644
--- a/tempest/api_schema/response/compute/v3/servers.py
+++ b/tempest/api_schema/response/compute/v3/servers.py
@@ -55,9 +55,7 @@
'mac_addr': {'type': 'string'}
})
addresses_v3['patternProperties']['^[a-zA-Z0-9-_.]+$']['items'][
- 'required'].extend(
- ['type', 'mac_addr']
- )
+ 'required'].extend(['type', 'mac_addr'])
update_server = copy.deepcopy(servers.base_update_get_server)
update_server['response_body']['properties']['server']['properties'].update({
diff --git a/tempest/auth.py b/tempest/auth.py
index 5df6224..c84ad6b 100644
--- a/tempest/auth.py
+++ b/tempest/auth.py
@@ -18,16 +18,17 @@
import datetime
import exceptions
import re
-import six
import urlparse
+import six
+
from tempest import config
+from tempest.openstack.common import log as logging
from tempest.services.identity.json import identity_client as json_id
from tempest.services.identity.v3.json import identity_client as json_v3id
from tempest.services.identity.v3.xml import identity_client as xml_v3id
from tempest.services.identity.xml import identity_client as xml_id
-from tempest.openstack.common import log as logging
CONF = config.CONF
LOG = logging.getLogger(__name__)
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index 02f8c05..c33589a 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -13,14 +13,18 @@
# License for the specific language governing permissions and limitations
# under the License.
+import functools
import os
import shlex
import subprocess
+import testtools
+
import tempest.cli.output_parser
from tempest import config
from tempest import exceptions
from tempest.openstack.common import log as logging
+from tempest.openstack.common import versionutils
import tempest.test
@@ -29,6 +33,65 @@
CONF = config.CONF
+def execute(cmd, action, flags='', params='', fail_ok=False,
+ merge_stderr=False):
+ """Executes specified command for the given action."""
+ cmd = ' '.join([os.path.join(CONF.cli.cli_dir, cmd),
+ flags, action, params])
+ LOG.info("running: '%s'" % cmd)
+ cmd = shlex.split(cmd.encode('utf-8'))
+ result = ''
+ result_err = ''
+ stdout = subprocess.PIPE
+ stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
+ proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
+ result, result_err = proc.communicate()
+ if not fail_ok and proc.returncode != 0:
+ raise exceptions.CommandFailed(proc.returncode,
+ cmd,
+ result,
+ result_err)
+ return result
+
+
+def check_client_version(client, version):
+ """Checks if the client's version is compatible with the given version
+
+ @param client: The client to check.
+ @param version: The version to compare against.
+ @return: True if the client version is compatible with the given version
+ parameter, False otherwise.
+ """
+ current_version = execute(client, '', params='--version',
+ merge_stderr=True)
+
+ if not current_version.strip():
+ raise exceptions.TempestException('"%s --version" output was empty' %
+ client)
+
+ return versionutils.is_compatible(version, current_version,
+ same_major=False)
+
+
+def min_client_version(*args, **kwargs):
+ """A decorator to skip tests if the client used isn't of the right version.
+
+ @param client: The client command to run. For python-novaclient, this is
+ 'nova', for python-cinderclient this is 'cinder', etc.
+ @param version: The minimum version required to run the CLI test.
+ """
+ def decorator(func):
+ @functools.wraps(func)
+ def wrapper(*func_args, **func_kwargs):
+ if not check_client_version(kwargs['client'], kwargs['version']):
+ msg = "requires %s client version >= %s" % (kwargs['client'],
+ kwargs['version'])
+ raise testtools.TestCase.skipException(msg)
+ return func(*func_args, **func_kwargs)
+ return wrapper
+ return decorator
+
+
class ClientTestBase(tempest.test.BaseTestCase):
@classmethod
def setUpClass(cls):
@@ -50,7 +113,7 @@
def nova_manage(self, action, flags='', params='', fail_ok=False,
merge_stderr=False):
"""Executes nova-manage command for the given action."""
- return self.cmd(
+ return execute(
'nova-manage', action, flags, params, fail_ok, merge_stderr)
def keystone(self, action, flags='', params='', admin=True, fail_ok=False):
@@ -114,28 +177,7 @@
CONF.identity.admin_password,
CONF.identity.uri))
flags = creds + ' ' + flags
- return self.cmd(cmd, action, flags, params, fail_ok, merge_stderr)
-
- def cmd(self, cmd, action, flags='', params='', fail_ok=False,
- merge_stderr=False):
- """Executes specified command for the given action."""
- cmd = ' '.join([os.path.join(CONF.cli.cli_dir, cmd),
- flags, action, params])
- LOG.info("running: '%s'" % cmd)
- cmd = shlex.split(cmd.encode('utf-8'))
- result = ''
- result_err = ''
- stdout = subprocess.PIPE
- stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
- proc = subprocess.Popen(
- cmd, stdout=stdout, stderr=stderr)
- result, result_err = proc.communicate()
- if not fail_ok and proc.returncode != 0:
- raise exceptions.CommandFailed(proc.returncode,
- cmd,
- result,
- result_err)
- return result
+ return execute(cmd, action, flags, params, fail_ok, merge_stderr)
def assertTableStruct(self, items, field_names):
"""Verify that all items has keys listed in field_names."""
diff --git a/tempest/cli/simple_read_only/test_cinder.py b/tempest/cli/simple_read_only/test_cinder.py
index 04971c1..3a9a7a6 100644
--- a/tempest/cli/simple_read_only/test_cinder.py
+++ b/tempest/cli/simple_read_only/test_cinder.py
@@ -15,6 +15,7 @@
import logging
import re
+
import testtools
from tempest import cli
diff --git a/tempest/cli/simple_read_only/test_heat.py b/tempest/cli/simple_read_only/test_heat.py
index 7a952fc..8e413a9 100644
--- a/tempest/cli/simple_read_only/test_heat.py
+++ b/tempest/cli/simple_read_only/test_heat.py
@@ -12,6 +12,7 @@
import json
import os
+
import yaml
import tempest.cli
@@ -85,6 +86,7 @@
def test_heat_help(self):
self.heat('help')
+ @tempest.cli.min_client_version(client='heat', version='0.2.7')
def test_heat_bash_completion(self):
self.heat('bash-completion')
diff --git a/tempest/cli/simple_read_only/test_nova.py b/tempest/cli/simple_read_only/test_nova.py
index 7085cc9..9bac7a6 100644
--- a/tempest/cli/simple_read_only/test_nova.py
+++ b/tempest/cli/simple_read_only/test_nova.py
@@ -144,6 +144,7 @@
def test_admin_secgroup_list_rules(self):
self.nova('secgroup-list-rules')
+ @tempest.cli.min_client_version(client='nova', version='2.18')
def test_admin_server_group_list(self):
self.nova('server-group-list')
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 3616a82..6761a69 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -19,13 +19,13 @@
"""
+import argparse
import logging
import os
import sys
import unittest
-import yaml
-import argparse
+import yaml
import tempest.auth
from tempest import config
@@ -249,8 +249,8 @@
if return_code is 0:
break
self.assertNotEqual(count, 59,
- "Server %s is not pingable at %s" % (
- server['name'], addr))
+ "Server %s is not pingable at %s" % (
+ server['name'], addr))
def check_volumes(self):
"""Check that the volumes are still there and attached."""
@@ -401,7 +401,7 @@
image_id = _get_image_by_name(client, server['image'])['id']
flavor_id = _get_flavor_by_name(client, server['flavor'])['id']
resp, body = client.servers.create_server(server['name'], image_id,
- flavor_id)
+ flavor_id)
server_id = body['id']
client.servers.wait_for_server_status(server_id, 'ACTIVE')
@@ -420,7 +420,7 @@
client.servers.delete_server(response['id'])
client.servers.wait_for_server_termination(response['id'],
- ignore_error=True)
+ ignore_error=True)
#######################
diff --git a/tempest/cmd/run_stress.py b/tempest/cmd/run_stress.py
index 07f3f66..a3f185c 100755
--- a/tempest/cmd/run_stress.py
+++ b/tempest/cmd/run_stress.py
@@ -18,13 +18,14 @@
import inspect
import json
import sys
-from testtools import testsuite
try:
from unittest import loader
except ImportError:
# unittest in python 2.6 does not contain loader, so uses unittest2
from unittest2 import loader
+from testtools import testsuite
+
from tempest.openstack.common import log as logging
from tempest.stress import driver
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 673da4f..70fd27b 100755
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -269,7 +269,7 @@
if getattr(CONF.service_available, codename_match[cfgname]):
print('Endpoint type %s not found either disable service '
'%s or fix the catalog_type in the config file' % (
- catalog_type, codename_match[cfgname]))
+ catalog_type, codename_match[cfgname]))
if update:
change_option(codename_match[cfgname],
'service_available', False)
@@ -278,7 +278,7 @@
codename_match[cfgname]):
print('Endpoint type %s is available, service %s should be'
' set as available in the config file.' % (
- catalog_type, codename_match[cfgname]))
+ catalog_type, codename_match[cfgname]))
if update:
change_option(codename_match[cfgname],
'service_available', True)
diff --git a/tempest/common/accounts.py b/tempest/common/accounts.py
index 80f5f91..ad88ea2 100644
--- a/tempest/common/accounts.py
+++ b/tempest/common/accounts.py
@@ -14,6 +14,7 @@
import hashlib
import os
+
import yaml
from tempest import auth
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
index dc4f049..9808ed1 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -12,6 +12,7 @@
# limitations under the License.
import abc
+
import six
from tempest import config
diff --git a/tempest/common/debug.py b/tempest/common/debug.py
index 228be7a..16e5ffe 100644
--- a/tempest/common/debug.py
+++ b/tempest/common/debug.py
@@ -14,7 +14,6 @@
from tempest.common import commands
from tempest import config
-
from tempest.openstack.common import log as logging
CONF = config.CONF
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index 55aca5a..5f35c85 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -19,15 +19,17 @@
import hashlib
import httplib
import json
-import OpenSSL
import posixpath
import re
-from six import moves
import socket
import StringIO
import struct
import urlparse
+
+import OpenSSL
+from six import moves
+
from tempest import exceptions as exc
from tempest.openstack.common import log as logging
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index 05d758f..f711f2f 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -373,31 +373,6 @@
LOG.warn('network with name: %s not found for delete' %
network_name)
- def _cleanup_ports(self, network_id):
- # TODO(mlavalle) This method will be removed once patch
- # https://review.openstack.org/#/c/46563/ merges in Neutron
- if not self.ports:
- if self.tempest_client:
- resp, resp_body = self.network_admin_client.list_ports()
- else:
- resp_body = self.network_admin_client.list_ports()
- self.ports = resp_body['ports']
- ports_to_delete = [
- port
- for port in self.ports
- if (port['network_id'] == network_id and
- port['device_owner'] != 'network:router_interface' and
- port['device_owner'] != 'network:dhcp')
- ]
- for port in ports_to_delete:
- try:
- LOG.info('Cleaning up port id %s, name %s' %
- (port['id'], port['name']))
- self.network_admin_client.delete_port(port['id'])
- except exceptions.NotFound:
- LOG.warn('Port id: %s, name %s not found for clean-up' %
- (port['id'], port['name']))
-
def _clear_isolated_net_resources(self):
net_client = self.network_admin_client
for cred in self.isolated_net_resources:
@@ -419,11 +394,6 @@
router['name'])
self._clear_isolated_router(router['id'], router['name'])
if (not self.network_resources or
- self.network_resources.get('network')):
- # TODO(mlavalle) This method call will be removed once patch
- # https://review.openstack.org/#/c/46563/ merges in Neutron
- self._cleanup_ports(network['id'])
- if (not self.network_resources or
self.network_resources.get('subnet')):
self._clear_isolated_subnet(subnet['id'], subnet['name'])
if (not self.network_resources or
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 9e0f4d3..ff92b67 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -16,12 +16,12 @@
import collections
import json
-from lxml import etree
import re
import string
import time
import jsonschema
+from lxml import etree
from tempest.common import http
from tempest.common.utils import misc as misc_utils
@@ -209,8 +209,9 @@
pattern = """Unexpected http success status code {0},
The expected status code is {1}"""
if ((not isinstance(expected_code, list) and
- (read_code != expected_code)) or (isinstance(expected_code,
- list) and (read_code not in expected_code))):
+ (read_code != expected_code)) or
+ (isinstance(expected_code, list) and
+ (read_code not in expected_code))):
details = pattern.format(read_code, expected_code)
raise exceptions.InvalidHttpSuccessCode(details)
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index 531887c..c06ce3b 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -16,11 +16,12 @@
import cStringIO
import select
-import six
import socket
import time
import warnings
+import six
+
from tempest import exceptions
from tempest.openstack.common import log as logging
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 57a14a2..89904b2 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -11,9 +11,10 @@
# under the License.
import re
-import six
import time
+import six
+
from tempest.common import ssh
from tempest import config
from tempest import exceptions
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index d242c14..c4f1214 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -74,7 +74,7 @@
if (server_status == 'ERROR') and raise_on_error:
if 'fault' in body:
raise exceptions.BuildErrorException(body['fault'],
- server_id=server_id)
+ server_id=server_id)
else:
raise exceptions.BuildErrorException(server_id=server_id)
diff --git a/tempest/config.py b/tempest/config.py
index 3b61700..af6dd7a 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -273,6 +273,9 @@
cfg.BoolOpt('api_v3',
default=False,
help="If false, skip all nova v3 tests."),
+ cfg.BoolOpt('xml_api_v2',
+ default=True,
+ help="If false skip all v2 api tests with xml"),
cfg.BoolOpt('disk_config',
default=True,
help="If false, skip disk config tests"),
@@ -292,6 +295,10 @@
default=False,
help="Does the test environment support changing the admin "
"password?"),
+ cfg.BoolOpt('console_output',
+ default=True,
+ help="Does the test environment support obtaining instance "
+ "serial console output?"),
cfg.BoolOpt('resize',
default=False,
help="Does the test environment support resizing?"),
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 9d443cc..cc31fad 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -223,5 +223,8 @@
def __str__(self):
return ("Command '%s' returned non-zero exit status %d.\n"
- "stdout:\n%s\n"
- "stderr:\n%s" % (self.cmd, self.returncode, self.stdout, self.stderr))
+ "stdout:\n%s\n"
+ "stderr:\n%s" % (self.cmd,
+ self.returncode,
+ self.stdout,
+ self.stderr))
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 4e6b464..57c07d1 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -17,7 +17,6 @@
import logging
import os
import re
-import six
import subprocess
import time
@@ -27,6 +26,7 @@
import netaddr
from neutronclient.common import exceptions as exc
from novaclient import exceptions as nova_exceptions
+import six
from tempest.api.network import common as net_common
from tempest import auth
@@ -506,6 +506,9 @@
return linux_client
def _log_console_output(self, servers=None):
+ if not CONF.compute_feature_enabled.console_output:
+ LOG.debug('Console output not supported, cannot log')
+ return
if not servers:
servers = self.compute_client.servers.list()
for server in servers:
diff --git a/tempest/scenario/orchestration/test_autoscaling.py b/tempest/scenario/orchestration/test_autoscaling.py
index aa7b6f8..8894106 100644
--- a/tempest/scenario/orchestration/test_autoscaling.py
+++ b/tempest/scenario/orchestration/test_autoscaling.py
@@ -10,9 +10,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-import heatclient.exc as heat_exceptions
import time
+import heatclient.exc as heat_exceptions
+
from tempest import config
from tempest.scenario import manager
from tempest import test
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 8c7af3d..fc6c66c 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -15,6 +15,7 @@
# under the License.
import collections
import re
+
import testtools
from tempest.api.network import common as net_common
diff --git a/tempest/services/compute/xml/floating_ips_client.py b/tempest/services/compute/xml/floating_ips_client.py
index fa4aa07..730e870 100644
--- a/tempest/services/compute/xml/floating_ips_client.py
+++ b/tempest/services/compute/xml/floating_ips_client.py
@@ -13,9 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-from lxml import etree
import urllib
+from lxml import etree
+
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
diff --git a/tempest/services/compute/xml/hosts_client.py b/tempest/services/compute/xml/hosts_client.py
index 23a7dd6..ddb92b6 100644
--- a/tempest/services/compute/xml/hosts_client.py
+++ b/tempest/services/compute/xml/hosts_client.py
@@ -15,6 +15,7 @@
import urllib
from lxml import etree
+
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
diff --git a/tempest/services/compute/xml/security_groups_client.py b/tempest/services/compute/xml/security_groups_client.py
index 9eccb90..56ac7ba 100644
--- a/tempest/services/compute/xml/security_groups_client.py
+++ b/tempest/services/compute/xml/security_groups_client.py
@@ -13,9 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-from lxml import etree
import urllib
+from lxml import etree
+
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 8e53b8d..2e28bfe 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -110,7 +110,7 @@
def add_router_interface_with_subnet_id(self, router_id, subnet_id):
uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
- router_id)
+ router_id)
update_body = {"subnet_id": subnet_id}
update_body = json.dumps(update_body)
resp, body = self.put(uri, update_body)
@@ -119,7 +119,7 @@
def add_router_interface_with_port_id(self, router_id, port_id):
uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
- router_id)
+ router_id)
update_body = {"port_id": port_id}
update_body = json.dumps(update_body)
resp, body = self.put(uri, update_body)
@@ -128,7 +128,7 @@
def remove_router_interface_with_subnet_id(self, router_id, subnet_id):
uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
- router_id)
+ router_id)
update_body = {"subnet_id": subnet_id}
update_body = json.dumps(update_body)
resp, body = self.put(uri, update_body)
@@ -137,7 +137,7 @@
def remove_router_interface_with_port_id(self, router_id, port_id):
uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
- router_id)
+ router_id)
update_body = {"port_id": port_id}
update_body = json.dumps(update_body)
resp, body = self.put(uri, update_body)
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index 22cc948..ea9dc77 100644
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -10,9 +10,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-from lxml import etree
import xml.etree.ElementTree as ET
+from lxml import etree
+
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest.services.network import network_client_base as client_base
@@ -136,7 +137,7 @@
def add_router_interface_with_subnet_id(self, router_id, subnet_id):
uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
- router_id)
+ router_id)
subnet = common.Element("subnet_id", subnet_id)
resp, body = self.put(uri, str(common.Document(subnet)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
@@ -144,7 +145,7 @@
def add_router_interface_with_port_id(self, router_id, port_id):
uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
- router_id)
+ router_id)
port = common.Element("port_id", port_id)
resp, body = self.put(uri, str(common.Document(port)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
@@ -152,7 +153,7 @@
def remove_router_interface_with_subnet_id(self, router_id, subnet_id):
uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
- router_id)
+ router_id)
subnet = common.Element("subnet_id", subnet_id)
resp, body = self.put(uri, str(common.Document(subnet)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
@@ -160,7 +161,7 @@
def remove_router_interface_with_port_id(self, router_id, port_id):
uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
- router_id)
+ router_id)
port = common.Element("port_id", port_id)
resp, body = self.put(uri, str(common.Document(port)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index a0506f2..be0f888 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -15,12 +15,12 @@
import json
import urllib
+from xml.etree import ElementTree as etree
from tempest.common import http
from tempest.common import rest_client
from tempest import config
from tempest import exceptions
-from xml.etree import ElementTree as etree
CONF = config.CONF
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index 546b557..ffc1326 100644
--- a/tempest/services/object_storage/container_client.py
+++ b/tempest/services/object_storage/container_client.py
@@ -15,10 +15,10 @@
import json
import urllib
+from xml.etree import ElementTree as etree
from tempest.common import rest_client
from tempest import config
-from xml.etree import ElementTree as etree
CONF = config.CONF
diff --git a/tempest/services/telemetry/telemetry_client_base.py b/tempest/services/telemetry/telemetry_client_base.py
index b45c239..a184a77 100644
--- a/tempest/services/telemetry/telemetry_client_base.py
+++ b/tempest/services/telemetry/telemetry_client_base.py
@@ -14,9 +14,10 @@
# under the License.
import abc
-import six
import urllib
+import six
+
from tempest import config
CONF = config.CONF
diff --git a/tempest/services/volume/json/admin/volume_quotas_client.py b/tempest/services/volume/json/admin/volume_quotas_client.py
index 961c7da..7b3e5de 100644
--- a/tempest/services/volume/json/admin/volume_quotas_client.py
+++ b/tempest/services/volume/json/admin/volume_quotas_client.py
@@ -16,10 +16,9 @@
import urllib
-from tempest.openstack.common import jsonutils
-
from tempest.common import rest_client
from tempest import config
+from tempest.openstack.common import jsonutils
CONF = config.CONF
diff --git a/tempest/services/volume/xml/admin/volume_quotas_client.py b/tempest/services/volume/xml/admin/volume_quotas_client.py
index a38410b..a8c92fc 100644
--- a/tempest/services/volume/xml/admin/volume_quotas_client.py
+++ b/tempest/services/volume/xml/admin/volume_quotas_client.py
@@ -15,6 +15,7 @@
# under the License.
import ast
+
from lxml import etree
from tempest.common import xml_utils as xml
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index 2d4a9e9..89947fb 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -15,9 +15,9 @@
import time
import urllib
+from xml.sax import saxutils
from lxml import etree
-from xml.sax import saxutils
from tempest.common import rest_client
from tempest.common import xml_utils as common
diff --git a/tempest/stress/actions/volume_attach_delete.py b/tempest/stress/actions/volume_attach_delete.py
index b438f52..e0238d3 100644
--- a/tempest/stress/actions/volume_attach_delete.py
+++ b/tempest/stress/actions/volume_attach_delete.py
@@ -48,7 +48,7 @@
# Step 3: attach volume to vm
self.logger.info("attach volume (%s) to vm %s" %
- (volume['id'], server_id))
+ (volume['id'], server_id))
resp, body = self.manager.servers_client.attach_volume(server_id,
volume['id'],
'/dev/vdc')
diff --git a/tempest/stress/actions/volume_attach_verify.py b/tempest/stress/actions/volume_attach_verify.py
index a3ca0b7..0d3cb23 100644
--- a/tempest/stress/actions/volume_attach_verify.py
+++ b/tempest/stress/actions/volume_attach_verify.py
@@ -192,7 +192,7 @@
self._create_volume()
servers_client = self.manager.servers_client
self.logger.info("attach volume (%s) to vm %s" %
- (self.volume['id'], self.server_id))
+ (self.volume['id'], self.server_id))
resp, body = servers_client.attach_volume(self.server_id,
self.volume['id'],
self.part_name)
diff --git a/tempest/test.py b/tempest/test.py
index 95ae23f..1e67d18 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -399,16 +399,21 @@
cls.admin_client = os_admin.negative_client
@staticmethod
- def load_schema(file):
+ def load_schema(file_or_dict):
"""
- Loads a schema from a file on a specified location.
+ Loads a schema from a file_or_dict on a specified location.
- :param file: the file name
+ :param file_or_dict: just a dict or filename
"""
+ # NOTE(mkoderer): we will get rid of this function when all test are
+ # ported to dicts
+ if isinstance(file_or_dict, dict):
+ return file_or_dict
+
# NOTE(mkoderer): must be extended for xml support
fn = os.path.join(
os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
- "etc", "schemas", file)
+ "etc", "schemas", file_or_dict)
LOG.debug("Open schema file: %s" % (fn))
return json.load(open(fn))
@@ -425,17 +430,21 @@
standard_tests, module, loader = args
for test in testtools.iterate_tests(standard_tests):
schema_file = getattr(test, '_schema_file', None)
+ schema = getattr(test, '_schema', None)
if schema_file is not None:
setattr(test, 'scenarios',
NegativeAutoTest.generate_scenario(schema_file))
+ elif schema is not None:
+ setattr(test, 'scenarios',
+ NegativeAutoTest.generate_scenario(schema))
return testscenarios.load_tests_apply_scenarios(*args)
@staticmethod
- def generate_scenario(description_file):
+ def generate_scenario(description):
"""
Generates the test scenario list for a given description.
- :param description: A dictionary with the following entries:
+ :param description: A file or dictionary with the following entries:
name (required) name for the api
http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
url (required) the url to be appended to the catalog url with '%s'
@@ -451,7 +460,7 @@
the data is used to generate query strings appended to the url,
otherwise for the body of the http call.
"""
- description = NegativeAutoTest.load_schema(description_file)
+ description = NegativeAutoTest.load_schema(description)
LOG.debug(description)
generator = importutils.import_class(
CONF.negative.test_generator)()
@@ -481,13 +490,14 @@
LOG.debug(scenario_list)
return scenario_list
- def execute(self, description_file):
+ def execute(self, description):
"""
Execute a http call on an api that are expected to
result in client errors. First it uses invalid resources that are part
of the url, and then invalid data for queries and http request bodies.
- :param description: A dictionary with the following entries:
+ :param description: A json file or dictionary with the following
+ entries:
name (required) name for the api
http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
url (required) the url to be appended to the catalog url with '%s'
@@ -504,7 +514,7 @@
otherwise for the body of the http call.
"""
- description = NegativeAutoTest.load_schema(description_file)
+ description = NegativeAutoTest.load_schema(description)
LOG.info("Executing %s" % description["name"])
LOG.debug(description)
method = description["http-method"]
@@ -594,7 +604,10 @@
"""
@attr(type=['negative', 'gate'])
def generic_test(self):
- self.execute(self._schema_file)
+ if hasattr(self, '_schema_file'):
+ self.execute(self._schema_file)
+ if hasattr(self, '_schema'):
+ self.execute(self._schema)
cn = klass.__name__
cn = cn.replace('JSON', '')
diff --git a/tempest/tests/base.py b/tempest/tests/base.py
index f4df3b9..27eb2c4 100644
--- a/tempest/tests/base.py
+++ b/tempest/tests/base.py
@@ -13,7 +13,6 @@
# under the License.
import mock
-
from oslotest import base
from oslotest import moxstubout
diff --git a/tempest/tests/cli/test_cli.py b/tempest/tests/cli/test_cli.py
new file mode 100644
index 0000000..1fd5ccb
--- /dev/null
+++ b/tempest/tests/cli/test_cli.py
@@ -0,0 +1,59 @@
+# Copyright 2014 IBM Corp.
+#
+# 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 testtools
+
+from tempest import cli
+from tempest import exceptions
+from tempest.tests import base
+
+
+class TestMinClientVersion(base.TestCase):
+ """Tests for the min_client_version decorator.
+ """
+
+ def _test_min_version(self, required, installed, expect_skip):
+
+ @cli.min_client_version(client='nova', version=required)
+ def fake(self, expect_skip):
+ if expect_skip:
+ # If we got here, the decorator didn't raise a skipException as
+ # expected so we need to fail.
+ self.fail('Should not have gotten past the decorator.')
+
+ with mock.patch.object(cli, 'execute',
+ return_value=installed) as mock_cmd:
+ if expect_skip:
+ self.assertRaises(testtools.TestCase.skipException, fake,
+ self, expect_skip)
+ else:
+ fake(self, expect_skip)
+ mock_cmd.assert_called_once_with('nova', '', params='--version',
+ merge_stderr=True)
+
+ def test_min_client_version(self):
+ # required, installed, expect_skip
+ cases = (('2.17.0', '2.17.0', False),
+ ('2.17.0', '2.18.0', False),
+ ('2.18.0', '2.17.0', True))
+
+ for case in cases:
+ self._test_min_version(*case)
+
+ @mock.patch.object(cli, 'execute', return_value=' ')
+ def test_check_client_version_empty_output(self, mock_execute):
+ # Tests that an exception is raised if the command output is empty.
+ self.assertRaises(exceptions.TempestException,
+ cli.check_client_version, 'nova', '2.18.0')
diff --git a/tempest/tests/fake_http.py b/tempest/tests/fake_http.py
index ce2b2c0..7d77484 100644
--- a/tempest/tests/fake_http.py
+++ b/tempest/tests/fake_http.py
@@ -13,6 +13,7 @@
# under the License.
import copy
+
import httplib2
diff --git a/tempest/tests/fake_identity.py b/tempest/tests/fake_identity.py
index 1900fc9..97098e1 100644
--- a/tempest/tests/fake_identity.py
+++ b/tempest/tests/fake_identity.py
@@ -13,9 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+import json
import httplib2
-import json
TOKEN = "fake_token"
diff --git a/tempest/tests/negative/test_negative_auto_test.py b/tempest/tests/negative/test_negative_auto_test.py
index 7a1909a..edff3a8 100644
--- a/tempest/tests/negative/test_negative_auto_test.py
+++ b/tempest/tests/negative/test_negative_auto_test.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import json
+
import mock
from tempest import config
@@ -30,9 +32,9 @@
"http-method": "GET",
"url": "flavors/detail",
"json-schema": {"type": "object",
- "properties":
- {"minRam": {"type": "integer"},
- "minDisk": {"type": "integer"}}
+ "properties":
+ {"minRam": {"type": "integer"},
+ "minDisk": {"type": "integer"}}
},
"resources": ["flavor", "volume", "image"]
}
@@ -70,3 +72,13 @@
self._check_prop_entries(scenarios, "prop_minRam")
self._check_prop_entries(scenarios, "prop_minDisk")
self._check_resource_entries(scenarios, "inv_res")
+
+ def test_load_schema(self):
+ json_schema = json.dumps(self.fake_input_desc)
+ with mock.patch('tempest.test.open',
+ mock.mock_open(read_data=json_schema),
+ create=True):
+ return_file = test.NegativeAutoTest.load_schema('filename')
+ self.assertEqual(return_file, self.fake_input_desc)
+ return_dict = test.NegativeAutoTest.load_schema(self.fake_input_desc)
+ self.assertEqual(return_file, return_dict)
diff --git a/tempest/tests/test_commands.py b/tempest/tests/test_commands.py
index 1e2925b..2379741 100644
--- a/tempest/tests/test_commands.py
+++ b/tempest/tests/test_commands.py
@@ -12,9 +12,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-import mock
import subprocess
+import mock
+
from tempest.common import commands
from tempest.tests import base
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
index 88b8129..9a6c9de 100644
--- a/tempest/tests/test_glance_http.py
+++ b/tempest/tests/test_glance_http.py
@@ -15,9 +15,10 @@
import httplib
import json
+import socket
+
import mock
import six
-import socket
from tempest.common import glance_http
from tempest import exceptions
@@ -41,17 +42,17 @@
self.fake_auth.base_url = mock.MagicMock(return_value=self.endpoint)
- self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
- 'request',
- side_effect=self.fake_http.request(self.endpoint)[1]))
+ self.useFixture(mockpatch.PatchObject(
+ httplib.HTTPConnection,
+ 'request',
+ side_effect=self.fake_http.request(self.endpoint)[1]))
self.client = glance_http.HTTPClient(self.fake_auth, {})
def _set_response_fixture(self, header, status, resp_body):
resp = fake_http.fake_httplib(header, status=status,
body=six.StringIO(resp_body))
self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
- 'getresponse',
- return_value=resp))
+ 'getresponse', return_value=resp))
return resp
def test_json_request_without_content_type_header_in_response(self):
diff --git a/tempest/tests/test_rest_client.py b/tempest/tests/test_rest_client.py
index a351bd5..5f55ca2 100644
--- a/tempest/tests/test_rest_client.py
+++ b/tempest/tests/test_rest_client.py
@@ -12,9 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-import httplib2
import json
+import httplib2
from oslotest import mockpatch
from tempest.common import rest_client
diff --git a/tempest/tests/test_tenant_isolation.py b/tempest/tests/test_tenant_isolation.py
index bbc3d15..eddbb1d 100644
--- a/tempest/tests/test_tenant_isolation.py
+++ b/tempest/tests/test_tenant_isolation.py
@@ -335,9 +335,11 @@
remove_router_interface_mock = self.patch(
'tempest.services.network.json.network_client.NetworkClientJSON.'
'remove_router_interface_with_subnet_id')
+ return_values = ({'status': 200}, {'ports': []})
port_list_mock = mock.patch.object(iso_creds.network_admin_client,
- 'list_ports', return_value=(
- {'status': 200}, {'ports': []}))
+ 'list_ports',
+ return_value=return_values)
+
port_list_mock.start()
iso_creds.clear_isolated_creds()
# Verify remove router interface calls
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index d3cbc4b..4bf71f3 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -17,7 +17,6 @@
import logging as orig_logging
import os
import re
-import six
import urlparse
import boto
@@ -25,6 +24,7 @@
from boto import exception
from boto import s3
import keystoneclient.exceptions
+import six
import tempest.clients
from tempest.common.utils import file_utils
@@ -187,7 +187,7 @@
if len(args):
string += ", "
string += ", ".join("=".join(map(str, (key, value)))
- for (key, value) in kwargs.items())
+ for (key, value) in kwargs.items())
return string + ")"
@@ -592,83 +592,83 @@
for code in (('AccessDenied', 403),
- ('AccountProblem', 403),
- ('AmbiguousGrantByEmailAddress', 400),
- ('BadDigest', 400),
- ('BucketAlreadyExists', 409),
- ('BucketAlreadyOwnedByYou', 409),
- ('BucketNotEmpty', 409),
- ('CredentialsNotSupported', 400),
- ('CrossLocationLoggingProhibited', 403),
- ('EntityTooSmall', 400),
- ('EntityTooLarge', 400),
- ('ExpiredToken', 400),
- ('IllegalVersioningConfigurationException', 400),
- ('IncompleteBody', 400),
- ('IncorrectNumberOfFilesInPostRequest', 400),
- ('InlineDataTooLarge', 400),
- ('InvalidAccessKeyId', 403),
+ ('AccountProblem', 403),
+ ('AmbiguousGrantByEmailAddress', 400),
+ ('BadDigest', 400),
+ ('BucketAlreadyExists', 409),
+ ('BucketAlreadyOwnedByYou', 409),
+ ('BucketNotEmpty', 409),
+ ('CredentialsNotSupported', 400),
+ ('CrossLocationLoggingProhibited', 403),
+ ('EntityTooSmall', 400),
+ ('EntityTooLarge', 400),
+ ('ExpiredToken', 400),
+ ('IllegalVersioningConfigurationException', 400),
+ ('IncompleteBody', 400),
+ ('IncorrectNumberOfFilesInPostRequest', 400),
+ ('InlineDataTooLarge', 400),
+ ('InvalidAccessKeyId', 403),
'InvalidAddressingHeader',
- ('InvalidArgument', 400),
- ('InvalidBucketName', 400),
- ('InvalidBucketState', 409),
- ('InvalidDigest', 400),
- ('InvalidLocationConstraint', 400),
- ('InvalidPart', 400),
- ('InvalidPartOrder', 400),
- ('InvalidPayer', 403),
- ('InvalidPolicyDocument', 400),
- ('InvalidRange', 416),
- ('InvalidRequest', 400),
- ('InvalidSecurity', 403),
- ('InvalidSOAPRequest', 400),
- ('InvalidStorageClass', 400),
- ('InvalidTargetBucketForLogging', 400),
- ('InvalidToken', 400),
- ('InvalidURI', 400),
- ('KeyTooLong', 400),
- ('MalformedACLError', 400),
- ('MalformedPOSTRequest', 400),
- ('MalformedXML', 400),
- ('MaxMessageLengthExceeded', 400),
- ('MaxPostPreDataLengthExceededError', 400),
- ('MetadataTooLarge', 400),
- ('MethodNotAllowed', 405),
- ('MissingAttachment'),
- ('MissingContentLength', 411),
- ('MissingRequestBodyError', 400),
- ('MissingSecurityElement', 400),
- ('MissingSecurityHeader', 400),
- ('NoLoggingStatusForKey', 400),
- ('NoSuchBucket', 404),
- ('NoSuchKey', 404),
- ('NoSuchLifecycleConfiguration', 404),
- ('NoSuchUpload', 404),
- ('NoSuchVersion', 404),
- ('NotSignedUp', 403),
- ('NotSuchBucketPolicy', 404),
- ('OperationAborted', 409),
- ('PermanentRedirect', 301),
- ('PreconditionFailed', 412),
- ('Redirect', 307),
- ('RequestIsNotMultiPartContent', 400),
- ('RequestTimeout', 400),
- ('RequestTimeTooSkewed', 403),
- ('RequestTorrentOfBucketError', 400),
- ('SignatureDoesNotMatch', 403),
- ('TemporaryRedirect', 307),
- ('TokenRefreshRequired', 400),
- ('TooManyBuckets', 400),
- ('UnexpectedContent', 400),
- ('UnresolvableGrantByEmailAddress', 400),
- ('UserKeyMustBeSpecified', 400)):
+ ('InvalidArgument', 400),
+ ('InvalidBucketName', 400),
+ ('InvalidBucketState', 409),
+ ('InvalidDigest', 400),
+ ('InvalidLocationConstraint', 400),
+ ('InvalidPart', 400),
+ ('InvalidPartOrder', 400),
+ ('InvalidPayer', 403),
+ ('InvalidPolicyDocument', 400),
+ ('InvalidRange', 416),
+ ('InvalidRequest', 400),
+ ('InvalidSecurity', 403),
+ ('InvalidSOAPRequest', 400),
+ ('InvalidStorageClass', 400),
+ ('InvalidTargetBucketForLogging', 400),
+ ('InvalidToken', 400),
+ ('InvalidURI', 400),
+ ('KeyTooLong', 400),
+ ('MalformedACLError', 400),
+ ('MalformedPOSTRequest', 400),
+ ('MalformedXML', 400),
+ ('MaxMessageLengthExceeded', 400),
+ ('MaxPostPreDataLengthExceededError', 400),
+ ('MetadataTooLarge', 400),
+ ('MethodNotAllowed', 405),
+ ('MissingAttachment'),
+ ('MissingContentLength', 411),
+ ('MissingRequestBodyError', 400),
+ ('MissingSecurityElement', 400),
+ ('MissingSecurityHeader', 400),
+ ('NoLoggingStatusForKey', 400),
+ ('NoSuchBucket', 404),
+ ('NoSuchKey', 404),
+ ('NoSuchLifecycleConfiguration', 404),
+ ('NoSuchUpload', 404),
+ ('NoSuchVersion', 404),
+ ('NotSignedUp', 403),
+ ('NotSuchBucketPolicy', 404),
+ ('OperationAborted', 409),
+ ('PermanentRedirect', 301),
+ ('PreconditionFailed', 412),
+ ('Redirect', 307),
+ ('RequestIsNotMultiPartContent', 400),
+ ('RequestTimeout', 400),
+ ('RequestTimeTooSkewed', 403),
+ ('RequestTorrentOfBucketError', 400),
+ ('SignatureDoesNotMatch', 403),
+ ('TemporaryRedirect', 307),
+ ('TokenRefreshRequired', 400),
+ ('TooManyBuckets', 400),
+ ('UnexpectedContent', 400),
+ ('UnresolvableGrantByEmailAddress', 400),
+ ('UserKeyMustBeSpecified', 400)):
_add_matcher_class(BotoTestCase.s3_error_code.client,
code, base=ClientError)
for code in (('InternalError', 500),
- ('NotImplemented', 501),
- ('ServiceUnavailable', 503),
- ('SlowDown', 503)):
+ ('NotImplemented', 501),
+ ('ServiceUnavailable', 503),
+ ('SlowDown', 503)):
_add_matcher_class(BotoTestCase.s3_error_code.server,
code, base=ServerError)
diff --git a/tools/check_logs.py b/tools/check_logs.py
index bc4eaca..eab9f73 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -22,6 +22,7 @@
import StringIO
import sys
import urllib2
+
import yaml
diff --git a/tools/colorizer.py b/tools/colorizer.py
index a3a0616..e7152f2 100755
--- a/tools/colorizer.py
+++ b/tools/colorizer.py
@@ -42,10 +42,10 @@
"""Display a subunit stream through a colorized unittest test runner."""
import heapq
-import subunit
import sys
import unittest
+import subunit
import testtools
diff --git a/tools/find_stack_traces.py b/tools/find_stack_traces.py
index c905976..4862d01 100755
--- a/tools/find_stack_traces.py
+++ b/tools/find_stack_traces.py
@@ -16,12 +16,13 @@
# under the License.
import gzip
+import pprint
import re
import StringIO
import sys
import urllib2
-import pprint
+
pp = pprint.PrettyPrinter()
NOVA_TIMESTAMP = r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d"
diff --git a/tox.ini b/tox.ini
index 9c32121..b3d4397 100644
--- a/tox.ini
+++ b/tox.ini
@@ -109,7 +109,9 @@
[flake8]
# E125 is a won't fix until https://github.com/jcrocholl/pep8/issues/126 is resolved. For further detail see https://review.openstack.org/#/c/36788/
# H402 skipped because some docstrings aren't sentences
-# Skipped because of new hacking 0.9: H407,H405,H904,H305,E123,H307,E122,E129,E128
-ignore = E125,H402,H404,H407,H405,H904,H305,E123,H307,E122,E129,E128
+# E123 skipped because it is ignored by default in the default pep8
+# E129 skipped because it is too limiting when combined with other rules
+# Skipped because of new hacking 0.9: H405,H904
+ignore = E125,H402,E123,E129,H404,H405,H904
show-source = True
exclude = .git,.venv,.tox,dist,doc,openstack,*egg