Merge "Add NotImplementedError to the abstract method"
diff --git a/HACKING.rst b/HACKING.rst
index 103f8cd..fed4130 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -56,8 +56,8 @@
import logging
import random
import StringIO
+ import testtools
import time
- import unittest
import eventlet
import webob.exc
diff --git a/etc/TEMPEST_README.txt b/etc/TEMPEST_README.txt
index e46e195..50fa688 100644
--- a/etc/TEMPEST_README.txt
+++ b/etc/TEMPEST_README.txt
@@ -2,13 +2,6 @@
-rename the /etc/tempest.conf.sample file to tempest.conf
-Set the fields in the file to values relevant to your system
-Set the "authentication" value (basic or keystone_v2 currently supported)
--from the root directory of the project, run "nosetests tempest/tests" to
- run all tests
-
-TODO:
-Use virtualenv to install all needed packages. Till then, the following
-packages must be installed:
--httplib2
--unittest2
--paramiko
--nose
\ No newline at end of file
+-From the root directory of the project, run "./run_tests.sh" this will
+create the venv to install the project dependencies and run nosetests tempest
+to run all the tests
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 53892e8..e1196f0 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -162,12 +162,6 @@
# The version of the OpenStack Images API to use
api_version = 1
-# This is the main host address of the Image API
-host = 127.0.0.1
-
-# Port that the Image API is running on
-port = 9292
-
[network]
# This section contains configuration options used when executing tests
# against the OpenStack Network API.
diff --git a/openstack-common.conf b/openstack-common.conf
index a75279f..0c9e43e 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -1,7 +1,7 @@
[DEFAULT]
# The list of modules to copy from openstack-common
-modules=setup,cfg,iniparser
+modules=setup,cfg,iniparser,install_venv_common
# The base module to hold the copy of openstack.common
base=tempest
diff --git a/tempest/clients.py b/tempest/clients.py
index 29e83bf..7a7ea13 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -32,7 +32,7 @@
from tempest.services.compute.json.security_groups_client import \
SecurityGroupsClientJSON
from tempest.services.compute.json.keypairs_client import KeyPairsClientJSON
-from tempest.services.compute.json.quotas_client import QuotasClient
+from tempest.services.compute.json.quotas_client import QuotasClientJSON
from tempest.services.compute.json.volumes_extensions_client import \
VolumesExtensionsClientJSON
from tempest.services.compute.json.console_output_client import \
@@ -44,6 +44,7 @@
from tempest.services.compute.xml.images_client import ImagesClientXML
from tempest.services.compute.xml.keypairs_client import KeyPairsClientXML
from tempest.services.compute.xml.limits_client import LimitsClientXML
+from tempest.services.compute.xml.quotas_client import QuotasClientXML
from tempest.services.compute.xml.security_groups_client \
import SecurityGroupsClientXML
from tempest.services.compute.xml.servers_client import ServersClientXML
@@ -79,6 +80,11 @@
"xml": KeyPairsClientXML,
}
+QUOTAS_CLIENTS = {
+ "json": QuotasClientJSON,
+ "xml": QuotasClientXML,
+}
+
SERVERS_CLIENTS = {
"json": ServersClientJSON,
"xml": ServersClientXML,
@@ -180,6 +186,7 @@
self.limits_client = LIMITS_CLIENTS[interface](*client_args)
self.images_client = IMAGES_CLIENTS[interface](*client_args)
self.keypairs_client = KEYPAIRS_CLIENTS[interface](*client_args)
+ self.quotas_client = QUOTAS_CLIENTS[interface](*client_args)
self.flavors_client = FLAVORS_CLIENTS[interface](*client_args)
ext_cli = EXTENSIONS_CLIENTS[interface](*client_args)
self.extensions_client = ext_cli
@@ -196,7 +203,6 @@
except KeyError:
msg = "Unsupported interface type `%s'" % interface
raise exceptions.InvalidConfiguration(msg)
- self.quotas_client = QuotasClient(*client_args)
self.network_client = NetworkClient(*client_args)
self.account_client = AccountClient(*client_args)
self.container_client = ContainerClient(*client_args)
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 8fc5545..824198f 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -269,18 +269,9 @@
if resp.status == 413:
resp_body = self._parse_resp(resp_body)
self._log(req_url, body, resp, resp_body)
- if 'overLimit' in resp_body:
- raise exceptions.OverLimit(resp_body['overLimit']['message'])
- elif 'exceeded' in resp_body['message']:
- raise exceptions.OverLimit(resp_body['message'])
- elif depth < MAX_RECURSION_DEPTH:
- delay = resp['Retry-After'] if 'Retry-After' in resp else 60
- time.sleep(int(delay))
- return self.request(method, url, headers, body, depth + 1)
- else:
- raise exceptions.RateLimitExceeded(
- message=resp_body['overLimitFault']['message'],
- details=resp_body['overLimitFault']['details'])
+ #Checking whether Absolute/Rate limit
+ return self.check_over_limit(resp_body, method, url, headers, body,
+ depth, wait)
if resp.status in (500, 501):
resp_body = self._parse_resp(resp_body)
@@ -309,6 +300,36 @@
return resp, resp_body
+ def check_over_limit(self, resp_body, method, url,
+ headers, body, depth, wait):
+ self.is_absolute_limit(resp_body['overLimit'])
+ return self.is_rate_limit_retry_max_recursion_depth(
+ resp_body['overLimit'], method, url, headers,
+ body, depth, wait)
+
+ def is_absolute_limit(self, resp_body):
+ if 'exceeded' in resp_body['message']:
+ raise exceptions.OverLimit(resp_body['message'])
+ else:
+ return
+
+ def is_rate_limit_retry_max_recursion_depth(self, resp_body, method,
+ url, headers, body, depth,
+ wait):
+ if 'retryAfter' in resp_body:
+ if depth < MAX_RECURSION_DEPTH:
+ delay = resp_body['retryAfter']
+ time.sleep(int(delay))
+ return self.request(method, url, headers=headers,
+ body=body,
+ depth=depth + 1, wait=wait)
+ else:
+ raise exceptions.RateLimitExceeded(
+ message=resp_body['overLimitFault']['message'],
+ details=resp_body['overLimitFault']['details'])
+ else:
+ raise exceptions.OverLimit(resp_body['message'])
+
def wait_for_resource_deletion(self, id):
"""Waits for a resource to be deleted."""
start_time = int(time.time())
@@ -333,3 +354,10 @@
def _parse_resp(self, body):
return xml_to_json(etree.fromstring(body))
+
+ def check_over_limit(self, resp_body, method, url,
+ headers, body, depth, wait):
+ self.is_absolute_limit(resp_body)
+ return self.is_rate_limit_retry_max_recursion_depth(
+ resp_body, method, url, headers,
+ body, depth, wait)
diff --git a/tempest/config.py b/tempest/config.py
index a166093..89fa2d9 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -37,22 +37,6 @@
cfg.StrOpt('uri',
default=None,
help="Full URI of the OpenStack Identity API (Keystone)"),
- cfg.StrOpt('host',
- default="127.0.0.1",
- help="(DEPRECATED, use uri) Host IP for making Identity "
- "API requests."),
- cfg.IntOpt('port',
- default=8773,
- help="(DEPRECATED, use uri) Port for the Identity service."),
- cfg.StrOpt('api_version',
- default="v1.1",
- help="(DEPRECATED, use uri) Version of the Identity API"),
- cfg.StrOpt('path',
- default='/',
- help="(IGNORED) Path of API request"),
- cfg.BoolOpt('use_ssl',
- default=False,
- help="(DEPRECATED, use uri) Specifies if we are using https."),
cfg.StrOpt('strategy',
default='keystone',
help="Which auth method does the environment use? "
@@ -102,16 +86,6 @@
for opt in IdentityGroup:
conf.register_opt(opt, group='identity')
- # Fall back to piecemeal identity URI for legacy support
- authurl = data_utils.build_url(conf.identity.host,
- str(conf.identity.port),
- conf.identity.api_version,
- path='', # Ignore path...
- use_ssl=conf.identity.use_ssl)
-
- if not conf.identity.uri:
- conf.identity.uri = authurl
-
compute_group = cfg.OptGroup(name='compute',
title='Compute Service Options')
@@ -249,13 +223,6 @@
title="Image Service Options")
ImageGroup = [
- cfg.StrOpt('host',
- default='127.0.0.1',
- help="Host IP for making Images API requests. Defaults to "
- "'127.0.0.1'."),
- cfg.IntOpt('port',
- default=9292,
- help="Listen port of the Images service."),
cfg.StrOpt('api_version',
default='1',
help="Version of the API"),
diff --git a/tempest/manager.py b/tempest/manager.py
index 4137ec3..cb1e52d 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -55,7 +55,7 @@
VolumesExtensionsClient = volumes_extensions_client.VolumesExtensionsClientJSON
VolumesClient = volumes_client.VolumesClientJSON
ConsoleOutputsClient = console_output_client.ConsoleOutputsClientJSON
-QuotasClient = quotas_client.QuotasClient
+QuotasClient = quotas_client.QuotasClientJSON
LOG = logging.getLogger(__name__)
diff --git a/tempest/services/compute/admin/json/quotas_client.py b/tempest/services/compute/admin/json/quotas_client.py
index 625d4d4..0a4bd72 100644
--- a/tempest/services/compute/admin/json/quotas_client.py
+++ b/tempest/services/compute/admin/json/quotas_client.py
@@ -17,14 +17,14 @@
import json
-from tempest.services.compute.json.quotas_client import QuotasClient
+from tempest.services.compute.json.quotas_client import QuotasClientJSON
-class AdminQuotasClient(QuotasClient):
+class AdminQuotasClientJSON(QuotasClientJSON):
def __init__(self, config, username, password, auth_url, tenant_name=None):
- super(AdminQuotasClient, self).__init__(config, username, password,
- auth_url, tenant_name)
+ super(AdminQuotasClientJSON, self).__init__(config, username, password,
+ auth_url, tenant_name)
def update_quota_set(self, tenant_id, injected_file_content_bytes=None,
metadata_items=None, ram=None, floating_ips=None,
diff --git a/tempest/services/compute/admin/xml/__init__.py b/tempest/services/compute/admin/xml/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/compute/admin/xml/__init__.py
diff --git a/tempest/services/compute/admin/xml/quotas_client.py b/tempest/services/compute/admin/xml/quotas_client.py
new file mode 100644
index 0000000..d567a9c
--- /dev/null
+++ b/tempest/services/compute/admin/xml/quotas_client.py
@@ -0,0 +1,88 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 NTT Data
+# 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 urllib
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import Document
+from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import xml_to_json
+from tempest.services.compute.xml.common import XMLNS_11
+from tempest.services.compute.xml.quotas_client import QuotasClientXML
+
+
+class AdminQuotasClientXML(QuotasClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(AdminQuotasClientXML, self).__init__(config, username, password,
+ auth_url, tenant_name)
+
+ def update_quota_set(self, tenant_id, injected_file_content_bytes=None,
+ metadata_items=None, ram=None, floating_ips=None,
+ key_pairs=None, instances=None,
+ security_group_rules=None, injected_files=None,
+ cores=None, injected_file_path_bytes=None,
+ security_groups=None):
+ """
+ Updates the tenant's quota limits for one or more resources
+ """
+ post_body = Element("quota_set",
+ xmlns=XMLNS_11)
+
+ if injected_file_content_bytes is not None:
+ post_body.add_attr('injected_file_content_bytes',
+ injected_file_content_bytes)
+
+ if metadata_items is not None:
+ post_body.add_attr('metadata_items', metadata_items)
+
+ if ram is not None:
+ post_body.add_attr('ram', ram)
+
+ if floating_ips is not None:
+ post_body.add_attr('floating_ips', floating_ips)
+
+ if key_pairs is not None:
+ post_body.add_attr('key_pairs', key_pairs)
+
+ if instances is not None:
+ post_body.add_attr('instances', instances)
+
+ if security_group_rules is not None:
+ post_body.add_attr('security_group_rules', security_group_rules)
+
+ if injected_files is not None:
+ post_body.add_attr('injected_files', injected_files)
+
+ if cores is not None:
+ post_body.add_attr('cores', cores)
+
+ if injected_file_path_bytes is not None:
+ post_body.add_attr('injected_file_path_bytes',
+ injected_file_path_bytes)
+
+ if security_groups is not None:
+ post_body.add_attr('security_groups', security_groups)
+
+ resp, body = self.put('os-quota-sets/%s' % str(tenant_id),
+ str(Document(post_body)),
+ self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ body = self._format_quota(body)
+ return resp, body
diff --git a/tempest/services/compute/json/quotas_client.py b/tempest/services/compute/json/quotas_client.py
index 543b015..a95ff1c 100644
--- a/tempest/services/compute/json/quotas_client.py
+++ b/tempest/services/compute/json/quotas_client.py
@@ -20,11 +20,11 @@
from tempest.common.rest_client import RestClient
-class QuotasClient(RestClient):
+class QuotasClientJSON(RestClient):
def __init__(self, config, username, password, auth_url, tenant_name=None):
- super(QuotasClient, self).__init__(config, username, password,
- auth_url, tenant_name)
+ super(QuotasClientJSON, self).__init__(config, username, password,
+ auth_url, tenant_name)
self.service = self.config.compute.catalog_type
def get_quota_set(self, tenant_id):
diff --git a/tempest/services/compute/xml/quotas_client.py b/tempest/services/compute/xml/quotas_client.py
new file mode 100644
index 0000000..8978214
--- /dev/null
+++ b/tempest/services/compute/xml/quotas_client.py
@@ -0,0 +1,58 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 NTT Data
+# 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 urllib
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import Document
+from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import xml_to_json
+from tempest.services.compute.xml.common import XMLNS_11
+
+
+class QuotasClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(QuotasClientXML, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def _format_quota(self, q):
+ quota = {}
+ for k, v in q.items():
+ try:
+ v = int(v)
+ except ValueError:
+ pass
+
+ quota[k] = v
+
+ return quota
+
+ def _parse_array(self, node):
+ return [self._format_quota(xml_to_json(x)) for x in node]
+
+ def get_quota_set(self, tenant_id):
+ """List the quota set for a tenant."""
+
+ url = 'os-quota-sets/%s' % str(tenant_id)
+ resp, body = self.get(url, self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ body = self._format_quota(body)
+ return resp, body
diff --git a/tempest/test.py b/tempest/test.py
index dc480d1..7804e12 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -18,14 +18,14 @@
import logging
import time
-import unittest2 as unittest
+import testtools
from tempest import manager
LOG = logging.getLogger(__name__)
-class TestCase(unittest.TestCase):
+class TestCase(testtools.TestCase):
"""
Base test case class for all Tempest tests
diff --git a/tempest/testboto.py b/tempest/testboto.py
index 29ac3ca..7031fe2 100644
--- a/tempest/testboto.py
+++ b/tempest/testboto.py
@@ -25,9 +25,8 @@
from boto.exception import EC2ResponseError
from boto.s3.bucket import Bucket
from boto.s3.key import Key
-import nose
import testresources
-import unittest2 as unittest
+import testtools
from tempest.exceptions import TearDownException
import tempest.tests.boto
@@ -123,11 +122,11 @@
return string + ")"
-class BotoTestCase(unittest.TestCase,
+class BotoTestCase(testtools.TestCase,
testresources.ResourcedTestCase):
"""Recommended to use as base class for boto related test."""
- resources = [('boto_init', tempest.tests.boto.generic_setup_package())]
+ resources = [('boto_init', tempest.tests.boto.BotoResource())]
@classmethod
def setUpClass(cls):
@@ -137,11 +136,11 @@
cls._sequence = -1
if (hasattr(cls, "EC2") and
tempest.tests.boto.EC2_CAN_CONNECT_ERROR is not None):
- raise nose.SkipTest("EC2 " + cls.__name__ + ": " +
+ raise cls.skipException("EC2 " + cls.__name__ + ": " +
tempest.tests.boto.EC2_CAN_CONNECT_ERROR)
if (hasattr(cls, "S3") and
tempest.tests.boto.S3_CAN_CONNECT_ERROR is not None):
- raise nose.SkipTest("S3 " + cls.__name__ + ": " +
+ raise cls.skipException("S3 " + cls.__name__ + ": " +
tempest.tests.boto.S3_CAN_CONNECT_ERROR)
@classmethod
diff --git a/tempest/tests/boto/__init__.py b/tempest/tests/boto/__init__.py
index 6d5149e..99dd8a9 100644
--- a/tempest/tests/boto/__init__.py
+++ b/tempest/tests/boto/__init__.py
@@ -26,6 +26,7 @@
import tempest.clients
from tempest.common.utils.file_utils import have_effective_read_access
import tempest.config
+from testresources import TestResourceManager
A_I_IMAGES_READY = False # ari,ami,aki
S3_CAN_CONNECT_ERROR = "Unknown Error"
@@ -96,3 +97,8 @@
else:
S3_CAN_CONNECT_ERROR = None
boto_logger.setLevel(level)
+
+
+class BotoResource(TestResourceManager):
+ def make(self, dependency_resources=None):
+ return generic_setup_package()
diff --git a/tempest/tests/boto/test_ec2_instance_run.py b/tempest/tests/boto/test_ec2_instance_run.py
index 6a8778a..1adb5fb 100644
--- a/tempest/tests/boto/test_ec2_instance_run.py
+++ b/tempest/tests/boto/test_ec2_instance_run.py
@@ -20,9 +20,8 @@
from boto.exception import EC2ResponseError
from boto.s3.key import Key
-import nose
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
@@ -44,8 +43,8 @@
def setUpClass(cls):
super(InstanceRunTest, cls).setUpClass()
if not tempest.tests.boto.A_I_IMAGES_READY:
- raise nose.SkipTest("".join(("EC2 ", cls.__name__,
- ": requires ami/aki/ari manifest")))
+ raise cls.skipException("".join(("EC2 ", cls.__name__,
+ ": requires ami/aki/ari manifest")))
cls.os = clients.Manager()
cls.s3_client = cls.os.s3_client
cls.ec2_client = cls.os.ec2api_client
@@ -122,7 +121,7 @@
self.cancelResourceCleanUp(rcuk)
@attr(type='smoke')
- @unittest.skip("Skipped until the Bug #1098891 is resolved")
+ @testtools.skip("Skipped until the Bug #1098891 is resolved")
def test_run_terminate_instance(self):
# EC2 run, terminate immediately
image_ami = self.ec2_client.get_image(self.images["ami"]
diff --git a/tempest/tests/boto/test_ec2_keys.py b/tempest/tests/boto/test_ec2_keys.py
index fcec02d..540374a 100644
--- a/tempest/tests/boto/test_ec2_keys.py
+++ b/tempest/tests/boto/test_ec2_keys.py
@@ -16,7 +16,7 @@
# under the License.
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
@@ -47,7 +47,7 @@
self.client.get_key_pair(key_name)))
@attr(type='smoke')
- @unittest.skip("Skipped until the Bug #1072318 is resolved")
+ @testtools.skip("Skipped until the Bug #1072318 is resolved")
def test_delete_ec2_keypair(self):
# EC2 delete KeyPair
key_name = rand_name("keypair-")
@@ -65,7 +65,7 @@
self.client.get_key_pair(key_name)))
@attr(type='smoke')
- @unittest.skip("Skipped until the Bug #1072762 is resolved")
+ @testtools.skip("Skipped until the Bug #1072762 is resolved")
def test_duplicate_ec2_keypair(self):
# EC2 duplicate KeyPair
key_name = rand_name("keypair-")
diff --git a/tempest/tests/boto/test_ec2_network.py b/tempest/tests/boto/test_ec2_network.py
index 27649e6..76103c2 100644
--- a/tempest/tests/boto/test_ec2_network.py
+++ b/tempest/tests/boto/test_ec2_network.py
@@ -16,7 +16,7 @@
# under the License.
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.testboto import BotoTestCase
@@ -32,7 +32,7 @@
cls.client = cls.os.ec2api_client
#Note(afazekas): these tests for things duable without an instance
- @unittest.skip("Skipped until the Bug #1080406 is resolved")
+ @testtools.skip("Skipped until the Bug #1080406 is resolved")
@attr(type='smoke')
def test_disassociate_not_associated_floating_ip(self):
# EC2 disassociate not associated floating ip
diff --git a/tempest/tests/boto/test_ec2_security_groups.py b/tempest/tests/boto/test_ec2_security_groups.py
index 09da82c..ed7bedb 100644
--- a/tempest/tests/boto/test_ec2_security_groups.py
+++ b/tempest/tests/boto/test_ec2_security_groups.py
@@ -16,7 +16,7 @@
# under the License.
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
diff --git a/tempest/tests/boto/test_ec2_volumes.py b/tempest/tests/boto/test_ec2_volumes.py
index ee7fa3f..7397cdb 100644
--- a/tempest/tests/boto/test_ec2_volumes.py
+++ b/tempest/tests/boto/test_ec2_volumes.py
@@ -19,7 +19,7 @@
import time
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.testboto import BotoTestCase
diff --git a/tempest/tests/boto/test_s3_buckets.py b/tempest/tests/boto/test_s3_buckets.py
index beed28b..36aa319 100644
--- a/tempest/tests/boto/test_s3_buckets.py
+++ b/tempest/tests/boto/test_s3_buckets.py
@@ -16,7 +16,7 @@
# under the License.
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
@@ -33,7 +33,7 @@
cls.client = cls.os.s3_client
cls.config = cls.os.config
- @unittest.skip("Skipped until the Bug #1076965 is resolved")
+ @testtools.skip("Skipped until the Bug #1076965 is resolved")
@attr(type='smoke')
def test_create_and_get_delete_bucket(self):
# S3 Create, get and delete bucket
diff --git a/tempest/tests/boto/test_s3_ec2_images.py b/tempest/tests/boto/test_s3_ec2_images.py
index f14115a..e0dc124 100644
--- a/tempest/tests/boto/test_s3_ec2_images.py
+++ b/tempest/tests/boto/test_s3_ec2_images.py
@@ -19,9 +19,8 @@
import os
from boto.s3.key import Key
-import nose
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
@@ -38,8 +37,8 @@
def setUpClass(cls):
super(S3ImagesTest, cls).setUpClass()
if not tempest.tests.boto.A_I_IMAGES_READY:
- raise nose.SkipTest("".join(("EC2 ", cls.__name__,
- ": requires ami/aki/ari manifest")))
+ raise cls.skipException("".join(("EC2 ", cls.__name__,
+ ": requires ami/aki/ari manifest")))
cls.os = clients.Manager()
cls.s3_client = cls.os.s3_client
cls.images_client = cls.os.ec2api_client
@@ -88,7 +87,7 @@
#TODO(afazekas): double deregister ?
self.cancelResourceCleanUp(image["cleanUp"])
- @unittest.skip("Skipped until the Bug #1074904 is resolved")
+ @testtools.skip("Skipped until the Bug #1074904 is resolved")
def test_register_get_deregister_aki_image(self):
# Register and deregister aki image
image = {"name": rand_name("aki-name-"),
@@ -116,7 +115,7 @@
self.assertIn(retrieved_image.state, self.valid_image_state)
self.cancelResourceCleanUp(image["cleanUp"])
- @unittest.skip("Skipped until the Bug #1074908 and #1074904 is resolved")
+ @testtools.skip("Skipped until the Bug #1074908 and #1074904 is resolved")
def test_register_get_deregister_ari_image(self):
# Register and deregister ari image
image = {"name": rand_name("ari-name-"),
diff --git a/tempest/tests/boto/test_s3_objects.py b/tempest/tests/boto/test_s3_objects.py
index 6e89539..cacd99f 100644
--- a/tempest/tests/boto/test_s3_objects.py
+++ b/tempest/tests/boto/test_s3_objects.py
@@ -19,7 +19,7 @@
from boto.s3.key import Key
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
@@ -37,7 +37,7 @@
cls.client = cls.os.s3_client
cls.config = cls.os.config
- @unittest.skip("Skipped until the Bug #1076534 is resolved")
+ @testtools.skip("Skipped until the Bug #1076534 is resolved")
@attr(type='smoke')
def test_create_get_delete_object(self):
# S3 Create, get and delete object
diff --git a/tempest/tests/boto/utils/wait.py b/tempest/tests/boto/utils/wait.py
index 77fe037..c2d4ea3 100644
--- a/tempest/tests/boto/utils/wait.py
+++ b/tempest/tests/boto/utils/wait.py
@@ -20,7 +20,7 @@
import time
from boto.exception import BotoServerError
-from unittest2 import TestCase
+from testtools import TestCase
import tempest.config
diff --git a/tempest/tests/compute/__init__.py b/tempest/tests/compute/__init__.py
index 398f982..08e5091 100644
--- a/tempest/tests/compute/__init__.py
+++ b/tempest/tests/compute/__init__.py
@@ -17,11 +17,11 @@
import logging
-import nose
from tempest import clients
from tempest import config
from tempest.exceptions import InvalidConfiguration
+from testresources import TestResourceManager
LOG = logging.getLogger(__name__)
@@ -80,3 +80,8 @@
% (user2_tenant_name, user2_password))
raise InvalidConfiguration(msg)
MULTI_USER = True
+
+
+class ComputeResource(TestResourceManager):
+ def make(self, dependency_resources=None):
+ return generic_setup_package()
diff --git a/tempest/tests/compute/admin/test_flavors.py b/tempest/tests/compute/admin/test_flavors.py
index 4859308..eb2392c 100644
--- a/tempest/tests/compute/admin/test_flavors.py
+++ b/tempest/tests/compute/admin/test_flavors.py
@@ -15,9 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-import nose
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest.common.utils.data_utils import rand_int_id
from tempest.common.utils.data_utils import rand_name
@@ -35,7 +34,7 @@
def setUpClass(self, cls):
if not compute.FLAVOR_EXTRA_DATA_ENABLED:
msg = "FlavorExtraData extension not enabled."
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
cls.client = cls.os.flavors_client
cls.flavor_name_prefix = 'test_flavor_'
diff --git a/tempest/tests/compute/admin/test_quotas.py b/tempest/tests/compute/admin/test_quotas.py
index 2c2e5da..b2b515a 100644
--- a/tempest/tests/compute/admin/test_quotas.py
+++ b/tempest/tests/compute/admin/test_quotas.py
@@ -18,23 +18,21 @@
from nose.plugins.attrib import attr
from tempest import exceptions
-from tempest.services.compute.admin.json import quotas_client as adm_quotas
-from tempest.tests.compute.base import BaseComputeTest
+from tempest.services.compute.admin.json \
+ import quotas_client as adm_quotas_json
+from tempest.services.compute.admin.xml import quotas_client as adm_quotas_xml
+from tempest.tests import compute
+from tempest.tests.compute import base
-class QuotasTest(BaseComputeTest):
+class QuotasAdminTestBase(object):
@classmethod
def setUpClass(cls):
- super(QuotasTest, cls).setUpClass()
- c_adm_user = cls.config.compute_admin.username
- c_adm_pass = cls.config.compute_admin.password
- c_adm_tenant = cls.config.compute_admin.tenant_name
- auth_url = cls.config.identity.uri
-
- cls.adm_client = adm_quotas.AdminQuotasClient(cls.config, c_adm_user,
- c_adm_pass, auth_url,
- c_adm_tenant)
+ cls.c_adm_user = cls.config.compute_admin.username
+ cls.c_adm_pass = cls.config.compute_admin.password
+ cls.c_adm_tenant = cls.config.compute_admin.tenant_name
+ cls.auth_url = cls.config.identity.uri
cls.client = cls.os.quotas_client
cls.identity_admin_client = cls._get_identity_admin_client()
resp, tenants = cls.identity_admin_client.list_tenants()
@@ -57,7 +55,7 @@
'cores': 20, 'security_groups': 10}
@classmethod
- def tearDown(cls):
+ def tearDownClass(cls):
for server in cls.servers:
try:
cls.servers_client.delete_server(server['id'])
@@ -154,3 +152,43 @@
finally:
self.adm_client.update_quota_set(self.demo_tenant_id,
ram=default_mem_quota)
+
+
+class QuotasAdminTestJSON(QuotasAdminTestBase, base.BaseComputeAdminTestJSON,
+ base.BaseComputeTest):
+
+ @classmethod
+ def setUpClass(cls):
+ base.BaseComputeAdminTestJSON.setUpClass()
+ base.BaseComputeTest.setUpClass()
+ super(QuotasAdminTestJSON, cls).setUpClass()
+
+ cls.adm_client = adm_quotas_json.AdminQuotasClientJSON(
+ cls.config, cls.c_adm_user, cls.c_adm_pass,
+ cls.auth_url, cls.c_adm_tenant)
+
+ @classmethod
+ def tearDownClass(cls):
+ super(QuotasAdminTestJSON, cls).tearDownClass()
+ base.BaseComputeTest.tearDownClass()
+
+
+class QuotasAdminTestXML(QuotasAdminTestBase, base.BaseComputeAdminTestXML,
+ base.BaseComputeTest):
+
+ @classmethod
+ def setUpClass(cls):
+ base.BaseComputeAdminTestXML.setUpClass()
+ base.BaseComputeTest.setUpClass()
+ super(QuotasAdminTestXML, cls).setUpClass()
+
+ cls.adm_client = adm_quotas_xml.AdminQuotasClientXML(cls.config,
+ cls.c_adm_user,
+ cls.c_adm_pass,
+ cls.auth_url,
+ cls.c_adm_tenant)
+
+ @classmethod
+ def tearDownClass(cls):
+ super(QuotasAdminTestXML, cls).tearDownClass()
+ base.BaseComputeTest.tearDownClass()
diff --git a/tempest/tests/compute/base.py b/tempest/tests/compute/base.py
index 9e2e883..2312931 100644
--- a/tempest/tests/compute/base.py
+++ b/tempest/tests/compute/base.py
@@ -18,9 +18,8 @@
import logging
import time
-import nose
import testresources
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
@@ -34,12 +33,11 @@
LOG = logging.getLogger(__name__)
-class BaseCompTest(unittest.TestCase,
+class BaseCompTest(testtools.TestCase,
testresources.ResourcedTestCase):
-
"""Base test case class for all Compute API tests."""
- resources = [('compute_init', compute.generic_setup_package())]
+ resources = [('compute_init', compute.ComputeResource())]
@classmethod
def setUpClass(cls):
@@ -249,7 +247,7 @@
super(BaseComputeTestXML, cls).setUpClass()
-class BaseComputeAdminTest(unittest.TestCase):
+class BaseComputeAdminTest(testtools.TestCase):
"""Base test case class for all Compute Admin API tests."""
@@ -263,7 +261,7 @@
if not cls.admin_username and cls.admin_password and cls.admin_tenant:
msg = ("Missing Compute Admin API credentials "
"in configuration.")
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
cls.os = clients.ComputeAdminManager(interface=cls._interface)
diff --git a/tempest/tests/compute/floating_ips/test_floating_ips_actions.py b/tempest/tests/compute/floating_ips/test_floating_ips_actions.py
index 9a9914a..59faf66 100644
--- a/tempest/tests/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/tests/compute/floating_ips/test_floating_ips_actions.py
@@ -16,7 +16,7 @@
# under the License.
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
diff --git a/tempest/tests/compute/floating_ips/test_list_floating_ips.py b/tempest/tests/compute/floating_ips/test_list_floating_ips.py
index 9eec27c..ea99e89 100644
--- a/tempest/tests/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/tests/compute/floating_ips/test_list_floating_ips.py
@@ -16,7 +16,7 @@
# under the License.
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
diff --git a/tempest/tests/compute/images/test_image_metadata.py b/tempest/tests/compute/images/test_image_metadata.py
index cdf4249..94bdca7 100644
--- a/tempest/tests/compute/images/test_image_metadata.py
+++ b/tempest/tests/compute/images/test_image_metadata.py
@@ -53,6 +53,7 @@
super(ImagesMetadataTest, cls).tearDownClass()
def setUp(self):
+ super(ImagesMetadataTest, self).setUp()
meta = {'key1': 'value1', 'key2': 'value2'}
resp, _ = self.client.set_image_metadata(self.image_id, meta)
self.assertEqual(resp.status, 200)
diff --git a/tempest/tests/compute/images/test_images.py b/tempest/tests/compute/images/test_images.py
index 2557f16..1fc03b8 100644
--- a/tempest/tests/compute/images/test_images.py
+++ b/tempest/tests/compute/images/test_images.py
@@ -15,9 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-import nose
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import parse_image_id
@@ -113,7 +112,7 @@
self.assertRaises(exceptions.Duplicate, self.client.create_image,
server['id'], snapshot_name)
- @unittest.skip("Until Bug 1039739 is fixed")
+ @testtools.skip("Until Bug 1039739 is fixed")
@attr(type='negative')
def test_create_image_when_server_is_rebooting(self):
# Return error when creating an image of server that is rebooting
@@ -217,6 +216,7 @@
ImagesTestBase):
def tearDown(self):
ImagesTestBase.tearDown(self)
+ base.BaseComputeTestJSON.tearDown(self)
@classmethod
def setUpClass(cls):
@@ -243,6 +243,7 @@
ImagesTestBase):
def tearDown(self):
ImagesTestBase.tearDown(self)
+ base.BaseComputeTestXML.tearDown(self)
@classmethod
def setUpClass(cls):
diff --git a/tempest/tests/compute/images/test_images_oneserver.py b/tempest/tests/compute/images/test_images_oneserver.py
index f8b560b..f3b1e01 100644
--- a/tempest/tests/compute/images/test_images_oneserver.py
+++ b/tempest/tests/compute/images/test_images_oneserver.py
@@ -15,9 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-import nose
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import parse_image_id
@@ -43,7 +42,7 @@
self.image_ids.remove(image_id)
@attr(type='negative')
- @unittest.skip("Until Bug 1006725 is fixed")
+ @testtools.skip("Until Bug 1006725 is fixed")
def test_create_image_specify_multibyte_character_image_name(self):
# Return an error if the image name has multi-byte characters
try:
@@ -56,7 +55,7 @@
" are used for image name")
@attr(type='negative')
- @unittest.skip("Until Bug 1005423 is fixed")
+ @testtools.skip("Until Bug 1005423 is fixed")
def test_create_image_specify_invalid_metadata(self):
# Return an error when creating image with invalid metadata
try:
@@ -69,7 +68,7 @@
self.fail("Should raise 400 Bad Request if meta data is invalid")
@attr(type='negative')
- @unittest.skip("Until Bug 1005423 is fixed")
+ @testtools.skip("Until Bug 1005423 is fixed")
def test_create_image_specify_metadata_over_limits(self):
# Return an error when creating image with meta data over 256 chars
try:
@@ -82,8 +81,8 @@
self.fail("Should raise 413 Over Limit if meta data was too long")
@attr(type='negative')
- @unittest.skipUnless(compute.MULTI_USER,
- 'Need multiple users for this test.')
+ @testtools.skipUnless(compute.MULTI_USER,
+ 'Need multiple users for this test.')
def test_delete_image_of_another_tenant(self):
# Return an error while trying to delete another tenant's image
self.servers_client.wait_for_server_status(self.server['id'], 'ACTIVE')
@@ -99,8 +98,8 @@
self.alt_client.delete_image, image_id)
@attr(type='smoke')
- @unittest.skipUnless(compute.CREATE_IMAGE_ENABLED,
- 'Environment unable to create images.')
+ @testtools.skipUnless(compute.CREATE_IMAGE_ENABLED,
+ 'Environment unable to create images.')
def test_create_delete_image(self):
# Create a new image
@@ -123,8 +122,8 @@
self.assertEqual(original_image['minDisk'], image['minDisk'])
@attr(type='negative')
- @unittest.skipUnless(compute.MULTI_USER,
- 'Need multiple users for this test.')
+ @testtools.skipUnless(compute.MULTI_USER,
+ 'Need multiple users for this test.')
def test_create_image_for_server_in_another_tenant(self):
# Creating image of another tenant's server should be return error
@@ -157,7 +156,7 @@
"of the server is still being saved")
@attr(type='negative')
- @unittest.skip("Until Bug 1004564 is fixed")
+ @testtools.skip("Until Bug 1004564 is fixed")
def test_create_image_specify_name_over_256_chars(self):
# Return an error if snapshot name over 256 characters is passed
@@ -192,6 +191,7 @@
def tearDown(self):
ImagesOneServerTestBase.tearDown(self)
+ base.BaseComputeTestJSON.tearDown(self)
@classmethod
def setUpClass(cls):
@@ -220,6 +220,7 @@
def tearDown(self):
ImagesOneServerTestBase.tearDown(self)
+ base.BaseComputeTestXML.tearDown(self)
@classmethod
def setUpClass(cls):
diff --git a/tempest/tests/compute/images/test_images_whitebox.py b/tempest/tests/compute/images/test_images_whitebox.py
index 2987534..b2419b4 100644
--- a/tempest/tests/compute/images/test_images_whitebox.py
+++ b/tempest/tests/compute/images/test_images_whitebox.py
@@ -50,7 +50,7 @@
cls.image_ids.remove(image_id)
@classmethod
- def update_state(self, server_id, vm_state, task_state, deleted=False):
+ def update_state(self, server_id, vm_state, task_state, deleted=0):
"""Update states of an instance in database for validation."""
if not task_state:
task_state = "NULL"
@@ -63,7 +63,7 @@
self.connection.execute(stmt, autocommit=True)
- def _test_create_image_409_base(self, vm_state, task_state, deleted=False):
+ def _test_create_image_409_base(self, vm_state, task_state, deleted=0):
"""Base method for create image tests based on vm and task states."""
try:
self.update_state(self.shared_server['id'], vm_state,
diff --git a/tempest/tests/compute/keypairs/test_keypairs.py b/tempest/tests/compute/keypairs/test_keypairs.py
index 7d95a9b..45c9079 100644
--- a/tempest/tests/compute/keypairs/test_keypairs.py
+++ b/tempest/tests/compute/keypairs/test_keypairs.py
@@ -16,7 +16,7 @@
# under the License.
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
@@ -77,7 +77,7 @@
self.assertEqual(202, resp.status)
@attr(type='positive')
- @unittest.skip("Skipped until the Bug #980688 is resolved")
+ @testtools.skip("Skipped until the Bug #980688 is resolved")
def test_get_keypair_detail(self):
# Keypair should be created, Got details by name and deleted
k_name = rand_name('keypair-')
@@ -137,7 +137,7 @@
self.fail('Expected BadRequest for invalid public key')
@attr(type='negative')
- @unittest.skip("Skipped until the Bug #1086980 is resolved")
+ @testtools.skip("Skipped until the Bug #1086980 is resolved")
def test_keypair_delete_nonexistant_key(self):
# Non-existant key deletion should throw a proper error
k_name = rand_name("keypair-non-existant-")
diff --git a/tempest/tests/compute/limits/test_absolute_limits.py b/tempest/tests/compute/limits/test_absolute_limits.py
index 89c5b25..d520b92 100644
--- a/tempest/tests/compute/limits/test_absolute_limits.py
+++ b/tempest/tests/compute/limits/test_absolute_limits.py
@@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import unittest2 as unittest
+import testtools
from tempest.tests.compute import base
@@ -26,7 +26,7 @@
def setUpClass(cls):
cls.client = cls.limits_client
- @unittest.skip("Skipped until the Bug #1025294 is resolved")
+ @testtools.skip("Skipped until the Bug #1025294 is resolved")
def test_absLimits_get(self):
# To check if all limits are present in the response
resp, absolute_limits = self.client.get_absolute_limits()
diff --git a/tempest/tests/compute/servers/test_console_output.py b/tempest/tests/compute/servers/test_console_output.py
index 3ad29a1..b26220b 100644
--- a/tempest/tests/compute/servers/test_console_output.py
+++ b/tempest/tests/compute/servers/test_console_output.py
@@ -16,7 +16,7 @@
# under the License.
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
@@ -63,7 +63,7 @@
pass
@attr(type='positive')
- @unittest.skip('Until tempest bug 1014683 is fixed.')
+ @testtools.skip('Until tempest bug 1014683 is fixed.')
def test_get_console_output_server_id_in_reboot_status(self):
# Positive test:Should be able to GET the console output
# for a given server_id in reboot status
diff --git a/tempest/tests/compute/servers/test_create_server.py b/tempest/tests/compute/servers/test_create_server.py
index c5a54dc..0dcc79f 100644
--- a/tempest/tests/compute/servers/test_create_server.py
+++ b/tempest/tests/compute/servers/test_create_server.py
@@ -16,10 +16,9 @@
# under the License.
import base64
-import nose
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest.common.utils.data_utils import rand_name
@@ -98,14 +97,14 @@
self.assertTrue(found)
@attr(type='positive')
- @unittest.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+ @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
def test_can_log_into_created_server(self):
# Check that the user can authenticate with the generated password
linux_client = RemoteClient(self.server, self.ssh_user, self.password)
self.assertTrue(linux_client.can_authenticate())
@attr(type='positive')
- @unittest.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+ @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
def test_verify_created_server_vcpus(self):
# Verify that the number of vcpus reported by the instance matches
# the amount stated by the flavor
@@ -114,7 +113,7 @@
self.assertEqual(flavor['vcpus'], linux_client.get_number_of_vcpus())
@attr(type='positive')
- @unittest.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+ @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
def test_host_name_is_same_as_server_name(self):
# Verify the instance host name is the same as the server name
linux_client = RemoteClient(self.server, self.ssh_user, self.password)
@@ -128,7 +127,7 @@
def setUpClass(cls):
if not compute.DISK_CONFIG_ENABLED:
msg = "DiskConfig extension not enabled."
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
super(ServersTestAutoDisk, cls).setUpClass()
cls.disk_config = 'AUTO'
ServersTest.setUpClass(cls)
@@ -146,7 +145,7 @@
def setUpClass(cls):
if not compute.DISK_CONFIG_ENABLED:
msg = "DiskConfig extension not enabled."
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
super(ServersTestManualDisk, cls).setUpClass()
cls.disk_config = 'MANUAL'
ServersTest.setUpClass(cls)
diff --git a/tempest/tests/compute/servers/test_disk_config.py b/tempest/tests/compute/servers/test_disk_config.py
index 490156b..c3a37ff 100644
--- a/tempest/tests/compute/servers/test_disk_config.py
+++ b/tempest/tests/compute/servers/test_disk_config.py
@@ -15,9 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-import nose
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
@@ -31,7 +30,7 @@
def setUpClass(cls):
if not compute.DISK_CONFIG_ENABLED:
msg = "DiskConfig extension not enabled."
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
super(TestServerDiskConfig, cls).setUpClass()
cls.client = cls.os.servers_client
@@ -96,7 +95,7 @@
resp, body = self.client.delete_server(server['id'])
@attr(type='positive')
- @unittest.skipUnless(compute.RESIZE_AVAILABLE, 'Resize not available.')
+ @testtools.skipUnless(compute.RESIZE_AVAILABLE, 'Resize not available.')
def test_resize_server_from_manual_to_auto(self):
# A server should be resized from manual to auto disk config
name = rand_name('server')
@@ -122,7 +121,7 @@
resp, body = self.client.delete_server(server['id'])
@attr(type='positive')
- @unittest.skipUnless(compute.RESIZE_AVAILABLE, 'Resize not available.')
+ @testtools.skipUnless(compute.RESIZE_AVAILABLE, 'Resize not available.')
def test_resize_server_from_auto_to_manual(self):
# A server should be resized from auto to manual disk config
name = rand_name('server')
diff --git a/tempest/tests/compute/servers/test_list_server_filters.py b/tempest/tests/compute/servers/test_list_server_filters.py
index d943e5d..45ea3a0 100644
--- a/tempest/tests/compute/servers/test_list_server_filters.py
+++ b/tempest/tests/compute/servers/test_list_server_filters.py
@@ -16,10 +16,8 @@
# under the License.
-import nose
from nose.plugins.attrib import attr
-import nose.plugins.skip
-import unittest2 as unittest
+import testtools
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
diff --git a/tempest/tests/compute/servers/test_list_servers_negative.py b/tempest/tests/compute/servers/test_list_servers_negative.py
index 035ffe8..eb4ea02 100644
--- a/tempest/tests/compute/servers/test_list_servers_negative.py
+++ b/tempest/tests/compute/servers/test_list_servers_negative.py
@@ -18,8 +18,7 @@
import re
import sys
-import nose
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
@@ -64,7 +63,7 @@
tenant_name = cls.os.tenant_name
msg = ("User/tenant %(username)s/%(tenant_name)s already have "
"existing server instances. Skipping test.") % locals()
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
resp, body = cls.alt_client.list_servers()
servers = body['servers']
@@ -74,7 +73,7 @@
tenant_name = cls.alt_manager.tenant_name
msg = ("Alt User/tenant %(username)s/%(tenant_name)s already have "
"existing server instances. Skipping test.") % locals()
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
# The following servers are created for use
# by the test methods in this class. These
diff --git a/tempest/tests/compute/servers/test_server_actions.py b/tempest/tests/compute/servers/test_server_actions.py
index 2fe8464..fd35461 100644
--- a/tempest/tests/compute/servers/test_server_actions.py
+++ b/tempest/tests/compute/servers/test_server_actions.py
@@ -19,7 +19,7 @@
import time
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest.common.utils.data_utils import rand_name
from tempest.common.utils.linux.remote_client import RemoteClient
@@ -35,8 +35,8 @@
run_ssh = tempest.config.TempestConfig().compute.run_ssh
@attr(type='smoke')
- @unittest.skipUnless(compute.CHANGE_PASSWORD_AVAILABLE,
- 'Change password not available.')
+ @testtools.skipUnless(compute.CHANGE_PASSWORD_AVAILABLE,
+ 'Change password not available.')
def test_change_server_password(self):
# The server's password should be set to the provided password
new_password = 'Newpass1234'
@@ -70,7 +70,7 @@
self.assertGreater(new_boot_time, boot_time)
@attr(type='smoke')
- @unittest.skip('Until bug 1014647 is dealt with.')
+ @testtools.skip('Until bug 1014647 is dealt with.')
def test_reboot_server_soft(self):
# The server should be signaled to reboot gracefully
if self.run_ssh:
@@ -123,7 +123,7 @@
self.assertTrue(linux_client.can_authenticate())
@attr(type='smoke')
- @unittest.skipIf(not resize_available, 'Resize not available.')
+ @testtools.skipIf(not resize_available, 'Resize not available.')
def test_resize_server_confirm(self):
# The server's RAM and disk space should be modified to that of
# the provided flavor
@@ -139,7 +139,7 @@
self.assertEqual(self.flavor_ref_alt, server['flavor']['id'])
@attr(type='positive')
- @unittest.skipIf(not resize_available, 'Resize not available.')
+ @testtools.skipIf(not resize_available, 'Resize not available.')
def test_resize_server_revert(self):
# The server's RAM and disk space should return to its original
# values after a resize is reverted
diff --git a/tempest/tests/compute/servers/test_server_advanced_ops.py b/tempest/tests/compute/servers/test_server_advanced_ops.py
index 4e85b04..f949f2e 100644
--- a/tempest/tests/compute/servers/test_server_advanced_ops.py
+++ b/tempest/tests/compute/servers/test_server_advanced_ops.py
@@ -17,7 +17,6 @@
import logging
-import nose
from tempest.common.utils.data_utils import rand_name
from tempest import test
@@ -39,13 +38,13 @@
if not cls.config.compute.resize_available:
msg = "Skipping test - resize not available on this host"
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
resize_flavor = cls.config.compute.flavor_ref_alt
if resize_flavor == cls.config.compute.flavor_ref:
msg = "Skipping test - flavor_ref and flavor_ref_alt are identical"
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
@classmethod
def tearDownClass(cls):
diff --git a/tempest/tests/compute/servers/test_server_metadata.py b/tempest/tests/compute/servers/test_server_metadata.py
index 6c44c3c..7db963e 100644
--- a/tempest/tests/compute/servers/test_server_metadata.py
+++ b/tempest/tests/compute/servers/test_server_metadata.py
@@ -44,6 +44,7 @@
super(ServerMetadataTest, cls).tearDownClass()
def setUp(self):
+ super(ServerMetadataTest, self).setUp()
meta = {'key1': 'value1', 'key2': 'value2'}
resp, _ = self.client.set_server_metadata(self.server_id, meta)
self.assertEqual(resp.status, 200)
diff --git a/tempest/tests/compute/servers/test_servers.py b/tempest/tests/compute/servers/test_servers.py
index 8054c1f..e0d4d44 100644
--- a/tempest/tests/compute/servers/test_servers.py
+++ b/tempest/tests/compute/servers/test_servers.py
@@ -168,7 +168,6 @@
# deletes are running slow we could very well overrun system
# memory
self.clear_servers()
-
super(ServersTestJSON, self).tearDown()
@@ -184,5 +183,4 @@
# deletes are running slow we could very well overrun system
# memory
self.clear_servers()
-
super(ServersTestXML, self).tearDown()
diff --git a/tempest/tests/compute/servers/test_servers_negative.py b/tempest/tests/compute/servers/test_servers_negative.py
index f7624b3..ea63360 100644
--- a/tempest/tests/compute/servers/test_servers_negative.py
+++ b/tempest/tests/compute/servers/test_servers_negative.py
@@ -17,9 +17,8 @@
import sys
-import nose
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
@@ -31,7 +30,7 @@
@classmethod
def setUpClass(cls):
- raise nose.SkipTest("Until Bug 1046870 is fixed")
+ raise cls.skipException("Until Bug 1046870 is fixed")
super(ServersNegativeTest, cls).setUpClass()
cls.client = cls.servers_client
cls.img_client = cls.images_client
diff --git a/tempest/tests/compute/servers/test_servers_whitebox.py b/tempest/tests/compute/servers/test_servers_whitebox.py
index 33519b0..502f16b 100644
--- a/tempest/tests/compute/servers/test_servers_whitebox.py
+++ b/tempest/tests/compute/servers/test_servers_whitebox.py
@@ -15,7 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import nose
from nose.plugins.attrib import attr
from tempest import exceptions
@@ -28,7 +27,7 @@
@classmethod
def setUpClass(cls):
- raise nose.SkipTest("Until Bug 1034129 is fixed")
+ raise cls.skipException("Until Bug 1034129 is fixed")
super(ServersWhiteboxTest, cls).setUpClass()
BaseIdentityAdminTest.setUpClass()
cls.client = cls.servers_client
diff --git a/tempest/tests/compute/test_authorization.py b/tempest/tests/compute/test_authorization.py
index 78661d1..4d3b553 100644
--- a/tempest/tests/compute/test_authorization.py
+++ b/tempest/tests/compute/test_authorization.py
@@ -16,9 +16,8 @@
# under the License.
from nose.plugins.attrib import attr
-from nose import SkipTest
from nose.tools import raises
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import parse_image_id
@@ -34,7 +33,7 @@
def setUpClass(cls):
if not compute.MULTI_USER:
msg = "Need >1 user"
- raise SkipTest(msg)
+ raise cls.skipException(msg)
super(AuthorizationTest, cls).setUpClass()
@@ -225,7 +224,7 @@
@raises(exceptions.NotFound)
@attr(type='negative')
- @unittest.skip("Skipped until the Bug #1086980 is resolved")
+ @testtools.skip("Skipped until the Bug #1086980 is resolved")
def test_delete_keypair_of_alt_account_fails(self):
# A DELETE request for another user's keypair should fail
self.alt_keypairs_client.delete_keypair(self.keypairname)
diff --git a/tempest/tests/compute/test_live_block_migration.py b/tempest/tests/compute/test_live_block_migration.py
index eceaaba..1b651ab 100644
--- a/tempest/tests/compute/test_live_block_migration.py
+++ b/tempest/tests/compute/test_live_block_migration.py
@@ -18,9 +18,8 @@
import random
import string
-import nose
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest.common.utils.linux.remote_client import RemoteClient
from tempest import config
@@ -101,12 +100,12 @@
return server_id
@attr(type='positive')
- @unittest.skipIf(not live_migration_available,
- 'Block Live migration not available')
+ @testtools.skipIf(not live_migration_available,
+ 'Block Live migration not available')
def test_001_live_block_migration(self):
# Live block migrate an instance to another host
if len(self._get_compute_hostnames()) < 2:
- raise nose.SkipTest(
+ raise self.skipTest(
"Less than 2 compute nodes, skipping migration test.")
server_id = self._get_an_active_server()
actual_host = self._get_host_for_server(server_id)
@@ -116,9 +115,9 @@
self.assertEquals(target_host, self._get_host_for_server(server_id))
@attr(type='positive', bug='lp1051881')
- @unittest.skip('Until bug 1051881 is dealt with.')
- @unittest.skipIf(not live_migration_available,
- 'Block Live migration not available')
+ @testtools.skip('Until bug 1051881 is dealt with.')
+ @testtools.skipIf(not live_migration_available,
+ 'Block Live migration not available')
def test_002_invalid_host_for_migration(self):
# Migrating to an invalid host should not change the status
diff --git a/tempest/tests/compute/test_quotas.py b/tempest/tests/compute/test_quotas.py
index 3dc2515..9306351 100644
--- a/tempest/tests/compute/test_quotas.py
+++ b/tempest/tests/compute/test_quotas.py
@@ -17,14 +17,13 @@
from nose.plugins.attrib import attr
-from tempest.tests.compute.base import BaseComputeTest
+from tempest.tests.compute import base
-class QuotasTest(BaseComputeTest):
+class QuotasTestBase(object):
@classmethod
def setUpClass(cls):
- super(QuotasTest, cls).setUpClass()
cls.client = cls.quotas_client
cls.admin_client = cls._get_identity_admin_client()
resp, tenants = cls.admin_client.list_tenants()
@@ -47,3 +46,19 @@
self.assertSequenceEqual(expected_quota_set, quota_set)
except Exception:
self.fail("Quota set for tenant did not have default limits")
+
+
+class QuotasTestJSON(QuotasTestBase, base.BaseComputeTestJSON):
+
+ @classmethod
+ def setUpClass(cls):
+ base.BaseComputeTestJSON.setUpClass()
+ super(QuotasTestJSON, cls).setUpClass()
+
+
+class QuotasTestXML(QuotasTestBase, base.BaseComputeTestXML):
+
+ @classmethod
+ def setUpClass(cls):
+ base.BaseComputeTestXML.setUpClass()
+ super(QuotasTestXML, cls).setUpClass()
diff --git a/tempest/tests/compute/volumes/test_attach_volume.py b/tempest/tests/compute/volumes/test_attach_volume.py
index 9581026..0e0e4a5 100644
--- a/tempest/tests/compute/volumes/test_attach_volume.py
+++ b/tempest/tests/compute/volumes/test_attach_volume.py
@@ -16,7 +16,7 @@
# under the License.
from nose.plugins.attrib import attr
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
@@ -68,7 +68,7 @@
return server, volume
@attr(type='positive')
- @unittest.skipIf(not run_ssh, 'SSH required for this test')
+ @testtools.skipIf(not run_ssh, 'SSH required for this test')
def test_attach_detach_volume(self):
# Stop and Start a server with an attached volume, ensuring that
# the volume remains attached.
diff --git a/tempest/tests/compute/volumes/test_volumes_list.py b/tempest/tests/compute/volumes/test_volumes_list.py
index fef9c8d..cc690a5 100644
--- a/tempest/tests/compute/volumes/test_volumes_list.py
+++ b/tempest/tests/compute/volumes/test_volumes_list.py
@@ -15,7 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import nose
from tempest.common.utils.data_utils import rand_name
from tempest.tests.compute import base
@@ -96,7 +95,7 @@
"test. This typically means that the backing file "
"size of the nova-volumes group is too small to "
"create the 3 volumes needed by this test case")
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
raise
@classmethod
@@ -142,7 +141,7 @@
"test. This typically means that the backing file "
"size of the nova-volumes group is too small to "
"create the 3 volumes needed by this test case")
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
raise
@classmethod
diff --git a/tempest/tests/identity/admin/test_roles.py b/tempest/tests/identity/admin/test_roles.py
index f0dd8d9..2779b51 100644
--- a/tempest/tests/identity/admin/test_roles.py
+++ b/tempest/tests/identity/admin/test_roles.py
@@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import unittest2 as unittest
+import testtools
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
diff --git a/tempest/tests/identity/admin/test_services.py b/tempest/tests/identity/admin/test_services.py
index 30dfeb0..5261b9d 100644
--- a/tempest/tests/identity/admin/test_services.py
+++ b/tempest/tests/identity/admin/test_services.py
@@ -15,7 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import nose
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
@@ -76,4 +75,4 @@
@classmethod
def setUpClass(cls):
super(ServicesTestXML, cls).setUpClass()
- raise nose.SkipTest("Skipping until Bug #1061738 resolved")
+ raise cls.skipException("Skipping until Bug #1061738 resolved")
diff --git a/tempest/tests/identity/admin/test_tenants.py b/tempest/tests/identity/admin/test_tenants.py
index 578af4a..54383f1 100644
--- a/tempest/tests/identity/admin/test_tenants.py
+++ b/tempest/tests/identity/admin/test_tenants.py
@@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import unittest2 as unittest
+import testtools
from nose.plugins.attrib import attr
from tempest.common.utils.data_utils import rand_name
diff --git a/tempest/tests/identity/admin/test_users.py b/tempest/tests/identity/admin/test_users.py
index 7ad932b..ef7d934 100644
--- a/tempest/tests/identity/admin/test_users.py
+++ b/tempest/tests/identity/admin/test_users.py
@@ -16,11 +16,11 @@
# under the License.
from nose.plugins.attrib import attr
-import unittest2 as unittest
-
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
from tempest.tests.identity import base
+import testtools
+from testtools.matchers._basic import Contains
class UsersTestBase(object):
@@ -76,7 +76,7 @@
self.data.tenant['id'], self.data.test_email)
@attr(type='negative')
- @unittest.skip("Until Bug 999084 is fixed")
+ @testtools.skip("Until Bug 999084 is fixed")
def test_create_user_with_empty_password(self):
# User with an empty password should not be created
self.data.setup_test_tenant()
@@ -85,7 +85,7 @@
self.alt_email)
@attr(type='nagative')
- @unittest.skip("Until Bug 999084 is fixed")
+ @testtools.skip("Until Bug 999084 is fixed")
def test_create_user_with_long_password(self):
# User having password exceeding max length should not be created
self.data.setup_test_tenant()
@@ -94,7 +94,7 @@
self.alt_email)
@attr(type='negative')
- @unittest.skip("Until Bug 999084 is fixed")
+ @testtools.skip("Until Bug 999084 is fixed")
def test_create_user_with_invalid_email_format(self):
# Email format should be validated while creating a user
self.data.setup_test_tenant()
@@ -227,8 +227,9 @@
# Get a list of users and find the test user
self.data.setup_test_user()
resp, users = self.client.get_users()
- self.assertIn(self.data.test_user, [u['name'] for u in users],
- "Could not find %s" % self.data.test_user)
+ self.assertThat([u['name'] for u in users],
+ Contains(self.data.test_user),
+ "Could not find %s" % self.data.test_user)
@attr(type='negative')
def test_get_users_by_unauthorized_user(self):
diff --git a/tempest/tests/identity/base.py b/tempest/tests/identity/base.py
index 9c318cd..cbd943e 100644
--- a/tempest/tests/identity/base.py
+++ b/tempest/tests/identity/base.py
@@ -15,14 +15,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-import nose
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
-class BaseIdAdminTest(unittest.TestCase):
+class BaseIdAdminTest(testtools.TestCase):
@classmethod
def setUpClass(cls):
@@ -31,7 +30,7 @@
cls.token_client = os.token_client
if not cls.client.has_admin_extensions():
- raise nose.SkipTest("Admin extensions disabled")
+ raise cls.skipException("Admin extensions disabled")
cls.data = DataGenerator(cls.client)
diff --git a/tempest/tests/image/test_images.py b/tempest/tests/image/test_images.py
index 2429a32..0a1a25f 100644
--- a/tempest/tests/image/test_images.py
+++ b/tempest/tests/image/test_images.py
@@ -18,10 +18,10 @@
import cStringIO as StringIO
import random
-import unittest2 as unittest
+import testtools
from nose.plugins.attrib import attr
-from nose.plugins.skip import SkipTest
+
GLANCE_INSTALLED = False
try:
@@ -33,7 +33,7 @@
from tempest import clients
-class CreateRegisterImagesTest(unittest.TestCase):
+class CreateRegisterImagesTest(testtools.TestCase):
"""
Here we test the registration and creation of images
@@ -42,7 +42,7 @@
@classmethod
def setUpClass(cls):
if not GLANCE_INSTALLED:
- raise SkipTest('Glance not installed')
+ raise cls.skipException('Glance not installed')
cls.os = clients.ServiceManager()
cls.client = cls.os.images.get_client()
cls.created_images = []
@@ -128,7 +128,7 @@
self.assertEqual('active', results.status)
-class ListImagesTest(unittest.TestCase):
+class ListImagesTest(testtools.TestCase):
"""
Here we test the listing of image information
@@ -137,7 +137,7 @@
@classmethod
def setUpClass(cls):
if not GLANCE_INSTALLED:
- raise SkipTest('Glance not installed')
+ raise cls.skipException('Glance not installed')
cls.os = clients.ServiceManager()
cls.client = cls.os.images.get_client()
cls.created_images = []
diff --git a/tempest/tests/network/base.py b/tempest/tests/network/base.py
index 90b351d..01330cc 100644
--- a/tempest/tests/network/base.py
+++ b/tempest/tests/network/base.py
@@ -15,15 +15,14 @@
# License for the specific language governing permissions and limitations
# under the License.
-import nose
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
-class BaseNetworkTest(unittest.TestCase):
+class BaseNetworkTest(testtools.TestCase):
@classmethod
def setUpClass(cls):
@@ -40,7 +39,7 @@
except exceptions.EndpointNotFound:
enabled = False
skip_msg = "No OpenStack Network API endpoint"
- raise nose.SkipTest(skip_msg)
+ raise cls.skipException(skip_msg)
@classmethod
def tearDownClass(cls):
diff --git a/tempest/tests/network/test_network_basic_ops.py b/tempest/tests/network/test_network_basic_ops.py
index 3c99f77..bdebced 100644
--- a/tempest/tests/network/test_network_basic_ops.py
+++ b/tempest/tests/network/test_network_basic_ops.py
@@ -19,7 +19,6 @@
import subprocess
import netaddr
-import nose
from quantumclient.common import exceptions as exc
@@ -179,7 +178,7 @@
cls.enabled = not bool(msg)
if msg:
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
@classmethod
def setUpClass(cls):
@@ -458,7 +457,7 @@
def test_005_create_servers(self):
if not (self.keypairs or self.security_groups or self.networks):
- raise nose.SkipTest('Necessary resources have not been defined')
+ raise self.skipTest('Necessary resources have not been defined')
for i, network in enumerate(self.networks):
tenant_id = network.tenant_id
name = rand_name('server-smoke-%d-' % i)
@@ -471,9 +470,9 @@
def test_006_check_tenant_network_connectivity(self):
if not self.config.network.tenant_networks_reachable:
msg = 'Tenant networks not configured to be reachable.'
- raise nose.SkipTest(msg)
+ raise self.skipTest(msg)
if not self.servers:
- raise nose.SkipTest("No VM's have been created")
+ raise self.skipTest("No VM's have been created")
for server in self.servers:
for net_name, ip_addresses in server.networks.iteritems():
for ip_address in ip_addresses:
@@ -484,9 +483,9 @@
def test_007_assign_floating_ips(self):
public_network_id = self.config.network.public_network_id
if not public_network_id:
- raise nose.SkipTest('Public network not configured')
+ raise self.skipTest('Public network not configured')
if not self.servers:
- raise nose.SkipTest("No VM's have been created")
+ raise self.skipTest("No VM's have been created")
for server in self.servers:
floating_ip = self._create_floating_ip(server, public_network_id)
self.floating_ips.setdefault(server, [])
@@ -494,7 +493,7 @@
def test_008_check_public_network_connectivity(self):
if not self.floating_ips:
- raise nose.SkipTest('No floating ips have been allocated.')
+ raise self.skipTest('No floating ips have been allocated.')
for server, floating_ips in self.floating_ips.iteritems():
for floating_ip in floating_ips:
ip_address = floating_ip.floating_ip_address
diff --git a/tempest/tests/object_storage/base.py b/tempest/tests/object_storage/base.py
index 5e3d0bc..8c32ffc 100644
--- a/tempest/tests/object_storage/base.py
+++ b/tempest/tests/object_storage/base.py
@@ -15,8 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import nose
-import unittest2 as unittest
+import testtools
from tempest import clients
import tempest.config
@@ -24,7 +23,7 @@
from tempest.tests.identity.base import DataGenerator
-class BaseObjectTest(unittest.TestCase):
+class BaseObjectTest(testtools.TestCase):
@classmethod
def setUpClass(cls):
@@ -50,4 +49,4 @@
except exceptions.EndpointNotFound:
enabled = False
skip_msg = "No OpenStack Object Storage API endpoint"
- raise nose.SkipTest(skip_msg)
+ raise cls.skipException(skip_msg)
diff --git a/tempest/tests/object_storage/test_container_sync.py b/tempest/tests/object_storage/test_container_sync.py
index 3dea259..597fd86 100644
--- a/tempest/tests/object_storage/test_container_sync.py
+++ b/tempest/tests/object_storage/test_container_sync.py
@@ -19,7 +19,7 @@
from tempest.common.utils.data_utils import arbitrary_string
from tempest.common.utils.data_utils import rand_name
from tempest.tests.object_storage import base
-import unittest2 as unittest
+import testtools
class ContainerSyncTest(base.BaseObjectTest):
@@ -61,7 +61,7 @@
#Attempt to delete the container
resp, _ = client[0].delete_container(cont_name)
- @unittest.skip('Until Bug 1093743 is resolved.')
+ @testtools.skip('Until Bug 1093743 is resolved.')
@attr(type='positive')
def test_container_synchronization(self):
#Container to container synchronization
diff --git a/tempest/tests/object_storage/test_object_expiry.py b/tempest/tests/object_storage/test_object_expiry.py
index 099fc16..8e6b23b 100644
--- a/tempest/tests/object_storage/test_object_expiry.py
+++ b/tempest/tests/object_storage/test_object_expiry.py
@@ -20,8 +20,8 @@
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
from tempest.tests.object_storage import base
+import testtools
from time import sleep
-import unittest2 as unittest
class ObjectExpiryTest(base.BaseObjectTest):
@@ -54,7 +54,7 @@
#Attempt to delete the container
resp, _ = cls.container_client.delete_container(cls.container_name)
- @unittest.skip('Until bug 1069849 is resolved.')
+ @testtools.skip('Until bug 1069849 is resolved.')
@attr(type='regression')
def test_get_object_after_expiry_time(self):
# GET object after expiry time
diff --git a/tempest/tests/object_storage/test_object_services.py b/tempest/tests/object_storage/test_object_services.py
index a65401c..d5b6d5c 100644
--- a/tempest/tests/object_storage/test_object_services.py
+++ b/tempest/tests/object_storage/test_object_services.py
@@ -21,8 +21,8 @@
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
from tempest.tests.object_storage import base
+import testtools
from time import time
-import unittest2 as unittest
class ObjectTest(base.BaseObjectTest):
@@ -327,7 +327,7 @@
self.assertIn('x-container-read', resp)
self.assertEqual(resp['x-container-read'], 'x')
- @unittest.skip('Until Bug 1091669 is resolved.')
+ @testtools.skip('Until Bug 1091669 is resolved.')
@attr(type='smoke')
def test_access_public_object_with_another_user_creds(self):
#Make container public-readable, and access the object
@@ -382,7 +382,7 @@
self.assertIn('x-container-read', resp)
self.assertEqual(resp['x-container-read'], 'x')
- @unittest.skip('Until Bug #1020722 is resolved.')
+ @testtools.skip('Until Bug #1020722 is resolved.')
@attr(type='smoke')
def test_write_public_object_without_using_creds(self):
#Make container public-writable, and create object
@@ -433,7 +433,7 @@
self.assertIn('x-container-write', resp)
self.assertEqual(resp['x-container-write'], 'x')
- @unittest.skip('Until Bug #1020722 is resolved.')
+ @testtools.skip('Until Bug #1020722 is resolved.')
@attr(type='smoke')
def test_write_public_with_another_user_creds(self):
#Make container public-writable, and create object
@@ -592,7 +592,7 @@
self.container_name, object_name,
metadata=self.custom_headers)
- @unittest.skip('Until bug 1097137 is resolved.')
+ @testtools.skip('Until bug 1097137 is resolved.')
@attr(type='positive')
def test_get_object_using_temp_url(self):
#Access object using temp url within expiry time
diff --git a/tempest/tests/utils.py b/tempest/tests/utils.py
index 571fc2a..0738201 100644
--- a/tempest/tests/utils.py
+++ b/tempest/tests/utils.py
@@ -17,41 +17,7 @@
"""Common utilities used in testing."""
-import nose.plugins.skip
-
-
-class skip_if(object):
- """Decorator that skips a test if condition is true."""
- def __init__(self, condition, msg):
- self.condition = condition
- self.message = msg
-
- def __call__(self, func):
- def _skipper(*args, **kw):
- """Wrapped skipper function."""
- if self.condition:
- raise nose.SkipTest(self.message)
- func(*args, **kw)
- _skipper.__name__ = func.__name__
- _skipper.__doc__ = func.__doc__
- return _skipper
-
-
-class skip_unless(object):
- """Decorator that skips a test if condition is not true."""
- def __init__(self, condition, msg):
- self.condition = condition
- self.message = msg
-
- def __call__(self, func):
- def _skipper(*args, **kw):
- """Wrapped skipper function."""
- if not self.condition:
- raise nose.SkipTest(self.message)
- func(*args, **kw)
- _skipper.__name__ = func.__name__
- _skipper.__doc__ = func.__doc__
- return _skipper
+from testtools import TestCase
class skip_unless_attr(object):
@@ -66,7 +32,7 @@
"""Wrapped skipper function."""
testobj = args[0]
if not getattr(testobj, self.attr, False):
- raise nose.SkipTest(self.message)
+ raise TestCase.skipException(self.message)
func(*args, **kw)
_skipper.__name__ = func.__name__
_skipper.__doc__ = func.__doc__
diff --git a/tempest/tests/volume/admin/base.py b/tempest/tests/volume/admin/base.py
index 364be54..21425be 100644
--- a/tempest/tests/volume/admin/base.py
+++ b/tempest/tests/volume/admin/base.py
@@ -15,8 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import nose
-
from tempest import config
import tempest.services.volume.json.admin.volume_types_client \
@@ -40,7 +38,7 @@
if not cls.adm_user and cls.adm_pass and cls.adm_tenant:
msg = ("Missing Volume Admin API credentials "
"in configuration.")
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
@classmethod
def tearDownClass(cls):
diff --git a/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py b/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py
index d139425..e7fe701 100644
--- a/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py
+++ b/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py
@@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import unittest
+import testtools
import uuid
from nose.plugins.attrib import attr
@@ -42,7 +42,7 @@
def tearDownClass(cls):
cls.client.delete_volume_type(cls.volume_type['id'])
- @unittest.skip('Until bug 1090320 is fixed')
+ @testtools.skip('Until bug 1090320 is fixed')
@raises(exceptions.BadRequest)
@attr(type='negative')
def test_update_no_body(self):
@@ -88,7 +88,7 @@
self.client.create_volume_type_extra_specs(str(uuid.uuid4()),
extra_specs)
- @unittest.skip('Until bug 1090322 is fixed')
+ @testtools.skip('Until bug 1090322 is fixed')
@raises(exceptions.BadRequest)
@attr(type='negative')
def test_create_none_body(self):
@@ -96,7 +96,7 @@
self.client.create_volume_type_extra_specs(self.volume_type['id'],
None)
- @unittest.skip('Until bug 1090322 is fixed')
+ @testtools.skip('Until bug 1090322 is fixed')
@raises(exceptions.BadRequest)
@attr(type='negative')
def test_create_invalid_body(self):
diff --git a/tempest/tests/volume/admin/test_volume_types_negative.py b/tempest/tests/volume/admin/test_volume_types_negative.py
index c2daef9..f53e33c 100644
--- a/tempest/tests/volume/admin/test_volume_types_negative.py
+++ b/tempest/tests/volume/admin/test_volume_types_negative.py
@@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import unittest
+import testtools
import uuid
from nose.plugins.attrib import attr
@@ -40,7 +40,7 @@
display_name=str(uuid.uuid4()),
volume_type=str(uuid.uuid4()))
- @unittest.skip('Until bug 1090356 is fixed')
+ @testtools.skip('Until bug 1090356 is fixed')
@raises(exceptions.BadRequest)
@attr(type='negative')
def test_create_with_empty_name(self):
diff --git a/tempest/tests/volume/base.py b/tempest/tests/volume/base.py
index 33bae45..de78c99 100644
--- a/tempest/tests/volume/base.py
+++ b/tempest/tests/volume/base.py
@@ -19,7 +19,7 @@
import time
import nose
-import unittest2 as unittest
+import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
@@ -29,7 +29,7 @@
LOG = logging.getLogger(__name__)
-class BaseVolumeTest(unittest.TestCase):
+class BaseVolumeTest(testtools.TestCase):
"""Base test case class for all Cinder API tests."""
@@ -66,7 +66,7 @@
cls.os.tenant_name)
except exceptions.EndpointNotFound:
cls.clear_isolated_creds()
- raise nose.SkipTest(skip_msg)
+ raise cls.skipException(skip_msg)
@classmethod
def _get_identity_admin_client(cls):
diff --git a/tempest/tests/volume/test_volumes_list.py b/tempest/tests/volume/test_volumes_list.py
index 2fc1353..92d3d3f 100644
--- a/tempest/tests/volume/test_volumes_list.py
+++ b/tempest/tests/volume/test_volumes_list.py
@@ -94,7 +94,7 @@
"test. This typically means that the backing file "
"size of the nova-volumes group is too small to "
"create the 3 volumes needed by this test case")
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
raise
@classmethod
@@ -141,7 +141,7 @@
"test. This typically means that the backing file "
"size of the nova-volumes group is too small to "
"create the 3 volumes needed by this test case")
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
raise
@classmethod
diff --git a/tempest/whitebox.py b/tempest/whitebox.py
index d78b9e0..03ad63b 100644
--- a/tempest/whitebox.py
+++ b/tempest/whitebox.py
@@ -21,7 +21,6 @@
import subprocess
import sys
-import nose
from sqlalchemy import create_engine, MetaData
from tempest.common.ssh import Client
@@ -59,7 +58,7 @@
def setUpClass(cls):
if not compute.WHITEBOX_ENABLED:
msg = "Whitebox testing disabled"
- raise nose.SkipTest(msg)
+ raise cls.skipException(msg)
super(ComputeWhiteboxTest, cls).setUpClass()
diff --git a/tools/install_venv.py b/tools/install_venv.py
index 28275ba..52dbe39 100644
--- a/tools/install_venv.py
+++ b/tools/install_venv.py
@@ -5,6 +5,7 @@
# All Rights Reserved.
#
# Copyright 2010 OpenStack, LLC
+# Copyright 2013 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
@@ -25,123 +26,10 @@
import subprocess
import sys
-
-ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-VENV = os.path.join(ROOT, '.venv')
-PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires')
-TEST_REQUIRES = os.path.join(ROOT, 'tools', 'test-requires')
-PY_VERSION = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
+import install_venv_common as install_venv
-def die(message, *args):
- print >> sys.stderr, message % args
- sys.exit(1)
-
-
-def check_python_version():
- if sys.version_info < (2, 6):
- die("Need Python Version >= 2.6")
-
-
-def run_command_with_code(cmd, redirect_output=True, check_exit_code=True):
- """Runs a command in an out-of-process shell.
-
- Returns the output of that command. Working directory is ROOT.
- """
- if redirect_output:
- stdout = subprocess.PIPE
- else:
- stdout = None
-
- proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout)
- output = proc.communicate()[0]
- if check_exit_code and proc.returncode != 0:
- die('Command "%s" failed.\n%s', ' '.join(cmd), output)
- return (output, proc.returncode)
-
-
-def run_command(cmd, redirect_output=True, check_exit_code=True):
- return run_command_with_code(cmd, redirect_output, check_exit_code)[0]
-
-
-class Distro(object):
-
- def check_cmd(self, cmd):
- return bool(run_command(['which', cmd], check_exit_code=False).strip())
-
- def install_virtualenv(self):
- if self.check_cmd('virtualenv'):
- return
-
- if self.check_cmd('easy_install'):
- print 'Installing virtualenv via easy_install...',
- if run_command(['easy_install', 'virtualenv']):
- print 'Succeeded'
- return
- else:
- print 'Failed'
-
- die('ERROR: virtualenv not found.\n\nTempest development'
- ' requires virtualenv, please install it using your'
- ' favorite package management tool')
-
- def post_process(self):
- """Any distribution-specific post-processing gets done here.
-
- In particular, this is useful for applying patches to code inside
- the venv.
- """
- pass
-
-
-class Fedora(Distro):
- """This covers Fedora-based distributions.
-
- Includes: Fedora, RHEL, Scientific Linux"""
-
- def check_pkg(self, pkg):
- return run_command_with_code(['rpm', '-q', pkg],
- check_exit_code=False)[1] == 0
-
- def yum_install(self, pkg, **kwargs):
- print "Attempting to install '%s' via yum" % pkg
- run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs)
-
- def apply_patch(self, originalfile, patchfile):
- run_command(['patch', originalfile, patchfile])
-
- def install_virtualenv(self):
- if self.check_cmd('virtualenv'):
- return
-
- if not self.check_pkg('python-virtualenv'):
- self.yum_install('python-virtualenv', check_exit_code=False)
-
- super(Fedora, self).install_virtualenv()
-
- def post_process(self):
- """Workaround for a bug in eventlet.
-
- This currently affects RHEL6.1, but the fix can safely be
- applied to all RHEL and Fedora distributions.
-
- This can be removed when the fix is applied upstream.
-
- Nova: https://bugs.launchpad.net/nova/+bug/884915
- Upstream: https://bitbucket.org/which_linden/eventlet/issue/89
- """
-
- # Install "patch" program if it's not there
- if not self.check_pkg('patch'):
- self.yum_install('patch')
-
- # Apply the eventlet patch
- self.apply_patch(os.path.join(VENV, 'lib', PY_VERSION, 'site-packages',
- 'eventlet/green/subprocess.py'),
- 'contrib/redhat-eventlet.patch')
-
-
-class CentOS(Fedora):
+class CentOS(install_venv.Fedora):
"""This covers CentOS."""
def post_process(self):
@@ -149,73 +37,6 @@
self.yum.install('openssl-devel', check_exit_code=False)
-def get_distro():
- if os.path.exists('/etc/redhat-release'):
- with open('/etc/redhat-release') as rh_release:
- if 'CentOS' in rh_release.read():
- return CentOS()
- return Fedora()
-
- if os.path.exists('/etc/fedora-release'):
- return Fedora()
-
- return Distro()
-
-
-def check_dependencies():
- get_distro().install_virtualenv()
-
-
-def create_virtualenv(venv=VENV, no_site_packages=True):
- """Creates the virtual environment and installs PIP.
-
- Creates the virtual environment and installs PIP only into the
- virtual environment.
- """
- print 'Creating venv...',
- if no_site_packages:
- run_command(['virtualenv', '-q', '--no-site-packages', VENV])
- else:
- run_command(['virtualenv', '-q', VENV])
- print 'done.'
- print 'Installing pip in virtualenv...',
- if not run_command(['tools/with_venv.sh', 'easy_install',
- 'pip>1.0']).strip():
- die("Failed to install pip.")
- print 'done.'
-
-
-def pip_install(*args):
- run_command(['tools/with_venv.sh',
- 'pip', 'install', '--upgrade'] + list(args),
- redirect_output=False)
-
-
-def install_dependencies(venv=VENV):
- print 'Installing dependencies with pip (this can take a while)...'
-
- # First things first, make sure our venv has the latest pip and distribute.
- # NOTE: we keep pip at version 1.1 since the most recent version causes
- # the .venv creation to fail. See:
- # https://bugs.launchpad.net/nova/+bug/1047120
- pip_install('pip==1.1')
- pip_install('distribute')
-
- # Install greenlet by hand - just listing it in the requires file does not
- # get it in stalled in the right order
- pip_install('greenlet')
-
- pip_install('-r', PIP_REQUIRES)
- pip_install('-r', TEST_REQUIRES)
-
- # Install tempest into the virtual_env. No more path munging!
- run_command([os.path.join(venv, 'bin/python'), 'setup.py', 'develop'])
-
-
-def post_process():
- get_distro().post_process()
-
-
def print_help():
help = """
Tempest development environment setup is complete.
@@ -238,23 +59,25 @@
print help
-def parse_args():
- """Parses command-line arguments."""
- parser = optparse.OptionParser()
- parser.add_option("-n", "--no-site-packages", dest="no_site_packages",
- default=False, action="store_true",
- help="Do not inherit packages from global Python"
- " install")
- return parser.parse_args()
-
-
def main(argv):
- (options, args) = parse_args()
- check_python_version()
- check_dependencies()
- create_virtualenv(no_site_packages=options.no_site_packages)
- install_dependencies()
- post_process()
+ root = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+ venv = os.path.join(root, '.venv')
+ pip_requires = os.path.join(root, 'tools', 'pip-requires')
+ test_requires = os.path.join(root, 'tools', 'test-requires')
+ py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
+ project = 'Tempest'
+ install = install_venv.InstallVenv(root, venv, pip_requires, test_requires,
+ py_version, project)
+ if os.path.exists('/etc/redhat-release'):
+ with open('/etc/redhat-release') as rh_release:
+ if 'CentOS' in rh_release.read():
+ install_venv.Fedora = CentOS
+ options = install.parse_args(argv)
+ install.check_python_version()
+ install.check_dependencies()
+ install.create_virtualenv(no_site_packages=options.no_site_packages)
+ install.install_dependencies()
+ install.post_process()
print_help()
if __name__ == '__main__':
diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py
new file mode 100644
index 0000000..3dbcafe
--- /dev/null
+++ b/tools/install_venv_common.py
@@ -0,0 +1,225 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack, LLC
+# Copyright 2013 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.
+
+"""Provides methods needed by installation script for OpenStack development
+virtual environments.
+
+Synced in from openstack-common
+"""
+
+import os
+import subprocess
+import sys
+
+from tempest.openstack.common import cfg
+
+
+class InstallVenv(object):
+
+ def __init__(self, root, venv, pip_requires, test_requires, py_version,
+ project):
+ self.root = root
+ self.venv = venv
+ self.pip_requires = pip_requires
+ self.test_requires = test_requires
+ self.py_version = py_version
+ self.project = project
+
+ def die(self, message, *args):
+ print >> sys.stderr, message % args
+ sys.exit(1)
+
+ def check_python_version(self):
+ if sys.version_info < (2, 6):
+ self.die("Need Python Version >= 2.6")
+
+ def run_command_with_code(self, cmd, redirect_output=True,
+ check_exit_code=True):
+ """Runs a command in an out-of-process shell.
+
+ Returns the output of that command. Working directory is ROOT.
+ """
+ if redirect_output:
+ stdout = subprocess.PIPE
+ else:
+ stdout = None
+
+ proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
+ output = proc.communicate()[0]
+ if check_exit_code and proc.returncode != 0:
+ self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
+ return (output, proc.returncode)
+
+ def run_command(self, cmd, redirect_output=True, check_exit_code=True):
+ return self.run_command_with_code(cmd, redirect_output,
+ check_exit_code)[0]
+
+ def get_distro(self):
+ if (os.path.exists('/etc/fedora-release') or
+ os.path.exists('/etc/redhat-release')):
+ return Fedora(self.root, self.venv, self.pip_requires,
+ self.test_requires, self.py_version, self.project)
+ else:
+ return Distro(self.root, self.venv, self.pip_requires,
+ self.test_requires, self.py_version, self.project)
+
+ def check_dependencies(self):
+ self.get_distro().install_virtualenv()
+
+ def create_virtualenv(self, no_site_packages=True):
+ """Creates the virtual environment and installs PIP.
+
+ Creates the virtual environment and installs PIP only into the
+ virtual environment.
+ """
+ if not os.path.isdir(self.venv):
+ print 'Creating venv...',
+ if no_site_packages:
+ self.run_command(['virtualenv', '-q', '--no-site-packages',
+ self.venv])
+ else:
+ self.run_command(['virtualenv', '-q', self.venv])
+ print 'done.'
+ print 'Installing pip in virtualenv...',
+ if not self.run_command(['tools/with_venv.sh', 'easy_install',
+ 'pip>1.0']).strip():
+ self.die("Failed to install pip.")
+ print 'done.'
+ else:
+ print "venv already exists..."
+ pass
+
+ def pip_install(self, *args):
+ self.run_command(['tools/with_venv.sh',
+ 'pip', 'install', '--upgrade'] + list(args),
+ redirect_output=False)
+
+ def install_dependencies(self):
+ print 'Installing dependencies with pip (this can take a while)...'
+
+ # First things first, make sure our venv has the latest pip and
+ # distribute.
+ # NOTE: we keep pip at version 1.1 since the most recent version causes
+ # the .venv creation to fail. See:
+ # https://bugs.launchpad.net/nova/+bug/1047120
+ self.pip_install('pip==1.1')
+ self.pip_install('distribute')
+
+ # Install greenlet by hand - just listing it in the requires file does
+ # not
+ # get it installed in the right order
+ self.pip_install('greenlet')
+
+ self.pip_install('-r', self.pip_requires)
+ self.pip_install('-r', self.test_requires)
+
+ def post_process(self):
+ self.get_distro().post_process()
+
+ def parse_args(self, argv):
+ """Parses command-line arguments."""
+ cli_opts = [
+ cfg.BoolOpt('no-site-packages',
+ default=False,
+ short='n',
+ help="Do not inherit packages from global Python"
+ "install"),
+ ]
+ CLI = cfg.ConfigOpts()
+ CLI.register_cli_opts(cli_opts)
+ CLI(argv[1:])
+ return CLI
+
+
+class Distro(InstallVenv):
+
+ def check_cmd(self, cmd):
+ return bool(self.run_command(['which', cmd],
+ check_exit_code=False).strip())
+
+ def install_virtualenv(self):
+ if self.check_cmd('virtualenv'):
+ return
+
+ if self.check_cmd('easy_install'):
+ print 'Installing virtualenv via easy_install...',
+ if self.run_command(['easy_install', 'virtualenv']):
+ print 'Succeeded'
+ return
+ else:
+ print 'Failed'
+
+ self.die('ERROR: virtualenv not found.\n\n%s development'
+ ' requires virtualenv, please install it using your'
+ ' favorite package management tool' % self.project)
+
+ def post_process(self):
+ """Any distribution-specific post-processing gets done here.
+
+ In particular, this is useful for applying patches to code inside
+ the venv.
+ """
+ pass
+
+
+class Fedora(Distro):
+ """This covers all Fedora-based distributions.
+
+ Includes: Fedora, RHEL, CentOS, Scientific Linux
+ """
+
+ def check_pkg(self, pkg):
+ return self.run_command_with_code(['rpm', '-q', pkg],
+ check_exit_code=False)[1] == 0
+
+ def yum_install(self, pkg, **kwargs):
+ print "Attempting to install '%s' via yum" % pkg
+ self.run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs)
+
+ def apply_patch(self, originalfile, patchfile):
+ self.run_command(['patch', originalfile, patchfile])
+
+ def install_virtualenv(self):
+ if self.check_cmd('virtualenv'):
+ return
+
+ if not self.check_pkg('python-virtualenv'):
+ self.yum_install('python-virtualenv', check_exit_code=False)
+
+ super(Fedora, self).install_virtualenv()
+
+ def post_process(self):
+ """Workaround for a bug in eventlet.
+
+ This currently affects RHEL6.1, but the fix can safely be
+ applied to all RHEL and Fedora distributions.
+
+ This can be removed when the fix is applied upstream.
+
+ Nova: https://bugs.launchpad.net/nova/+bug/884915
+ Upstream: https://bitbucket.org/which_linden/eventlet/issue/89
+ """
+
+ # Install "patch" program if it's not there
+ if not self.check_pkg('patch'):
+ self.yum_install('patch')
+
+ # Apply the eventlet patch
+ self.apply_patch(os.path.join(self.venv, 'lib', self.py_version,
+ 'site-packages',
+ 'eventlet/green/subprocess.py'),
+ 'contrib/redhat-eventlet.patch')
diff --git a/tools/pip-requires b/tools/pip-requires
index fcf1690..dcc859f 100644
--- a/tools/pip-requires
+++ b/tools/pip-requires
@@ -1,7 +1,7 @@
anyjson
nose
httplib2>=0.7.0
-unittest2
+testtools
lxml
boto>=2.2.1
paramiko