Merge "Move the singleton to a common location"
diff --git a/etc/TEMPEST_README.txt b/etc/TEMPEST_README.txt
index 4f907a0..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
--testtools
--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 e1196f0..43277d6 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -107,9 +107,6 @@
# Level to log Compute API request/response details.
log_level = ERROR
-# Whitebox options for compute. Whitebox options enable the
-# whitebox test cases, which look at internal Nova database state,
-# SSH into VMs to check instance state, etc.
# Run live migration tests (requires 2 hosts)
live_migration_available = false
@@ -123,6 +120,12 @@
# are forced to skip, regardless of the extension status
disk_config_enabled_override = true
+
+[whitebox]
+# Whitebox options for compute. Whitebox options enable the
+# whitebox test cases, which look at internal Nova database state,
+# SSH into VMs to check instance state, etc.
+
# Should we run whitebox tests for Compute?
whitebox_enabled = true
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/setup.cfg b/setup.cfg
index c28d129..b522f3a 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,7 +1,5 @@
[nosetests]
# NOTE(jkoelker) To run the test suite under nose install the following
# coverage http://pypi.python.org/pypi/coverage
-# tissue http://pypi.python.org/pypi/tissue (pep8 checker)
# openstack-nose https://github.com/openstack-dev/openstack-nose
verbosity=2
-detailed-errors=1
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 5710f4c..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())
@@ -323,7 +344,9 @@
"""
Subclasses override with specific deletion detection.
"""
- return False
+ message = ('"%s" does not implement is_resource_deleted'
+ % self.__class__.__name__)
+ raise NotImplementedError(message)
class RestClientXML(RestClient):
@@ -331,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 3b425ca..f6fcd90 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -172,22 +172,6 @@
default=True,
help="If false, skip config tests regardless of the "
"extension status"),
- cfg.BoolOpt('whitebox_enabled',
- default=False,
- help="Does the test environment support whitebox tests for "
- "Compute?"),
- cfg.StrOpt('db_uri',
- default=None,
- help="Connection string to the database of Compute service"),
- cfg.StrOpt('source_dir',
- default="/opt/stack/nova",
- help="Path of nova source directory"),
- cfg.StrOpt('config_path',
- default='/etc/nova/nova.conf',
- help="Path of nova configuration file"),
- cfg.StrOpt('bin_dir',
- default="/usr/local/bin/",
- help="Directory containing nova binaries such as nova-manage"),
]
@@ -220,6 +204,35 @@
conf.register_opt(opt, group='compute-admin')
+whitebox_group = cfg.OptGroup(name='whitebox',
+ title="Whitebox Options")
+
+WhiteboxGroup = [
+ cfg.BoolOpt('whitebox_enabled',
+ default=False,
+ help="Does the test environment support whitebox tests for "
+ "Compute?"),
+ cfg.StrOpt('db_uri',
+ default=None,
+ help="Connection string to the database of Compute service"),
+ cfg.StrOpt('source_dir',
+ default="/opt/stack/nova",
+ help="Path of nova source directory"),
+ cfg.StrOpt('config_path',
+ default='/etc/nova/nova.conf',
+ help="Path of nova configuration file"),
+ cfg.StrOpt('bin_dir',
+ default="/usr/local/bin/",
+ help="Directory containing nova binaries such as nova-manage"),
+]
+
+
+def register_whitebox_opts(conf):
+ conf.register_group(whitebox_group)
+ for opt in WhiteboxGroup:
+ conf.register_opt(opt, group='whitebox')
+
+
image_group = cfg.OptGroup(name='image',
title="Image Service Options")
@@ -407,6 +420,7 @@
register_compute_opts(cfg.CONF)
register_identity_opts(cfg.CONF)
+ register_whitebox_opts(cfg.CONF)
register_image_opts(cfg.CONF)
register_network_opts(cfg.CONF)
register_volume_opts(cfg.CONF)
@@ -414,6 +428,7 @@
register_boto_opts(cfg.CONF)
register_compute_admin_opts(cfg.CONF)
self.compute = cfg.CONF.compute
+ self.whitebox = cfg.CONF.whitebox
self.identity = cfg.CONF.identity
self.images = cfg.CONF.image
self.network = cfg.CONF.network
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/services/identity/json/identity_client.py b/tempest/services/identity/json/identity_client.py
index 9cc30d9..403a3ac 100644
--- a/tempest/services/identity/json/identity_client.py
+++ b/tempest/services/identity/json/identity_client.py
@@ -197,6 +197,12 @@
body = json.loads(body)
return resp, body['OS-KSADM:service']
+ def list_services(self):
+ """List Service - Returns Services."""
+ resp, body = self.get('/OS-KSADM/services/')
+ body = json.loads(body)
+ return resp, body['OS-KSADM:services']
+
def delete_service(self, service_id):
"""Delete Service."""
url = '/OS-KSADM/services/%s' % service_id
diff --git a/tempest/services/identity/xml/identity_client.py b/tempest/services/identity/xml/identity_client.py
index 02be91e..f79c3d5 100644
--- a/tempest/services/identity/xml/identity_client.py
+++ b/tempest/services/identity/xml/identity_client.py
@@ -226,6 +226,12 @@
body = self._parse_body(etree.fromstring(body))
return resp, body
+ def list_services(self):
+ """Returns services."""
+ resp, body = self.get('OS-KSADM/services', self.headers)
+ body = self._parse_array(etree.fromstring(body))
+ return resp, body
+
def get_service(self, service_id):
"""Get Service."""
url = '/OS-KSADM/services/%s' % service_id
diff --git a/tempest/testboto.py b/tempest/testboto.py
index 7031fe2..ab79f3c 100644
--- a/tempest/testboto.py
+++ b/tempest/testboto.py
@@ -202,8 +202,10 @@
s3_error_code.server = ServerError()
s3_error_code.client = ClientError()
valid_image_state = set(('available', 'pending', 'failed'))
+ #NOTE(afazekas): 'paused' is not valid status in EC2, but it does not have
+ # a good mapping, because it uses memory, but not really a running machine
valid_instance_state = set(('pending', 'running', 'shutting-down',
- 'terminated', 'stopping', 'stopped'))
+ 'terminated', 'stopping', 'stopped', 'paused'))
valid_volume_status = set(('creating', 'available', 'in-use',
'deleting', 'deleted', 'error'))
valid_snapshot_status = set(('pending', 'completed', 'error'))
diff --git a/tempest/tests/compute/__init__.py b/tempest/tests/compute/__init__.py
index 08e5091..5b59a70 100644
--- a/tempest/tests/compute/__init__.py
+++ b/tempest/tests/compute/__init__.py
@@ -29,7 +29,7 @@
CREATE_IMAGE_ENABLED = CONFIG.compute.create_image_enabled
RESIZE_AVAILABLE = CONFIG.compute.resize_available
CHANGE_PASSWORD_AVAILABLE = CONFIG.compute.change_password_available
-WHITEBOX_ENABLED = CONFIG.compute.whitebox_enabled
+WHITEBOX_ENABLED = CONFIG.whitebox.whitebox_enabled
DISK_CONFIG_ENABLED = False
DISK_CONFIG_ENABLED_OVERRIDE = CONFIG.compute.disk_config_enabled_override
FLAVOR_EXTRA_DATA_ENABLED = False
diff --git a/tempest/tests/compute/admin/test_quotas.py b/tempest/tests/compute/admin/test_quotas.py
index 6a7a5ea..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()
@@ -63,7 +61,6 @@
cls.servers_client.delete_server(server['id'])
except exceptions.NotFound:
continue
- super(QuotasTest, cls).tearDownClass()
@attr(type='smoke')
def test_get_default_quotas(self):
@@ -155,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/servers/test_server_addresses.py b/tempest/tests/compute/servers/test_server_addresses.py
index 6e819a2..6b0f7ae 100644
--- a/tempest/tests/compute/servers/test_server_addresses.py
+++ b/tempest/tests/compute/servers/test_server_addresses.py
@@ -74,9 +74,9 @@
# We do not know the exact network configuration, but an instance
# should at least have a single public or private address
- self.assertGreaterEqual(len(addresses), 1)
+ self.assertTrue(len(addresses) >= 1)
for network_name, network_addresses in addresses.iteritems():
- self.assertGreaterEqual(len(network_addresses), 1)
+ self.assertTrue(len(network_addresses) >= 1)
for address in network_addresses:
self.assertTrue(address['addr'])
self.assertTrue(address['version'])
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/identity/admin/test_services.py b/tempest/tests/identity/admin/test_services.py
index 5261b9d..73f4a90 100644
--- a/tempest/tests/identity/admin/test_services.py
+++ b/tempest/tests/identity/admin/test_services.py
@@ -16,6 +16,9 @@
# 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
@@ -30,8 +33,8 @@
name = rand_name('service-')
type = rand_name('type--')
description = rand_name('description-')
- resp, service_data = \
- self.client.create_service(name, type, description=description)
+ resp, service_data = self.client.create_service(
+ name, type, description=description)
self.assertTrue(resp['status'].startswith('2'))
#Verifying response body of create service
self.assertTrue('id' in service_data)
@@ -63,6 +66,32 @@
self.assertRaises(exceptions.NotFound, self.client.get_service,
service_data['id'])
+ def test_list_services(self):
+ # Create, List, Verify and Delete Services
+ services = []
+ for _ in xrange(3):
+ name = rand_name('service-')
+ type = rand_name('type--')
+ description = rand_name('description-')
+ resp, service = self.client.create_service(
+ name, type, description=description)
+ services.append(service)
+ service_ids = map(lambda x: x['id'], services)
+
+ # List and Verify Services
+ resp, body = self.client.list_services()
+ self.assertTrue(resp['status'].startswith('2'))
+ found = [service for service in body if service['id'] in service_ids]
+ self.assertEqual(len(found), len(services), 'Services not found')
+
+ # Delete Services
+ for service in services:
+ resp, body = self.client.delete_service(service['id'])
+ self.assertTrue(resp['status'].startswith('2'))
+ resp, body = self.client.list_services()
+ found = [service for service in body if service['id'] in service_ids]
+ self.assertFalse(any(found), 'Services failed to delete')
+
class ServicesTestJSON(base.BaseIdentityAdminTestJSON, ServicesTestBase):
@classmethod
diff --git a/tempest/tests/network/test_network_basic_ops.py b/tempest/tests/network/test_network_basic_ops.py
index bdebced..d297c98 100644
--- a/tempest/tests/network/test_network_basic_ops.py
+++ b/tempest/tests/network/test_network_basic_ops.py
@@ -186,9 +186,9 @@
cls.check_preconditions()
cfg = cls.config.network
cls.tenant_id = cls.manager._get_identity_client(
- cfg.username,
- cfg.password,
- cfg.tenant_name).tenant_id
+ cls.config.identity.username,
+ cls.config.identity.password,
+ cls.config.identity.tenant_name).tenant_id
# TODO(mnewby) Consider looking up entities as needed instead
# of storing them as collections on the class.
cls.keypairs = {}
diff --git a/tempest/tests/object_storage/test_container_sync.py b/tempest/tests/object_storage/test_container_sync.py
index 597fd86..d612880 100644
--- a/tempest/tests/object_storage/test_container_sync.py
+++ b/tempest/tests/object_storage/test_container_sync.py
@@ -103,7 +103,7 @@
self.assertEqual(resp['status'], '200',
'Error listing the destination container`s'
' "%s" contents' % (self.containers[0]))
- object_list_0 = {obj['name']: obj for obj in object_list_0}
+ object_list_0 = dict((obj['name'], obj) for obj in object_list_0)
# get second container content
resp, object_list_1 = \
cont_client[1].\
@@ -111,7 +111,7 @@
self.assertEqual(resp['status'], '200',
'Error listing the destination container`s'
' "%s" contents' % (self.containers[1]))
- object_list_1 = {obj['name']: obj for obj in object_list_1}
+ object_list_1 = dict((obj['name'], obj) for obj in object_list_1)
# check that containers is not empty and has equal keys()
# or wait for next attepmt
if not object_list_0 or not object_list_1 or \
diff --git a/tempest/whitebox.py b/tempest/whitebox.py
index 03ad63b..b7a1e68 100644
--- a/tempest/whitebox.py
+++ b/tempest/whitebox.py
@@ -63,9 +63,9 @@
super(ComputeWhiteboxTest, cls).setUpClass()
# Add some convenience attributes that tests use...
- cls.nova_dir = cls.config.compute.source_dir
- cls.compute_bin_dir = cls.config.compute.bin_dir
- cls.compute_config_path = cls.config.compute.config_path
+ cls.nova_dir = cls.config.whitebox.source_dir
+ cls.compute_bin_dir = cls.config.whitebox.bin_dir
+ cls.compute_config_path = cls.config.whitebox.config_path
cls.servers_client = cls.manager.servers_client
cls.images_client = cls.manager.images_client
cls.flavors_client = cls.manager.flavors_client
@@ -126,7 +126,7 @@
}
try:
- engine = create_engine(cls.config.compute.db_uri, **engine_args)
+ engine = create_engine(cls.config.whitebox.db_uri, **engine_args)
connection = engine.connect()
meta = MetaData()
meta.reflect(bind=engine)
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')