Merge "Add aggregates scenario test"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 7ce0c0b..b216c5c 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -427,7 +427,7 @@
# (string value)
#alt_password=<None>
-# Administrative Username to use forKeystone API requests.
+# Administrative Username to use for Keystone API requests.
# (string value)
#admin_username=admin
@@ -582,21 +582,10 @@
# Options defined in tempest.config
#
-# Set to True if the Container Quota middleware is enabled
-# (boolean value)
-#container_quotas=true
-
-# Set to True if the Account Quota middleware is enabled
-# (boolean value)
-#accounts_quotas=true
-
-# Set to True if the Crossdomain middleware is enabled
-# (boolean value)
-#crossdomain=true
-
-# Set to True if the TempURL middleware is enabled (boolean
-# value)
-#tempurl=true
+# A list of the enabled optional discoverable apis. A single
+# entry, all, indicates that all of these features are
+# expected to be enabled (list value)
+#discoverable_apis=all
[orchestration]
diff --git a/etc/whitelist.yaml b/etc/whitelist.yaml
index a8c5276..1c12b6c 100644
--- a/etc/whitelist.yaml
+++ b/etc/whitelist.yaml
@@ -39,6 +39,8 @@
message: "Instance failed network setup after 1 attempt"
- module: "nova.compute.manager"
message: "Periodic sync_power_state task had an error"
+ - module: "nova.virt.driver"
+ message: "Info cache for instance .* could not be found"
g-api:
- module: "glance.store.sheepdog"
@@ -73,6 +75,10 @@
- module: "ceilometer.compute.pollsters.disk"
message: ".*"
+ceilometer-acentral:
+ - module: "ceilometer.central.manager"
+ message: "403 Forbidden"
+
ceilometer-alarm-evaluator:
- module: "ceilometer.alarm.service"
message: "alarm evaluation cycle failed"
diff --git a/run_tests.sh b/run_tests.sh
index 285b372..3f00453 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -126,7 +126,7 @@
exit
fi
-if [$coverage -eq 1] ; then
+if [ $coverage -eq 1 ]; then
$testrargs = "--coverage $testrargs"
fi
diff --git a/tempest/api/__init__.py b/tempest/api/__init__.py
index 0b3d2db..e69de29 100644
--- a/tempest/api/__init__.py
+++ b/tempest/api/__init__.py
@@ -1,16 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 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.
diff --git a/tempest/api/compute/admin/test_hosts_negative.py b/tempest/api/compute/admin/test_hosts_negative.py
index dbf7967..a1afe0e 100644
--- a/tempest/api/compute/admin/test_hosts_negative.py
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -66,7 +66,9 @@
self.assertRaises(exceptions.Unauthorized,
self.non_admin_client.update_host,
- hostname)
+ hostname,
+ status='enable',
+ maintenance_mode='enable')
@test.skip_because(bug="1261964", interface="xml")
@test.attr(type=['negative', 'gate'])
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 6fe3186..2160949 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -44,6 +44,7 @@
cls.s2_name = data_utils.rand_name('server')
resp, server = cls.create_test_server(name=cls.s2_name,
wait_until='ACTIVE')
+ cls.s2_id = server['id']
def _get_unused_flavor_id(self):
flavor_id = data_utils.rand_int_id(start=1000)
@@ -64,6 +65,22 @@
self.assertEqual([], servers)
@attr(type='gate')
+ def test_list_servers_filter_by_error_status(self):
+ # Filter the list of servers by server error status
+ params = {'status': 'error'}
+ resp, server = self.client.reset_state(self.s1_id, state='error')
+ resp, body = self.non_admin_client.list_servers(params)
+ # Reset server's state to 'active'
+ resp, server = self.client.reset_state(self.s1_id, state='active')
+ # Verify server's state
+ resp, server = self.client.get_server(self.s1_id)
+ self.assertEqual(server['status'], 'ACTIVE')
+ servers = body['servers']
+ # Verify error server in list result
+ self.assertIn(self.s1_id, map(lambda x: x['id'], servers))
+ self.assertNotIn(self.s2_id, map(lambda x: x['id'], servers))
+
+ @attr(type='gate')
def test_list_servers_by_admin_with_all_tenants(self):
# Listing servers by admin user with all tenants parameter
# Here should be listed all servers
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 5d62e1b..e45161b 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -109,6 +109,104 @@
self.assertTrue(linux_client.hostname_equals_servername(self.name))
+class ServersWithSpecificFlavorTestJSON(base.BaseV2ComputeAdminTest):
+ _interface = 'json'
+ run_ssh = CONF.compute.run_ssh
+ disk_config = 'AUTO'
+
+ @classmethod
+ def setUpClass(cls):
+ super(ServersWithSpecificFlavorTestJSON, cls).setUpClass()
+ cls.meta = {'hello': 'world'}
+ cls.accessIPv4 = '1.1.1.1'
+ cls.accessIPv6 = '0000:0000:0000:0000:0000:babe:220.12.22.2'
+ cls.name = data_utils.rand_name('server')
+ file_contents = 'This is a test file.'
+ personality = [{'path': '/test.txt',
+ 'contents': base64.b64encode(file_contents)}]
+ cls.client = cls.servers_client
+ cls.flavor_client = cls.os_adm.flavors_client
+ cli_resp = cls.create_test_server(name=cls.name,
+ meta=cls.meta,
+ accessIPv4=cls.accessIPv4,
+ accessIPv6=cls.accessIPv6,
+ personality=personality,
+ disk_config=cls.disk_config)
+ cls.resp, cls.server_initial = cli_resp
+ cls.password = cls.server_initial['adminPass']
+ cls.client.wait_for_server_status(cls.server_initial['id'], 'ACTIVE')
+ resp, cls.server = cls.client.get_server(cls.server_initial['id'])
+
+ @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+ @attr(type='gate')
+ def test_verify_created_server_ephemeral_disk(self):
+ # Verify that the ephemeral disk is created when creating server
+
+ def create_flavor_with_extra_specs(self):
+ flavor_with_eph_disk_name = data_utils.rand_name('eph_flavor')
+ flavor_with_eph_disk_id = data_utils.rand_int_id(start=1000)
+ ram = 64
+ vcpus = 1
+ disk = 0
+
+ # Create a flavor with extra specs
+ resp, flavor = (self.flavor_client.
+ create_flavor(flavor_with_eph_disk_name,
+ ram, vcpus, disk,
+ flavor_with_eph_disk_id,
+ ephemeral=1))
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ self.assertEqual(200, resp.status)
+
+ return flavor['id']
+
+ def create_flavor_without_extra_specs(self):
+ flavor_no_eph_disk_name = data_utils.rand_name('no_eph_flavor')
+ flavor_no_eph_disk_id = data_utils.rand_int_id(start=1000)
+
+ ram = 64
+ vcpus = 1
+ disk = 0
+
+ # Create a flavor without extra specs
+ resp, flavor = (self.flavor_client.
+ create_flavor(flavor_no_eph_disk_name,
+ ram, vcpus, disk,
+ flavor_no_eph_disk_id))
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ self.assertEqual(200, resp.status)
+
+ return flavor['id']
+
+ def flavor_clean_up(self, flavor_id):
+ resp, body = self.flavor_client.delete_flavor(flavor_id)
+ self.assertEqual(resp.status, 202)
+ self.flavor_client.wait_for_resource_deletion(flavor_id)
+
+ flavor_with_eph_disk_id = self.create_flavor_with_extra_specs()
+ flavor_no_eph_disk_id = self.create_flavor_without_extra_specs()
+
+ admin_pass = self.image_ssh_password
+
+ resp, server_no_eph_disk = (self.
+ create_test_server(
+ wait_until='ACTIVE',
+ adminPass=admin_pass,
+ flavor=flavor_no_eph_disk_id))
+ resp, server_with_eph_disk = (self.create_test_server(
+ wait_until='ACTIVE',
+ adminPass=admin_pass,
+ flavor=flavor_with_eph_disk_id))
+ # Get partition number of server without extra specs.
+ linux_client = RemoteClient(server_no_eph_disk,
+ self.ssh_user, self.password)
+ partition_num = len(linux_client.get_partitions())
+
+ linux_client = RemoteClient(server_with_eph_disk,
+ self.ssh_user, self.password)
+ self.assertEqual(partition_num + 1, linux_client.get_partitions())
+
+
class ServersTestManualDisk(ServersTestJSON):
disk_config = 'MANUAL'
@@ -122,3 +220,7 @@
class ServersTestXML(ServersTestJSON):
_interface = 'xml'
+
+
+class ServersWithSpecificFlavorTestXML(ServersWithSpecificFlavorTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 3748e37..f3863b3 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -121,6 +121,23 @@
self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
@attr(type='gate')
+ def test_list_servers_filter_by_shutoff_status(self):
+ # Filter the list of servers by server shutoff status
+ params = {'status': 'shutoff'}
+ self.client.stop(self.s1['id'])
+ self.client.wait_for_server_status(self.s1['id'],
+ 'SHUTOFF')
+ resp, body = self.client.list_servers(params)
+ self.client.start(self.s1['id'])
+ self.client.wait_for_server_status(self.s1['id'],
+ 'ACTIVE')
+ servers = body['servers']
+
+ self.assertIn(self.s1['id'], map(lambda x: x['id'], servers))
+ self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
+ self.assertNotIn(self.s3['id'], map(lambda x: x['id'], servers))
+
+ @attr(type='gate')
def test_list_servers_filter_by_limit(self):
# Verify only the expected number of servers are returned
params = {'limit': 1}
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index f195562..e4af2e1 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -174,7 +174,7 @@
def _detect_server_image_flavor(self, server_id):
# Detects the current server image flavor ref.
- resp, server = self.client.get_server(self.server_id)
+ resp, server = self.client.get_server(server_id)
current_flavor = server['flavor']['id']
new_flavor_ref = self.flavor_ref_alt \
if current_flavor == self.flavor_ref else self.flavor_ref
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index f9b44d8..56929a3 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -19,7 +19,6 @@
from tempest.api.compute import base
from tempest.openstack.common import log as logging
from tempest import test
-import testtools
LOG = logging.getLogger(__name__)
@@ -28,21 +27,25 @@
class ExtensionsTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
- @testtools.skipIf(not test.is_extension_enabled('os-consoles', 'compute'),
- 'os-consoles extension not enabled.')
@test.attr(type='gate')
def test_list_extensions(self):
# List of all extensions
+ if len(self.config.compute_feature_enabled.api_extensions) == 0:
+ raise self.skipException('There are not any extensions configured')
resp, extensions = self.extensions_client.list_extensions()
- self.assertIn("extensions", extensions)
- extension_list = [extension.get('alias')
- for extension in extensions.get('extensions', {})]
- LOG.debug("Nova extensions: %s" % ','.join(extension_list))
self.assertEqual(200, resp.status)
- self.assertTrue(self.extensions_client.is_enabled("Consoles"))
+ ext = self.config.compute_feature_enabled.api_extensions[0]
+ if ext == 'all':
+ self.assertIn('Hosts', map(lambda x: x['name'], extensions))
+ elif ext:
+ self.assertIn(ext, map(lambda x: x['name'], extensions))
+ else:
+ raise self.skipException('There are not any extensions configured')
+ # Log extensions list
+ extension_list = map(lambda x: x['name'], extensions)
+ LOG.debug("Nova extensions: %s" % ','.join(extension_list))
- @testtools.skipIf(not test.is_extension_enabled('os-consoles', 'compute'),
- 'os-consoles extension not enabled.')
+ @test.requires_ext(extension='os-consoles', service='compute')
@test.attr(type='gate')
def test_get_extension(self):
# get the specified extensions
diff --git a/tempest/api/compute/v3/admin/test_hosts_negative.py b/tempest/api/compute/v3/admin/test_hosts_negative.py
index 755fa2b..598a1d3 100644
--- a/tempest/api/compute/v3/admin/test_hosts_negative.py
+++ b/tempest/api/compute/v3/admin/test_hosts_negative.py
@@ -66,7 +66,9 @@
self.assertRaises(exceptions.Unauthorized,
self.non_admin_client.update_host,
- hostname)
+ hostname,
+ status='enable',
+ maintenance_mode='enable')
@test.attr(type=['negative', 'gate'])
def test_update_host_with_extra_param(self):
diff --git a/tempest/api/compute/v3/admin/test_servers.py b/tempest/api/compute/v3/admin/test_servers.py
index 6fe3186..becdd78 100644
--- a/tempest/api/compute/v3/admin/test_servers.py
+++ b/tempest/api/compute/v3/admin/test_servers.py
@@ -21,7 +21,7 @@
from tempest.test import skip_because
-class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
+class ServersAdminV3TestJSON(base.BaseV3ComputeAdminTest):
"""
Tests Servers API using admin privileges
@@ -31,10 +31,10 @@
@classmethod
def setUpClass(cls):
- super(ServersAdminTestJSON, cls).setUpClass()
- cls.client = cls.os_adm.servers_client
+ super(ServersAdminV3TestJSON, cls).setUpClass()
+ cls.client = cls.servers_admin_client
cls.non_admin_client = cls.servers_client
- cls.flavors_client = cls.os_adm.flavors_client
+ cls.flavors_client = cls.flavors_admin_client
cls.s1_name = data_utils.rand_name('server')
resp, server = cls.create_test_server(name=cls.s1_name,
@@ -44,6 +44,7 @@
cls.s2_name = data_utils.rand_name('server')
resp, server = cls.create_test_server(name=cls.s2_name,
wait_until='ACTIVE')
+ cls.s2_id = server['id']
def _get_unused_flavor_id(self):
flavor_id = data_utils.rand_int_id(start=1000)
@@ -114,6 +115,22 @@
self.assertIn(key, str(diagnostic.keys()))
@attr(type='gate')
+ def test_list_servers_filter_by_error_status(self):
+ # Filter the list of servers by server error status
+ params = {'status': 'error'}
+ resp, server = self.client.reset_state(self.s1_id, state='error')
+ resp, body = self.non_admin_client.list_servers(params)
+ # Reset server's state to 'active'
+ resp, server = self.client.reset_state(self.s1_id, state='active')
+ # Verify server's state
+ resp, server = self.client.get_server(self.s1_id)
+ self.assertEqual(server['status'], 'ACTIVE')
+ servers = body['servers']
+ # Verify error server in list result
+ self.assertIn(self.s1_id, map(lambda x: x['id'], servers))
+ self.assertNotIn(self.s2_id, map(lambda x: x['id'], servers))
+
+ @attr(type='gate')
def test_rebuild_server_in_error_state(self):
# The server in error state should be rebuilt using the provided
# image and changed to ACTIVE state
@@ -142,5 +159,5 @@
self.assertEqual(self.image_ref_alt, rebuilt_image_id)
-class ServersAdminTestXML(ServersAdminTestJSON):
+class ServersAdminV3TestXML(ServersAdminV3TestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_servers_negative.py b/tempest/api/compute/v3/admin/test_servers_negative.py
index 77d873b..670bc2c 100644
--- a/tempest/api/compute/v3/admin/test_servers_negative.py
+++ b/tempest/api/compute/v3/admin/test_servers_negative.py
@@ -22,7 +22,7 @@
from tempest.test import attr
-class ServersAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+class ServersAdminNegativeV3TestJSON(base.BaseV3ComputeAdminTest):
"""
Tests Servers API using admin privileges
@@ -32,10 +32,10 @@
@classmethod
def setUpClass(cls):
- super(ServersAdminNegativeTestJSON, cls).setUpClass()
- cls.client = cls.os_adm.servers_client
+ super(ServersAdminNegativeV3TestJSON, cls).setUpClass()
+ cls.client = cls.servers_admin_client
cls.non_adm_client = cls.servers_client
- cls.flavors_client = cls.os_adm.flavors_client
+ cls.flavors_client = cls.flavors_admin_client
cls.identity_client = cls._get_identity_admin_client()
tenant = cls.identity_client.get_tenant_by_name(
cls.client.tenant_name)
@@ -139,5 +139,5 @@
server_id)
-class ServersAdminNegativeTestXML(ServersAdminNegativeTestJSON):
+class ServersAdminNegativeV3TestXML(ServersAdminNegativeV3TestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_create_server.py b/tempest/api/compute/v3/servers/test_create_server.py
index e1bb160..1555442 100644
--- a/tempest/api/compute/v3/servers/test_create_server.py
+++ b/tempest/api/compute/v3/servers/test_create_server.py
@@ -117,6 +117,104 @@
self.assertTrue(linux_client.hostname_equals_servername(self.name))
+class ServersWithSpecificFlavorV3TestJSON(base.BaseV3ComputeAdminTest):
+ _interface = 'json'
+ run_ssh = CONF.compute.run_ssh
+ disk_config = 'AUTO'
+
+ @classmethod
+ def setUpClass(cls):
+ super(ServersWithSpecificFlavorV3TestJSON, cls).setUpClass()
+ cls.meta = {'hello': 'world'}
+ cls.accessIPv4 = '1.1.1.1'
+ cls.accessIPv6 = '0000:0000:0000:0000:0000:babe:220.12.22.2'
+ cls.name = data_utils.rand_name('server')
+ file_contents = 'This is a test file.'
+ personality = [{'path': '/test.txt',
+ 'contents': base64.b64encode(file_contents)}]
+ cls.client = cls.servers_client
+ cls.flavor_client = cls.flavors_admin_client
+ cli_resp = cls.create_test_server(name=cls.name,
+ meta=cls.meta,
+ access_ip_v4=cls.accessIPv4,
+ access_ip_v6=cls.accessIPv6,
+ personality=personality,
+ disk_config=cls.disk_config)
+ cls.resp, cls.server_initial = cli_resp
+ cls.password = cls.server_initial['admin_password']
+ cls.client.wait_for_server_status(cls.server_initial['id'], 'ACTIVE')
+ resp, cls.server = cls.client.get_server(cls.server_initial['id'])
+
+ @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+ @test.attr(type='gate')
+ def test_verify_created_server_ephemeral_disk(self):
+ # Verify that the ephemeral disk is created when creating server
+
+ def create_flavor_with_extra_specs(self):
+ flavor_with_eph_disk_name = data_utils.rand_name('eph_flavor')
+ flavor_with_eph_disk_id = data_utils.rand_int_id(start=1000)
+ ram = 512
+ vcpus = 1
+ disk = 10
+
+ # Create a flavor with extra specs
+ resp, flavor = (self.flavor_client.
+ create_flavor(flavor_with_eph_disk_name,
+ ram, vcpus, disk,
+ flavor_with_eph_disk_id,
+ ephemeral=1, swap=1024, rxtx=1))
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ self.assertEqual(200, resp.status)
+
+ return flavor['id']
+
+ def create_flavor_without_extra_specs(self):
+ flavor_no_eph_disk_name = data_utils.rand_name('no_eph_flavor')
+ flavor_no_eph_disk_id = data_utils.rand_int_id(start=1000)
+
+ ram = 512
+ vcpus = 1
+ disk = 10
+
+ # Create a flavor without extra specs
+ resp, flavor = (self.flavor_client.
+ create_flavor(flavor_no_eph_disk_name,
+ ram, vcpus, disk,
+ flavor_no_eph_disk_id))
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ self.assertEqual(200, resp.status)
+
+ return flavor['id']
+
+ def flavor_clean_up(self, flavor_id):
+ resp, body = self.flavor_client.delete_flavor(flavor_id)
+ self.assertEqual(resp.status, 202)
+ self.flavor_client.wait_for_resource_deletion(flavor_id)
+
+ flavor_with_eph_disk_id = self.create_flavor_with_extra_specs()
+ flavor_no_eph_disk_id = self.create_flavor_without_extra_specs()
+
+ admin_pass = self.image_ssh_password
+
+ resp, server_no_eph_disk = (self.
+ create_test_server(
+ wait_until='ACTIVE',
+ adminPass=admin_pass,
+ flavor=flavor_no_eph_disk_id))
+ resp, server_with_eph_disk = (self.create_test_server(
+ wait_until='ACTIVE',
+ adminPass=admin_pass,
+ flavor=flavor_with_eph_disk_id))
+ # Get partition number of server without extra specs.
+ linux_client = RemoteClient(server_no_eph_disk,
+ self.ssh_user, self.password)
+ partition_num = len(linux_client.get_partitions())
+
+ linux_client = RemoteClient(server_with_eph_disk,
+ self.ssh_user, self.password)
+ self.assertEqual(partition_num + 1, linux_client.get_partitions())
+
+
class ServersV3TestManualDisk(ServersV3TestJSON):
disk_config = 'MANUAL'
@@ -130,3 +228,7 @@
class ServersV3TestXML(ServersV3TestJSON):
_interface = 'xml'
+
+
+class ServersWithSpecificFlavorV3TestXML(ServersWithSpecificFlavorV3TestJSON):
+ _interface = 'xml'
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 3dd7b0b..140cc2f 100644
--- a/tempest/api/compute/v3/servers/test_list_server_filters.py
+++ b/tempest/api/compute/v3/servers/test_list_server_filters.py
@@ -176,6 +176,23 @@
self.assertEqual(['ACTIVE'] * 3, [x['status'] for x in servers])
@attr(type='gate')
+ def test_list_servers_filter_by_shutoff_status(self):
+ # Filter the list of servers by server shutoff status
+ params = {'status': 'shutoff'}
+ self.client.stop(self.s1['id'])
+ self.client.wait_for_server_status(self.s1['id'],
+ 'SHUTOFF')
+ resp, body = self.client.list_servers(params)
+ self.client.start(self.s1['id'])
+ self.client.wait_for_server_status(self.s1['id'],
+ 'ACTIVE')
+ servers = body['servers']
+
+ self.assertIn(self.s1['id'], map(lambda x: x['id'], servers))
+ self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
+ self.assertNotIn(self.s3['id'], map(lambda x: x['id'], servers))
+
+ @attr(type='gate')
def test_list_servers_filtered_by_name_wildcard(self):
# List all servers that contains '-instance' in name
params = {'name': '-instance'}
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index 602bd5b..f462cc2 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -15,7 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import base64
import time
import testtools
@@ -113,15 +112,11 @@
# The server should be rebuilt using the provided image and data
meta = {'rebuild': 'server'}
new_name = data_utils.rand_name('server')
- file_contents = 'Test server rebuild.'
- personality = [{'path': 'rebuild.txt',
- 'contents': base64.b64encode(file_contents)}]
password = 'rebuildPassw0rd'
resp, rebuilt_server = self.client.rebuild(self.server_id,
self.image_ref_alt,
name=new_name,
metadata=meta,
- personality=personality,
admin_password=password)
self.addCleanup(self.client.rebuild, self.server_id, self.image_ref)
@@ -134,9 +129,9 @@
# Verify the server properties after the rebuild completes
self.client.wait_for_server_status(rebuilt_server['id'], 'ACTIVE')
resp, server = self.client.get_server(rebuilt_server['id'])
- rebuilt_image_id = rebuilt_server['image']['id']
+ rebuilt_image_id = server['image']['id']
self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
- self.assertEqual(new_name, rebuilt_server['name'])
+ self.assertEqual(new_name, server['name'])
if self.run_ssh:
# Verify that the user can authenticate with the provided password
@@ -174,7 +169,7 @@
def _detect_server_image_flavor(self, server_id):
# Detects the current server image flavor ref.
- resp, server = self.client.get_server(self.server_id)
+ resp, server = self.client.get_server(server_id)
current_flavor = server['flavor']['id']
new_flavor_ref = self.flavor_ref_alt \
if current_flavor == self.flavor_ref else self.flavor_ref
diff --git a/tempest/api/compute/v3/servers/test_server_metadata.py b/tempest/api/compute/v3/servers/test_server_metadata.py
index ee0f4a9..f36feb3 100644
--- a/tempest/api/compute/v3/servers/test_server_metadata.py
+++ b/tempest/api/compute/v3/servers/test_server_metadata.py
@@ -20,12 +20,12 @@
from tempest.test import attr
-class ServerMetadataTestJSON(base.BaseV2ComputeTest):
+class ServerMetadataV3TestJSON(base.BaseV3ComputeTest):
_interface = 'json'
@classmethod
def setUpClass(cls):
- super(ServerMetadataTestJSON, cls).setUpClass()
+ super(ServerMetadataV3TestJSON, cls).setUpClass()
cls.client = cls.servers_client
cls.quotas = cls.quotas_client
cls.admin_client = cls._get_identity_admin_client()
@@ -37,7 +37,7 @@
cls.server_id = server['id']
def setUp(self):
- super(ServerMetadataTestJSON, self).setUp()
+ super(ServerMetadataV3TestJSON, self).setUp()
meta = {'key1': 'value1', 'key2': 'value2'}
resp, _ = self.client.set_server_metadata(self.server_id, meta)
self.assertEqual(resp.status, 200)
@@ -88,7 +88,7 @@
meta = {'key1': 'alt1', 'key3': 'value3'}
resp, metadata = self.client.update_server_metadata(self.server_id,
meta)
- self.assertEqual(200, resp.status)
+ self.assertEqual(201, resp.status)
# Verify the values have been updated to the proper values
resp, resp_metadata = self.client.list_server_metadata(self.server_id)
@@ -213,5 +213,5 @@
self.server_id, meta=meta, no_metadata_field=True)
-class ServerMetadataTestXML(ServerMetadataTestJSON):
+class ServerMetadataV3TestXML(ServerMetadataV3TestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_server_personality.py b/tempest/api/compute/v3/servers/test_server_personality.py
deleted file mode 100644
index c6d2e44..0000000
--- a/tempest/api/compute/v3/servers/test_server_personality.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 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 base64
-
-from tempest.api.compute import base
-from tempest import exceptions
-from tempest.test import attr
-
-
-class ServerPersonalityTestJSON(base.BaseV2ComputeTest):
- _interface = 'json'
-
- @classmethod
- def setUpClass(cls):
- super(ServerPersonalityTestJSON, cls).setUpClass()
- cls.client = cls.servers_client
- cls.user_client = cls.limits_client
-
- @attr(type='gate')
- def test_personality_files_exceed_limit(self):
- # Server creation should fail if greater than the maximum allowed
- # number of files are injected into the server.
- file_contents = 'This is a test file.'
- personality = []
- max_file_limit = \
- self.user_client.get_specific_absolute_limit("maxPersonality")
- for i in range(0, int(max_file_limit) + 1):
- path = 'etc/test' + str(i) + '.txt'
- personality.append({'path': path,
- 'contents': base64.b64encode(file_contents)})
- self.assertRaises(exceptions.OverLimit, self.create_test_server,
- personality=personality)
-
- @attr(type='gate')
- def test_can_create_server_with_max_number_personality_files(self):
- # Server should be created successfully if maximum allowed number of
- # files is injected into the server during creation.
- file_contents = 'This is a test file.'
- max_file_limit = \
- self.user_client.get_specific_absolute_limit("maxPersonality")
- person = []
- for i in range(0, int(max_file_limit)):
- path = 'etc/test' + str(i) + '.txt'
- person.append({
- 'path': path,
- 'contents': base64.b64encode(file_contents),
- })
- resp, server = self.create_test_server(personality=person)
- self.assertEqual('202', resp['status'])
-
-
-class ServerPersonalityTestXML(ServerPersonalityTestJSON):
- _interface = "xml"
diff --git a/tempest/api/compute/v3/test_extensions.py b/tempest/api/compute/v3/test_extensions.py
index 72dfe14..f37ebcf 100644
--- a/tempest/api/compute/v3/test_extensions.py
+++ b/tempest/api/compute/v3/test_extensions.py
@@ -30,13 +30,20 @@
@test.attr(type='gate')
def test_list_extensions(self):
# List of all extensions
+ if len(self.config.compute_feature_enabled.api_v3_extensions) == 0:
+ raise self.skipException('There are not any extensions configured')
resp, extensions = self.extensions_client.list_extensions()
- self.assertIn("extensions", extensions)
- extension_list = [extension.get('alias')
- for extension in extensions.get('extensions', {})]
- LOG.debug("Nova extensions: %s" % ','.join(extension_list))
self.assertEqual(200, resp.status)
- self.assertTrue(self.extensions_client.is_enabled("Consoles"))
+ ext = self.config.compute_feature_enabled.api_v3_extensions[0]
+ if ext == 'all':
+ self.assertIn('Hosts', map(lambda x: x['name'], extensions))
+ elif ext:
+ self.assertIn(ext, map(lambda x: x['name'], extensions))
+ else:
+ raise self.skipException('There are not any extensions configured')
+ # Log extensions list
+ extension_list = map(lambda x: x['name'], extensions)
+ LOG.debug("Nova extensions: %s" % ','.join(extension_list))
@test.attr(type='gate')
def test_get_extension(self):
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index ae6996d..f6fed5b 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -18,6 +18,7 @@
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.test import attr
+from testtools.matchers import ContainsAll
class VolumesGetTestJSON(base.BaseV2ComputeTest):
@@ -65,29 +66,10 @@
fetched_volume['id'],
'The fetched Volume is different '
'from the created Volume')
- self.assertEqual(metadata,
- fetched_volume['metadata'],
- 'The fetched Volume is different '
- 'from the created Volume')
-
- @attr(type='gate')
- def test_volume_get_metadata_none(self):
- # CREATE, GET empty metadata dict
- v_name = data_utils.rand_name('Volume-')
- # Create volume
- resp, volume = self.client.create_volume(size=1,
- display_name=v_name,
- metadata={})
- self.addCleanup(self._delete_volume, volume)
- self.assertEqual(200, resp.status)
- self.assertIn('id', volume)
- self.assertIn('displayName', volume)
- # Wait for Volume status to become ACTIVE
- self.client.wait_for_volume_status(volume['id'], 'available')
- # GET Volume
- resp, fetched_volume = self.client.get_volume(volume['id'])
- self.assertEqual(200, resp.status)
- self.assertEqual(fetched_volume['metadata'], {})
+ self.assertThat(fetched_volume['metadata'].items(),
+ ContainsAll(metadata.items()),
+ 'The fetched Volume metadata misses data '
+ 'from the created Volume')
def _delete_volume(self, volume):
# Delete the Volume created in this method
diff --git a/tempest/api/network/admin/test_agent_management.py b/tempest/api/network/admin/test_agent_management.py
index 94659b2..4b36197 100644
--- a/tempest/api/network/admin/test_agent_management.py
+++ b/tempest/api/network/admin/test_agent_management.py
@@ -36,6 +36,12 @@
agents = body['agents']
self.assertIn(self.agent, agents)
+ @attr(type=['smoke'])
+ def test_list_agents_non_admin(self):
+ resp, body = self.client.list_agents()
+ self.assertEqual('200', resp['status'])
+ self.assertEqual(len(body["agents"]), 0)
+
@attr(type='smoke')
def test_show_agent(self):
resp, body = self.admin_client.show_agent(self.agent['id'])
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 68ca66a..849d62e 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -127,6 +127,21 @@
self.assertIsNotNone(found, msg)
@attr(type='smoke')
+ def test_list_networks_fields(self):
+ # Verify listing some fields of the networks
+ resp, body = self.client.list_networks(fields='id')
+ self.assertEqual('200', resp['status'])
+ networks = body['networks']
+ found = None
+ for n in networks:
+ self.assertEqual(len(n), 1)
+ self.assertIn('id', n)
+ if (n['id'] == self.network['id']):
+ found = n['id']
+ self.assertIsNotNone(found,
+ "Created network id not found in the list")
+
+ @attr(type='smoke')
def test_show_subnet(self):
# Verifies the details of a subnet
resp, body = self.client.show_subnet(self.subnet['id'])
@@ -149,6 +164,21 @@
self.assertIsNotNone(found, msg)
@attr(type='smoke')
+ def test_list_subnets_fields(self):
+ # Verify listing some fields of the subnets
+ resp, body = self.client.list_subnets(fields='id')
+ self.assertEqual('200', resp['status'])
+ subnets = body['subnets']
+ found = None
+ for n in subnets:
+ self.assertEqual(len(n), 1)
+ self.assertIn('id', n)
+ if (n['id'] == self.subnet['id']):
+ found = n['id']
+ self.assertIsNotNone(found,
+ "Created subnet id not found in the list")
+
+ @attr(type='smoke')
def test_create_update_delete_port(self):
# Verify that successful port creation, update & deletion
resp, body = self.client.create_port(self.network['id'])
@@ -184,6 +214,21 @@
found = n['id']
self.assertIsNotNone(found, "Port list doesn't contain created port")
+ @attr(type='smoke')
+ def test_list_ports_fields(self):
+ # Verify listing some fields of the ports
+ resp, body = self.client.list_ports(fields='id')
+ self.assertEqual('200', resp['status'])
+ ports_list = body['ports']
+ found = None
+ for n in ports_list:
+ self.assertEqual(len(n), 1)
+ self.assertIn('id', n)
+ if (n['id'] == self.port['id']):
+ found = n['id']
+ self.assertIsNotNone(found,
+ "Created port id not found in the list")
+
class NetworksTestXML(NetworksTestJSON):
_interface = 'xml'
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index ac1c7d1..6312f69 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -14,21 +14,17 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
-
from tempest.api.object_storage import base
from tempest import clients
from tempest.common.utils import data_utils
from tempest import config
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
CONF = config.CONF
class AccountQuotasTest(base.BaseObjectTest):
- accounts_quotas_available = \
- CONF.object_storage_feature_enabled.accounts_quotas
@classmethod
def setUpClass(cls):
@@ -99,9 +95,8 @@
cls.data.teardown_all()
super(AccountQuotasTest, cls).tearDownClass()
- @testtools.skipIf(not accounts_quotas_available,
- "Account Quotas middleware not available")
- @attr(type="smoke")
+ @test.attr(type="smoke")
+ @test.requires_ext(extension='account_quotas', service='object')
def test_upload_valid_object(self):
object_name = data_utils.rand_name(name="TestObject")
data = data_utils.arbitrary_string()
@@ -111,9 +106,8 @@
self.assertEqual(resp["status"], "201")
self.assertHeaders(resp, 'Object', 'PUT')
- @testtools.skipIf(not accounts_quotas_available,
- "Account Quotas middleware not available")
- @attr(type=["negative", "smoke"])
+ @test.attr(type=["negative", "smoke"])
+ @test.requires_ext(extension='account_quotas', service='object')
def test_upload_large_object(self):
object_name = data_utils.rand_name(name="TestObject")
data = data_utils.arbitrary_string(30)
@@ -121,9 +115,8 @@
self.object_client.create_object,
self.container_name, object_name, data)
- @testtools.skipIf(not accounts_quotas_available,
- "Account Quotas middleware not available")
- @attr(type=["smoke"])
+ @test.attr(type=["smoke"])
+ @test.requires_ext(extension='account_quotas', service='object')
def test_admin_modify_quota(self):
"""Test that the ResellerAdmin is able to modify and remove the quota
on a user's account.
@@ -146,9 +139,8 @@
self.assertEqual(resp["status"], "204")
self.assertHeaders(resp, 'Account', 'POST')
- @testtools.skipIf(not accounts_quotas_available,
- "Account Quotas middleware not available")
- @attr(type=["negative", "smoke"])
+ @test.attr(type=["negative", "smoke"])
+ @test.requires_ext(extension='account_quotas', service='object')
def test_user_modify_quota(self):
"""Test that a user is not able to modify or remove a quota on
its account.
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index 513d24a..103ee89 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -15,25 +15,19 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
-
from tempest.api.object_storage import base
from tempest.common.utils import data_utils
from tempest import config
from tempest import exceptions
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
CONF = config.CONF
QUOTA_BYTES = 10
QUOTA_COUNT = 3
-SKIP_MSG = "Container quotas middleware not available."
class ContainerQuotasTest(base.BaseObjectTest):
"""Attemps to test the perfect behavior of quotas in a container."""
- container_quotas_available = \
- CONF.object_storage_feature_enabled.container_quotas
def setUp(self):
"""Creates and sets a container with quotas.
@@ -58,8 +52,8 @@
self.delete_containers([self.container_name])
super(ContainerQuotasTest, self).tearDown()
- @testtools.skipIf(not container_quotas_available, SKIP_MSG)
- @attr(type="smoke")
+ @test.requires_ext(extension='container_quotas', service='object')
+ @test.attr(type="smoke")
def test_upload_valid_object(self):
"""Attempts to uploads an object smaller than the bytes quota."""
object_name = data_utils.rand_name(name="TestObject")
@@ -69,14 +63,14 @@
resp, _ = self.object_client.create_object(
self.container_name, object_name, data)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'PUT')
nafter = self._get_bytes_used()
self.assertEqual(nbefore + len(data), nafter)
- @testtools.skipIf(not container_quotas_available, SKIP_MSG)
- @attr(type="smoke")
+ @test.requires_ext(extension='container_quotas', service='object')
+ @test.attr(type="smoke")
def test_upload_large_object(self):
"""Attempts to upload an object lagger than the bytes quota."""
object_name = data_utils.rand_name(name="TestObject")
@@ -91,8 +85,8 @@
nafter = self._get_bytes_used()
self.assertEqual(nbefore, nafter)
- @testtools.skipIf(not container_quotas_available, SKIP_MSG)
- @attr(type="smoke")
+ @test.requires_ext(extension='container_quotas', service='object')
+ @test.attr(type="smoke")
def test_upload_too_many_objects(self):
"""Attempts to upload many objects that exceeds the count limit."""
for _ in range(QUOTA_COUNT):
diff --git a/tempest/api/object_storage/test_crossdomain.py b/tempest/api/object_storage/test_crossdomain.py
index 41430c8..debd432 100644
--- a/tempest/api/object_storage/test_crossdomain.py
+++ b/tempest/api/object_storage/test_crossdomain.py
@@ -19,27 +19,14 @@
from tempest.api.object_storage import base
from tempest import clients
from tempest.common import custom_matchers
-from tempest import config
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
-
-CONF = config.CONF
+from tempest import test
class CrossdomainTest(base.BaseObjectTest):
- crossdomain_available = \
- CONF.object_storage_feature_enabled.crossdomain
@classmethod
def setUpClass(cls):
super(CrossdomainTest, cls).setUpClass()
-
- # skip this test if CORS isn't enabled in the conf file.
- if not cls.crossdomain_available:
- skip_msg = ("%s skipped as Crossdomain middleware not available"
- % cls.__name__)
- raise cls.skipException(skip_msg)
-
# creates a test user. The test user will set its base_url to the Swift
# endpoint and test the healthcheck feature.
cls.data.setup_test_user()
@@ -75,12 +62,13 @@
super(CrossdomainTest, self).tearDown()
- @attr('gate')
+ @test.attr('gate')
+ @test.requires_ext(extension='crossdomain', service='object')
def test_get_crossdomain_policy(self):
resp, body = self.os_test_user.account_client.get("crossdomain.xml",
{})
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertTrue(body.startswith(self.xml_start) and
body.endswith(self.xml_end))
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index d0e5353..d1b3674 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -24,27 +24,16 @@
from tempest.common.utils import data_utils
from tempest import config
from tempest import exceptions
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
CONF = config.CONF
class ObjectTempUrlTest(base.BaseObjectTest):
- tempurl_available = \
- CONF.object_storage_feature_enabled.tempurl
-
@classmethod
def setUpClass(cls):
super(ObjectTempUrlTest, cls).setUpClass()
-
- # skip this test if TempUrl isn't enabled in the conf file.
- if not cls.tempurl_available:
- skip_msg = ("%s skipped as TempUrl middleware not available"
- % cls.__name__)
- raise cls.skipException(skip_msg)
-
# create a container
cls.container_name = data_utils.rand_name(name='TestContainer')
cls.container_client.create_container(cls.container_name)
@@ -108,7 +97,8 @@
return url
- @attr(type='gate')
+ @test.attr(type='gate')
+ @test.requires_ext(extension='tempurl', service='object')
def test_get_object_using_temp_url(self):
expires = self._get_expiry_date()
@@ -119,16 +109,17 @@
# trying to get object using temp url within expiry time
resp, body = self.object_client.get(url)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'GET')
self.assertEqual(body, self.content)
# Testing a HEAD on this Temp URL
resp, body = self.object_client.head(url)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'HEAD')
- @attr(type='gate')
+ @test.attr(type='gate')
+ @test.requires_ext(extension='tempurl', service='object')
def test_get_object_using_temp_url_key_2(self):
key2 = 'Meta2-'
metadata = {'Temp-URL-Key-2': key2}
@@ -149,10 +140,11 @@
self.object_name, "GET",
expires, key2)
resp, body = self.object_client.get(url)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertEqual(body, self.content)
- @attr(type='gate')
+ @test.attr(type='gate')
+ @test.requires_ext(extension='tempurl', service='object')
def test_put_object_using_temp_url(self):
new_data = data_utils.arbitrary_string(
size=len(self.object_name),
@@ -165,12 +157,12 @@
# trying to put random data in the object using temp url
resp, body = self.object_client.put(url, new_data, None)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'PUT')
# Testing a HEAD on this Temp URL
resp, body = self.object_client.head(url)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'HEAD')
# Validate that the content of the object has been modified
@@ -181,7 +173,8 @@
_, body = self.object_client.get(url)
self.assertEqual(body, new_data)
- @attr(type='gate')
+ @test.attr(type='gate')
+ @test.requires_ext(extension='tempurl', service='object')
def test_head_object_using_temp_url(self):
expires = self._get_expiry_date()
@@ -192,10 +185,11 @@
# Testing a HEAD on this Temp URL
resp, body = self.object_client.head(url)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'HEAD')
- @attr(type=['gate', 'negative'])
+ @test.attr(type=['gate', 'negative'])
+ @test.requires_ext(extension='tempurl', service='object')
def test_get_object_after_expiration_time(self):
expires = self._get_expiry_date(1)
diff --git a/tempest/api/orchestration/stacks/test_non_empty_stack.py b/tempest/api/orchestration/stacks/test_non_empty_stack.py
index eead234..282758c 100644
--- a/tempest/api/orchestration/stacks/test_non_empty_stack.py
+++ b/tempest/api/orchestration/stacks/test_non_empty_stack.py
@@ -112,6 +112,18 @@
self.assertEqual('fluffy', stack['outputs'][0]['output_key'])
@attr(type='gate')
+ def test_suspend_resume_stack(self):
+ """suspend and resume a stack."""
+ resp, suspend_stack = self.client.suspend_stack(self.stack_identifier)
+ self.assertEqual('200', resp['status'])
+ self.client.wait_for_stack_status(self.stack_identifier,
+ 'SUSPEND_COMPLETE')
+ resp, resume_stack = self.client.resume_stack(self.stack_identifier)
+ self.assertEqual('200', resp['status'])
+ self.client.wait_for_stack_status(self.stack_identifier,
+ 'RESUME_COMPLETE')
+
+ @attr(type='gate')
def test_list_resources(self):
"""Getting list of created resources for the stack should be possible.
"""
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 49a2f74..fa3f924 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -21,6 +21,7 @@
from tempest.common.utils import data_utils
from tempest.openstack.common import log as logging
from tempest.test import attr
+from testtools.matchers import ContainsAll
LOG = logging.getLogger(__name__)
@@ -110,7 +111,12 @@
for key in params:
msg = "Failed to list volumes %s by %s" % \
('details' if with_detail else '', key)
- self.assertEqual(params[key], volume[key], msg)
+ if key == 'metadata':
+ self.assertThat(volume[key].items(),
+ ContainsAll(params[key].items()),
+ msg)
+ else:
+ self.assertEqual(params[key], volume[key], msg)
@attr(type='smoke')
def test_volume_list(self):
diff --git a/tempest/config.py b/tempest/config.py
index 0ea39ab..c346aef 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -78,7 +78,7 @@
secret=True),
cfg.StrOpt('admin_username',
default='admin',
- help="Administrative Username to use for"
+ help="Administrative Username to use for "
"Keystone API requests."),
cfg.StrOpt('admin_tenant_name',
default='admin',
@@ -415,19 +415,11 @@
title='Enabled object-storage features')
ObjectStoreFeaturesGroup = [
- cfg.BoolOpt('container_quotas',
- default=True,
- help="Set to True if the Container Quota middleware "
- "is enabled"),
- cfg.BoolOpt('accounts_quotas',
- default=True,
- help="Set to True if the Account Quota middleware is enabled"),
- cfg.BoolOpt('crossdomain',
- default=True,
- help="Set to True if the Crossdomain middleware is enabled"),
- cfg.BoolOpt('tempurl',
- default=True,
- help="Set to True if the TempURL middleware is enabled"),
+ cfg.ListOpt('discoverable_apis',
+ default=['all'],
+ help="A list of the enabled optional discoverable apis. "
+ "A single entry, all, indicates that all of these "
+ "features are expected to be enabled"),
]
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 409fcc2..46f0d02 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -527,6 +527,13 @@
private_key = self.keypair.private_key
return RemoteClient(ip, username, pkey=private_key)
+ def _log_console_output(self, servers=None):
+ if not servers:
+ servers = self.compute_client.servers.list()
+ for server in servers:
+ LOG.debug('Console output for %s', server.id)
+ LOG.debug(server.get_console_output())
+
class NetworkScenarioTest(OfficialClientTest):
"""
diff --git a/tempest/scenario/test_cross_tenant_connectivity.py b/tempest/scenario/test_cross_tenant_connectivity.py
index faba987..a269017 100644
--- a/tempest/scenario/test_cross_tenant_connectivity.py
+++ b/tempest/scenario/test_cross_tenant_connectivity.py
@@ -371,6 +371,7 @@
msg)
except Exception:
debug.log_ip_ns()
+ self._log_console_output(servers=self.servers)
raise
def _test_in_tenant_block(self, tenant):
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 8a51cd1..890d00f 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -131,7 +131,12 @@
self.server.add_floating_ip(self.floating_ip)
def ssh_to_server(self):
- self.linux_client = self.get_remote_client(self.floating_ip.ip)
+ try:
+ self.linux_client = self.get_remote_client(self.floating_ip.ip)
+ except Exception:
+ LOG.exception('ssh to server failed')
+ self._log_console_output()
+ raise
def check_partitions(self):
partitions = self.linux_client.get_partitions()
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 52a36e6..94e0f81 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -213,11 +213,6 @@
name = data_utils.rand_name('server-smoke-%d-' % i)
self._create_server(name, network)
- def _log_console_output(self):
- for server, key in self.servers.items():
- LOG.debug('Console output for %s', server.id)
- LOG.debug(server.get_console_output())
-
def _check_tenant_network_connectivity(self):
if not CONF.network.tenant_networks_reachable:
msg = 'Tenant networks not configured to be reachable.'
@@ -234,7 +229,8 @@
key.private_key)
except Exception:
LOG.exception('Tenant connectivity check failed')
- self._log_console_output()
+ self._log_console_output(
+ servers=[server for server, _key in self.servers])
debug.log_ip_ns()
raise
@@ -272,7 +268,8 @@
should_connect=should_connect)
except Exception:
LOG.exception('Public network connectivity check failed')
- self._log_console_output()
+ self._log_console_output(
+ servers=[server for server, _key in self.servers])
debug.log_ip_ns()
raise
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index ca3035d..279b80e 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -151,10 +151,15 @@
instance = self.get_resource('instance')
instance.add_floating_ip(floating_ip)
# Check ssh
- self.get_remote_client(
- server_or_ip=floating_ip.ip,
- username=self.image_utils.ssh_user(self.image_ref),
- private_key=self.keypair.private)
+ try:
+ self.get_remote_client(
+ server_or_ip=floating_ip.ip,
+ username=self.image_utils.ssh_user(self.image_ref),
+ private_key=self.keypair.private)
+ except Exception:
+ LOG.exception('ssh to server failed')
+ self._log_console_output()
+ raise
@services('compute', 'network')
def test_server_basicops(self):
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 00139f0..874bc60 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -15,10 +15,14 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.openstack.common import log
from tempest.scenario import manager
from tempest.test import services
+LOG = log.getLogger(__name__)
+
+
class TestSnapshotPattern(manager.OfficialClientTest):
"""
This test is for snapshotting an instance and booting with it.
@@ -40,7 +44,11 @@
self.keypair = self.create_keypair()
def _ssh_to_server(self, server_or_ip):
- linux_client = self.get_remote_client(server_or_ip)
+ try:
+ linux_client = 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):
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index fa9a228..2a2b527 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -13,10 +13,14 @@
# under the License.
from tempest.common.utils import data_utils
+from tempest.openstack.common import log
from tempest.scenario import manager
from tempest.test import services
+LOG = log.getLogger(__name__)
+
+
class TestVolumeBootPattern(manager.OfficialClientTest):
"""
@@ -96,8 +100,14 @@
network_name_for_ssh = self.config.compute.network_for_ssh
ip = server.networks[network_name_for_ssh][0]
- client = self.get_remote_client(ip,
- private_key=keypair.private_key)
+ try:
+ client = 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):
diff --git a/tempest/services/compute/json/extensions_client.py b/tempest/services/compute/json/extensions_client.py
index ad5354c..c293f80 100644
--- a/tempest/services/compute/json/extensions_client.py
+++ b/tempest/services/compute/json/extensions_client.py
@@ -31,7 +31,7 @@
url = 'extensions'
resp, body = self.get(url)
body = json.loads(body)
- return resp, body
+ return resp, body['extensions']
def is_enabled(self, extension):
_, extensions = self.list_extensions()
diff --git a/tempest/services/compute/v3/json/extensions_client.py b/tempest/services/compute/v3/json/extensions_client.py
index 6e0dc9d..bd8287e 100644
--- a/tempest/services/compute/v3/json/extensions_client.py
+++ b/tempest/services/compute/v3/json/extensions_client.py
@@ -32,7 +32,7 @@
url = 'extensions'
resp, body = self.get(url)
body = json.loads(body)
- return resp, body
+ return resp, body['extensions']
def is_enabled(self, extension):
_, extensions = self.list_extensions()
diff --git a/tempest/services/compute/v3/json/flavors_client.py b/tempest/services/compute/v3/json/flavors_client.py
index e99ac91..a790c40 100644
--- a/tempest/services/compute/v3/json/flavors_client.py
+++ b/tempest/services/compute/v3/json/flavors_client.py
@@ -61,7 +61,7 @@
'id': flavor_id,
}
if kwargs.get('ephemeral'):
- post_body['OS-FLV-EXT-DATA:ephemeral'] = kwargs.get('ephemeral')
+ post_body['ephemeral'] = kwargs.get('ephemeral')
if kwargs.get('swap'):
post_body['swap'] = kwargs.get('swap')
if kwargs.get('rxtx'):
diff --git a/tempest/services/compute/v3/json/limits_client.py b/tempest/services/compute/v3/json/limits_client.py
deleted file mode 100644
index 3e53e3e..0000000
--- a/tempest/services/compute/v3/json/limits_client.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 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 json
-from tempest.common.rest_client import RestClient
-
-
-class LimitsClientJSON(RestClient):
-
- def __init__(self, config, username, password, auth_url, tenant_name=None):
- super(LimitsClientJSON, self).__init__(config, username, password,
- auth_url, tenant_name)
- self.service = self.config.compute.catalog_type
-
- def get_absolute_limits(self):
- resp, body = self.get("limits")
- body = json.loads(body)
- return resp, body['limits']['absolute']
-
- def get_specific_absolute_limit(self, absolute_limit):
- resp, body = self.get("limits")
- body = json.loads(body)
- if absolute_limit not in body['limits']['absolute']:
- return None
- else:
- return body['limits']['absolute'][absolute_limit]
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index da31036..3342660 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -45,8 +45,6 @@
admin_password: Sets the initial root password.
key_name: Key name of keypair that was created earlier.
meta: A dictionary of values to be used as metadata.
- personality: A list of dictionaries for files to be injected into
- the server.
security_groups: A list of security group dicts.
networks: A list of network dicts with UUID and fixed_ip.
user_data: User data for instance.
@@ -64,7 +62,7 @@
'flavor_ref': flavor_ref
}
- for option in ['personality', 'admin_password', 'key_name', 'networks',
+ for option in ['admin_password', 'key_name', 'networks',
('os-security-groups:security_groups',
'security_groups'),
('os-user-data:user_data', 'user_data'),
@@ -103,7 +101,6 @@
Updates the properties of an existing server.
server_id: The id of an existing server.
name: The name of the server.
- personality: A list of files to be injected into the server.
access_ip_v4: The IPv4 access address for the server.
access_ip_v6: The IPv6 access address for the server.
"""
@@ -163,10 +160,12 @@
body = json.loads(body)
return resp, body
- def wait_for_server_status(self, server_id, status, extra_timeout=0):
+ def wait_for_server_status(self, server_id, status, extra_timeout=0,
+ raise_on_error=True):
"""Waits for a server to reach a given status."""
return waiters.wait_for_server_status(self, server_id, status,
- extra_timeout=extra_timeout)
+ extra_timeout=extra_timeout,
+ raise_on_error=raise_on_error)
def wait_for_server_termination(self, server_id, ignore_error=False):
"""Waits for server to reach termination."""
@@ -346,19 +345,19 @@
return self.action(server_id, 'unlock', None, **kwargs)
def suspend_server(self, server_id, **kwargs):
- """Suspends the provded server."""
+ """Suspends the provided server."""
return self.action(server_id, 'suspend', None, **kwargs)
def resume_server(self, server_id, **kwargs):
- """Un-suspends the provded server."""
+ """Un-suspends the provided server."""
return self.action(server_id, 'resume', None, **kwargs)
def pause_server(self, server_id, **kwargs):
- """Pauses the provded server."""
+ """Pauses the provided server."""
return self.action(server_id, 'pause', None, **kwargs)
def unpause_server(self, server_id, **kwargs):
- """Un-pauses the provded server."""
+ """Un-pauses the provided server."""
return self.action(server_id, 'unpause', None, **kwargs)
def reset_state(self, server_id, state='error'):
diff --git a/tempest/services/compute/v3/xml/extensions_client.py b/tempest/services/compute/v3/xml/extensions_client.py
index 8f97692..926770e 100644
--- a/tempest/services/compute/v3/xml/extensions_client.py
+++ b/tempest/services/compute/v3/xml/extensions_client.py
@@ -37,7 +37,7 @@
url = 'extensions'
resp, body = self.get(url, self.headers)
body = self._parse_array(etree.fromstring(body))
- return resp, {'extensions': body}
+ return resp, body
def is_enabled(self, extension):
_, extensions = self.list_extensions()
diff --git a/tempest/services/compute/v3/xml/limits_client.py b/tempest/services/compute/v3/xml/limits_client.py
deleted file mode 100644
index 704de52..0000000
--- a/tempest/services/compute/v3/xml/limits_client.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-#
-# Copyright 2012 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 lxml import objectify
-
-from tempest.common.rest_client import RestClientXML
-
-NS = "{http://docs.openstack.org/common/api/v1.0}"
-
-
-class LimitsClientXML(RestClientXML):
-
- def __init__(self, config, username, password, auth_url, tenant_name=None):
- super(LimitsClientXML, self).__init__(config, username, password,
- auth_url, tenant_name)
- self.service = self.config.compute.catalog_type
-
- def get_absolute_limits(self):
- resp, body = self.get("limits", self.headers)
- body = objectify.fromstring(body)
- lim = NS + 'absolute'
- ret = {}
-
- for el in body[lim].iterchildren():
- attributes = el.attrib
- ret[attributes['name']] = attributes['value']
- return resp, ret
-
- def get_specific_absolute_limit(self, absolute_limit):
- resp, body = self.get("limits", self.headers)
- body = objectify.fromstring(body)
- lim = NS + 'absolute'
- ret = {}
-
- for el in body[lim].iterchildren():
- attributes = el.attrib
- ret[attributes['name']] = attributes['value']
- if absolute_limit not in ret:
- return None
- else:
- return ret[absolute_limit]
diff --git a/tempest/services/compute/v3/xml/servers_client.py b/tempest/services/compute/v3/xml/servers_client.py
index a5cc291..5aca7e8 100644
--- a/tempest/services/compute/v3/xml/servers_client.py
+++ b/tempest/services/compute/v3/xml/servers_client.py
@@ -194,6 +194,10 @@
server = self._parse_server(etree.fromstring(body))
return resp, server
+ def migrate_server(self, server_id, **kwargs):
+ """Migrates the given server ."""
+ return self.action(server_id, 'migrate', None, **kwargs)
+
def lock_server(self, server_id, **kwargs):
"""Locks the given server."""
return self.action(server_id, 'lock', None, **kwargs)
@@ -300,8 +304,6 @@
admin_password: Sets the initial root password.
key_name: Key name of keypair that was created earlier.
meta: A dictionary of values to be used as metadata.
- personality: A list of dictionaries for files to be injected into
- the server.
security_groups: A list of security group dicts.
networks: A list of network dicts with UUID and fixed_ip.
user_data: User data for instance.
@@ -400,22 +402,16 @@
meta.append(Text(v))
metadata.append(meta)
- if 'personality' in kwargs:
- personality = Element('personality')
- server.append(personality)
- for k in kwargs['personality']:
- temp = Element('file', path=k['path'])
- temp.append(Text(k['contents']))
- personality.append(temp)
-
resp, body = self.post('servers', str(Document(server)), self.headers)
server = self._parse_server(etree.fromstring(body))
return resp, server
- def wait_for_server_status(self, server_id, status, extra_timeout=0):
+ def wait_for_server_status(self, server_id, status, extra_timeout=0,
+ raise_on_error=True):
"""Waits for a server to reach a given status."""
return waiters.wait_for_server_status(self, server_id, status,
- extra_timeout=extra_timeout)
+ extra_timeout=extra_timeout,
+ raise_on_error=raise_on_error)
def wait_for_server_termination(self, server_id, ignore_error=False):
"""Waits for server to reach termination."""
@@ -608,7 +604,7 @@
def set_server_metadata_item(self, server_id, key, meta):
doc = Document()
for k, v in meta.items():
- meta_element = Element("meta", key=k)
+ meta_element = Element("metadata", key=k)
meta_element.append(Text(v))
doc.append(meta_element)
resp, body = self.put('servers/%s/metadata/%s' % (str(server_id), key),
diff --git a/tempest/services/compute/xml/extensions_client.py b/tempest/services/compute/xml/extensions_client.py
index b17fc4f..8b7b90a 100644
--- a/tempest/services/compute/xml/extensions_client.py
+++ b/tempest/services/compute/xml/extensions_client.py
@@ -37,7 +37,7 @@
url = 'extensions'
resp, body = self.get(url, self.headers)
body = self._parse_array(etree.fromstring(body))
- return resp, {'extensions': body}
+ return resp, body
def is_enabled(self, extension):
_, extensions = self.list_extensions()
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index e896e0d..d5ae828 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -102,6 +102,20 @@
body = json.loads(body)
return resp, body['stack']
+ def suspend_stack(self, stack_identifier):
+ """Suspend a stack."""
+ url = 'stacks/%s/actions' % stack_identifier
+ body = {'suspend': None}
+ resp, body = self.post(url, json.dumps(body), self.headers)
+ return resp, body
+
+ def resume_stack(self, stack_identifier):
+ """Resume a stack."""
+ url = 'stacks/%s/actions' % stack_identifier
+ body = {'resume': None}
+ resp, body = self.post(url, json.dumps(body), self.headers)
+ return resp, body
+
def list_resources(self, stack_identifier):
"""Returns the details of a single resource."""
url = "stacks/%s/resources" % stack_identifier
diff --git a/tempest/stress/__init__.py b/tempest/stress/__init__.py
index 1caf74a..e69de29 100644
--- a/tempest/stress/__init__.py
+++ b/tempest/stress/__init__.py
@@ -1,13 +0,0 @@
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-# 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.
diff --git a/tempest/stress/actions/__init__.py b/tempest/stress/actions/__init__.py
index 1caf74a..e69de29 100644
--- a/tempest/stress/actions/__init__.py
+++ b/tempest/stress/actions/__init__.py
@@ -1,13 +0,0 @@
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-# 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.
diff --git a/tempest/test.py b/tempest/test.py
index 342846f..d57ed83 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -163,6 +163,7 @@
'compute_v3': configs.compute_feature_enabled.api_v3_extensions,
'volume': configs.volume_feature_enabled.api_extensions,
'network': configs.network_feature_enabled.api_extensions,
+ 'object': configs.object_storage_feature_enabled.discoverable_apis,
}
if config_dict[service][0] == 'all':
return True
diff --git a/test-requirements.txt b/test-requirements.txt
index 9486244..d7340f3 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -6,3 +6,4 @@
oslo.sphinx
mox>=0.5.3
mock>=1.0
+coverage>=3.6
diff --git a/tox.ini b/tox.ini
index 6d596e3..88f2537 100644
--- a/tox.ini
+++ b/tox.ini
@@ -25,6 +25,10 @@
setenv = OS_TEST_PATH=./tempest/tests
commands = python setup.py test --slowest --testr-arg='tempest\.tests {posargs}'
+[testenv:cover]
+setenv = OS_TEST_PATH=./tempest/tests
+commands = python setup.py testr --coverage --testr-arg='tempest\.tests {posargs}'
+
[testenv:all]
setenv = VIRTUAL_ENV={envdir}
commands =