Merge "Add unit tests for wait_for_resource_deletion"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 068a666..8ab3505 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -128,6 +128,9 @@
# AWS Access Key (string value)
#aws_access=<None>
+# AWS Zone for EC2 tests (string value)
+#aws_zone=nova
+
# S3 Materials Path (string value)
#s3_materials_path=/opt/stack/devstack/files/images/s3-materials/cirros-0.3.0
@@ -307,14 +310,14 @@
# Administrative Username to use for Nova API requests.
# (string value)
-#username=admin
+#username=<None>
# Administrative Tenant name to use for Nova API requests.
# (string value)
-#tenant_name=admin
+#tenant_name=<None>
# API key to use when authenticating as admin. (string value)
-#password=pass
+#password=<None>
[compute-feature-enabled]
@@ -341,9 +344,6 @@
# password? (boolean value)
#change_password=false
-# Does the test environment support snapshots? (boolean value)
-#create_image=false
-
# Does the test environment support resizing? (boolean value)
#resize=false
@@ -451,16 +451,16 @@
#endpoint_type=publicURL
# Username to use for Nova API requests. (string value)
-#username=demo
+#username=<None>
# Tenant name to use for Nova API requests. (string value)
-#tenant_name=demo
+#tenant_name=<None>
# Role required to administrate keystone. (string value)
#admin_role=admin
# API key to use when authenticating. (string value)
-#password=pass
+#password=<None>
# Username of alternate user to use for Nova API requests.
# (string value)
@@ -476,14 +476,14 @@
# Administrative Username to use for Keystone API requests.
# (string value)
-#admin_username=admin
+#admin_username=<None>
# Administrative Tenant name to use for Keystone API requests.
# (string value)
-#admin_tenant_name=admin
+#admin_tenant_name=<None>
# API key to use when authenticating as admin. (string value)
-#admin_password=pass
+#admin_password=<None>
[identity-feature-enabled]
@@ -617,6 +617,14 @@
# (string value)
#public_router_id=
+# Timeout in seconds to wait for network operation to
+# complete. (integer value)
+#build_timeout=300
+
+# Time in seconds between network operation status checks.
+# (integer value)
+#build_interval=10
+
[network-feature-enabled]
@@ -799,9 +807,9 @@
# value)
#horizon=true
-# Whether or not Savanna is expected to be available (boolean
+# Whether or not Sahara is expected to be available (boolean
# value)
-#savanna=false
+#sahara=false
# Whether or not Ironic is expected to be available (boolean
# value)
diff --git a/requirements.txt b/requirements.txt
index 48d1b12..434e12e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -20,6 +20,6 @@
testrepository>=0.0.18
oslo.config>=1.2.0
six>=1.5.2
-iso8601>=0.1.8
+iso8601>=0.1.9
fixtures>=0.3.14
testscenarios>=0.4
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log.py b/tempest/api/compute/admin/test_instance_usage_audit_log.py
index 32c8656..055a177 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log.py
@@ -14,10 +14,10 @@
# under the License.
import datetime
+import urllib
from tempest.api.compute import base
from tempest import test
-import urllib
class InstanceUsageAuditLogTestJSON(base.BaseV2ComputeAdminTest):
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
index fe4a184..6a5fc96 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
@@ -14,11 +14,11 @@
# under the License.
import datetime
+import urllib
from tempest.api.compute import base
from tempest import exceptions
from tempest import test
-import urllib
class InstanceUsageAuditLogNegativeTestJSON(base.BaseV2ComputeAdminTest):
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 5af091e..09c7274 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -92,6 +92,29 @@
self.assertEqual(200, resp.status)
self.assertEqual(quota_set['ram'], 5120)
+ @test.attr(type='gate')
+ def test_delete_quota(self):
+ # Admin can delete the resource quota set for a tenant
+ tenant_name = data_utils.rand_name('ram_quota_tenant_')
+ tenant_desc = tenant_name + '-desc'
+ identity_client = self.os_adm.identity_client
+ _, tenant = identity_client.create_tenant(name=tenant_name,
+ description=tenant_desc)
+ tenant_id = tenant['id']
+ self.addCleanup(identity_client.delete_tenant, tenant_id)
+ resp, quota_set_default = self.adm_client.get_quota_set(tenant_id)
+ ram_default = quota_set_default['ram']
+
+ resp, body = self.adm_client.update_quota_set(tenant_id, ram='5120')
+ self.assertEqual(200, resp.status)
+
+ resp, body = self.adm_client.delete_quota_set(tenant_id)
+ self.assertEqual(202, resp.status)
+
+ resp, quota_set_new = self.adm_client.get_quota_set(tenant_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(ram_default, quota_set_new['ram'])
+
class QuotasAdminTestXML(QuotasAdminTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/admin/test_services.py b/tempest/api/compute/admin/test_services.py
index 9dd429b..2feb825 100644
--- a/tempest/api/compute/admin/test_services.py
+++ b/tempest/api/compute/admin/test_services.py
@@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.api.compute.api_schema import services as schema
from tempest.api.compute import base
from tempest import test
@@ -33,7 +32,7 @@
@test.attr(type='gate')
def test_list_services(self):
resp, services = self.client.list_services()
- self.validate_response(schema.list_services, resp, services)
+ self.assertEqual(200, resp.status)
self.assertNotEqual(0, len(services))
@test.attr(type='gate')
@@ -41,7 +40,7 @@
binary_name = 'nova-compute'
params = {'binary': binary_name}
resp, services = self.client.list_services(params)
- self.validate_response(schema.list_services, resp, services)
+ self.assertEqual(200, resp.status)
self.assertNotEqual(0, len(services))
for service in services:
self.assertEqual(binary_name, service['binary'])
@@ -49,14 +48,12 @@
@test.attr(type='gate')
def test_get_service_by_host_name(self):
resp, services = self.client.list_services()
- self.validate_response(schema.list_services, resp, services)
host_name = services[0]['host']
services_on_host = [service for service in services if
service['host'] == host_name]
params = {'host': host_name}
resp, services = self.client.list_services(params)
- self.validate_response(schema.list_services, resp, services)
# we could have a periodic job checkin between the 2 service
# lookups, so only compare binary lists.
@@ -70,13 +67,12 @@
@test.attr(type='gate')
def test_get_service_by_service_and_host_name(self):
resp, services = self.client.list_services()
- self.validate_response(schema.list_services, resp, services)
host_name = services[0]['host']
binary_name = services[0]['binary']
params = {'host': host_name, 'binary': binary_name}
resp, services = self.client.list_services(params)
- self.validate_response(schema.list_services, resp, services)
+ self.assertEqual(200, resp.status)
self.assertEqual(1, len(services))
self.assertEqual(host_name, services[0]['host'])
self.assertEqual(binary_name, services[0]['binary'])
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index cc8641f..33cd6f3 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -14,10 +14,10 @@
# under the License.
import datetime
+import time
from tempest.api.compute import base
from tempest import test
-import time
class TenantUsagesTestJSON(base.BaseV2ComputeAdminTest):
diff --git a/tempest/api/compute/api_schema/services.py b/tempest/api/compute/api_schema/services.py
deleted file mode 100644
index ef5868c..0000000
--- a/tempest/api/compute/api_schema/services.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2014 NEC Corporation. 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.
-
-list_services = {
- 'status_code': [200],
- 'response_body': {
- 'type': 'array',
- 'items': {
- 'type': 'object',
- 'properties': {
- # NOTE: Now the type of 'id' is integer, but here allows
- # 'string' also because we will be able to change it to
- # 'uuid' in the future.
- 'id': {'type': ['integer', 'string']},
- 'zone': {'type': 'string'},
- 'host': {'type': 'string'},
- 'state': {'type': 'string'},
- 'binary': {'type': 'string'},
- 'status': {'type': 'string'},
- 'updated_at': {'type': 'string'},
- 'disabled_reason': {'type': ['string', 'null']},
- },
- 'required': ['id', 'zone', 'host', 'state', 'binary', 'status',
- 'updated_at', 'disabled_reason'],
- },
- }
-}
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index e9b9efa..abd36a6 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -15,8 +15,6 @@
import time
-import jsonschema
-
from tempest import clients
from tempest.common.utils import data_utils
from tempest import config
@@ -178,25 +176,6 @@
return resp, body
- @classmethod
- def validate_response(cls, schema, resp, body):
- response_code = schema['status_code']
- if resp.status not in response_code:
- msg = ("The status code(%s) is different than the expected "
- "one(%s)") % (resp.status, response_code)
- raise exceptions.InvalidHttpSuccessCode(msg)
- response_schema = schema.get('response_body')
- if response_schema:
- try:
- jsonschema.validate(body, response_schema)
- except jsonschema.ValidationError as ex:
- msg = ("HTTP response body is invalid (%s)") % ex
- raise exceptions.InvalidHTTPResponseBody(msg)
- else:
- if body:
- msg = ("HTTP response body should not exist (%s)") % body
- raise exceptions.InvalidHTTPResponseBody(msg)
-
def wait_for(self, condition):
"""Repeatedly calls condition() until a timeout."""
start_time = int(time.time())
@@ -432,3 +411,4 @@
cls.aggregates_admin_client = cls.os_adm.aggregates_v3_client
cls.hosts_admin_client = cls.os_adm.hosts_v3_client
cls.quotas_admin_client = cls.os_adm.quotas_v3_client
+ cls.agents_admin_client = cls.os_adm.agents_v3_client
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index c0f7af0..abd8a4c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -24,10 +24,11 @@
floating_ip = None
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(FloatingIPsTestJSON, cls).setUpClass()
cls.client = cls.floating_ips_client
- #cls.servers_client = cls.servers_client
+ cls.floating_ip_id = None
# Server creation
resp, server = cls.create_test_server(wait_until='ACTIVE')
@@ -40,7 +41,8 @@
@classmethod
def tearDownClass(cls):
# Deleting the floating IP which is created in this method
- resp, body = cls.client.delete_floating_ip(cls.floating_ip_id)
+ if cls.floating_ip_id:
+ resp, body = cls.client.delete_floating_ip(cls.floating_ip_id)
super(FloatingIPsTestJSON, cls).tearDownClass()
@test.attr(type='gate')
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 195a018..91eb4c5 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -24,14 +24,15 @@
class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ImagesMetadataTestJSON, cls).setUpClass()
if not CONF.service_available.glance:
skip_msg = ("%s skipped as glance is not available" % cls.__name__)
raise cls.skipException(skip_msg)
- cls.servers_client = cls.servers_client
cls.client = cls.images_client
+ cls.image_id = None
resp, server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
@@ -45,7 +46,8 @@
@classmethod
def tearDownClass(cls):
- cls.client.delete_image(cls.image_id)
+ if cls.image_id:
+ cls.client.delete_image(cls.image_id)
super(ImagesMetadataTestJSON, cls).tearDownClass()
def setUp(self):
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index b152c3c..d2fd970 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
from tempest.api.compute import base
from tempest.common.utils import data_utils
@@ -61,8 +60,6 @@
resp, flavor = self.flavors_client.get_flavor_details(flavor_id)
return flavor['disk']
- @testtools.skipUnless(CONF.compute_feature_enabled.create_image,
- 'Environment unable to create images.')
@test.attr(type='smoke')
def test_create_delete_image(self):
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index ddf37ce..778294e 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -186,8 +186,7 @@
admin_pass = self.image_ssh_password
- resp, server_no_eph_disk = (self.
- create_test_server(
+ resp, server_no_eph_disk = (self.create_test_server(
wait_until='ACTIVE',
adminPass=admin_pass,
flavor=flavor_no_eph_disk_id))
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 837114c..f0913f1 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -26,6 +26,7 @@
class ListServerFiltersTestJSON(base.BaseV2ComputeTest):
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ListServerFiltersTestJSON, cls).setUpClass()
cls.client = cls.servers_client
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 71d6018..21465d8 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -17,6 +17,7 @@
import time
import testtools
+import urlparse
from tempest.api.compute import base
from tempest.common.utils import data_utils
@@ -427,6 +428,29 @@
self.assertEqual(202, resp.status)
self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+ def _validate_url(self, url):
+ valid_scheme = ['http', 'https']
+ parsed_url = urlparse.urlparse(url)
+ self.assertNotEqual('None', parsed_url.port)
+ self.assertNotEqual('None', parsed_url.hostname)
+ self.assertIn(parsed_url.scheme, valid_scheme)
+
+ @testtools.skipUnless(CONF.compute_feature_enabled.vnc_console,
+ 'VNC Console feature is disabled.')
+ @test.attr(type='gate')
+ def test_get_vnc_console(self):
+ # Get the VNC console of type 'novnc' and 'xvpvnc'
+ console_types = ['novnc', 'xvpvnc']
+ for console_type in console_types:
+ resp, body = self.servers_client.get_vnc_console(self.server_id,
+ console_type)
+ self.assertEqual(
+ 200, resp.status,
+ "Failed to get Console Type: %s" % (console_types))
+ self.assertEqual(console_type, body['type'])
+ self.assertNotEqual('', body['url'])
+ self._validate_url(body['url'])
+
class ServerActionsTestXML(ServerActionsTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index 277f28f..e027567 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -45,6 +45,11 @@
cls.rescue_id, adminPass=rescue_password)
cls.servers_client.wait_for_server_status(cls.rescue_id, 'RESCUE')
+ @classmethod
+ def tearDownClass(cls):
+ cls.delete_volume(cls.volume['id'])
+ super(ServerRescueNegativeTestJSON, cls).tearDownClass()
+
def _detach(self, server_id, volume_id):
self.servers_client.detach_volume(server_id, volume_id)
self.volumes_extensions_client.wait_for_volume_status(volume_id,
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 230d433..4db8c56 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -27,6 +27,9 @@
resp, tenants = cls.admin_client.list_tenants()
cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
cls.client.tenant_name][0]
+ resp, users = cls.admin_client.list_users_for_tenant(cls.tenant_id)
+ cls.user_id = [user['id'] for user in users if user['name'] ==
+ cls.client.user][0]
cls.default_quota_set = set(('injected_file_content_bytes',
'metadata_items', 'injected_files',
'ram', 'floating_ips',
@@ -45,6 +48,14 @@
sorted(quota_set.keys()))
self.assertEqual(quota_set['id'], self.tenant_id)
+ # get the quota set using user id
+ resp, quota_set = self.client.get_quota_set(self.tenant_id,
+ self.user_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(sorted(expected_quota_set),
+ sorted(quota_set.keys()))
+ self.assertEqual(quota_set['id'], self.tenant_id)
+
@test.attr(type='smoke')
def test_get_default_quotas(self):
# User can get the default quota set for it's tenant
diff --git a/tempest/api/compute/v3/admin/test_agents.py b/tempest/api/compute/v3/admin/test_agents.py
new file mode 100644
index 0000000..9d01b71
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_agents.py
@@ -0,0 +1,91 @@
+# Copyright 2014 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest import test
+
+
+class AgentsAdminV3Test(base.BaseV3ComputeAdminTest):
+
+ """
+ Tests Agents API that require admin privileges
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(AgentsAdminV3Test, cls).setUpClass()
+ cls.client = cls.agents_admin_client
+
+ @test.attr(type='gate')
+ def test_create_update_list_delete_agents(self):
+
+ """
+ 1. Create 2 agents.
+ 2. Update one of the agents.
+ 3. List all agent builds.
+ 4. List the agent builds by the filter.
+ 5. Delete agents.
+ """
+ params_kvm = expected_kvm = {'hypervisor': 'kvm',
+ 'os': 'win',
+ 'architecture': 'x86',
+ 'version': '7.0',
+ 'url': 'xxx://xxxx/xxx/xxx',
+ 'md5hash': ("""add6bb58e139be103324d04d"""
+ """82d8f545""")}
+
+ resp, agent_kvm = self.client.create_agent(**params_kvm)
+ self.assertEqual(201, resp.status)
+ for expected_item, value in expected_kvm.items():
+ self.assertEqual(value, agent_kvm[expected_item])
+
+ params_xen = expected_xen = {'hypervisor': 'xen',
+ 'os': 'linux',
+ 'architecture': 'x86',
+ 'version': '7.0',
+ 'url': 'xxx://xxxx/xxx/xxx1',
+ 'md5hash': """add6bb58e139be103324d04d8"""
+ """2d8f546"""}
+
+ resp, agent_xen = self.client.create_agent(**params_xen)
+ self.assertEqual(201, resp.status)
+
+ for expected_item, value in expected_xen.items():
+ self.assertEqual(value, agent_xen[expected_item])
+
+ params_kvm_new = expected_kvm_new = {'version': '8.0',
+ 'url': 'xxx://xxxx/xxx/xxx2',
+ 'md5hash': """add6bb58e139be103"""
+ """324d04d82d8f547"""}
+
+ resp, resp_agent_kvm = self.client.update_agent(agent_kvm['agent_id'],
+ **params_kvm_new)
+ self.assertEqual(200, resp.status)
+ for expected_item, value in expected_kvm_new.items():
+ self.assertEqual(value, resp_agent_kvm[expected_item])
+
+ resp, agents = self.client.list_agents()
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(agents) > 1)
+
+ params_filter = {'hypervisor': 'kvm'}
+ resp, agent = self.client.list_agents(params_filter)
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(agent) > 0)
+ self.assertEqual('kvm', agent[0]['hypervisor'])
+
+ resp, _ = self.client.delete_agent(agent_kvm['agent_id'])
+ self.assertEqual(204, resp.status)
+ resp, _ = self.client.delete_agent(agent_xen['agent_id'])
+ self.assertEqual(204, resp.status)
diff --git a/tempest/api/compute/v3/admin/test_quotas.py b/tempest/api/compute/v3/admin/test_quotas.py
index 0c138bb..917c115 100644
--- a/tempest/api/compute/v3/admin/test_quotas.py
+++ b/tempest/api/compute/v3/admin/test_quotas.py
@@ -56,7 +56,7 @@
def test_get_quota_set_detail(self):
# Admin can get the detail of resource quota set for a tenant
expected_quota_set = self.default_quota_set | set(['id'])
- expected_detail = {'reserved', 'limit', 'in_use'}
+ expected_detail = ['reserved', 'limit', 'in_use']
resp, quota_set = self.adm_client.get_quota_set_detail(
self.demo_tenant_id)
self.assertEqual(200, resp.status)
@@ -109,3 +109,26 @@
resp, quota_set = self.adm_client.get_quota_set(tenant_id)
self.assertEqual(200, resp.status)
self.assertEqual(quota_set['ram'], 5120)
+
+ @test.attr(type='gate')
+ def test_delete_quota(self):
+ # Admin can delete the resource quota set for a tenant
+ tenant_name = data_utils.rand_name('cpu_quota_tenant_')
+ tenant_desc = tenant_name + '-desc'
+ identity_client = self.os_adm.identity_client
+ _, tenant = identity_client.create_tenant(name=tenant_name,
+ description=tenant_desc)
+ tenant_id = tenant['id']
+ self.addCleanup(identity_client.delete_tenant, tenant_id)
+ resp, quota_set_default = self.adm_client.get_quota_set(tenant_id)
+ self.assertEqual(200, resp.status)
+ ram_default = quota_set_default['ram']
+
+ self.adm_client.update_quota_set(tenant_id, ram='5120')
+ self.assertEqual(200, resp.status)
+ resp, _ = self.adm_client.delete_quota_set(tenant_id)
+ self.assertEqual(204, resp.status)
+
+ resp, quota_set_new = self.adm_client.get_quota_set(tenant_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(ram_default, quota_set_new['ram'])
diff --git a/tempest/api/compute/v3/admin/test_services.py b/tempest/api/compute/v3/admin/test_services.py
index 0a7c7f1..b367dad 100644
--- a/tempest/api/compute/v3/admin/test_services.py
+++ b/tempest/api/compute/v3/admin/test_services.py
@@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.api.compute.api_schema import services as schema
from tempest.api.compute import base
from tempest.test import attr
@@ -33,7 +32,7 @@
@attr(type='gate')
def test_list_services(self):
resp, services = self.client.list_services()
- self.validate_response(schema.list_services, resp, services)
+ self.assertEqual(200, resp.status)
self.assertNotEqual(0, len(services))
@attr(type='gate')
@@ -41,7 +40,7 @@
binary_name = 'nova-compute'
params = {'binary': binary_name}
resp, services = self.client.list_services(params)
- self.validate_response(schema.list_services, resp, services)
+ self.assertEqual(200, resp.status)
self.assertNotEqual(0, len(services))
for service in services:
self.assertEqual(binary_name, service['binary'])
@@ -49,14 +48,13 @@
@attr(type='gate')
def test_get_service_by_host_name(self):
resp, services = self.client.list_services()
- self.validate_response(schema.list_services, resp, services)
+ self.assertEqual(200, resp.status)
host_name = services[0]['host']
services_on_host = [service for service in services if
service['host'] == host_name]
params = {'host': host_name}
resp, services = self.client.list_services(params)
- self.validate_response(schema.list_services, resp, services)
# we could have a periodic job checkin between the 2 service
# lookups, so only compare binary lists.
@@ -70,13 +68,12 @@
@attr(type='gate')
def test_get_service_by_service_and_host_name(self):
resp, services = self.client.list_services()
- self.validate_response(schema.list_services, resp, services)
host_name = services[0]['host']
binary_name = services[0]['binary']
params = {'host': host_name, 'binary': binary_name}
resp, services = self.client.list_services(params)
- self.validate_response(schema.list_services, resp, services)
+ self.assertEqual(200, resp.status)
self.assertEqual(1, len(services))
self.assertEqual(host_name, services[0]['host'])
self.assertEqual(binary_name, services[0]['binary'])
diff --git a/tempest/api/compute/v3/images/test_images_negative.py b/tempest/api/compute/v3/images/test_images_negative.py
index c38373f..0705bdc 100644
--- a/tempest/api/compute/v3/images/test_images_negative.py
+++ b/tempest/api/compute/v3/images/test_images_negative.py
@@ -35,7 +35,7 @@
resp, body = self.servers_client.create_image(server_id, name, meta)
image_id = data_utils.parse_image_id(resp['location'])
self.addCleanup(self.client.delete_image, image_id)
- self.client.wait_for_image_status(image_id, 'ACTIVE')
+ self.client.wait_for_image_status(image_id, 'active')
return resp, body
@test.attr(type=['negative', 'gate'])
diff --git a/tempest/api/compute/v3/images/test_images_oneserver.py b/tempest/api/compute/v3/images/test_images_oneserver.py
index 48a885e..3aab1e1 100644
--- a/tempest/api/compute/v3/images/test_images_oneserver.py
+++ b/tempest/api/compute/v3/images/test_images_oneserver.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
from tempest.api.compute import base
from tempest.common.utils import data_utils
@@ -61,8 +60,6 @@
resp, flavor = self.flavors_client.get_flavor_details(flavor_id)
return flavor['disk']
- @testtools.skipUnless(CONF.compute_feature_enabled.create_image,
- 'Environment unable to create images.')
@test.attr(type='smoke')
def test_create_delete_image(self):
@@ -73,26 +70,26 @@
name, meta)
self.assertEqual(202, resp.status)
image_id = data_utils.parse_image_id(resp['location'])
- self.client.wait_for_image_status(image_id, 'ACTIVE')
+ self.client.wait_for_image_status(image_id, 'active')
# Verify the image was created correctly
- resp, image = self.client.get_image(image_id)
+ resp, image = self.client.get_image_meta(image_id)
self.assertEqual(name, image['name'])
- self.assertEqual('test', image['metadata']['image_type'])
+ self.assertEqual('test', image['properties']['image_type'])
- resp, original_image = self.client.get_image(self.image_ref)
+ resp, original_image = self.client.get_image_meta(self.image_ref)
# Verify minRAM is the same as the original image
- self.assertEqual(image['minRam'], original_image['minRam'])
+ self.assertEqual(image['min_ram'], original_image['min_ram'])
# Verify minDisk is the same as the original image or the flavor size
flavor_disk_size = self._get_default_flavor_disk_size(self.flavor_ref)
- self.assertIn(str(image['minDisk']),
- (str(original_image['minDisk']), str(flavor_disk_size)))
+ self.assertIn(str(image['min_disk']),
+ (str(original_image['min_disk']), str(flavor_disk_size)))
# Verify the image was deleted correctly
resp, body = self.client.delete_image(image_id)
- self.assertEqual('204', resp['status'])
+ self.assertEqual('200', resp['status'])
self.client.wait_for_resource_deletion(image_id)
@test.attr(type=['gate'])
diff --git a/tempest/api/compute/v3/servers/test_create_server.py b/tempest/api/compute/v3/servers/test_create_server.py
index a212ca5..14a4338 100644
--- a/tempest/api/compute/v3/servers/test_create_server.py
+++ b/tempest/api/compute/v3/servers/test_create_server.py
@@ -92,14 +92,6 @@
@testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
@test.attr(type='gate')
- def test_can_log_into_created_server(self):
- # Check that the user can authenticate with the generated password
- linux_client = remote_client.RemoteClient(self.server,
- self.ssh_user, self.password)
- self.assertTrue(linux_client.can_authenticate())
-
- @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
- @test.attr(type='gate')
def test_verify_created_server_vcpus(self):
# Verify that the number of vcpus reported by the instance matches
# the amount stated by the flavor
@@ -195,8 +187,7 @@
admin_pass = self.image_ssh_password
- resp, server_no_eph_disk = (self.
- create_test_server(
+ resp, server_no_eph_disk = (self.create_test_server(
wait_until='ACTIVE',
adminPass=admin_pass,
flavor=flavor_no_eph_disk_id))
diff --git a/tempest/api/compute/v3/servers/test_list_server_filters.py b/tempest/api/compute/v3/servers/test_list_server_filters.py
index ec31e8e..2cb176c 100644
--- a/tempest/api/compute/v3/servers/test_list_server_filters.py
+++ b/tempest/api/compute/v3/servers/test_list_server_filters.py
@@ -26,6 +26,7 @@
class ListServerFiltersV3Test(base.BaseV3ComputeTest):
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ListServerFiltersV3Test, cls).setUpClass()
cls.client = cls.servers_client
diff --git a/tempest/api/compute/v3/test_quotas.py b/tempest/api/compute/v3/test_quotas.py
index b53d9be..3fe62e9 100644
--- a/tempest/api/compute/v3/test_quotas.py
+++ b/tempest/api/compute/v3/test_quotas.py
@@ -27,6 +27,9 @@
resp, tenants = cls.admin_client.list_tenants()
cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
cls.client.tenant_name][0]
+ resp, users = cls.admin_client.list_users_for_tenant(cls.tenant_id)
+ cls.user_id = [user['id'] for user in users if user['name'] ==
+ cls.client.user][0]
cls.default_quota_set = set(('metadata_items',
'ram', 'floating_ips',
'fixed_ips', 'key_pairs',
@@ -43,6 +46,14 @@
sorted(quota_set.keys()))
self.assertEqual(quota_set['id'], self.tenant_id)
+ # get the quota set using user id
+ resp, quota_set = self.client.get_quota_set(self.tenant_id,
+ self.user_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(sorted(expected_quota_set),
+ sorted(quota_set.keys()))
+ self.assertEqual(quota_set['id'], self.tenant_id)
+
@test.attr(type='smoke')
def test_get_default_quotas(self):
# User can get the default quota set for it's tenant
diff --git a/tempest/api/data_processing/base.py b/tempest/api/data_processing/base.py
index 5b272ef..73ad22b 100644
--- a/tempest/api/data_processing/base.py
+++ b/tempest/api/data_processing/base.py
@@ -27,8 +27,8 @@
def setUpClass(cls):
super(BaseDataProcessingTest, cls).setUpClass()
os = cls.get_client_manager()
- if not CONF.service_available.savanna:
- raise cls.skipException("Savanna support is required")
+ if not CONF.service_available.sahara:
+ raise cls.skipException("Sahara support is required")
cls.client = os.data_processing_client
# set some constants
diff --git a/tempest/api/data_processing/test_node_group_templates.py b/tempest/api/data_processing/test_node_group_templates.py
index ff4fa6a..a64c345 100644
--- a/tempest/api/data_processing/test_node_group_templates.py
+++ b/tempest/api/data_processing/test_node_group_templates.py
@@ -28,7 +28,7 @@
if template_name is None:
# generate random name if it's not specified
- template_name = data_utils.rand_name('savanna')
+ template_name = data_utils.rand_name('sahara')
# create simple node group template
resp, body, template_id = self.create_node_group_template(
diff --git a/tempest/api/identity/admin/test_tokens.py b/tempest/api/identity/admin/test_tokens.py
index 533f374..c931bcf 100644
--- a/tempest/api/identity/admin/test_tokens.py
+++ b/tempest/api/identity/admin/test_tokens.py
@@ -56,6 +56,49 @@
resp, body = self.client.delete_token(token_id)
self.assertEqual(resp['status'], '204')
+ @attr(type='gate')
+ def test_rescope_token(self):
+ """An unscoped token can be requested, that token can be used to
+ request a scoped token.
+ """
+
+ # Create a user.
+ user_name = data_utils.rand_name(name='user-')
+ user_password = data_utils.rand_name(name='pass-')
+ tenant_id = None # No default tenant so will get unscoped token.
+ email = ''
+ resp, user = self.client.create_user(user_name, user_password,
+ tenant_id, email)
+ self.assertEqual(200, resp.status)
+ self.data.users.append(user)
+
+ # Create a tenant.
+ tenant_name = data_utils.rand_name(name='tenant-')
+ resp, tenant = self.client.create_tenant(tenant_name)
+ self.assertEqual(200, resp.status)
+ self.data.tenants.append(tenant)
+
+ # Create a role
+ role_name = data_utils.rand_name(name='role-')
+ resp, role = self.client.create_role(role_name)
+ self.assertEqual(200, resp.status)
+ self.data.roles.append(role)
+
+ # Grant the user the role on the tenant.
+ resp, _ = self.client.assign_user_role(tenant['id'], user['id'],
+ role['id'])
+ self.assertEqual(200, resp.status)
+
+ # Get an unscoped token.
+ rsp, body = self.token_client.auth(user_name, user_password)
+ self.assertEqual(200, resp.status)
+
+ token_id = body['token']['id']
+
+ # Use the unscoped token to get a scoped token.
+ rsp, body = self.token_client.auth_token(token_id, tenant=tenant_name)
+ self.assertEqual(200, resp.status)
+
class TokensTestXML(TokensTestJSON):
_interface = 'xml'
diff --git a/tempest/api/identity/admin/test_users_negative.py b/tempest/api/identity/admin/test_users_negative.py
index 1188325..4e8ebe5 100644
--- a/tempest/api/identity/admin/test_users_negative.py
+++ b/tempest/api/identity/admin/test_users_negative.py
@@ -13,11 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
+import uuid
+
from tempest.api.identity import base
from tempest.common.utils import data_utils
from tempest import exceptions
from tempest.test import attr
-import uuid
class UsersNegativeTestJSON(base.BaseIdentityV2AdminTest):
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 05b704f..0e4d66b 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -102,6 +102,7 @@
self.client.create_endpoint(self.service_id, interface1,
url1, region=region1,
enabled=True)
+ self.addCleanup(self.client.delete_endpoint, endpoint_for_update['id'])
# Creating service so as update endpoint with new service ID
s_name = data_utils.rand_name('service-')
s_type = data_utils.rand_name('type--')
@@ -126,7 +127,6 @@
self.assertEqual(url2, endpoint['url'])
self.assertEqual(region2, endpoint['region'])
self.assertEqual('false', str(endpoint['enabled']).lower())
- self.addCleanup(self.client.delete_endpoint, endpoint_for_update['id'])
class EndPointsTestXML(EndPointsTestJSON):
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
new file mode 100644
index 0000000..28615a4
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -0,0 +1,94 @@
+
+# Copyright 2013 IBM Corp.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from tempest.api.identity import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class EndpointsNegativeTestJSON(base.BaseIdentityV3AdminTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(EndpointsNegativeTestJSON, cls).setUpClass()
+ cls.identity_client = cls.client
+ cls.client = cls.endpoints_client
+ cls.service_ids = list()
+ s_name = data_utils.rand_name('service-')
+ s_type = data_utils.rand_name('type--')
+ s_description = data_utils.rand_name('description-')
+ resp, cls.service_data = (
+ cls.service_client.create_service(s_name, s_type,
+ description=s_description))
+ cls.service_id = cls.service_data['id']
+ cls.service_ids.append(cls.service_id)
+
+ @classmethod
+ def tearDownClass(cls):
+ for s in cls.service_ids:
+ cls.service_client.delete_service(s)
+ super(EndpointsNegativeTestJSON, cls).tearDownClass()
+
+ @attr(type=['negative', 'gate'])
+ def test_create_with_enabled_False(self):
+ # Enabled should be a boolean, not a string like 'False'
+ interface = 'public'
+ url = data_utils.rand_name('url')
+ region = data_utils.rand_name('region')
+ self.assertRaises(exceptions.BadRequest, self.client.create_endpoint,
+ self.service_id, interface, url, region=region,
+ force_enabled='False')
+
+ @attr(type=['negative', 'gate'])
+ def test_create_with_enabled_True(self):
+ # Enabled should be a boolean, not a string like 'True'
+ interface = 'public'
+ url = data_utils.rand_name('url')
+ region = data_utils.rand_name('region')
+ self.assertRaises(exceptions.BadRequest, self.client.create_endpoint,
+ self.service_id, interface, url, region=region,
+ force_enabled='True')
+
+ def _assert_update_raises_bad_request(self, enabled):
+
+ # Create an endpoint
+ region1 = data_utils.rand_name('region')
+ url1 = data_utils.rand_name('url')
+ interface1 = 'public'
+ resp, endpoint_for_update = (
+ self.client.create_endpoint(self.service_id, interface1,
+ url1, region=region1, enabled=True))
+ self.addCleanup(self.client.delete_endpoint, endpoint_for_update['id'])
+
+ self.assertRaises(exceptions.BadRequest, self.client.update_endpoint,
+ endpoint_for_update['id'], force_enabled=enabled)
+
+ @attr(type=['negative', 'gate'])
+ def test_update_with_enabled_False(self):
+ # Enabled should be a boolean, not a string like 'False'
+ self._assert_update_raises_bad_request('False')
+
+ @attr(type=['negative', 'gate'])
+ def test_update_with_enabled_True(self):
+ # Enabled should be a boolean, not a string like 'True'
+ self._assert_update_raises_bad_request('True')
+
+
+class EndpointsNegativeTestXML(EndpointsNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index ce11911..abde8f7 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -101,6 +101,7 @@
disk_format='iso',
visibility='public')
self.assertEqual(201, resp.status)
+ self.addCleanup(self.client.delete_image, body['id'])
self.assertEqual('queued', body['status'])
image_id = body['id']
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index eb397ba..f4050c5 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -76,11 +76,8 @@
resp, body = self.admin_client.remove_router_from_l3_agent(
self.agent['id'], router['router']['id'])
self.assertEqual('204', resp['status'])
- resp, body = self.admin_client.list_l3_agents_hosting_router(
- router['router']['id'])
- for agent in body['agents']:
- l3_agent_ids.append(agent['id'])
- self.assertNotIn(self.agent['id'], l3_agent_ids)
+ # NOTE(afazekas): The deletion not asserted, because neutron
+ # is not forbidden to reschedule the router to the same agent
class L3AgentSchedulerTestXML(L3AgentSchedulerTestJSON):
diff --git a/tempest/api/network/admin/test_load_balancer_admin_actions.py b/tempest/api/network/admin/test_load_balancer_admin_actions.py
new file mode 100644
index 0000000..34a8e32
--- /dev/null
+++ b/tempest/api/network/admin/test_load_balancer_admin_actions.py
@@ -0,0 +1,94 @@
+# Copyright 2014 Mirantis.inc
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.network import base
+from tempest.common.utils import data_utils
+from tempest import test
+
+
+class LoadBalancerAdminTestJSON(base.BaseAdminNetworkTest):
+ _interface = 'json'
+
+ """
+ Test admin actions for load balancer.
+
+ Create VIP for another tenant
+ Create health monitor for another tenant
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(LoadBalancerAdminTestJSON, cls).setUpClass()
+ if not test.is_extension_enabled('lbaas', 'network'):
+ msg = "lbaas extension not enabled."
+ raise cls.skipException(msg)
+ cls.force_tenant_isolation = True
+ manager = cls.get_client_manager()
+ cls.client = manager.network_client
+ username, tenant_name, passwd = cls.isolated_creds.get_primary_creds()
+ cls.tenant_id = cls.os_adm.identity_client.get_tenant_by_name(
+ tenant_name)['id']
+ cls.network = cls.create_network()
+ cls.subnet = cls.create_subnet(cls.network)
+
+ @test.attr(type='smoke')
+ def test_create_vip_as_admin_for_another_tenant(self):
+ name = data_utils.rand_name('vip-')
+ resp, body = self.admin_client.create_pool(
+ name=data_utils.rand_name('pool-'), lb_method="ROUND_ROBIN",
+ protocol="HTTP", subnet_id=self.subnet['id'],
+ tenant_id=self.tenant_id)
+ self.assertEqual('201', resp['status'])
+ pool = body['pool']
+ self.addCleanup(self.admin_client.delete_pool, pool['id'])
+ resp, body = self.admin_client.create_vip(name=name,
+ protocol="HTTP",
+ protocol_port=80,
+ subnet_id=self.subnet['id'],
+ pool_id=pool['id'],
+ tenant_id=self.tenant_id)
+ self.assertEqual('201', resp['status'])
+ vip = body['vip']
+ self.addCleanup(self.admin_client.delete_vip, vip['id'])
+ self.assertIsNotNone(vip['id'])
+ self.assertEqual(self.tenant_id, vip['tenant_id'])
+ resp, body = self.client.show_vip(vip['id'])
+ self.assertEqual('200', resp['status'])
+ show_vip = body['vip']
+ self.assertEqual(vip['id'], show_vip['id'])
+ self.assertEqual(vip['name'], show_vip['name'])
+
+ @test.attr(type='smoke')
+ def test_create_health_monitor_as_admin_for_another_tenant(self):
+ resp, body = (
+ self.admin_client.create_health_monitor(delay=4,
+ max_retries=3,
+ type="TCP",
+ timeout=1,
+ tenant_id=self.tenant_id))
+ self.assertEqual('201', resp['status'])
+ health_monitor = body['health_monitor']
+ self.addCleanup(self.admin_client.delete_health_monitor,
+ health_monitor['id'])
+ self.assertIsNotNone(health_monitor['id'])
+ self.assertEqual(self.tenant_id, health_monitor['tenant_id'])
+ resp, body = self.client.show_health_monitor(health_monitor['id'])
+ self.assertEqual('200', resp['status'])
+ show_health_monitor = body['health_monitor']
+ self.assertEqual(health_monitor['id'], show_health_monitor['id'])
+
+
+class LoadBalancerAdminTestXML(LoadBalancerAdminTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/network/test_load_balancer.py b/tempest/api/network/test_load_balancer.py
index ba92199..792d61d 100644
--- a/tempest/api/network/test_load_balancer.py
+++ b/tempest/api/network/test_load_balancer.py
@@ -108,6 +108,7 @@
def test_create_update_delete_pool_vip(self):
# Creates a vip
name = data_utils.rand_name('vip-')
+ address = self.subnet['allocation_pools'][0]['end']
resp, body = self.client.create_pool(
name=data_utils.rand_name("pool-"),
lb_method='ROUND_ROBIN',
@@ -118,19 +119,40 @@
protocol="HTTP",
protocol_port=80,
subnet_id=self.subnet['id'],
- pool_id=pool['id'])
+ pool_id=pool['id'],
+ address=address)
self.assertEqual('201', resp['status'])
vip = body['vip']
vip_id = vip['id']
+ # Confirm VIP's address correctness with a show
+ resp, body = self.client.show_vip(vip_id)
+ self.assertEqual('200', resp['status'])
+ vip = body['vip']
+ self.assertEqual(address, vip['address'])
# Verification of vip update
new_name = "New_vip"
- resp, body = self.client.update_vip(vip_id, name=new_name)
+ new_description = "New description"
+ persistence_type = "HTTP_COOKIE"
+ update_data = {"session_persistence": {
+ "type": persistence_type}}
+ resp, body = self.client.update_vip(vip_id,
+ name=new_name,
+ description=new_description,
+ connection_limit=10,
+ admin_state_up=False,
+ **update_data)
self.assertEqual('200', resp['status'])
updated_vip = body['vip']
- self.assertEqual(updated_vip['name'], new_name)
+ self.assertEqual(new_name, updated_vip['name'])
+ self.assertEqual(new_description, updated_vip['description'])
+ self.assertEqual(10, updated_vip['connection_limit'])
+ self.assertFalse(updated_vip['admin_state_up'])
+ self.assertEqual(persistence_type,
+ updated_vip['session_persistence']['type'])
# Verification of vip delete
resp, body = self.client.delete_vip(vip['id'])
self.assertEqual('204', resp['status'])
+ self.client.wait_for_resource_deletion('vip', vip['id'])
# Verification of pool update
new_name = "New_pool"
resp, body = self.client.update_pool(pool['id'],
@@ -273,6 +295,40 @@
self.assertEqual('204', resp['status'])
@test.attr(type='smoke')
+ def test_create_health_monitor_http_type(self):
+ hm_type = "HTTP"
+ resp, body = self.client.create_health_monitor(delay=4,
+ max_retries=3,
+ type=hm_type,
+ timeout=1)
+ self.assertEqual('201', resp['status'])
+ health_monitor = body['health_monitor']
+ self.addCleanup(self.client.delete_health_monitor,
+ health_monitor['id'])
+ self.assertEqual(hm_type, health_monitor['type'])
+
+ @test.attr(type='smoke')
+ def test_update_health_monitor_http_method(self):
+ resp, body = self.client.create_health_monitor(delay=4,
+ max_retries=3,
+ type="HTTP",
+ timeout=1)
+ self.assertEqual('201', resp['status'])
+ health_monitor = body['health_monitor']
+ self.addCleanup(self.client.delete_health_monitor,
+ health_monitor['id'])
+ resp, body = (self.client.update_health_monitor
+ (health_monitor['id'],
+ http_method="POST",
+ url_path="/home/user",
+ expected_codes="290"))
+ self.assertEqual('200', resp['status'])
+ updated_health_monitor = body['health_monitor']
+ self.assertEqual("POST", updated_health_monitor['http_method'])
+ self.assertEqual("/home/user", updated_health_monitor['url_path'])
+ self.assertEqual("290", updated_health_monitor['expected_codes'])
+
+ @test.attr(type='smoke')
def test_show_health_monitor(self):
# Verifies the details of a health_monitor
resp, body = self.client.show_health_monitor(self.health_monitor['id'])
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 88e7238..70fb00a 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -67,7 +67,6 @@
cls.name = cls.network['name']
cls.subnet = cls.create_subnet(cls.network)
cls.cidr = cls.subnet['cidr']
- cls.port = cls.create_port(cls.network)
@attr(type='smoke')
def test_create_update_delete_network_subnet(self):
@@ -184,90 +183,6 @@
self.assertEqual(len(subnet), 1)
self.assertIn('id', subnet)
- @attr(type='smoke')
- def test_create_update_delete_port(self):
- # Verify port creation
- resp, body = self.client.create_port(network_id=self.network['id'])
- self.assertEqual('201', resp['status'])
- port = body['port']
- self.assertTrue(port['admin_state_up'])
- # Verify port update
- new_name = "New_Port"
- resp, body = self.client.update_port(
- port['id'],
- name=new_name,
- admin_state_up=False)
- self.assertEqual('200', resp['status'])
- updated_port = body['port']
- self.assertEqual(updated_port['name'], new_name)
- self.assertFalse(updated_port['admin_state_up'])
- # Verify port deletion
- resp, body = self.client.delete_port(port['id'])
- self.assertEqual('204', resp['status'])
-
- @attr(type='smoke')
- def test_show_port(self):
- # Verify the details of port
- resp, body = self.client.show_port(self.port['id'])
- self.assertEqual('200', resp['status'])
- port = body['port']
- self.assertIn('id', port)
- self.assertEqual(port['id'], self.port['id'])
-
- @attr(type='smoke')
- def test_show_port_fields(self):
- # Verify specific fields of a port
- field_list = [('fields', 'id'), ]
- resp, body = self.client.show_port(self.port['id'],
- field_list=field_list)
- self.assertEqual('200', resp['status'])
- port = body['port']
- self.assertEqual(len(port), len(field_list))
- for label, field_name in field_list:
- self.assertEqual(port[field_name], self.port[field_name])
-
- @attr(type='smoke')
- def test_list_ports(self):
- # Verify the port exists in the list of all ports
- resp, body = self.client.list_ports()
- self.assertEqual('200', resp['status'])
- ports = [port['id'] for port in body['ports']
- if port['id'] == self.port['id']]
- self.assertNotEmpty(ports, "Created port not found in the list")
-
- @attr(type='smoke')
- def test_port_list_filter_by_router_id(self):
- # Create a router
- network = self.create_network()
- self.create_subnet(network)
- router = self.create_router(data_utils.rand_name('router-'))
- resp, port = self.client.create_port(network_id=network['id'])
- # Add router interface to port created above
- resp, interface = self.client.add_router_interface_with_port_id(
- router['id'], port['port']['id'])
- self.addCleanup(self.client.remove_router_interface_with_port_id,
- router['id'], port['port']['id'])
- # List ports filtered by router_id
- resp, port_list = self.client.list_ports(
- device_id=router['id'])
- self.assertEqual('200', resp['status'])
- ports = port_list['ports']
- self.assertEqual(len(ports), 1)
- self.assertEqual(ports[0]['id'], port['port']['id'])
- self.assertEqual(ports[0]['device_id'], router['id'])
-
- @attr(type='smoke')
- def test_list_ports_fields(self):
- # Verify specific fields of ports
- resp, body = self.client.list_ports(fields='id')
- self.assertEqual('200', resp['status'])
- ports = body['ports']
- self.assertNotEmpty(ports, "Port list returned is empty")
- # Asserting the fields returned are correct
- for port in ports:
- self.assertEqual(len(port), 1)
- self.assertIn('id', port)
-
class NetworksTestXML(NetworksTestJSON):
_interface = 'xml'
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
new file mode 100644
index 0000000..fbb25a8
--- /dev/null
+++ b/tempest/api/network/test_ports.py
@@ -0,0 +1,249 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import socket
+
+from tempest.api.network import base
+from tempest.common.utils import data_utils
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class PortsTestJSON(base.BaseNetworkTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(PortsTestJSON, cls).setUpClass()
+ cls.network = cls.create_network()
+ cls.port = cls.create_port(cls.network)
+
+ def _delete_port(self, port_id):
+ resp, body = self.client.delete_port(port_id)
+ self.assertEqual('204', resp['status'])
+ resp, body = self.client.list_ports()
+ self.assertEqual('200', resp['status'])
+ ports_list = body['ports']
+ self.assertFalse(port_id in [n['id'] for n in ports_list])
+
+ @test.attr(type='smoke')
+ def test_create_update_delete_port(self):
+ # Verify port creation
+ resp, body = self.client.create_port(network_id=self.network['id'])
+ self.assertEqual('201', resp['status'])
+ port = body['port']
+ self.assertTrue(port['admin_state_up'])
+ # Verify port update
+ new_name = "New_Port"
+ resp, body = self.client.update_port(
+ port['id'],
+ name=new_name,
+ admin_state_up=False)
+ self.assertEqual('200', resp['status'])
+ updated_port = body['port']
+ self.assertEqual(updated_port['name'], new_name)
+ self.assertFalse(updated_port['admin_state_up'])
+ # Verify port deletion
+ resp, body = self.client.delete_port(port['id'])
+ self.assertEqual('204', resp['status'])
+
+ @test.attr(type='smoke')
+ def test_show_port(self):
+ # Verify the details of port
+ resp, body = self.client.show_port(self.port['id'])
+ self.assertEqual('200', resp['status'])
+ port = body['port']
+ self.assertIn('id', port)
+ self.assertEqual(port['id'], self.port['id'])
+ self.assertEqual(self.port['admin_state_up'], port['admin_state_up'])
+ self.assertEqual(self.port['device_id'], port['device_id'])
+ self.assertEqual(self.port['device_owner'], port['device_owner'])
+ self.assertEqual(self.port['mac_address'], port['mac_address'])
+ self.assertEqual(self.port['name'], port['name'])
+ self.assertEqual(self.port['security_groups'],
+ port['security_groups'])
+ self.assertEqual(self.port['network_id'], port['network_id'])
+ self.assertEqual(self.port['security_groups'],
+ port['security_groups'])
+
+ @test.attr(type='smoke')
+ def test_show_port_fields(self):
+ # Verify specific fields of a port
+ field_list = [('fields', 'id'), ]
+ resp, body = self.client.show_port(self.port['id'],
+ field_list=field_list)
+ self.assertEqual('200', resp['status'])
+ port = body['port']
+ self.assertEqual(len(port), len(field_list))
+ for label, field_name in field_list:
+ self.assertEqual(port[field_name], self.port[field_name])
+
+ @test.attr(type='smoke')
+ def test_list_ports(self):
+ # Verify the port exists in the list of all ports
+ resp, body = self.client.list_ports()
+ self.assertEqual('200', resp['status'])
+ ports = [port['id'] for port in body['ports']
+ if port['id'] == self.port['id']]
+ self.assertNotEmpty(ports, "Created port not found in the list")
+
+ @test.attr(type='smoke')
+ def test_port_list_filter_by_router_id(self):
+ # Create a router
+ network = self.create_network()
+ self.create_subnet(network)
+ router = self.create_router(data_utils.rand_name('router-'))
+ resp, port = self.client.create_port(network_id=network['id'])
+ # Add router interface to port created above
+ resp, interface = self.client.add_router_interface_with_port_id(
+ router['id'], port['port']['id'])
+ self.addCleanup(self.client.remove_router_interface_with_port_id,
+ router['id'], port['port']['id'])
+ # List ports filtered by router_id
+ resp, port_list = self.client.list_ports(
+ device_id=router['id'])
+ self.assertEqual('200', resp['status'])
+ ports = port_list['ports']
+ self.assertEqual(len(ports), 1)
+ self.assertEqual(ports[0]['id'], port['port']['id'])
+ self.assertEqual(ports[0]['device_id'], router['id'])
+
+ @test.attr(type='smoke')
+ def test_list_ports_fields(self):
+ # Verify specific fields of ports
+ resp, body = self.client.list_ports(fields='id')
+ self.assertEqual('200', resp['status'])
+ ports = body['ports']
+ self.assertNotEmpty(ports, "Port list returned is empty")
+ # Asserting the fields returned are correct
+ for port in ports:
+ self.assertEqual(len(port), 1)
+ self.assertIn('id', port)
+
+
+class PortsTestXML(PortsTestJSON):
+ _interface = 'xml'
+
+
+class PortsAdminExtendedAttrsTestJSON(base.BaseAdminNetworkTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(PortsAdminExtendedAttrsTestJSON, cls).setUpClass()
+ cls.identity_client = cls._get_identity_admin_client()
+ cls.tenant = cls.identity_client.get_tenant_by_name(
+ CONF.identity.tenant_name)
+ cls.network = cls.create_network()
+ cls.host_id = socket.gethostname()
+
+ @test.attr(type='smoke')
+ def test_create_port_binding_ext_attr(self):
+ post_body = {"network_id": self.network['id'],
+ "binding:host_id": self.host_id}
+ resp, body = self.admin_client.create_port(**post_body)
+ self.assertEqual('201', resp['status'])
+ port = body['port']
+ self.addCleanup(self.admin_client.delete_port, port['id'])
+ host_id = port['binding:host_id']
+ self.assertIsNotNone(host_id)
+ self.assertEqual(self.host_id, host_id)
+
+ @test.attr(type='smoke')
+ def test_update_port_binding_ext_attr(self):
+ post_body = {"network_id": self.network['id']}
+ resp, body = self.admin_client.create_port(**post_body)
+ self.assertEqual('201', resp['status'])
+ port = body['port']
+ self.addCleanup(self.admin_client.delete_port, port['id'])
+ update_body = {"binding:host_id": self.host_id}
+ resp, body = self.admin_client.update_port(port['id'], **update_body)
+ self.assertEqual('200', resp['status'])
+ updated_port = body['port']
+ host_id = updated_port['binding:host_id']
+ self.assertIsNotNone(host_id)
+ self.assertEqual(self.host_id, host_id)
+
+ @test.attr(type='smoke')
+ def test_list_ports_binding_ext_attr(self):
+ resp, body = self.admin_client.list_ports(
+ **{'tenant_id': self.tenant['id']})
+ self.assertEqual('200', resp['status'])
+ ports_list = body['ports']
+ for port in ports_list:
+ vif_type = port['binding:vif_type']
+ self.assertIsNotNone(vif_type)
+ vif_details = port['binding:vif_details']['port_filter']
+ self.assertIsNotNone(vif_details)
+
+ @test.attr(type='smoke')
+ def test_show_port_binding_ext_attr(self):
+ resp, body = self.admin_client.create_port(
+ network_id=self.network['id'])
+ self.assertEqual('201', resp['status'])
+ port = body['port']
+ self.addCleanup(self.admin_client.delete_port, port['id'])
+ resp, body = self.admin_client.show_port(port['id'])
+ self.assertEqual('200', resp['status'])
+ show_port = body['port']
+ self.assertEqual(port['binding:host_id'],
+ show_port['binding:host_id'])
+ self.assertEqual(port['binding:vif_type'],
+ show_port['binding:vif_type'])
+ self.assertEqual(port['binding:vif_details'],
+ show_port['binding:vif_details'])
+
+
+class PortsAdminExtendedAttrsTestXML(PortsAdminExtendedAttrsTestJSON):
+ _interface = 'xml'
+
+
+class PortsIpV6TestJSON(PortsTestJSON):
+ _ip_version = 6
+ _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
+ _tenant_network_mask_bits = CONF.network.tenant_network_v6_mask_bits
+
+ @classmethod
+ def setUpClass(cls):
+ super(PortsIpV6TestJSON, cls).setUpClass()
+ if not CONF.network_feature_enabled.ipv6:
+ cls.tearDownClass()
+ skip_msg = "IPv6 Tests are disabled."
+ raise cls.skipException(skip_msg)
+
+
+class PortsIpV6TestXML(PortsIpV6TestJSON):
+ _interface = 'xml'
+
+
+class PortsAdminExtendedAttrsIpV6TestJSON(PortsAdminExtendedAttrsTestJSON):
+ _ip_version = 6
+ _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
+ _tenant_network_mask_bits = CONF.network.tenant_network_v6_mask_bits
+
+ @classmethod
+ def setUpClass(cls):
+ super(PortsAdminExtendedAttrsIpV6TestJSON, cls).setUpClass()
+ if not CONF.network_feature_enabled.ipv6:
+ cls.tearDownClass()
+ skip_msg = "IPv6 Tests are disabled."
+ raise cls.skipException(skip_msg)
+
+
+class PortsAdminExtendedAttrsIpV6TestXML(
+ PortsAdminExtendedAttrsIpV6TestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index b14adc0..a3098a5 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -68,7 +68,7 @@
# Retrieve a ResellerAdmin auth data and use it to set a quota
# on the client's account
cls.reselleradmin_auth_data = \
- cls.os_reselleradmin.get_auth_provider().auth_data
+ cls.os_reselleradmin.auth_provider.auth_data
def setUp(self):
super(AccountQuotasTest, self).setUp()
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index 402cd90..7648ea1 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -68,7 +68,7 @@
# Retrieve a ResellerAdmin auth data and use it to set a quota
# on the client's account
cls.reselleradmin_auth_data = \
- cls.os_reselleradmin.get_auth_provider().auth_data
+ cls.os_reselleradmin.auth_provider.auth_data
def setUp(self):
super(AccountQuotasNegativeTest, self).setUp()
diff --git a/tempest/api/object_storage/test_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index ea93aa3..71eaab5 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -31,7 +31,7 @@
test_os = clients.Manager(self.data.test_user,
self.data.test_password,
self.data.test_tenant)
- test_auth_provider = test_os.get_auth_provider()
+ test_auth_provider = test_os.auth_provider
# Get auth for the test user
test_auth_provider.auth_data
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index 085ef51..c865ee1 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -27,7 +27,7 @@
test_os = clients.Manager(cls.data.test_user,
cls.data.test_password,
cls.data.test_tenant)
- cls.test_auth_data = test_os.get_auth_provider().auth_data
+ cls.test_auth_data = test_os.auth_provider.auth_data
@classmethod
def tearDownClass(cls):
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index a5a0950..547bf87 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -29,7 +29,7 @@
test_os = clients.Manager(cls.data.test_user,
cls.data.test_password,
cls.data.test_tenant)
- cls.test_auth_data = test_os.get_auth_provider().auth_data
+ cls.test_auth_data = test_os.auth_provider.auth_data
@classmethod
def tearDownClass(cls):
diff --git a/tempest/api/orchestration/stacks/test_server_cfn_init.py b/tempest/api/orchestration/stacks/test_server_cfn_init.py
index 7e8bc2d..95deaf5 100644
--- a/tempest/api/orchestration/stacks/test_server_cfn_init.py
+++ b/tempest/api/orchestration/stacks/test_server_cfn_init.py
@@ -17,6 +17,7 @@
from tempest.common.utils import data_utils
from tempest.common.utils.linux import remote_client
from tempest import config
+from tempest import exceptions
from tempest.openstack.common import log as logging
from tempest import test
@@ -66,18 +67,13 @@
content: smoke test complete
/etc/cfn/cfn-credentials:
content:
- Fn::Join:
- - ''
- - - AWSAccessKeyId=
- - {Ref: SmokeKeys}
- - '
-
- '
- - AWSSecretKey=
- - Fn::GetAtt: [SmokeKeys, SecretAccessKey]
- - '
-
- '
+ Fn::Replace:
+ - SmokeKeys: {Ref: SmokeKeys}
+ SecretAccessKey:
+ 'Fn::GetAtt': [SmokeKeys, SecretAccessKey]
+ - |
+ AWSAccessKeyId=SmokeKeys
+ AWSSecretKey=SecretAccessKey
mode: '000400'
owner: root
group: root
@@ -90,19 +86,13 @@
networks:
- uuid: {Ref: network}
user_data:
- Fn::Base64:
- Fn::Join:
- - ''
- - - |-
- #!/bin/bash -v
- /opt/aws/bin/cfn-init
- - |-
- || error_exit ''Failed to run cfn-init''
- /opt/aws/bin/cfn-signal -e 0 --data "`cat /tmp/smoke-status`" '
- - {Ref: WaitHandle}
- - '''
-
- '
+ Fn::Replace:
+ - WaitHandle: {Ref: WaitHandle}
+ - |
+ #!/bin/bash -v
+ /opt/aws/bin/cfn-init
+ /opt/aws/bin/cfn-signal -e 0 --data "`cat /tmp/smoke-status`" \
+ "WaitHandle"
WaitHandle:
Type: AWS::CloudFormation::WaitConditionHandle
WaitCondition:
@@ -172,9 +162,32 @@
linux_client.validate_authentication()
@test.attr(type='slow')
- def test_stack_wait_condition_data(self):
-
+ def test_all_resources_created(self):
sid = self.stack_identifier
+ self.client.wait_for_resource_status(
+ sid, 'WaitHandle', 'CREATE_COMPLETE')
+ self.client.wait_for_resource_status(
+ sid, 'SmokeSecurityGroup', 'CREATE_COMPLETE')
+ self.client.wait_for_resource_status(
+ sid, 'SmokeKeys', 'CREATE_COMPLETE')
+ self.client.wait_for_resource_status(
+ sid, 'CfnUser', 'CREATE_COMPLETE')
+ self.client.wait_for_resource_status(
+ sid, 'SmokeServer', 'CREATE_COMPLETE')
+ try:
+ self.client.wait_for_resource_status(
+ sid, 'WaitCondition', 'CREATE_COMPLETE')
+ except exceptions.TimeoutException as e:
+ # attempt to log the server console to help with debugging
+ # the cause of the server not signalling the waitcondition
+ # to heat.
+ resp, body = self.client.get_resource(sid, 'SmokeServer')
+ server_id = body['physical_resource_id']
+ LOG.debug('Console output for %s', server_id)
+ resp, output = self.servers_client.get_console_output(
+ server_id, None)
+ LOG.debug(output)
+ raise e
# wait for create to complete.
self.client.wait_for_stack_status(sid, 'CREATE_COMPLETE')
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index e140ad0..594c703 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -22,6 +22,7 @@
_interface = "json"
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(SnapshotsActionsTest, cls).setUpClass()
cls.client = cls.snapshots_client
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 31f6730..742f7e1 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -23,6 +23,7 @@
class VolumeQuotasAdminTestJSON(base.BaseVolumeV1AdminTest):
_interface = "json"
+ force_tenant_isolation = True
@classmethod
def setUpClass(cls):
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 8183999..ee1d09a 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -116,3 +116,35 @@
self.assertEqual(extra_specs, fetched_volume_type['extra_specs'],
'The fetched Volume_type is different '
'from the created Volume_type')
+
+ @test.attr(type='smoke')
+ def test_volume_type_encryption_create_get(self):
+ # Create/get encryption type.
+ provider = "LuksEncryptor"
+ control_location = "front-end"
+ name = data_utils.rand_name("volume-type-")
+ resp, body = self.client.create_volume_type(name)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self._delete_volume_type, body['id'])
+ resp, encryption_type = self.client.create_encryption_type(
+ body['id'], provider=provider,
+ control_location=control_location)
+ self.assertEqual(200, resp.status)
+ self.assertIn('volume_type_id', encryption_type)
+ self.assertEqual(provider, encryption_type['provider'],
+ "The created encryption_type provider is not equal "
+ "to the requested provider")
+ self.assertEqual(control_location, encryption_type['control_location'],
+ "The created encryption_type control_location is not "
+ "equal to the requested control_location")
+ resp, fetched_encryption_type = self.client.get_encryption_type(
+ encryption_type['volume_type_id'])
+ self.assertEqual(200, resp.status)
+ self.assertEqual(provider,
+ fetched_encryption_type['provider'],
+ 'The fetched encryption_type provider is different '
+ 'from the created encryption_type')
+ self.assertEqual(control_location,
+ fetched_encryption_type['control_location'],
+ 'The fetched encryption_type control_location is '
+ 'different from the created encryption_type')
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index aa00700..4496f18 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -22,6 +22,7 @@
_interface = "json"
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(VolumesActionsTest, cls).setUpClass()
cls.client = cls.volumes_client
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index 1493b37..d2c4ab7 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -21,6 +21,7 @@
_interface = "json"
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(SnapshotMetadataTest, cls).setUpClass()
cls.client = cls.snapshots_client
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index a22ad32..cfab0bd 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -25,6 +25,7 @@
_interface = "json"
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(VolumesActionsTest, cls).setUpClass()
cls.client = cls.volumes_client
@@ -44,7 +45,7 @@
def tearDownClass(cls):
# Delete the test instance
cls.servers_client.delete_server(cls.server['id'])
- cls.client.wait_for_resource_deletion(cls.server['id'])
+ cls.servers_client.wait_for_server_termination(cls.server['id'])
super(VolumesActionsTest, cls).tearDownClass()
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 175da01..be5d76b 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -124,8 +124,8 @@
resp, new_volume = \
self.client.create_volume(size=1,
display_description=new_v_desc,
- availability_zone=volume[
- 'availability_zone'])
+ availability_zone=
+ volume['availability_zone'])
self.assertEqual(200, resp.status)
self.assertIn('id', new_volume)
self.addCleanup(self._delete_volume, new_volume['id'])
@@ -133,8 +133,8 @@
resp, update_volume = \
self.client.update_volume(new_volume['id'],
display_name=volume['display_name'],
- display_description=volume[
- 'display_description'])
+ display_description=
+ volume['display_description'])
self.assertEqual(200, resp.status)
# NOTE(jdg): Revert back to strict true/false checking
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 0e91371..fff40ed 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -116,8 +116,8 @@
('details' if with_detail else '', key)
if key == 'metadata':
self.assertThat(volume[key].items(),
- matchers.ContainsAll(params[key]
- .items()), msg)
+ matchers.ContainsAll(
+ params[key].items()), msg)
else:
self.assertEqual(params[key], volume[key], msg)
diff --git a/tempest/api/compute/api_schema/__init__.py b/tempest/api_schema/__init__.py
similarity index 100%
rename from tempest/api/compute/api_schema/__init__.py
rename to tempest/api_schema/__init__.py
diff --git a/tempest/api/compute/api_schema/__init__.py b/tempest/api_schema/compute/__init__.py
similarity index 100%
copy from tempest/api/compute/api_schema/__init__.py
copy to tempest/api_schema/compute/__init__.py
diff --git a/tempest/api_schema/compute/services.py b/tempest/api_schema/compute/services.py
new file mode 100644
index 0000000..4793f5a
--- /dev/null
+++ b/tempest/api_schema/compute/services.py
@@ -0,0 +1,44 @@
+# Copyright 2014 NEC Corporation. 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.
+
+list_services = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'services': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ # NOTE: Now the type of 'id' is integer, but here
+ # allows 'string' also because we will be able to
+ # change it to 'uuid' in the future.
+ 'id': {'type': ['integer', 'string']},
+ 'zone': {'type': 'string'},
+ 'host': {'type': 'string'},
+ 'state': {'type': 'string'},
+ 'binary': {'type': 'string'},
+ 'status': {'type': 'string'},
+ 'updated_at': {'type': 'string'},
+ 'disabled_reason': {'type': ['string', 'null']}
+ },
+ 'required': ['id', 'zone', 'host', 'state', 'binary',
+ 'status', 'updated_at', 'disabled_reason']
+ }
+ }
+ },
+ 'required': ['services']
+ }
+}
diff --git a/tempest/api/compute/api_schema/__init__.py b/tempest/api_schema/compute/v2/__init__.py
similarity index 100%
copy from tempest/api/compute/api_schema/__init__.py
copy to tempest/api_schema/compute/v2/__init__.py
diff --git a/tempest/api_schema/compute/v2/volumes.py b/tempest/api_schema/compute/v2/volumes.py
new file mode 100644
index 0000000..16ed7c2
--- /dev/null
+++ b/tempest/api_schema/compute/v2/volumes.py
@@ -0,0 +1,56 @@
+# Copyright 2014 NEC Corporation. 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.
+
+get_volume = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'volume': {
+ 'type': 'object',
+ 'properties': {
+ # NOTE: Now the type of 'id' is integer, but here allows
+ # 'string' also because we will be able to change it to
+ # 'uuid' in the future.
+ 'id': {'type': ['integer', 'string']},
+ 'status': {'type': 'string'},
+ 'displayName': {'type': ['string', 'null']},
+ 'availabilityZone': {'type': 'string'},
+ 'createdAt': {'type': 'string'},
+ 'displayDescription': {'type': ['string', 'null']},
+ 'volumeType': {'type': 'string'},
+ 'snapshotId': {'type': ['string', 'null']},
+ 'metadata': {'type': 'object'},
+ 'size': {'type': 'integer'},
+ 'attachments': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'id': {'type': ['integer', 'string']},
+ 'device': {'type': 'string'},
+ 'volumeId': {'type': ['integer', 'string']},
+ 'serverId': {'type': ['integer', 'string']}
+ }
+ }
+ }
+ },
+ 'required': ['id', 'status', 'displayName', 'availabilityZone',
+ 'createdAt', 'displayDescription', 'volumeType',
+ 'snapshotId', 'metadata', 'size', 'attachments']
+ }
+ },
+ 'required': ['volume']
+ }
+}
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index 8c4ec45..932b151 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -42,6 +42,7 @@
def nova(self, action, flags='', params='', admin=True, fail_ok=False):
"""Executes nova command for the given action."""
+ flags += ' --endpoint-type %s' % CONF.compute.endpoint_type
return self.cmd_with_auth(
'nova', action, flags, params, admin, fail_ok)
@@ -58,34 +59,41 @@
def glance(self, action, flags='', params='', admin=True, fail_ok=False):
"""Executes glance command for the given action."""
+ flags += ' --os-endpoint-type %s' % CONF.image.endpoint_type
return self.cmd_with_auth(
'glance', action, flags, params, admin, fail_ok)
def ceilometer(self, action, flags='', params='', admin=True,
fail_ok=False):
"""Executes ceilometer command for the given action."""
+ flags += ' --os-endpoint-type %s' % CONF.telemetry.endpoint_type
return self.cmd_with_auth(
'ceilometer', action, flags, params, admin, fail_ok)
def heat(self, action, flags='', params='', admin=True,
fail_ok=False):
"""Executes heat command for the given action."""
+ flags += ' --os-endpoint-type %s' % CONF.orchestration.endpoint_type
return self.cmd_with_auth(
'heat', action, flags, params, admin, fail_ok)
def cinder(self, action, flags='', params='', admin=True, fail_ok=False):
"""Executes cinder command for the given action."""
+ flags += ' --endpoint-type %s' % CONF.volume.endpoint_type
return self.cmd_with_auth(
'cinder', action, flags, params, admin, fail_ok)
def neutron(self, action, flags='', params='', admin=True, fail_ok=False):
"""Executes neutron command for the given action."""
+ flags += ' --endpoint-type %s' % CONF.network.endpoint_type
return self.cmd_with_auth(
'neutron', action, flags, params, admin, fail_ok)
- def savanna(self, action, flags='', params='', admin=True, fail_ok=False):
- """Executes savanna command for the given action."""
+ def sahara(self, action, flags='', params='', admin=True, fail_ok=False):
+ """Executes sahara command for the given action."""
+ flags += ' --endpoint-type %s' % CONF.data_processing.endpoint_type
return self.cmd_with_auth(
+ # TODO (slukjanov): replace with sahara when new client released
'savanna', action, flags, params, admin, fail_ok)
def cmd_with_auth(self, cmd, action, flags='', params='',
@@ -93,7 +101,7 @@
"""Executes given command with auth attributes appended."""
# TODO(jogo) make admin=False work
creds = ('--os-username %s --os-tenant-name %s --os-password %s '
- '--os-auth-url %s ' %
+ '--os-auth-url %s' %
(CONF.identity.admin_username,
CONF.identity.admin_tenant_name,
CONF.identity.admin_password,
diff --git a/tempest/cli/simple_read_only/test_ceilometer.py b/tempest/cli/simple_read_only/test_ceilometer.py
index 0b6ae22..1d2822d 100644
--- a/tempest/cli/simple_read_only/test_ceilometer.py
+++ b/tempest/cli/simple_read_only/test_ceilometer.py
@@ -16,6 +16,7 @@
from tempest import cli
from tempest import config
from tempest.openstack.common import log as logging
+from tempest import test
CONF = config.CONF
@@ -41,6 +42,7 @@
def test_ceilometer_meter_list(self):
self.ceilometer('meter-list')
+ @test.attr(type='slow')
def test_ceilometer_resource_list(self):
self.ceilometer('resource-list')
diff --git a/tempest/cli/simple_read_only/test_savanna.py b/tempest/cli/simple_read_only/test_sahara.py
similarity index 61%
rename from tempest/cli/simple_read_only/test_savanna.py
rename to tempest/cli/simple_read_only/test_sahara.py
index 1e30978..cd819a4 100644
--- a/tempest/cli/simple_read_only/test_savanna.py
+++ b/tempest/cli/simple_read_only/test_sahara.py
@@ -25,8 +25,8 @@
LOG = logging.getLogger(__name__)
-class SimpleReadOnlySavannaClientTest(cli.ClientTestBase):
- """Basic, read-only tests for Savanna CLI client.
+class SimpleReadOnlySaharaClientTest(cli.ClientTestBase):
+ """Basic, read-only tests for Sahara CLI client.
Checks return values and output of read-only commands.
These tests do not presume any content, nor do they create
@@ -35,36 +35,36 @@
@classmethod
def setUpClass(cls):
- if not CONF.service_available.savanna:
- msg = "Skipping all Savanna cli tests because it is not available"
+ if not CONF.service_available.sahara:
+ msg = "Skipping all Sahara cli tests because it is not available"
raise cls.skipException(msg)
- super(SimpleReadOnlySavannaClientTest, cls).setUpClass()
+ super(SimpleReadOnlySaharaClientTest, cls).setUpClass()
@test.attr(type='negative')
- def test_savanna_fake_action(self):
+ def test_sahara_fake_action(self):
self.assertRaises(subprocess.CalledProcessError,
- self.savanna,
+ self.sahara,
'this-does-not-exist')
- def test_savanna_plugins_list(self):
- plugins = self.parser.listing(self.savanna('plugin-list'))
+ def test_sahara_plugins_list(self):
+ plugins = self.parser.listing(self.sahara('plugin-list'))
self.assertTableStruct(plugins, ['name', 'versions', 'title'])
- def test_savanna_plugins_show(self):
- plugin = self.parser.listing(self.savanna('plugin-show',
- params='--name vanilla'))
+ def test_sahara_plugins_show(self):
+ plugin = self.parser.listing(self.sahara('plugin-show',
+ params='--name vanilla'))
self.assertTableStruct(plugin, ['Property', 'Value'])
- def test_savanna_node_group_template_list(self):
- plugins = self.parser.listing(self.savanna('node-group-template-list'))
+ def test_sahara_node_group_template_list(self):
+ plugins = self.parser.listing(self.sahara('node-group-template-list'))
self.assertTableStruct(plugins, ['name', 'id', 'plugin_name',
'node_processes', 'description'])
- def test_savanna_cluster_template_list(self):
- plugins = self.parser.listing(self.savanna('cluster-template-list'))
+ def test_sahara_cluster_template_list(self):
+ plugins = self.parser.listing(self.sahara('cluster-template-list'))
self.assertTableStruct(plugins, ['name', 'id', 'plugin_name',
'node_groups', 'description'])
- def test_savanna_cluster_list(self):
- plugins = self.parser.listing(self.savanna('cluster-list'))
+ def test_sahara_cluster_list(self):
+ plugins = self.parser.listing(self.sahara('cluster-list'))
self.assertTableStruct(plugins, ['name', 'id', 'status', 'node_count'])
diff --git a/tempest/clients.py b/tempest/clients.py
index ab7deb0..7ebd983 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -61,6 +61,7 @@
TenantUsagesClientJSON
from tempest.services.compute.json.volumes_extensions_client import \
VolumesExtensionsClientJSON
+from tempest.services.compute.v3.json.agents_client import AgentsV3ClientJSON
from tempest.services.compute.v3.json.aggregates_client import \
AggregatesV3ClientJSON
from tempest.services.compute.v3.json.availability_zone_client import \
@@ -315,6 +316,7 @@
self.services_v3_client = ServicesV3ClientJSON(
self.auth_provider)
self.service_client = ServiceClientJSON(self.auth_provider)
+ self.agents_v3_client = AgentsV3ClientJSON(self.auth_provider)
self.aggregates_v3_client = AggregatesV3ClientJSON(
self.auth_provider)
self.aggregates_client = AggregatesClientJSON(
diff --git a/tempest/common/commands.py b/tempest/common/commands.py
index 6405eaa..c31a038 100644
--- a/tempest/common/commands.py
+++ b/tempest/common/commands.py
@@ -73,3 +73,7 @@
def iptables_ns(ns, table):
return ip_ns_exec(ns, "iptables -v -S -t " + table)
+
+
+def ovs_db_dump():
+ return sudo_cmd_call("ovsdb-client dump")
diff --git a/tempest/common/debug.py b/tempest/common/debug.py
index 8325d4d..6a496c2 100644
--- a/tempest/common/debug.py
+++ b/tempest/common/debug.py
@@ -38,3 +38,15 @@
for table in ['filter', 'nat', 'mangle']:
LOG.info('ns(%s) table(%s):\n%s', ns, table,
commands.iptables_ns(ns, table))
+
+
+def log_ovs_db():
+ if not CONF.debug.enable or not CONF.service_available.neutron:
+ return
+ db_dump = commands.ovs_db_dump()
+ LOG.info("OVS DB:\n" + db_dump)
+
+
+def log_net_debug():
+ log_ip_ns()
+ log_ovs_db()
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 36ddb40..88dbe58 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -21,6 +21,8 @@
import re
import time
+import jsonschema
+
from tempest.common import http
from tempest import config
from tempest import exceptions
@@ -502,6 +504,31 @@
% self.__class__.__name__)
raise NotImplementedError(message)
+ @classmethod
+ def validate_response(cls, schema, resp, body):
+ # Only check the response if the status code is a success code
+ # TODO(cyeoh): Eventually we should be able to verify that a failure
+ # code if it exists is something that we expect. This is explicitly
+ # declared in the V3 API and so we should be able to export this in
+ # the response schema. For now we'll ignore it.
+ if str(resp.status).startswith('2'):
+ response_code = schema['status_code']
+ if resp.status not in response_code:
+ msg = ("The status code(%s) is different than the expected "
+ "one(%s)") % (resp.status, response_code)
+ raise exceptions.InvalidHttpSuccessCode(msg)
+ response_schema = schema.get('response_body')
+ if response_schema:
+ try:
+ jsonschema.validate(body, response_schema)
+ except jsonschema.ValidationError as ex:
+ msg = ("HTTP response body is invalid (%s)") % ex
+ raise exceptions.InvalidHTTPResponseBody(msg)
+ else:
+ if body:
+ msg = ("HTTP response body should not exist (%s)") % body
+ raise exceptions.InvalidHTTPResponseBody(msg)
+
class NegativeRestClient(RestClient):
"""
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 8420ad0..00e5e0d 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -43,6 +43,9 @@
ssh_timeout, pkey=pkey,
channel_timeout=ssh_channel_timeout)
+ def exec_command(self, cmd):
+ return self.ssh_client.exec_command(cmd)
+
def validate_authentication(self):
"""Validate ssh connection and authentication
This method raises an Exception when the validation fails.
@@ -51,33 +54,33 @@
def hostname_equals_servername(self, expected_hostname):
# Get host name using command "hostname"
- actual_hostname = self.ssh_client.exec_command("hostname").rstrip()
+ actual_hostname = self.exec_command("hostname").rstrip()
return expected_hostname == actual_hostname
def get_files(self, path):
# Return a list of comma separated files
command = "ls -m " + path
- return self.ssh_client.exec_command(command).rstrip('\n').split(', ')
+ return self.exec_command(command).rstrip('\n').split(', ')
def get_ram_size_in_mb(self):
- output = self.ssh_client.exec_command('free -m | grep Mem')
+ output = self.exec_command('free -m | grep Mem')
if output:
return output.split()[1]
def get_number_of_vcpus(self):
command = 'cat /proc/cpuinfo | grep processor | wc -l'
- output = self.ssh_client.exec_command(command)
+ output = self.exec_command(command)
return int(output)
def get_partitions(self):
# Return the contents of /proc/partitions
command = 'cat /proc/partitions'
- output = self.ssh_client.exec_command(command)
+ output = self.exec_command(command)
return output
def get_boot_time(self):
cmd = 'cut -f1 -d. /proc/uptime'
- boot_secs = self.ssh_client.exec_command(cmd)
+ boot_secs = self.exec_command(cmd)
boot_time = time.time() - int(boot_secs)
return time.localtime(boot_time)
@@ -85,27 +88,27 @@
message = re.sub("([$\\`])", "\\\\\\\\\\1", message)
# usually to /dev/ttyS0
cmd = 'sudo sh -c "echo \\"%s\\" >/dev/console"' % message
- return self.ssh_client.exec_command(cmd)
+ return self.exec_command(cmd)
def ping_host(self, host):
cmd = 'ping -c1 -w1 %s' % host
- return self.ssh_client.exec_command(cmd)
+ return self.exec_command(cmd)
def get_mac_address(self):
cmd = "/sbin/ifconfig | awk '/HWaddr/ {print $5}'"
- return self.ssh_client.exec_command(cmd)
+ return self.exec_command(cmd)
def get_ip_list(self):
cmd = "/bin/ip address"
- return self.ssh_client.exec_command(cmd)
+ return self.exec_command(cmd)
def assign_static_ip(self, nic, addr):
cmd = "sudo /bin/ip addr add {ip}/{mask} dev {nic}".format(
ip=addr, mask=CONF.network.tenant_network_mask_bits,
nic=nic
)
- return self.ssh_client.exec_command(cmd)
+ return self.exec_command(cmd)
def turn_nic_on(self, nic):
cmd = "sudo /bin/ip link set {nic} up".format(nic=nic)
- return self.ssh_client.exec_command(cmd)
+ return self.exec_command(cmd)
diff --git a/tempest/config.py b/tempest/config.py
index 46dcbcc..471a0de 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -60,16 +60,16 @@
'publicURL', 'adminURL', 'internalURL'],
help="The endpoint type to use for the identity service."),
cfg.StrOpt('username',
- default='demo',
+ default=None,
help="Username to use for Nova API requests."),
cfg.StrOpt('tenant_name',
- default='demo',
+ default=None,
help="Tenant name to use for Nova API requests."),
cfg.StrOpt('admin_role',
default='admin',
help="Role required to administrate keystone."),
cfg.StrOpt('password',
- default='pass',
+ default=None,
help="API key to use when authenticating.",
secret=True),
cfg.StrOpt('alt_username',
@@ -85,15 +85,15 @@
help="API key to use when authenticating as alternate user.",
secret=True),
cfg.StrOpt('admin_username',
- default='admin',
+ default=None,
help="Administrative Username to use for "
"Keystone API requests."),
cfg.StrOpt('admin_tenant_name',
- default='admin',
+ default=None,
help="Administrative Tenant name to use for Keystone API "
"requests."),
cfg.StrOpt('admin_password',
- default='pass',
+ default=None,
help="API key to use when authenticating as admin.",
secret=True),
]
@@ -246,9 +246,6 @@
default=False,
help="Does the test environment support changing the admin "
"password?"),
- cfg.BoolOpt('create_image',
- default=False,
- help="Does the test environment support snapshots?"),
cfg.BoolOpt('resize',
default=False,
help="Does the test environment support resizing?"),
@@ -276,14 +273,14 @@
ComputeAdminGroup = [
cfg.StrOpt('username',
- default='admin',
+ default=None,
help="Administrative Username to use for Nova API requests."),
cfg.StrOpt('tenant_name',
- default='admin',
+ default=None,
help="Administrative Tenant name to use for Nova API "
"requests."),
cfg.StrOpt('password',
- default='pass',
+ default=None,
help="API key to use when authenticating as admin.",
secret=True),
]
@@ -366,6 +363,14 @@
default="",
help="Id of the public router that provides external "
"connectivity"),
+ cfg.IntOpt('build_timeout',
+ default=300,
+ help="Timeout in seconds to wait for network operation to "
+ "complete."),
+ cfg.IntOpt('build_interval',
+ default=10,
+ help="Time in seconds between network operation status "
+ "checks."),
]
network_feature_group = cfg.OptGroup(name='network-feature-enabled',
@@ -621,6 +626,9 @@
cfg.StrOpt('aws_access',
default=None,
help="AWS Access Key"),
+ cfg.StrOpt('aws_zone',
+ default="nova",
+ help="AWS Zone for EC2 tests"),
cfg.StrOpt('s3_materials_path',
default="/opt/stack/devstack/files/images/"
"s3-materials/cirros-0.3.0",
@@ -753,9 +761,9 @@
cfg.BoolOpt('horizon',
default=True,
help="Whether or not Horizon is expected to be available"),
- cfg.BoolOpt('savanna',
+ cfg.BoolOpt('sahara',
default=False,
- help="Whether or not Savanna is expected to be available"),
+ help="Whether or not Sahara is expected to be available"),
cfg.BoolOpt('ironic',
default=False,
help="Whether or not Ironic is expected to be available"),
diff --git a/tempest/exceptions/__init__.py b/tempest/exceptions/__init__.py
index 06dee71..485f532 100644
--- a/tempest/exceptions/__init__.py
+++ b/tempest/exceptions/__init__.py
@@ -20,6 +20,10 @@
message = "Invalid Configuration"
+class InvalidCredentials(base.TempestException):
+ message = "Invalid Credentials"
+
+
class InvalidHttpSuccessCode(base.RestClientException):
message = "The success code is different than the expected one"
diff --git a/tempest/manager.py b/tempest/manager.py
index 708447e..63235db 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -75,13 +75,12 @@
tenant_name=CONF.identity.tenant_name
)
- def get_auth_provider(self, credentials=None):
- auth_params = dict(client_type=getattr(self, 'client_type', None),
- interface=getattr(self, 'interface', None))
+ def get_auth_provider(self, credentials):
+ if credentials is None:
+ raise exceptions.InvalidCredentials(
+ 'Credentials must be specified')
auth_provider_class = self.get_auth_provider_class(self.auth_version)
- # If invalid / incomplete credentials are provided, use default ones
- if credentials is None or \
- not auth_provider_class.check_credentials(credentials):
- credentials = self.credentials
- auth_params['credentials'] = credentials
- return auth_provider_class(**auth_params)
+ return auth_provider_class(
+ client_type=getattr(self, 'client_type', None),
+ interface=getattr(self, 'interface', None),
+ credentials=credentials)
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 39b7760..24d2677 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -97,7 +97,7 @@
except Exception:
LOG.exception('ssh to server failed')
self._log_console_output()
- debug.log_ip_ns()
+ debug.log_net_debug()
raise
def check_partitions(self):
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 489b271..d5ab3d3 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -172,7 +172,7 @@
except Exception:
LOG.exception('Tenant connectivity check failed')
self._log_console_output(servers=self.servers.keys())
- debug.log_ip_ns()
+ debug.log_net_debug()
raise
def _create_and_associate_floating_ips(self):
@@ -204,7 +204,7 @@
ex_msg += ": " + msg
LOG.exception(ex_msg)
self._log_console_output(servers=self.servers.keys())
- debug.log_ip_ns()
+ debug.log_net_debug()
raise
def _disassociate_floating_ips(self):
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index d404dd1..b9ee040 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -343,7 +343,7 @@
should_succeed),
msg)
except Exception:
- debug.log_ip_ns()
+ debug.log_net_debug()
raise
def _test_in_tenant_block(self, tenant):
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 37beb07..562020a 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -45,11 +45,10 @@
def _ssh_to_server(self, server_or_ip):
try:
- linux_client = self.get_remote_client(server_or_ip)
+ return self.get_remote_client(server_or_ip)
except Exception:
LOG.exception()
self._log_console_output()
- return linux_client.ssh_client
def _write_timestamp(self, server_or_ip):
ssh_client = self._ssh_to_server(server_or_ip)
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 841f9e1..128ec17 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -72,8 +72,7 @@
server.add_floating_ip(floating_ip)
def _ssh_to_server(self, server_or_ip):
- linux_client = self.get_remote_client(server_or_ip)
- return linux_client.ssh_client
+ return self.get_remote_client(server_or_ip)
def _create_volume_snapshot(self, volume):
snapshot_name = data_utils.rand_name('scenario-snapshot-')
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 9a250d7..e89ea70 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -53,7 +53,7 @@
'block_device_mapping': bd_map,
'key_name': keypair.name
}
- return self.create_server(create_kwargs=create_kwargs)
+ return self.create_server(image='', create_kwargs=create_kwargs)
def _create_snapshot_from_volume(self, vol_id):
volume_snapshots = self.volume_client.volume_snapshots
@@ -101,14 +101,13 @@
ip = server.networks[network_name_for_ssh][0]
try:
- client = self.get_remote_client(
+ return self.get_remote_client(
ip,
private_key=keypair.private_key)
except Exception:
LOG.exception('ssh to server failed')
self._log_console_output()
raise
- return client.ssh_client
def _get_content(self, ssh_client):
return ssh_client.exec_command('cat /tmp/text')
@@ -170,3 +169,15 @@
# deletion operations to succeed
self._stop_instances([instance_2nd, instance_from_snapshot])
self._detach_volumes([volume_origin, volume])
+
+
+class TestVolumeBootPatternV2(TestVolumeBootPattern):
+ def _boot_instance_from_volume(self, vol_id, keypair):
+ bdms = [{'uuid': vol_id, 'source_type': 'volume',
+ 'destination_type': 'volume', 'boot_index': 0,
+ 'delete_on_termination': False}]
+ create_kwargs = {
+ 'block_device_mapping_v2': bdms,
+ 'key_name': keypair.name
+ }
+ return self.create_server(image='', create_kwargs=create_kwargs)
diff --git a/tempest/services/botoclients.py b/tempest/services/botoclients.py
index b52d48c..7616a99 100644
--- a/tempest/services/botoclients.py
+++ b/tempest/services/botoclients.py
@@ -179,19 +179,6 @@
'revoke_security_group',
'revoke_security_group_egress'))
- def get_good_zone(self):
- """
- :rtype: BaseString
- :return: Returns with the first available zone name
- """
- for zone in self.get_all_zones():
- # NOTE(afazekas): zone.region_name was None
- if (zone.state == "available" and
- zone.region.name == self.connection_data["region"].name):
- return zone.name
- else:
- raise IndexError("Don't have a good zone")
-
class ObjectClientS3(BotoClientBase):
diff --git a/tempest/services/compute/json/quotas_client.py b/tempest/services/compute/json/quotas_client.py
index 459ab6d..2fae927 100644
--- a/tempest/services/compute/json/quotas_client.py
+++ b/tempest/services/compute/json/quotas_client.py
@@ -27,10 +27,12 @@
super(QuotasClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
- def get_quota_set(self, tenant_id):
+ def get_quota_set(self, tenant_id, user_id=None):
"""List the quota set for a tenant."""
url = 'os-quota-sets/%s' % str(tenant_id)
+ if user_id:
+ url += '?user_id=%s' % str(user_id)
resp, body = self.get(url)
body = json.loads(body)
return resp, body['quota_set']
@@ -100,3 +102,7 @@
body = json.loads(body)
return resp, body['quota_set']
+
+ def delete_quota_set(self, tenant_id):
+ """Delete the tenant's quota set."""
+ return self.delete('os-quota-sets/%s' % str(tenant_id))
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 70d075a..ca0f114 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -440,3 +440,8 @@
def inject_network_info(self, server_id, **kwargs):
"""Inject the Network Info into server"""
return self.action(server_id, 'injectNetworkInfo', None, **kwargs)
+
+ def get_vnc_console(self, server_id, console_type):
+ """Get URL of VNC console."""
+ return self.action(server_id, "os-getVNCConsole",
+ "console", type=console_type)
diff --git a/tempest/services/compute/json/services_client.py b/tempest/services/compute/json/services_client.py
index 1ab25ec..0f7d4cb 100644
--- a/tempest/services/compute/json/services_client.py
+++ b/tempest/services/compute/json/services_client.py
@@ -17,6 +17,7 @@
import json
import urllib
+from tempest.api_schema.compute import services as schema
from tempest.common import rest_client
from tempest import config
@@ -36,6 +37,7 @@
resp, body = self.get(url)
body = json.loads(body)
+ self.validate_response(schema.list_services, resp, body)
return resp, body['services']
def enable_service(self, host_name, binary):
diff --git a/tempest/services/compute/json/volumes_extensions_client.py b/tempest/services/compute/json/volumes_extensions_client.py
index 5ef11ed..451dbac 100644
--- a/tempest/services/compute/json/volumes_extensions_client.py
+++ b/tempest/services/compute/json/volumes_extensions_client.py
@@ -17,6 +17,7 @@
import time
import urllib
+from tempest.api_schema.compute.v2 import volumes as schema
from tempest.common import rest_client
from tempest import config
from tempest import exceptions
@@ -58,6 +59,7 @@
url = "os-volumes/%s" % str(volume_id)
resp, body = self.get(url)
body = json.loads(body)
+ self.validate_response(schema.get_volume, resp, body)
return resp, body['volume']
def create_volume(self, size, **kwargs):
diff --git a/tempest/services/compute/v3/json/agents_client.py b/tempest/services/compute/v3/json/agents_client.py
new file mode 100644
index 0000000..6893af2
--- /dev/null
+++ b/tempest/services/compute/v3/json/agents_client.py
@@ -0,0 +1,52 @@
+# Copyright 2014 NEC Corporation. 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 json
+import urllib
+
+from tempest.common import rest_client
+from tempest import config
+
+CONF = config.CONF
+
+
+class AgentsV3ClientJSON(rest_client.RestClient):
+
+ def __init__(self, auth_provider):
+ super(AgentsV3ClientJSON, self).__init__(auth_provider)
+ self.service = CONF.compute.catalog_v3_type
+
+ def list_agents(self, params=None):
+ """List all agent builds."""
+ url = 'os-agents'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
+ return resp, self._parse_resp(body)
+
+ def create_agent(self, **kwargs):
+ """Create an agent build."""
+ post_body = json.dumps({'agent': kwargs})
+ resp, body = self.post('os-agents', post_body)
+ return resp, self._parse_resp(body)
+
+ def delete_agent(self, agent_id):
+ """Delete an existing agent build."""
+ return self.delete('os-agents/%s' % str(agent_id))
+
+ def update_agent(self, agent_id, **kwargs):
+ """Update an agent build."""
+ put_body = json.dumps({'agent': kwargs})
+ resp, body = self.put('os-agents/%s' % str(agent_id), put_body)
+ return resp, self._parse_resp(body)
diff --git a/tempest/services/compute/v3/json/quotas_client.py b/tempest/services/compute/v3/json/quotas_client.py
index 32e31a3..ed92aae 100644
--- a/tempest/services/compute/v3/json/quotas_client.py
+++ b/tempest/services/compute/v3/json/quotas_client.py
@@ -27,10 +27,12 @@
super(QuotasV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
- def get_quota_set(self, tenant_id):
+ def get_quota_set(self, tenant_id, user_id=None):
"""List the quota set for a tenant."""
url = 'os-quota-sets/%s' % str(tenant_id)
+ if user_id:
+ url += '?user_id=%s' % str(user_id)
resp, body = self.get(url)
body = json.loads(body)
return resp, body['quota_set']
@@ -96,3 +98,7 @@
body = json.loads(body)
return resp, body['quota_set']
+
+ def delete_quota_set(self, tenant_id):
+ """Delete the tenant's quota set."""
+ return self.delete('os-quota-sets/%s' % str(tenant_id))
diff --git a/tempest/services/compute/v3/json/services_client.py b/tempest/services/compute/v3/json/services_client.py
index b4e65a0..88c4d16 100644
--- a/tempest/services/compute/v3/json/services_client.py
+++ b/tempest/services/compute/v3/json/services_client.py
@@ -17,6 +17,7 @@
import json
import urllib
+from tempest.api_schema.compute import services as schema
from tempest.common import rest_client
from tempest import config
@@ -36,6 +37,7 @@
resp, body = self.get(url)
body = json.loads(body)
+ self.validate_response(schema.list_services, resp, body)
return resp, body['services']
def enable_service(self, host_name, binary):
diff --git a/tempest/services/compute/xml/common.py b/tempest/services/compute/xml/common.py
index b29b932..b1bf789 100644
--- a/tempest/services/compute/xml/common.py
+++ b/tempest/services/compute/xml/common.py
@@ -19,6 +19,7 @@
XMLNS_V3 = "http://docs.openstack.org/compute/api/v1.1"
NEUTRON_NAMESPACES = {
+ 'binding': "http://docs.openstack.org/ext/binding/api/v1.0",
'router': "http://docs.openstack.org/ext/neutron/router/api/v1.0",
'provider': 'http://docs.openstack.org/ext/provider/api/v1.0',
}
diff --git a/tempest/services/compute/xml/quotas_client.py b/tempest/services/compute/xml/quotas_client.py
index b8b759f..911c476 100644
--- a/tempest/services/compute/xml/quotas_client.py
+++ b/tempest/services/compute/xml/quotas_client.py
@@ -44,10 +44,12 @@
return quota
- def get_quota_set(self, tenant_id):
+ def get_quota_set(self, tenant_id, user_id=None):
"""List the quota set for a tenant."""
url = 'os-quota-sets/%s' % str(tenant_id)
+ if user_id:
+ url += '?user_id=%s' % str(user_id)
resp, body = self.get(url)
body = xml_to_json(etree.fromstring(body))
body = self._format_quota(body)
@@ -121,3 +123,7 @@
body = xml_to_json(etree.fromstring(body))
body = self._format_quota(body)
return resp, body
+
+ def delete_quota_set(self, tenant_id):
+ """Delete the tenant's quota set."""
+ return self.delete('os-quota-sets/%s' % str(tenant_id))
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 4d3646c..1215b80 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -645,3 +645,8 @@
def inject_network_info(self, server_id, **kwargs):
"""Inject the Network Info into server"""
return self.action(server_id, 'injectNetworkInfo', None, **kwargs)
+
+ def get_vnc_console(self, server_id, console_type):
+ """Get URL of VNC console."""
+ return self.action(server_id, "os-getVNCConsole",
+ "console", type=console_type)
diff --git a/tempest/services/database/json/flavors_client.py b/tempest/services/database/json/flavors_client.py
index 1a8a4c1..2ec0405 100644
--- a/tempest/services/database/json/flavors_client.py
+++ b/tempest/services/database/json/flavors_client.py
@@ -13,9 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
+import urllib
+
from tempest.common import rest_client
from tempest import config
-import urllib
CONF = config.CONF
diff --git a/tempest/services/identity/json/identity_client.py b/tempest/services/identity/json/identity_client.py
index 9a31540..99b4036 100644
--- a/tempest/services/identity/json/identity_client.py
+++ b/tempest/services/identity/json/identity_client.py
@@ -134,9 +134,10 @@
post_body = {
'name': name,
'password': password,
- 'tenantId': tenant_id,
'email': email
}
+ if tenant_id is not None:
+ post_body['tenantId'] = tenant_id
if kwargs.get('enabled') is not None:
post_body['enabled'] = kwargs.get('enabled')
post_body = json.dumps({'user': post_body})
@@ -233,16 +234,36 @@
self.auth_url = auth_url
- def auth(self, user, password, tenant):
+ def auth(self, user, password, tenant=None):
creds = {
'auth': {
'passwordCredentials': {
'username': user,
'password': password,
},
- 'tenantName': tenant,
}
}
+
+ if tenant:
+ creds['auth']['tenantName'] = tenant
+
+ body = json.dumps(creds)
+ resp, body = self.post(self.auth_url, body=body)
+
+ return resp, body['access']
+
+ def auth_token(self, token_id, tenant=None):
+ creds = {
+ 'auth': {
+ 'token': {
+ 'id': token_id,
+ },
+ }
+ }
+
+ if tenant:
+ creds['auth']['tenantName'] = tenant
+
body = json.dumps(creds)
resp, body = self.post(self.auth_url, body=body)
diff --git a/tempest/services/identity/v3/json/endpoints_client.py b/tempest/services/identity/v3/json/endpoints_client.py
index c3c1e15..f7a894b 100644
--- a/tempest/services/identity/v3/json/endpoints_client.py
+++ b/tempest/services/identity/v3/json/endpoints_client.py
@@ -36,9 +36,17 @@
return resp, body['endpoints']
def create_endpoint(self, service_id, interface, url, **kwargs):
- """Create endpoint."""
+ """Create endpoint.
+
+ Normally this function wouldn't allow setting values that are not
+ allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
+
+ """
region = kwargs.get('region', None)
- enabled = kwargs.get('enabled', None)
+ if 'force_enabled' in kwargs:
+ enabled = kwargs.get('force_enabled', None)
+ else:
+ enabled = kwargs.get('enabled', None)
post_body = {
'service_id': service_id,
'interface': interface,
@@ -52,8 +60,13 @@
return resp, body['endpoint']
def update_endpoint(self, endpoint_id, service_id=None, interface=None,
- url=None, region=None, enabled=None):
- """Updates an endpoint with given parameters."""
+ url=None, region=None, enabled=None, **kwargs):
+ """Updates an endpoint with given parameters.
+
+ Normally this function wouldn't allow setting values that are not
+ allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
+
+ """
post_body = {}
if service_id is not None:
post_body['service_id'] = service_id
@@ -63,7 +76,9 @@
post_body['url'] = url
if region is not None:
post_body['region'] = region
- if enabled is not None:
+ if 'force_enabled' in kwargs:
+ post_body['enabled'] = kwargs['force_enabled']
+ elif enabled is not None:
post_body['enabled'] = enabled
post_body = json.dumps({'endpoint': post_body})
resp, body = self.patch('endpoints/%s' % endpoint_id, post_body)
diff --git a/tempest/services/identity/v3/xml/endpoints_client.py b/tempest/services/identity/v3/xml/endpoints_client.py
index cc9aa65..a1f9811 100644
--- a/tempest/services/identity/v3/xml/endpoints_client.py
+++ b/tempest/services/identity/v3/xml/endpoints_client.py
@@ -62,11 +62,19 @@
return resp, body
def create_endpoint(self, service_id, interface, url, **kwargs):
- """Create endpoint."""
+ """Create endpoint.
+
+ Normally this function wouldn't allow setting values that are not
+ allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
+
+ """
region = kwargs.get('region', None)
- enabled = kwargs.get('enabled', None)
- if enabled is not None:
- enabled = str(enabled).lower()
+ if 'force_enabled' in kwargs:
+ enabled = kwargs['force_enabled']
+ else:
+ enabled = kwargs.get('enabled', None)
+ if enabled is not None:
+ enabled = str(enabled).lower()
create_endpoint = common.Element("endpoint",
xmlns=XMLNS,
service_id=service_id,
@@ -79,8 +87,13 @@
return resp, body
def update_endpoint(self, endpoint_id, service_id=None, interface=None,
- url=None, region=None, enabled=None):
- """Updates an endpoint with given parameters."""
+ url=None, region=None, enabled=None, **kwargs):
+ """Updates an endpoint with given parameters.
+
+ Normally this function wouldn't allow setting values that are not
+ allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
+
+ """
doc = common.Document()
endpoint = common.Element("endpoint")
doc.append(endpoint)
@@ -93,8 +106,12 @@
endpoint.add_attr("url", url)
if region:
endpoint.add_attr("region", region)
- if enabled is not None:
+
+ if 'force_enabled' in kwargs:
+ endpoint.add_attr("enabled", kwargs['force_enabled'])
+ elif enabled is not None:
endpoint.add_attr("enabled", str(enabled).lower())
+
resp, body = self.patch('endpoints/%s' % str(endpoint_id), str(doc))
body = self._parse_body(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/identity/xml/identity_client.py b/tempest/services/identity/xml/identity_client.py
index 50403fb..c5bf310 100644
--- a/tempest/services/identity/xml/identity_client.py
+++ b/tempest/services/identity/xml/identity_client.py
@@ -75,8 +75,9 @@
xmlns=XMLNS,
name=name,
password=password,
- tenantId=tenant_id,
email=email)
+ if tenant_id:
+ create_user.add_attr('tenantId', tenant_id)
if 'enabled' in kwargs:
create_user.add_attr('enabled', str(kwargs['enabled']).lower())
@@ -116,11 +117,24 @@
class TokenClientXML(identity_client.TokenClientJSON):
TYPE = "xml"
- def auth(self, user, password, tenant):
- passwordCreds = xml.Element("passwordCredentials",
+ def auth(self, user, password, tenant=None):
+ passwordCreds = xml.Element('passwordCredentials',
username=user,
password=password)
- auth = xml.Element("auth", tenantName=tenant)
+ auth_kwargs = {}
+ if tenant:
+ auth_kwargs['tenantName'] = tenant
+ auth = xml.Element('auth', **auth_kwargs)
auth.append(passwordCreds)
resp, body = self.post(self.auth_url, body=str(xml.Document(auth)))
return resp, body['access']
+
+ def auth_token(self, token_id, tenant=None):
+ tokenCreds = xml.Element('token', id=token_id)
+ auth_kwargs = {}
+ if tenant:
+ auth_kwargs['tenantName'] = tenant
+ auth = xml.Element('auth', **auth_kwargs)
+ auth.append(tokenCreds)
+ resp, body = self.post(self.auth_url, body=str(xml.Document(auth)))
+ return resp, body['access']
diff --git a/tempest/services/network/network_client_base.py b/tempest/services/network/network_client_base.py
index f1bf548..41a7aa4 100644
--- a/tempest/services/network/network_client_base.py
+++ b/tempest/services/network/network_client_base.py
@@ -10,9 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
+import time
import urllib
from tempest import config
+from tempest import exceptions
CONF = config.CONF
@@ -54,6 +56,8 @@
self.rest_client.service = CONF.network.catalog_type
self.version = '2.0'
self.uri_prefix = "v%s" % (self.version)
+ self.build_timeout = CONF.network.build_timeout
+ self.build_interval = CONF.network.build_interval
def get_rest_client(self, auth_provider):
raise NotImplementedError
@@ -189,3 +193,23 @@
resp, body = self.post(uri, body)
body = {'ports': self.deserialize_list(body)}
return resp, body
+
+ def wait_for_resource_deletion(self, resource_type, id):
+ """Waits for a resource to be deleted."""
+ start_time = int(time.time())
+ while True:
+ if self.is_resource_deleted(resource_type, id):
+ return
+ if int(time.time()) - start_time >= self.build_timeout:
+ raise exceptions.TimeoutException
+ time.sleep(self.build_interval)
+
+ def is_resource_deleted(self, resource_type, id):
+ method = 'show_' + resource_type
+ try:
+ getattr(self, method)(id)
+ except AttributeError:
+ raise Exception("Unknown resource type %s " % resource_type)
+ except exceptions.NotFound:
+ return True
+ return False
diff --git a/tempest/services/volume/json/admin/volume_types_client.py b/tempest/services/volume/json/admin/volume_types_client.py
index 5554362..c9c0582 100644
--- a/tempest/services/volume/json/admin/volume_types_client.py
+++ b/tempest/services/volume/json/admin/volume_types_client.py
@@ -122,3 +122,31 @@
resp, body = self.put(url, put_body)
body = json.loads(body)
return resp, body
+
+ def get_encryption_type(self, vol_type_id):
+ """
+ Get the volume encryption type for the specified volume type.
+ vol_type_id: Id of volume_type.
+ """
+ url = "/types/%s/encryption" % str(vol_type_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body
+
+ def create_encryption_type(self, vol_type_id, **kwargs):
+ """
+ Create a new encryption type for the specified volume type.
+
+ vol_type_id: Id of volume_type.
+ provider: Class providing encryption support.
+ cipher: Encryption algorithm/mode to use.
+ key_size: Size of the encryption key, in bits.
+ control_location: Notional service where encryption is performed.
+ """
+ url = "/types/%s/encryption" % str(vol_type_id)
+ post_body = {}
+ post_body.update(kwargs)
+ post_body = json.dumps({'encryption': post_body})
+ resp, body = self.post(url, post_body)
+ body = json.loads(body)
+ return resp, body['encryption']
diff --git a/tempest/stress/actions/ssh_floating.py b/tempest/stress/actions/ssh_floating.py
index a34a20d..c330165 100644
--- a/tempest/stress/actions/ssh_floating.py
+++ b/tempest/stress/actions/ssh_floating.py
@@ -69,7 +69,7 @@
servers_client = self.manager.servers_client
self.logger.info("creating %s" % name)
vm_args = self.vm_extra_args.copy()
- vm_args['security_groups'] = [{'name': self.sec_grp}]
+ vm_args['security_groups'] = [self.sec_grp]
resp, server = servers_client.create_server(name, self.image,
self.flavor,
**vm_args)
@@ -90,16 +90,15 @@
sec_grp_cli = self.manager.security_groups_client
s_name = data_utils.rand_name('sec_grp-')
s_description = data_utils.rand_name('desc-')
- _, _sec_grp = sec_grp_cli.create_security_group(s_name,
- s_description)
- self.sec_grp = _sec_grp['id']
+ _, self.sec_grp = sec_grp_cli.create_security_group(s_name,
+ s_description)
create_rule = sec_grp_cli.create_security_group_rule
- create_rule(self.sec_grp, 'tcp', 22, 22)
- create_rule(self.sec_grp, 'icmp', -1, -1)
+ create_rule(self.sec_grp['id'], 'tcp', 22, 22)
+ create_rule(self.sec_grp['id'], 'icmp', -1, -1)
def _destroy_sec_grp(self):
sec_grp_cli = self.manager.security_groups_client
- sec_grp_cli.delete_security_group(self.sec_grp)
+ sec_grp_cli.delete_security_group(self.sec_grp['id'])
def _create_floating_ip(self):
floating_cli = self.manager.floating_ips_client
diff --git a/tempest/stress/cleanup.py b/tempest/stress/cleanup.py
index b46de35..2587331 100644
--- a/tempest/stress/cleanup.py
+++ b/tempest/stress/cleanup.py
@@ -45,6 +45,16 @@
except Exception:
pass
+ secgrp_client = admin_manager.security_groups_client
+ _, secgrp = secgrp_client.list_security_groups({"all_tenants": True})
+ secgrp_del = [grp for grp in secgrp if grp['name'] != 'default']
+ LOG.info("Cleanup::remove %s Security Group" % len(secgrp_del))
+ for g in secgrp_del:
+ try:
+ secgrp_client.delete_security_group(g['id'])
+ except Exception:
+ pass
+
_, floating_ips = admin_manager.floating_ips_client.list_floating_ips()
LOG.info("Cleanup::remove %s floating ips" % len(floating_ips))
for f in floating_ips:
diff --git a/tempest/test.py b/tempest/test.py
index d358510..75eb6be 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -26,6 +26,8 @@
import testresources
import testtools
+from oslo.config import cfg
+
from tempest import clients
import tempest.common.generator.valid_generator as valid
from tempest.common import isolated_creds
@@ -64,6 +66,25 @@
return decorator
+def safe_setup(f):
+ """A decorator used to wrap the setUpClass for cleaning up resources
+ when setUpClass failed.
+ """
+
+ def decorator(cls):
+ try:
+ f(cls)
+ except Exception as se:
+ LOG.exception("setUpClass failed: %s" % se)
+ try:
+ cls.tearDownClass()
+ except Exception as te:
+ LOG.exception("tearDownClass failed: %s" % te)
+ raise se
+
+ return decorator
+
+
def services(*args, **kwargs):
"""A decorator used to set an attr for each service used in a test case
@@ -199,13 +220,11 @@
def validate_tearDownClass():
if at_exit_set:
- raise RuntimeError("tearDownClass does not call the super's "
- "tearDownClass in these classes: "
- + str(at_exit_set) + "\n"
- "If you see the exception, with another "
- "exception please do not report this one! "
- "If you are changing tempest code, make sure you "
- "are calling the super class's tearDownClass!")
+ LOG.error(
+ "tearDownClass does not call the super's "
+ "tearDownClass in these classes: \n"
+ + str(at_exit_set))
+
atexit.register(validate_tearDownClass)
@@ -410,7 +429,17 @@
"""
description = NegativeAutoTest.load_schema(description_file)
LOG.debug(description)
- generator = importutils.import_class(CONF.negative.test_generator)()
+
+ # NOTE(mkoderer): since this will be executed on import level the
+ # config doesn't have to be in place (e.g. for the pep8 job).
+ # In this case simply return.
+ try:
+ generator = importutils.import_class(
+ CONF.negative.test_generator)()
+ except cfg.ConfigFilesNotFoundError:
+ LOG.critical(
+ "Tempest config not found. Test scenarios aren't created")
+ return
generator.validate_schema(description)
schema = description.get("json-schema", None)
resources = description.get("resources", [])
diff --git a/tempest/api/compute/api_schema/__init__.py b/tempest/tests/common/__init__.py
similarity index 100%
copy from tempest/api/compute/api_schema/__init__.py
copy to tempest/tests/common/__init__.py
diff --git a/tempest/api/compute/api_schema/__init__.py b/tempest/tests/common/utils/__init__.py
similarity index 100%
copy from tempest/api/compute/api_schema/__init__.py
copy to tempest/tests/common/utils/__init__.py
diff --git a/tempest/tests/common/utils/test_data_utils.py b/tempest/tests/common/utils/test_data_utils.py
new file mode 100644
index 0000000..7aafdb2
--- /dev/null
+++ b/tempest/tests/common/utils/test_data_utils.py
@@ -0,0 +1,77 @@
+# Copyright 2014 NEC Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from tempest.common.utils import data_utils
+from tempest.tests import base
+
+
+class TestDataUtils(base.TestCase):
+
+ def test_rand_uuid(self):
+ actual = data_utils.rand_uuid()
+ self.assertIsInstance(actual, str)
+ self.assertRegexpMatches(actual, "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]"
+ "{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
+ actual2 = data_utils.rand_uuid()
+ self.assertNotEqual(actual, actual2)
+
+ def test_rand_uuid_hex(self):
+ actual = data_utils.rand_uuid_hex()
+ self.assertIsInstance(actual, str)
+ self.assertRegexpMatches(actual, "^[0-9a-f]{32}$")
+
+ actual2 = data_utils.rand_uuid_hex()
+ self.assertNotEqual(actual, actual2)
+
+ def test_rand_name(self):
+ actual = data_utils.rand_name()
+ self.assertIsInstance(actual, str)
+ actual2 = data_utils.rand_name()
+ self.assertNotEqual(actual, actual2)
+
+ actual = data_utils.rand_name('foo')
+ self.assertTrue(actual.startswith('foo'))
+ actual2 = data_utils.rand_name('foo')
+ self.assertTrue(actual.startswith('foo'))
+ self.assertNotEqual(actual, actual2)
+
+ def test_rand_int(self):
+ actual = data_utils.rand_int_id()
+ self.assertIsInstance(actual, int)
+
+ actual2 = data_utils.rand_int_id()
+ self.assertNotEqual(actual, actual2)
+
+ def test_rand_mac_address(self):
+ actual = data_utils.rand_mac_address()
+ self.assertIsInstance(actual, str)
+ self.assertRegexpMatches(actual, "^([0-9a-f][0-9a-f]:){5}"
+ "[0-9a-f][0-9a-f]$")
+
+ actual2 = data_utils.rand_mac_address()
+ self.assertNotEqual(actual, actual2)
+
+ def test_parse_image_id(self):
+ actual = data_utils.parse_image_id("/foo/bar/deadbeaf")
+ self.assertEqual("deadbeaf", actual)
+
+ def test_arbitrary_string(self):
+ actual = data_utils.arbitrary_string()
+ self.assertEqual(actual, "test")
+ actual = data_utils.arbitrary_string(size=30, base_text="abc")
+ self.assertEqual(actual, "abc" * (30 / len("abc")))
+ actual = data_utils.arbitrary_string(size=5, base_text="deadbeaf")
+ self.assertEqual(actual, "deadb")
diff --git a/tempest/tests/common/utils/test_misc.py b/tempest/tests/common/utils/test_misc.py
new file mode 100644
index 0000000..b8c6184
--- /dev/null
+++ b/tempest/tests/common/utils/test_misc.py
@@ -0,0 +1,52 @@
+# Copyright 2014 NEC Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from tempest.common.utils import misc
+from tempest.tests import base
+
+
+@misc.singleton
+class TestFoo(object):
+
+ count = 0
+
+ def increment(self):
+ self.count += 1
+ return self.count
+
+
+@misc.singleton
+class TestBar(object):
+
+ count = 0
+
+ def increment(self):
+ self.count += 1
+ return self.count
+
+
+class TestMisc(base.TestCase):
+
+ def test_singleton(self):
+ test = TestFoo()
+ self.assertEqual(0, test.count)
+ self.assertEqual(1, test.increment())
+ test2 = TestFoo()
+ self.assertEqual(1, test.count)
+ self.assertEqual(1, test2.count)
+ self.assertEqual(test, test2)
+ test3 = TestBar()
+ self.assertNotEqual(test, test3)
diff --git a/tempest/tests/test_tenant_isolation.py b/tempest/tests/test_tenant_isolation.py
new file mode 100644
index 0000000..2e50cfd
--- /dev/null
+++ b/tempest/tests/test_tenant_isolation.py
@@ -0,0 +1,336 @@
+# Copyright 2014 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import keystoneclient.v2_0.client as keystoneclient
+from mock import patch
+import neutronclient.v2_0.client as neutronclient
+from oslo.config import cfg
+
+from tempest.common import isolated_creds
+from tempest import config
+from tempest.openstack.common.fixture import mockpatch
+from tempest.services.identity.json import identity_client as json_iden_client
+from tempest.services.identity.xml import identity_client as xml_iden_client
+from tempest.services.network.json import network_client as json_network_client
+from tempest.services.network.xml import network_client as xml_network_client
+from tempest.tests import base
+from tempest.tests import fake_config
+
+
+class TestTenantIsolation(base.TestCase):
+
+ def setUp(self):
+ super(TestTenantIsolation, self).setUp()
+ self.useFixture(fake_config.ConfigFixture())
+ self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+
+ def test_tempest_client(self):
+ iso_creds = isolated_creds.IsolatedCreds('test class')
+ self.assertTrue(isinstance(iso_creds.identity_admin_client,
+ json_iden_client.IdentityClientJSON))
+ self.assertTrue(isinstance(iso_creds.network_admin_client,
+ json_network_client.NetworkClientJSON))
+
+ def test_official_client(self):
+ self.useFixture(mockpatch.PatchObject(keystoneclient.Client,
+ 'authenticate'))
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ tempest_client=False)
+ self.assertTrue(isinstance(iso_creds.identity_admin_client,
+ keystoneclient.Client))
+ self.assertTrue(isinstance(iso_creds.network_admin_client,
+ neutronclient.Client))
+
+ def test_tempest_client_xml(self):
+ iso_creds = isolated_creds.IsolatedCreds('test class', interface='xml')
+ self.assertEqual(iso_creds.interface, 'xml')
+ self.assertTrue(isinstance(iso_creds.identity_admin_client,
+ xml_iden_client.IdentityClientXML))
+ self.assertTrue(isinstance(iso_creds.network_admin_client,
+ xml_network_client.NetworkClientXML))
+
+ def _mock_user_create(self, id, name):
+ user_fix = self.useFixture(mockpatch.PatchObject(
+ json_iden_client.IdentityClientJSON,
+ 'create_user',
+ return_value=({'status': 200},
+ {'id': id, 'name': name})))
+ return user_fix
+
+ def _mock_tenant_create(self, id, name):
+ tenant_fix = self.useFixture(mockpatch.PatchObject(
+ json_iden_client.IdentityClientJSON,
+ 'create_tenant',
+ return_value=({'status': 200},
+ {'id': id, 'name': name})))
+ return tenant_fix
+
+ def _mock_network_create(self, iso_creds, id, name):
+ net_fix = self.useFixture(mockpatch.PatchObject(
+ iso_creds.network_admin_client,
+ 'create_network',
+ return_value=({'status': 200},
+ {'network': {'id': id, 'name': name}})))
+ return net_fix
+
+ def _mock_subnet_create(self, iso_creds, id, name):
+ subnet_fix = self.useFixture(mockpatch.PatchObject(
+ iso_creds.network_admin_client,
+ 'create_subnet',
+ return_value=({'status': 200},
+ {'subnet': {'id': id, 'name': name}})))
+ return subnet_fix
+
+ def _mock_router_create(self, id, name):
+ router_fix = self.useFixture(mockpatch.PatchObject(
+ json_network_client.NetworkClientJSON,
+ 'create_router',
+ return_value=({'status': 200},
+ {'router': {'id': id, 'name': name}})))
+ return router_fix
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_primary_creds(self, MockRestClient):
+ cfg.CONF.set_default('neutron', False, 'service_available')
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ password='fake_password')
+ self._mock_tenant_create('1234', 'fake_prim_tenant')
+ self._mock_user_create('1234', 'fake_prim_user')
+ username, tenant_name, password = iso_creds.get_primary_creds()
+ self.assertEqual(username, 'fake_prim_user')
+ self.assertEqual(tenant_name, 'fake_prim_tenant')
+ # Verify helper methods
+ tenant = iso_creds.get_primary_tenant()
+ user = iso_creds.get_primary_user()
+ self.assertEqual(tenant['id'], '1234')
+ self.assertEqual(user['id'], '1234')
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_admin_creds(self, MockRestClient):
+ cfg.CONF.set_default('neutron', False, 'service_available')
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ password='fake_password')
+ self._mock_user_create('1234', 'fake_admin_user')
+ self._mock_tenant_create('1234', 'fake_admin_tenant')
+ self.useFixture(mockpatch.PatchObject(
+ json_iden_client.IdentityClientJSON,
+ 'list_roles',
+ return_value=({'status': 200},
+ [{'id': '1234', 'name': 'admin'}])))
+
+ user_mock = patch.object(json_iden_client.IdentityClientJSON,
+ 'assign_user_role')
+ user_mock.start()
+ self.addCleanup(user_mock.stop)
+ with patch.object(json_iden_client.IdentityClientJSON,
+ 'assign_user_role') as user_mock:
+ username, tenant_name, password = iso_creds.get_admin_creds()
+ user_mock.assert_called_once_with('1234', '1234', '1234')
+ self.assertEqual(username, 'fake_admin_user')
+ self.assertEqual(tenant_name, 'fake_admin_tenant')
+ # Verify helper methods
+ tenant = iso_creds.get_admin_tenant()
+ user = iso_creds.get_admin_user()
+ self.assertEqual(tenant['id'], '1234')
+ self.assertEqual(user['id'], '1234')
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_all_cred_cleanup(self, MockRestClient):
+ cfg.CONF.set_default('neutron', False, 'service_available')
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ password='fake_password')
+ tenant_fix = self._mock_tenant_create('1234', 'fake_prim_tenant')
+ user_fix = self._mock_user_create('1234', 'fake_prim_user')
+ username, tenant_name, password = iso_creds.get_primary_creds()
+ tenant_fix.cleanUp()
+ user_fix.cleanUp()
+ tenant_fix = self._mock_tenant_create('12345', 'fake_alt_tenant')
+ user_fix = self._mock_user_create('12345', 'fake_alt_user')
+ alt_username, alt_tenant, alt_password = iso_creds.get_alt_creds()
+ tenant_fix.cleanUp()
+ user_fix.cleanUp()
+ tenant_fix = self._mock_tenant_create('123456', 'fake_admin_tenant')
+ user_fix = self._mock_user_create('123456', 'fake_admin_user')
+ self.useFixture(mockpatch.PatchObject(
+ json_iden_client.IdentityClientJSON,
+ 'list_roles',
+ return_value=({'status': 200},
+ [{'id': '123456', 'name': 'admin'}])))
+ with patch.object(json_iden_client.IdentityClientJSON,
+ 'assign_user_role'):
+ admin_username, admin_tenant, admin_pass = \
+ iso_creds.get_admin_creds()
+ user_mock = self.patch(
+ 'tempest.services.identity.json.identity_client.'
+ 'IdentityClientJSON.delete_user')
+ tenant_mock = self.patch(
+ 'tempest.services.identity.json.identity_client.'
+ 'IdentityClientJSON.delete_tenant')
+ iso_creds.clear_isolated_creds()
+ # Verify user delete calls
+ calls = user_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1][0], calls)
+ self.assertIn('1234', args)
+ self.assertIn('12345', args)
+ self.assertIn('123456', args)
+ # Verify tenant delete calls
+ calls = tenant_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1][0], calls)
+ self.assertIn('1234', args)
+ self.assertIn('12345', args)
+ self.assertIn('123456', args)
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_alt_creds(self, MockRestClient):
+ cfg.CONF.set_default('neutron', False, 'service_available')
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ password='fake_password')
+ self._mock_user_create('1234', 'fake_alt_user')
+ self._mock_tenant_create('1234', 'fake_alt_tenant')
+ username, tenant_name, password = iso_creds.get_alt_creds()
+ self.assertEqual(username, 'fake_alt_user')
+ self.assertEqual(tenant_name, 'fake_alt_tenant')
+ # Verify helper methods
+ tenant = iso_creds.get_alt_tenant()
+ user = iso_creds.get_alt_user()
+ self.assertEqual(tenant['id'], '1234')
+ self.assertEqual(user['id'], '1234')
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_network_creation(self, MockRestClient):
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ password='fake_password')
+ self._mock_user_create('1234', 'fake_prim_user')
+ self._mock_tenant_create('1234', 'fake_prim_tenant')
+ self._mock_network_create(iso_creds, '1234', 'fake_net')
+ self._mock_subnet_create(iso_creds, '1234', 'fake_subnet')
+ self._mock_router_create('1234', 'fake_router')
+ router_interface_mock = self.patch(
+ 'tempest.services.network.json.network_client.NetworkClientJSON.'
+ 'add_router_interface_with_subnet_id')
+ username, tenant_name, password = iso_creds.get_primary_creds()
+ router_interface_mock.called_once_with('1234', '1234')
+ network = iso_creds.get_primary_network()
+ subnet = iso_creds.get_primary_subnet()
+ router = iso_creds.get_primary_router()
+ self.assertEqual(network['id'], '1234')
+ self.assertEqual(network['name'], 'fake_net')
+ self.assertEqual(subnet['id'], '1234')
+ self.assertEqual(subnet['name'], 'fake_subnet')
+ self.assertEqual(router['id'], '1234')
+ self.assertEqual(router['name'], 'fake_router')
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_network_cleanup(self, MockRestClient):
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ password='fake_password')
+ # Create primary tenant and network
+ user_fix = self._mock_user_create('1234', 'fake_prim_user')
+ tenant_fix = self._mock_tenant_create('1234', 'fake_prim_tenant')
+ net_fix = self._mock_network_create(iso_creds, '1234', 'fake_net')
+ subnet_fix = self._mock_subnet_create(iso_creds, '1234', 'fake_subnet')
+ router_fix = self._mock_router_create('1234', 'fake_router')
+ router_interface_mock = self.patch(
+ 'tempest.services.network.json.network_client.NetworkClientJSON.'
+ 'add_router_interface_with_subnet_id')
+ username, tenant_name, password = iso_creds.get_primary_creds()
+ router_interface_mock.called_once_with('1234', '1234')
+ router_interface_mock.reset_mock()
+ tenant_fix.cleanUp()
+ user_fix.cleanUp()
+ net_fix.cleanUp()
+ subnet_fix.cleanUp()
+ router_fix.cleanUp()
+ # Create alternate tenant and network
+ user_fix = self._mock_user_create('12345', 'fake_alt_user')
+ tenant_fix = self._mock_tenant_create('12345', 'fake_alt_tenant')
+ net_fix = self._mock_network_create(iso_creds, '12345', 'fake_alt_net')
+ subnet_fix = self._mock_subnet_create(iso_creds, '12345',
+ 'fake_alt_subnet')
+ router_fix = self._mock_router_create('12345', 'fake_alt_router')
+ alt_username, alt_tenant_name, password = iso_creds.get_alt_creds()
+ router_interface_mock.called_once_with('12345', '12345')
+ router_interface_mock.reset_mock()
+ tenant_fix.cleanUp()
+ user_fix.cleanUp()
+ net_fix.cleanUp()
+ subnet_fix.cleanUp()
+ router_fix.cleanUp()
+ # Create admin tenant and networks
+ user_fix = self._mock_user_create('123456', 'fake_admin_user')
+ tenant_fix = self._mock_tenant_create('123456', 'fake_admin_tenant')
+ net_fix = self._mock_network_create(iso_creds, '123456',
+ 'fake_admin_net')
+ subnet_fix = self._mock_subnet_create(iso_creds, '123456',
+ 'fake_admin_subnet')
+ router_fix = self._mock_router_create('123456', 'fake_admin_router')
+ self.useFixture(mockpatch.PatchObject(
+ json_iden_client.IdentityClientJSON,
+ 'list_roles',
+ return_value=({'status': 200},
+ [{'id': '123456', 'name': 'admin'}])))
+ with patch.object(json_iden_client.IdentityClientJSON,
+ 'assign_user_role'):
+ admin_user, admin_tenant, password = iso_creds.get_admin_creds()
+ self.patch('tempest.services.identity.json.identity_client.'
+ 'IdentityClientJSON.delete_user')
+ self.patch('tempest.services.identity.json.identity_client.'
+ 'IdentityClientJSON.delete_tenant')
+ net = patch.object(iso_creds.network_admin_client,
+ 'delete_network')
+ net_mock = net.start()
+ subnet = patch.object(iso_creds.network_admin_client,
+ 'delete_subnet')
+ subnet_mock = subnet.start()
+ router = patch.object(iso_creds.network_admin_client,
+ 'delete_router')
+ router_mock = router.start()
+ remove_router_interface_mock = self.patch(
+ 'tempest.services.network.json.network_client.NetworkClientJSON.'
+ 'remove_router_interface_with_subnet_id')
+ port_list_mock = patch.object(iso_creds.network_admin_client,
+ 'list_ports', return_value=(
+ {'status': 200}, {'ports': []}))
+ port_list_mock.start()
+ iso_creds.clear_isolated_creds()
+ # Verify remove router interface calls
+ calls = remove_router_interface_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1], calls)
+ self.assertIn(('1234', '1234'), args)
+ self.assertIn(('12345', '12345'), args)
+ self.assertIn(('123456', '123456'), args)
+ # Verify network delete calls
+ calls = net_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1][0], calls)
+ self.assertIn('1234', args)
+ self.assertIn('12345', args)
+ self.assertIn('123456', args)
+ # Verify subnet delete calls
+ calls = subnet_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1][0], calls)
+ self.assertIn('1234', args)
+ self.assertIn('12345', args)
+ self.assertIn('123456', args)
+ # Verify router delete calls
+ calls = router_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1][0], calls)
+ self.assertIn('1234', args)
+ self.assertIn('12345', args)
+ self.assertIn('123456', args)
diff --git a/tempest/tests/test_waiters.py b/tempest/tests/test_waiters.py
new file mode 100644
index 0000000..1f9825e
--- /dev/null
+++ b/tempest/tests/test_waiters.py
@@ -0,0 +1,49 @@
+# Copyright 2014 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import time
+
+import mock
+
+from tempest.common import waiters
+from tempest import exceptions
+from tempest.tests import base
+
+
+class TestImageWaiters(base.TestCase):
+ def setUp(self):
+ super(TestImageWaiters, self).setUp()
+ self.client = mock.MagicMock()
+ self.client.build_timeout = 1
+ self.client.build_interval = 1
+
+ def test_wait_for_image_status(self):
+ self.client.get_image.return_value = (None, {'status': 'active'})
+ start_time = int(time.time())
+ waiters.wait_for_image_status(self.client, 'fake_image_id', 'active')
+ end_time = int(time.time())
+ # Ensure waiter returns before build_timeout
+ self.assertTrue((end_time - start_time) < 10)
+
+ def test_wait_for_image_status_timeout(self):
+ self.client.get_image.return_value = (None, {'status': 'saving'})
+ self.assertRaises(exceptions.TimeoutException,
+ waiters.wait_for_image_status,
+ self.client, 'fake_image_id', 'active')
+
+ def test_wait_for_image_status_error_on_image_create(self):
+ self.client.get_image.return_value = (None, {'status': 'ERROR'})
+ self.assertRaises(exceptions.AddImageException,
+ waiters.wait_for_image_status,
+ self.client, 'fake_image_id', 'active')
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index 10d421e..4c39f78 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -108,6 +108,9 @@
CODE_RE = '.*' # regexp makes sense in group match
def match(self, exc):
+ """:returns: Retruns with an error string if not matches,
+ returns with None when matches.
+ """
if not isinstance(exc, exception.BotoServerError):
return "%r not an BotoServerError instance" % exc
LOG.info("Status: %s , error_code: %s", exc.status, exc.error_code)
@@ -119,6 +122,7 @@
return ("Error code (%s) does not match" +
"the expected re pattern \"%s\"") %\
(exc.error_code, self.CODE_RE)
+ return None
class ClientError(BotoExceptionMatcher):
@@ -313,7 +317,7 @@
except ValueError:
return "_GONE"
except exception.EC2ResponseError as exc:
- if colusure_matcher.match(exc):
+ if colusure_matcher.match(exc) is None:
return "_GONE"
else:
raise
@@ -449,7 +453,7 @@
return "_GONE"
except exception.EC2ResponseError as exc:
if cls.ec2_error_code.\
- client.InvalidInstanceID.NotFound.match(exc):
+ client.InvalidInstanceID.NotFound.match(exc) is None:
return "_GONE"
# NOTE(afazekas): incorrect code,
# but the resource must be destoreyd
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index bbfbb79..e6a1638 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -40,7 +40,7 @@
": requires ami/aki/ari manifest")))
cls.s3_client = cls.os.s3_client
cls.ec2_client = cls.os.ec2api_client
- cls.zone = cls.ec2_client.get_good_zone()
+ cls.zone = CONF.boto.aws_zone
cls.materials_path = CONF.boto.s3_materials_path
ami_manifest = CONF.boto.ami_manifest
aki_manifest = CONF.boto.aki_manifest
diff --git a/tempest/thirdparty/boto/test_ec2_volumes.py b/tempest/thirdparty/boto/test_ec2_volumes.py
index 6a771e5..12dea18 100644
--- a/tempest/thirdparty/boto/test_ec2_volumes.py
+++ b/tempest/thirdparty/boto/test_ec2_volumes.py
@@ -38,7 +38,7 @@
raise cls.skipException(skip_msg)
cls.client = cls.os.ec2api_client
- cls.zone = cls.client.get_good_zone()
+ cls.zone = CONF.boto.aws_zone
@test.attr(type='smoke')
def test_create_get_delete(self):