Merge "Move admin "delete a server" tests to test_servers"
diff --git a/HACKING.rst b/HACKING.rst
index e7e7651..c0df0fb 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -108,16 +108,36 @@
Negative Tests
--------------
-When adding negative tests to tempest there are 2 requirements. First the tests
-must be marked with a negative attribute. For example::
+Newly added negative tests should use the negative test framework. First step
+is to create an interface description in a json file under `etc/schemas`.
+These descriptions consists of two important sections for the test
+(one of those is mandatory):
- @attr(type=negative)
- def test_resource_no_uuid(self):
- ...
+ - A resource (part of the URL of the request): Resources needed for a test
+ must be created in `setUpClass` and registered with `set_resource` e.g.:
+ `cls.set_resource("server", server['id'])`
-The second requirement is that all negative tests must be added to a negative
-test file. If such a file doesn't exist for the particular resource being
-tested a new test file should be added.
+ - A json schema: defines properties for a request.
+
+After that a test class must be added to automatically generate test scenarios
+out of the given interface description:
+
+ class SampeTestNegativeTestJSON(<your base class>, test.NegativeAutoTest):
+ _interface = 'json'
+ _service = 'compute'
+ _schema_file = 'compute/servers/get_console_output.json'
+ scenarios = test.NegativeAutoTest.generate_scenario(_schema_file)
+
+Negative tests must be marked with a negative attribute::
+
+ @test.attr(type=['negative', 'gate'])
+ def test_get_console_output(self):
+ self.execute(self._schema_file)
+
+All negative tests should be added into a separate negative test file.
+If such a file doesn't exist for the particular resource being tested a new
+test file should be added. Old XML based negative tests can be kept but should
+be renamed to `_xml.py`.
Test skips because of Known Bugs
--------------------------------
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index a2d3877..0f18f5e 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -320,6 +320,10 @@
# If false, skip disk config tests (boolean value)
#disk_config=true
+# A list of enabled compute extensions with a special entry
+# all which indicates every extension is enabled (list value)
+#api_extensions=all
+
# A list of enabled v3 extensions with a special entry all
# which indicates every extension is enabled (list value)
#api_v3_extensions=all
@@ -582,16 +586,8 @@
# Options defined in tempest.config
#
-# A list of enabled extensions with a special entry all which
-# indicates every extension is enabled (list value)
-#api_extensions=all
-
-# A list of enabled extensions with a special entry all which
-# indicates every extension is enabled (list value)
-#api_extensions=all
-
-# A list of enabled extensions with a special entry all which
-# indicates every extension is enabled (list value)
+# A list of enabled network extensions with a special entry
+# all which indicates every extension is enabled (list value)
#api_extensions=all
@@ -877,6 +873,10 @@
# Runs Cinder volumes backup test (boolean value)
#backup=true
+# A list of enabled volume extensions with a special entry all
+# which indicates every extension is enabled (list value)
+#api_extensions=all
+
# Is the v1 volume API enabled (boolean value)
#api_v1=true
diff --git a/setup.cfg b/setup.cfg
index 79f538f..a701572 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -4,8 +4,8 @@
summary = OpenStack Integration Testing
description-file =
README.rst
-author = OpenStack QA
-author-email = openstack-qa@lists.openstack.org
+author = OpenStack
+author-email = openstack-dev@lists.openstack.org
home-page = http://www.openstack.org/
classifier =
Intended Audience :: Information Technology
diff --git a/tempest/api/compute/v3/admin/test_quotas.py b/tempest/api/compute/v3/admin/test_quotas.py
index 3ebbdeb..b388b70 100644
--- a/tempest/api/compute/v3/admin/test_quotas.py
+++ b/tempest/api/compute/v3/admin/test_quotas.py
@@ -16,7 +16,6 @@
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest import config
-from tempest import exceptions
from tempest import test
CONF = config.CONF
@@ -54,6 +53,22 @@
sorted(quota_set.keys()))
self.assertEqual(quota_set['id'], self.demo_tenant_id)
+ @test.attr(type='smoke')
+ 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'}
+ resp, quota_set = self.adm_client.get_quota_set_detail(
+ self.demo_tenant_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(sorted(expected_quota_set), sorted(quota_set.keys()))
+ self.assertEqual(quota_set['id'], self.demo_tenant_id)
+ for quota in quota_set:
+ if quota == 'id':
+ continue
+ self.assertEqual(sorted(expected_detail),
+ sorted(quota_set[quota].keys()))
+
@test.attr(type='gate')
def test_update_all_quota_resources_for_tenant(self):
# Admin can update all the resource quota limits for a tenant
@@ -95,56 +110,3 @@
resp, quota_set = self.adm_client.get_quota_set(tenant_id)
self.assertEqual(200, resp.status)
self.assertEqual(quota_set['ram'], 5120)
-
- # TODO(afazekas): Add dedicated tenant to the skiped quota tests
- # it can be moved into the setUpClass as well
- @test.attr(type='gate')
- def test_create_server_when_cpu_quota_is_full(self):
- # Disallow server creation when tenant's vcpu quota is full
- resp, quota_set = self.adm_client.get_quota_set(self.demo_tenant_id)
- default_vcpu_quota = quota_set['cores']
- vcpu_quota = 0 # Set the quota to zero to conserve resources
-
- resp, quota_set = self.adm_client.update_quota_set(self.demo_tenant_id,
- force=True,
- cores=vcpu_quota)
-
- self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
- cores=default_vcpu_quota)
- self.assertRaises(exceptions.OverLimit, self.create_test_server)
-
- @test.attr(type='gate')
- def test_create_server_when_memory_quota_is_full(self):
- # Disallow server creation when tenant's memory quota is full
- resp, quota_set = self.adm_client.get_quota_set(self.demo_tenant_id)
- default_mem_quota = quota_set['ram']
- mem_quota = 0 # Set the quota to zero to conserve resources
-
- self.adm_client.update_quota_set(self.demo_tenant_id,
- force=True,
- ram=mem_quota)
-
- self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
- ram=default_mem_quota)
- self.assertRaises(exceptions.OverLimit, self.create_test_server)
-
- @test.attr(type='gate')
- def test_update_quota_normal_user(self):
- self.assertRaises(exceptions.Unauthorized,
- self.client.update_quota_set,
- self.demo_tenant_id,
- ram=0)
-
- @test.attr(type=['negative', 'gate'])
- def test_create_server_when_instances_quota_is_full(self):
- # Once instances quota limit is reached, disallow server creation
- resp, quota_set = self.adm_client.get_quota_set(self.demo_tenant_id)
- default_instances_quota = quota_set['instances']
- instances_quota = 0 # Set quota to zero to disallow server creation
-
- self.adm_client.update_quota_set(self.demo_tenant_id,
- force=True,
- instances=instances_quota)
- self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
- instances=default_instances_quota)
- self.assertRaises(exceptions.OverLimit, self.create_test_server)
diff --git a/tempest/api/compute/v3/admin/test_quotas_negative.py b/tempest/api/compute/v3/admin/test_quotas_negative.py
new file mode 100644
index 0000000..c9f14f8
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_quotas_negative.py
@@ -0,0 +1,88 @@
+# Copyright 2013 OpenStack Foundation
+# 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 exceptions
+from tempest import test
+
+
+class QuotasAdminNegativeV3Test(base.BaseV3ComputeAdminTest):
+ _interface = 'json'
+ force_tenant_isolation = True
+
+ @classmethod
+ def setUpClass(cls):
+ super(QuotasAdminNegativeV3Test, cls).setUpClass()
+ cls.client = cls.quotas_client
+ cls.adm_client = cls.quotas_admin_client
+
+ # NOTE(afazekas): these test cases should always create and use a new
+ # tenant most of them should be skipped if we can't do that
+ cls.demo_tenant_id = cls.isolated_creds.get_primary_user().get(
+ 'tenantId')
+
+ # TODO(afazekas): Add dedicated tenant to the skiped quota tests
+ # it can be moved into the setUpClass as well
+ @test.attr(type=['negative', 'gate'])
+ def test_create_server_when_cpu_quota_is_full(self):
+ # Disallow server creation when tenant's vcpu quota is full
+ resp, quota_set = self.adm_client.get_quota_set(self.demo_tenant_id)
+ default_vcpu_quota = quota_set['cores']
+ vcpu_quota = 0 # Set the quota to zero to conserve resources
+
+ resp, quota_set = self.adm_client.update_quota_set(self.demo_tenant_id,
+ force=True,
+ cores=vcpu_quota)
+
+ self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
+ cores=default_vcpu_quota)
+ self.assertRaises(exceptions.OverLimit, self.create_test_server)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_server_when_memory_quota_is_full(self):
+ # Disallow server creation when tenant's memory quota is full
+ resp, quota_set = self.adm_client.get_quota_set(self.demo_tenant_id)
+ default_mem_quota = quota_set['ram']
+ mem_quota = 0 # Set the quota to zero to conserve resources
+
+ self.adm_client.update_quota_set(self.demo_tenant_id,
+ force=True,
+ ram=mem_quota)
+
+ self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
+ ram=default_mem_quota)
+ self.assertRaises(exceptions.OverLimit, self.create_test_server)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_update_quota_normal_user(self):
+ self.assertRaises(exceptions.Unauthorized,
+ self.client.update_quota_set,
+ self.demo_tenant_id,
+ ram=0)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_server_when_instances_quota_is_full(self):
+ # Once instances quota limit is reached, disallow server creation
+ resp, quota_set = self.adm_client.get_quota_set(self.demo_tenant_id)
+ default_instances_quota = quota_set['instances']
+ instances_quota = 0 # Set quota to zero to disallow server creation
+
+ self.adm_client.update_quota_set(self.demo_tenant_id,
+ force=True,
+ instances=instances_quota)
+ self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
+ instances=default_instances_quota)
+ self.assertRaises(exceptions.OverLimit, self.create_test_server)
diff --git a/tempest/api/compute/v3/servers/test_instance_actions.py b/tempest/api/compute/v3/servers/test_instance_actions.py
index d536871..c4f6e14 100644
--- a/tempest/api/compute/v3/servers/test_instance_actions.py
+++ b/tempest/api/compute/v3/servers/test_instance_actions.py
@@ -14,8 +14,7 @@
# under the License.
from tempest.api.compute import base
-from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class InstanceActionsV3Test(base.BaseV3ComputeTest):
@@ -29,7 +28,7 @@
cls.request_id = resp['x-compute-request-id']
cls.server_id = server['id']
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_instance_actions(self):
# List actions of the provided server
resp, body = self.client.reboot(self.server_id, 'HARD')
@@ -41,7 +40,7 @@
self.assertTrue(any([i for i in body if i['action'] == 'create']))
self.assertTrue(any([i for i in body if i['action'] == 'reboot']))
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_instance_action(self):
# Get the action details of the provided server
resp, body = self.client.get_instance_action(self.server_id,
@@ -49,15 +48,3 @@
self.assertEqual(200, resp.status)
self.assertEqual(self.server_id, body['instance_uuid'])
self.assertEqual('create', body['action'])
-
- @attr(type=['negative', 'gate'])
- def test_list_instance_actions_invalid_server(self):
- # List actions of the invalid server id
- self.assertRaises(exceptions.NotFound,
- self.client.list_instance_actions, 'server-999')
-
- @attr(type=['negative', 'gate'])
- def test_get_instance_action_invalid_request(self):
- # Get the action details of the provided server with invalid request
- self.assertRaises(exceptions.NotFound, self.client.get_instance_action,
- self.server_id, '999')
diff --git a/tempest/api/compute/v3/servers/test_instance_actions_negative.py b/tempest/api/compute/v3/servers/test_instance_actions_negative.py
new file mode 100644
index 0000000..bd741e0
--- /dev/null
+++ b/tempest/api/compute/v3/servers/test_instance_actions_negative.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.
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest import test
+
+
+class InstanceActionsNegativeV3Test(base.BaseV3ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(InstanceActionsNegativeV3Test, cls).setUpClass()
+ cls.client = cls.servers_client
+ resp, server = cls.create_test_server(wait_until='ACTIVE')
+ cls.server_id = server['id']
+
+ @test.attr(type=['negative', 'gate'])
+ def test_list_instance_actions_invalid_server(self):
+ # List actions of the invalid server id
+ invalid_server_id = data_utils.rand_uuid()
+ self.assertRaises(exceptions.NotFound,
+ self.client.list_instance_actions, invalid_server_id)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_get_instance_action_invalid_request(self):
+ # Get the action details of the provided server with invalid request
+ invalid_request_id = 'req-' + data_utils.rand_uuid()
+ self.assertRaises(exceptions.NotFound, self.client.get_instance_action,
+ self.server_id, invalid_request_id)
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index d08dc34..45c895b 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -130,7 +130,10 @@
objlist = container_client.list_all_container_objects(cont)
# delete every object in the container
for obj in objlist:
- object_client.delete_object(cont, obj['name'])
+ try:
+ object_client.delete_object(cont, obj['name'])
+ except exceptions.NotFound:
+ pass
container_client.delete_container(cont)
except exceptions.NotFound:
pass
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index dbeba8f..8e6b9fb 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -42,7 +42,7 @@
timeout = client.build_timeout + extra_timeout
while True:
# NOTE(afazekas): Now the BUILD status only reached
- # between the UNKOWN->ACTIVE transition.
+ # between the UNKNOWN->ACTIVE transition.
# TODO(afazekas): enumerate and validate the stable status set
if status == 'BUILD' and server_status != 'UNKNOWN':
return
diff --git a/tempest/config.py b/tempest/config.py
index c92a04d..4380608 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -15,6 +15,7 @@
from __future__ import print_function
+import logging as std_logging
import os
from oslo.config import cfg
@@ -235,8 +236,8 @@
help="If false, skip disk config tests"),
cfg.ListOpt('api_extensions',
default=['all'],
- help='A list of enabled extensions with a special entry all '
- 'which indicates every extension is enabled'),
+ help='A list of enabled compute extensions with a special '
+ 'entry all which indicates every extension is enabled'),
cfg.ListOpt('api_v3_extensions',
default=['all'],
help='A list of enabled v3 extensions with a special entry all'
@@ -372,8 +373,8 @@
NetworkFeaturesGroup = [
cfg.ListOpt('api_extensions',
default=['all'],
- help='A list of enabled extensions with a special entry all '
- 'which indicates every extension is enabled'),
+ help='A list of enabled network extensions with a special '
+ 'entry all which indicates every extension is enabled'),
]
volume_group = cfg.OptGroup(name='volume',
@@ -430,8 +431,8 @@
help='Runs Cinder volumes backup test'),
cfg.ListOpt('api_extensions',
default=['all'],
- help='A list of enabled extensions with a special entry all '
- 'which indicates every extension is enabled'),
+ help='A list of enabled volume extensions with a special '
+ 'entry all which indicates every extension is enabled'),
cfg.BoolOpt('api_v1',
default=True,
help="Is the v1 volume API enabled"),
@@ -876,6 +877,9 @@
self.compute_admin.password = self.identity.admin_password
self.compute_admin.tenant_name = self.identity.admin_tenant_name
+ if parse_conf:
+ cfg.CONF.log_opt_values(LOG, std_logging.DEBUG)
+
class TempestConfigProxy(object):
_config = None
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 6757da6..1763808 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -404,7 +404,7 @@
properties={'disk_format':
'qcow2'})
except IOError:
- LOG.debug("A qcow2 image was not got. Try to get a uec image.")
+ LOG.debug("A qcow2 image was not found. Try to get a uec image.")
kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
properties = {
diff --git a/tempest/scenario/test_load_balancer_basic.py b/tempest/scenario/test_load_balancer_basic.py
index 2f7d9d9..5bcdacd 100644
--- a/tempest/scenario/test_load_balancer_basic.py
+++ b/tempest/scenario/test_load_balancer_basic.py
@@ -61,12 +61,8 @@
super(TestLoadBalancerBasic, cls).setUpClass()
cls.check_preconditions()
cls.security_groups = {}
- cls.networks = []
- cls.subnets = []
cls.servers_keypairs = {}
- cls.pools = []
cls.members = []
- cls.vips = []
cls.floating_ips = {}
cls.port1 = 80
cls.port2 = 88
@@ -80,21 +76,19 @@
name = data_utils.rand_name("smoke_server-")
keypair = self.create_keypair(name='keypair-%s' % name)
security_groups = [self.security_groups[tenant_id].name]
- nets = self.network_client.list_networks()
- for net in nets['networks']:
- if net['tenant_id'] == self.tenant_id:
- self.networks.append(net)
- create_kwargs = {
- 'nics': [
- {'net-id': net['id']},
- ],
- 'key_name': keypair.name,
- 'security_groups': security_groups,
- }
- server = self.create_server(name=name,
- create_kwargs=create_kwargs)
- self.servers_keypairs[server] = keypair
- break
+ net = self.list_networks(tenant_id=self.tenant_id)[0]
+ self.network = net_common.DeletableNetwork(client=self.network_client,
+ **net['network'])
+ create_kwargs = {
+ 'nics': [
+ {'net-id': self.network.id},
+ ],
+ 'key_name': keypair.name,
+ 'security_groups': security_groups,
+ }
+ server = self.create_server(name=name,
+ create_kwargs=create_kwargs)
+ self.servers_keypairs[server] = keypair
self.assertTrue(self.servers_keypairs)
def _start_servers(self):
@@ -105,7 +99,7 @@
for server in self.servers_keypairs.keys():
ssh_login = config.compute.image_ssh_user
private_key = self.servers_keypairs[server].private_key
- network_name = self.networks[0]['name']
+ network_name = self.network.name
ip_address = server.networks[network_name][0]
ssh_client = ssh.Client(ip_address, ssh_login,
@@ -138,17 +132,15 @@
def _create_pool(self):
"""Create a pool with ROUND_ROBIN algorithm."""
- subnets = self.network_client.list_subnets()
- for subnet in subnets['subnets']:
- if subnet['tenant_id'] == self.tenant_id:
- self.subnets.append(subnet)
- pool = super(TestLoadBalancerBasic, self)._create_pool(
- 'ROUND_ROBIN',
- 'HTTP',
- subnet['id'])
- self.pools.append(pool)
- break
- self.assertTrue(self.pools)
+ # get tenant subnet and verify there's only one
+ subnet = self._list_subnets(tenant_id=self.tenant_id)[0]
+ self.subnet = net_common.DeletableSubnet(client=self.network_client,
+ **subnet['subnet'])
+ self.pool = super(TestLoadBalancerBasic, self)._create_pool(
+ 'ROUND_ROBIN',
+ 'HTTP',
+ self.subnet.id)
+ self.assertTrue(self.pool)
def _create_members(self, network_name, server_ids):
"""
@@ -161,7 +153,7 @@
for server in servers:
if server.id in server_ids:
ip = server.networks[network_name][0]
- pool_id = self.pools[0]['id']
+ pool_id = self.pool.id
if len(set(server_ids)) == 1 or len(servers) == 1:
member1 = self._create_member(ip, self.port1, pool_id)
member2 = self._create_member(ip, self.port2, pool_id)
@@ -173,28 +165,27 @@
def _assign_floating_ip_to_vip(self, vip):
public_network_id = config.network.public_network_id
- port_id = vip['port_id']
+ port_id = vip.port_id
floating_ip = self._create_floating_ip(vip, public_network_id,
port_id=port_id)
- self.floating_ips.setdefault(vip['id'], [])
- self.floating_ips[vip['id']].append(floating_ip)
+ self.floating_ips.setdefault(vip.id, [])
+ self.floating_ips[vip.id].append(floating_ip)
def _create_load_balancer(self):
self._create_pool()
- self._create_members(self.networks[0]['name'],
+ self._create_members(self.network.name,
[self.servers_keypairs.keys()[0].id])
- subnet_id = self.subnets[0]['id']
- pool_id = self.pools[0]['id']
- vip = super(TestLoadBalancerBasic, self)._create_vip('HTTP', 80,
- subnet_id,
- pool_id)
- self.vips.append(vip)
+ subnet_id = self.subnet.id
+ pool_id = self.pool.id
+ self.vip = super(TestLoadBalancerBasic, self)._create_vip('HTTP', 80,
+ subnet_id,
+ pool_id)
self._status_timeout(NeutronRetriever(self.network_client,
self.network_client.vip_path,
net_common.DeletableVip),
- self.vips[0]['id'],
+ self.vip.id,
expected_status='ACTIVE')
- self._assign_floating_ip_to_vip(self.vips[0])
+ self._assign_floating_ip_to_vip(self.vip)
def _check_load_balancing(self):
"""
@@ -204,9 +195,8 @@
of the requests
"""
- vip = self.vips[0]
- floating_ip_vip = self.floating_ips[
- vip['id']][0]['floating_ip_address']
+ vip = self.vip
+ floating_ip_vip = self.floating_ips[vip.id][0]['floating_ip_address']
self._check_connection(floating_ip_vip)
resp = []
for count in range(10):
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index a4002d4..998a474 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -13,6 +13,7 @@
# 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 collections
from tempest.common import debug
from tempest.common.utils import data_utils
@@ -24,6 +25,9 @@
CONF = config.CONF
LOG = logging.getLogger(__name__)
+Floating_IP_tuple = collections.namedtuple('Floating_IP_tuple',
+ ['floating_ip', 'server'])
+
class TestNetworkBasicOps(manager.NetworkScenarioTest):
@@ -134,7 +138,7 @@
serv_dict = self._create_server(name, self.network)
self.servers[serv_dict['server']] = serv_dict['keypair']
self._check_tenant_network_connectivity()
- self.floating_ips = {}
+
self._create_and_associate_floating_ips()
def check_networks(self):
@@ -178,11 +182,6 @@
self.addCleanup(self.cleanup_wrapper, server)
return dict(server=server, keypair=keypair)
- def _create_servers(self):
- for i, network in enumerate(self.networks):
- name = data_utils.rand_name('server-smoke-%d-' % i)
- self._create_server(name, network)
-
def _check_tenant_network_connectivity(self):
if not CONF.network.tenant_networks_reachable:
msg = 'Tenant networks not configured to be reachable.'
@@ -207,7 +206,7 @@
public_network_id = CONF.network.public_network_id
for server in self.servers.keys():
floating_ip = self._create_floating_ip(server, public_network_id)
- self.floating_ips[floating_ip] = server
+ self.floating_ip_tuple = Floating_IP_tuple(floating_ip, server)
self.addCleanup(self.cleanup_wrapper, floating_ip)
def _check_public_network_connectivity(self, should_connect=True,
@@ -216,16 +215,16 @@
# key-based authentication by cloud-init.
ssh_login = CONF.compute.image_ssh_user
LOG.debug('checking network connections')
+ floating_ip, server = self.floating_ip_tuple
+ ip_address = floating_ip.floating_ip_address
+ private_key = None
+ if should_connect:
+ private_key = self.servers[server].private_key
try:
- for floating_ip, server in self.floating_ips.iteritems():
- ip_address = floating_ip.floating_ip_address
- private_key = None
- if should_connect:
- private_key = self.servers[server].private_key
- self._check_vm_connectivity(ip_address,
- ssh_login,
- private_key,
- should_connect=should_connect)
+ self._check_vm_connectivity(ip_address,
+ ssh_login,
+ private_key,
+ should_connect=should_connect)
except Exception:
ex_msg = 'Public network connectivity check failed'
if msg:
@@ -236,18 +235,20 @@
raise
def _disassociate_floating_ips(self):
- for floating_ip, server in self.floating_ips.iteritems():
- self._disassociate_floating_ip(floating_ip)
- self.floating_ips[floating_ip] = None
+ floating_ip, server = self.floating_ip_tuple
+ self._disassociate_floating_ip(floating_ip)
+ self.floating_ip_tuple = Floating_IP_tuple(
+ floating_ip, None)
def _reassociate_floating_ips(self):
- for floating_ip in self.floating_ips.keys():
- name = data_utils.rand_name('new_server-smoke-')
- # create a new server for the floating ip
- serv_dict = self._create_server(name, self.network)
- self.servers[serv_dict['server']] = serv_dict['keypair']
- self._associate_floating_ip(floating_ip, serv_dict['server'])
- self.floating_ips[floating_ip] = serv_dict['server']
+ floating_ip, server = self.floating_ip_tuple
+ name = data_utils.rand_name('new_server-smoke-')
+ # create a new server for the floating ip
+ serv_dict = self._create_server(name, self.network)
+ self.servers[serv_dict['server']] = serv_dict['keypair']
+ self._associate_floating_ip(floating_ip, serv_dict['server'])
+ self.floating_ip_tuple = Floating_IP_tuple(
+ floating_ip, serv_dict['server'])
@test.attr(type='smoke')
@test.services('compute', 'network')
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 8e4192d..a26e0cf 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -148,9 +148,6 @@
cls.check_preconditions()
# TODO(mnewby) Consider looking up entities as needed instead
# of storing them as collections on the class.
- cls.networks = []
- cls.subnets = []
- cls.routers = []
cls.floating_ips = {}
cls.tenants = {}
cls.primary_tenant = cls.TenantProperties(cls.tenant_id,
diff --git a/tempest/services/compute/v3/json/quotas_client.py b/tempest/services/compute/v3/json/quotas_client.py
index a01b9d2..aa8bfaf 100644
--- a/tempest/services/compute/v3/json/quotas_client.py
+++ b/tempest/services/compute/v3/json/quotas_client.py
@@ -35,6 +35,14 @@
body = json.loads(body)
return resp, body['quota_set']
+ def get_quota_set_detail(self, tenant_id):
+ """Get the quota set detail for a tenant."""
+
+ url = 'os-quota-sets/%s/detail' % str(tenant_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['quota_set']
+
def get_default_quota_set(self, tenant_id):
"""List the default quota set for a tenant."""
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index 42237ca..41b0558 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -21,6 +21,9 @@
class fake_identity(object):
disable_ssl_certificate_validation = True
+ catalog_type = 'identity'
+ uri = 'http://fake_uri.com/auth'
+ uri_v3 = 'http://fake_uri_v3.com/auth'
class fake_default_feature_enabled(object):
api_extensions = ['all']
diff --git a/tempest/tests/fake_http.py b/tempest/tests/fake_http.py
index ac5f765..a09d5ba 100644
--- a/tempest/tests/fake_http.py
+++ b/tempest/tests/fake_http.py
@@ -17,7 +17,7 @@
class fake_httplib2(object):
- def __init__(self, return_type=None):
+ def __init__(self, return_type=None, *args, **kwargs):
self.return_type = return_type
def request(self, uri, method="GET", body=None, headers=None,
diff --git a/tempest/tests/fake_identity.py b/tempest/tests/fake_identity.py
new file mode 100644
index 0000000..ea2bd44
--- /dev/null
+++ b/tempest/tests/fake_identity.py
@@ -0,0 +1,156 @@
+# Copyright 2014 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.
+
+
+import httplib2
+import json
+
+
+TOKEN = "fake_token"
+ALT_TOKEN = "alt_fake_token"
+
+# Fake Identity v2 constants
+COMPUTE_ENDPOINTS_V2 = {
+ "endpoints": [
+ {
+ "adminURL": "http://fake_url/api/admin",
+ "region": "NoMatchRegion",
+ "internalURL": "http://fake_url/api/internal",
+ "publicURL": "http://fake_url/api/public"
+ },
+ {
+ "adminURL": "http://fake_url/api/admin",
+ "region": "FakeRegion",
+ "internalURL": "http://fake_url/api/internal",
+ "publicURL": "http://fake_url/api/public"
+ },
+ ],
+ "type": "compute",
+ "name": "nova"
+}
+
+CATALOG_V2 = [COMPUTE_ENDPOINTS_V2, ]
+
+ALT_IDENTITY_V2_RESPONSE = {
+ "access": {
+ "token": {
+ "expires": "2020-01-01T00:00:10Z",
+ "id": ALT_TOKEN,
+ "tenant": {
+ "id": "fake_tenant_id"
+ },
+ },
+ "user": {
+ "id": "fake_user_id",
+ },
+ "serviceCatalog": CATALOG_V2,
+ },
+}
+
+IDENTITY_V2_RESPONSE = {
+ "access": {
+ "token": {
+ "expires": "2020-01-01T00:00:10Z",
+ "id": TOKEN,
+ "tenant": {
+ "id": "fake_tenant_id"
+ },
+ },
+ "user": {
+ "id": "fake_user_id",
+ },
+ "serviceCatalog": CATALOG_V2,
+ },
+}
+
+# Fake Identity V3 constants
+COMPUTE_ENDPOINTS_V3 = {
+ "endpoints": [
+ {
+ "id": "fake_service",
+ "interface": "public",
+ "region": "NoMatchRegion",
+ "url": "http://fake_url/v3"
+ },
+ {
+ "id": "another_fake_service",
+ "interface": "public",
+ "region": "FakeRegion",
+ "url": "http://fake_url/v3"
+ }
+ ],
+ "type": "compute",
+ "id": "fake_compute_endpoint"
+}
+
+CATALOG_V3 = [COMPUTE_ENDPOINTS_V3, ]
+
+IDENTITY_V3_RESPONSE = {
+ "token": {
+ "methods": [
+ "token",
+ "password"
+ ],
+ "expires_at": "2020-01-01T00:00:10.000123Z",
+ "project": {
+ "domain": {
+ "id": "fake_id",
+ "name": "fake"
+ },
+ "id": "project_id",
+ "name": "project_name"
+ },
+ "user": {
+ "domain": {
+ "id": "domain_id",
+ "name": "domain_name"
+ },
+ "id": "fake_user_id",
+ "name": "username"
+ },
+ "issued_at": "2013-05-29T16:55:21.468960Z",
+ "catalog": CATALOG_V3
+ }
+}
+
+ALT_IDENTITY_V3 = IDENTITY_V3_RESPONSE
+
+
+def _fake_v3_response(self, uri, method="GET", body=None, headers=None,
+ redirections=5, connection_type=None):
+ fake_headers = {
+ "status": "201",
+ "x-subject-token": TOKEN
+ }
+ return (httplib2.Response(fake_headers),
+ json.dumps(IDENTITY_V3_RESPONSE))
+
+
+def _fake_v2_response(self, uri, method="GET", body=None, headers=None,
+ redirections=5, connection_type=None):
+ return (httplib2.Response({"status": "200"}),
+ json.dumps(IDENTITY_V2_RESPONSE))
+
+
+def _fake_auth_failure_response():
+ # the response body isn't really used in this case, but lets send it anyway
+ # to have a safe check in some future change on the rest client.
+ body = {
+ "unauthorized": {
+ "message": "Unauthorized",
+ "code": "401"
+ }
+ }
+ return httplib2.Response({"status": "401"}), json.dumps(body)
diff --git a/tempest/tests/files/setup.cfg b/tempest/tests/files/setup.cfg
index 8639baa..f6f9f73 100644
--- a/tempest/tests/files/setup.cfg
+++ b/tempest/tests/files/setup.cfg
@@ -2,8 +2,8 @@
name = tempest_unit_tests
version = 1
summary = Fake Project for testing wrapper scripts
-author = OpenStack QA
-author-email = openstack-qa@lists.openstack.org
+author = OpenStack
+author-email = openstack-dev@lists.openstack.org
home-page = http://www.openstack.org/
classifier =
Intended Audience :: Information Technology
diff --git a/tempest/tests/test_auth.py b/tempest/tests/test_auth.py
new file mode 100644
index 0000000..5346052
--- /dev/null
+++ b/tempest/tests/test_auth.py
@@ -0,0 +1,210 @@
+# Copyright 2014 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.
+
+import copy
+
+from tempest import auth
+from tempest.common import http
+from tempest import config
+from tempest import exceptions
+from tempest.openstack.common.fixture import mockpatch
+from tempest.tests import base
+from tempest.tests import fake_config
+from tempest.tests import fake_http
+from tempest.tests import fake_identity
+
+
+class BaseAuthTestsSetUp(base.TestCase):
+ _auth_provider_class = None
+ credentials = {
+ 'username': 'fake_user',
+ 'password': 'fake_pwd',
+ 'tenant_name': 'fake_tenant'
+ }
+
+ def _auth(self, credentials, **params):
+ """
+ returns auth method according to keystone
+ """
+ return self._auth_provider_class(credentials, **params)
+
+ def setUp(self):
+ super(BaseAuthTestsSetUp, self).setUp()
+ self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakeConfig)
+ self.fake_http = fake_http.fake_httplib2(return_type=200)
+ self.stubs.Set(http.ClosingHttp, 'request', self.fake_http.request)
+ self.auth_provider = self._auth(self.credentials)
+
+
+class TestBaseAuthProvider(BaseAuthTestsSetUp):
+ """
+ This tests auth.AuthProvider class which is base for the other so we
+ obviously don't test not implemented method or the ones which strongly
+ depends on them.
+ """
+ _auth_provider_class = auth.AuthProvider
+
+ def test_check_credentials_is_dict(self):
+ self.assertTrue(self.auth_provider.check_credentials({}))
+
+ def test_check_credentials_bad_type(self):
+ self.assertFalse(self.auth_provider.check_credentials([]))
+
+ def test_instantiate_with_bad_credentials_type(self):
+ """
+ Assure that credentials with bad type fail with TypeError
+ """
+ self.assertRaises(TypeError, self._auth, [])
+
+ def test_auth_data_property(self):
+ self.assertRaises(NotImplementedError, getattr, self.auth_provider,
+ 'auth_data')
+
+ def test_auth_data_property_when_cache_exists(self):
+ self.auth_provider.cache = 'foo'
+ self.useFixture(mockpatch.PatchObject(self.auth_provider,
+ 'is_expired',
+ return_value=False))
+ self.assertEqual('foo', getattr(self.auth_provider, 'auth_data'))
+
+ def test_delete_auth_data_property_through_deleter(self):
+ self.auth_provider.cache = 'foo'
+ del self.auth_provider.auth_data
+ self.assertIsNone(self.auth_provider.cache)
+
+ def test_delete_auth_data_property_through_clear_auth(self):
+ self.auth_provider.cache = 'foo'
+ self.auth_provider.clear_auth()
+ self.assertIsNone(self.auth_provider.cache)
+
+ def test_set_and_reset_alt_auth_data(self):
+ self.auth_provider.set_alt_auth_data('foo', 'bar')
+ self.assertEqual(self.auth_provider.alt_part, 'foo')
+ self.assertEqual(self.auth_provider.alt_auth_data, 'bar')
+
+ self.auth_provider.reset_alt_auth_data()
+ self.assertIsNone(self.auth_provider.alt_part)
+ self.assertIsNone(self.auth_provider.alt_auth_data)
+
+
+class TestKeystoneV2AuthProvider(BaseAuthTestsSetUp):
+ _auth_provider_class = auth.KeystoneV2AuthProvider
+
+ def setUp(self):
+ super(TestKeystoneV2AuthProvider, self).setUp()
+ self.stubs.Set(http.ClosingHttp, 'request',
+ fake_identity._fake_v2_response)
+ self.target_url = 'test_api'
+
+ def _get_fake_alt_identity(self):
+ return fake_identity.ALT_IDENTITY_V2_RESPONSE['access']
+
+ def _get_result_url_from_fake_identity(self):
+ return fake_identity.COMPUTE_ENDPOINTS_V2['endpoints'][1]['publicURL']
+
+ def _get_token_from_fake_identity(self):
+ return fake_identity.TOKEN
+
+ def _test_request_helper(self):
+ filters = {
+ 'service': 'compute',
+ 'endpoint_type': 'publicURL',
+ 'region': 'fakeRegion'
+ }
+
+ url, headers, body = self.auth_provider.auth_request('GET',
+ self.target_url,
+ filters=filters)
+
+ result_url = self._get_result_url_from_fake_identity()
+ self.assertEqual(url, result_url + '/' + self.target_url)
+ self.assertEqual(self._get_token_from_fake_identity(),
+ headers['X-Auth-Token'])
+ self.assertEqual(body, None)
+
+ def test_request(self):
+ self._test_request_helper()
+
+ def test_request_with_alt_auth(self):
+ self.auth_provider.set_alt_auth_data(
+ 'body',
+ (fake_identity.ALT_TOKEN, self._get_fake_alt_identity()))
+ self._test_request_helper()
+ # Assert alt auth data is clear after it
+ self.assertIsNone(self.auth_provider.alt_part)
+ self.assertIsNone(self.auth_provider.alt_auth_data)
+
+ def test_request_with_bad_service(self):
+ filters = {
+ 'service': 'BAD_SERVICE',
+ 'endpoint_type': 'publicURL',
+ 'region': 'fakeRegion'
+ }
+ self.assertRaises(exceptions.EndpointNotFound,
+ self.auth_provider.auth_request, 'GET',
+ 'http://fakeurl.com/fake_api', filters=filters)
+
+ def test_request_without_service(self):
+ filters = {
+ 'service': None,
+ 'endpoint_type': 'publicURL',
+ 'region': 'fakeRegion'
+ }
+ self.assertRaises(exceptions.EndpointNotFound,
+ self.auth_provider.auth_request, 'GET',
+ 'http://fakeurl.com/fake_api', filters=filters)
+
+ def test_check_credentials_missing_attribute(self):
+ for attr in ['username', 'password']:
+ cred = copy.copy(self.credentials)
+ del cred[attr]
+ self.assertFalse(self.auth_provider.check_credentials(cred))
+
+ def test_check_credentials_not_scoped_missing_tenant_name(self):
+ cred = copy.copy(self.credentials)
+ del cred['tenant_name']
+ self.assertTrue(self.auth_provider.check_credentials(cred,
+ scoped=False))
+
+ def test_check_credentials_missing_tenant_name(self):
+ cred = copy.copy(self.credentials)
+ del cred['tenant_name']
+ self.assertFalse(self.auth_provider.check_credentials(cred))
+
+
+class TestKeystoneV3AuthProvider(TestKeystoneV2AuthProvider):
+ _auth_provider_class = auth.KeystoneV3AuthProvider
+ credentials = {
+ 'username': 'fake_user',
+ 'password': 'fake_pwd',
+ 'tenant_name': 'fake_tenant',
+ 'domain_name': 'fake_domain_name',
+ }
+
+ def setUp(self):
+ super(TestKeystoneV3AuthProvider, self).setUp()
+ self.stubs.Set(http.ClosingHttp, 'request',
+ fake_identity._fake_v3_response)
+
+ def _get_fake_alt_identity(self):
+ return fake_identity.ALT_IDENTITY_V3['token']
+
+ def _get_result_url_from_fake_identity(self):
+ return fake_identity.COMPUTE_ENDPOINTS_V3['endpoints'][1]['url']
+
+ def test_check_credentials_missing_tenant_name(self):
+ cred = copy.copy(self.credentials)
+ del cred['domain_name']
+ self.assertFalse(self.auth_provider.check_credentials(cred))