Merge "Handling network resources in tenant isolation"
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index 19d7973..bf09428 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -15,6 +15,7 @@
# under the License.
from tempest.api.compute import base
+from tempest.common import tempest_fixtures as fixtures
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
from tempest.test import attr
@@ -42,6 +43,7 @@
@attr(type='gate')
def test_list_hosts_with_zone(self):
+ self.useFixture(fixtures.LockFixture('availability_zone'))
resp, hosts = self.client.list_hosts()
host = hosts[0]
zone_name = host['zone']
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index cb47066..0abf779 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -15,7 +15,9 @@
# under the License.
from tempest.api.compute import base
+from tempest.common.utils.data_utils import rand_int_id
from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
from tempest.test import attr
@@ -31,6 +33,12 @@
def setUpClass(cls):
super(ServersAdminTestJSON, cls).setUpClass()
cls.client = cls.os_adm.servers_client
+ cls.flavors_client = cls.os_adm.flavors_client
+
+ cls.admin_client = cls._get_identity_admin_client()
+ tenant = cls.admin_client.get_tenant_by_name(
+ cls.client.tenant_name)
+ cls.tenant_id = tenant['id']
cls.s1_name = rand_name('server')
resp, server = cls.create_server(name=cls.s1_name,
@@ -39,6 +47,16 @@
resp, server = cls.create_server(name=cls.s2_name,
wait_until='ACTIVE')
+ def _get_unused_flavor_id(self):
+ flavor_id = rand_int_id(start=1000)
+ while True:
+ try:
+ resp, body = self.flavors_client.get_flavor_details(flavor_id)
+ except exceptions.NotFound:
+ break
+ flavor_id = rand_int_id(start=1000)
+ return flavor_id
+
@attr(type='gate')
def test_list_servers_by_admin(self):
# Listing servers by admin user returns empty list by default
@@ -59,6 +77,42 @@
self.assertIn(self.s1_name, servers_name)
self.assertIn(self.s2_name, servers_name)
+ @attr(type=['negative', 'gate'])
+ def test_resize_server_using_overlimit_ram(self):
+ flavor_name = rand_name("flavor-")
+ flavor_id = self._get_unused_flavor_id()
+ resp, quota_set = self.quotas_client.get_default_quota_set(
+ self.tenant_id)
+ ram = int(quota_set['ram']) + 1
+ vcpus = 8
+ disk = 10
+ resp, flavor_ref = self.flavors_client.create_flavor(flavor_name,
+ ram, vcpus, disk,
+ flavor_id)
+ self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
+ self.assertRaises(exceptions.OverLimit,
+ self.client.resize,
+ self.servers[0]['id'],
+ flavor_ref['id'])
+
+ @attr(type=['negative', 'gate'])
+ def test_resize_server_using_overlimit_vcpus(self):
+ flavor_name = rand_name("flavor-")
+ flavor_id = self._get_unused_flavor_id()
+ ram = 512
+ resp, quota_set = self.quotas_client.get_default_quota_set(
+ self.tenant_id)
+ vcpus = int(quota_set['cores']) + 1
+ disk = 10
+ resp, flavor_ref = self.flavors_client.create_flavor(flavor_name,
+ ram, vcpus, disk,
+ flavor_id)
+ self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
+ self.assertRaises(exceptions.OverLimit,
+ self.client.resize,
+ self.servers[0]['id'],
+ flavor_ref['id'])
+
class ServersAdminTestXML(ServersAdminTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index d2f437b..0b527d9 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -22,6 +22,7 @@
from tempest.common import isolated_creds
from tempest.common.utils.data_utils import parse_image_id
from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
from tempest.openstack.common import log as logging
import tempest.test
@@ -103,6 +104,9 @@
for image_id in cls.images:
try:
cls.images_client.delete_image(image_id)
+ except exceptions.NotFound:
+ # The image may have already been deleted which is OK.
+ pass
except Exception as exc:
LOG.info('Exception raised deleting image %s', image_id)
LOG.exception(exc)
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 77f92c9..aec1d49 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -196,6 +196,18 @@
raise exceptions.TimeoutException(message)
@attr(type=['negative', 'gate'])
+ def test_resize_server_using_nonexist_flavor(self):
+ flavor_id = -1
+ self.assertRaises(exceptions.BadRequest,
+ self.client.resize, self.server_id, flavor_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_resize_server_using_null_flavor(self):
+ flavor_id = ""
+ self.assertRaises(exceptions.BadRequest,
+ self.client.resize, self.server_id, flavor_id)
+
+ @attr(type=['negative', 'gate'])
def test_reboot_nonexistent_server_soft(self):
# Negative Test: The server reboot on non existent server should return
# an error
diff --git a/tempest/api/network/test_extensions.py b/tempest/api/network/test_extensions.py
new file mode 100644
index 0000000..1b27d1b
--- /dev/null
+++ b/tempest/api/network/test_extensions.py
@@ -0,0 +1,79 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
+
+
+from tempest.api.network import base
+from tempest.test import attr
+
+
+class ExtensionsTestJSON(base.BaseNetworkTest):
+ _interface = 'json'
+
+ """
+ Tests the following operations in the Neutron API using the REST client for
+ Neutron:
+
+ List all available extensions
+
+ v2.0 of the Neutron API is assumed. It is also assumed that the following
+ options are defined in the [network] section of etc/tempest.conf:
+
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(ExtensionsTestJSON, cls).setUpClass()
+
+ @attr(type='smoke')
+ def test_list_show_extensions(self):
+ # List available extensions for the tenant
+ expected_alias = ['security-group', 'l3_agent_scheduler',
+ 'ext-gw-mode', 'binding', 'quotas',
+ 'agent', 'dhcp_agent_scheduler', 'provider',
+ 'router', 'extraroute', 'external-net',
+ 'allowed-address-pairs', 'extra_dhcp_opt']
+ actual_alias = list()
+ resp, extensions = self.client.list_extensions()
+ self.assertEqual('200', resp['status'])
+ list_extensions = extensions['extensions']
+ # Show and verify the details of the available extensions
+ for ext in list_extensions:
+ ext_name = ext['name']
+ ext_alias = ext['alias']
+ actual_alias.append(ext['alias'])
+ resp, ext_details = self.client.show_extension_details(ext_alias)
+ self.assertEqual('200', resp['status'])
+ ext_details = ext_details['extension']
+
+ self.assertIsNotNone(ext_details)
+ self.assertIn('updated', ext_details.keys())
+ self.assertIn('name', ext_details.keys())
+ self.assertIn('description', ext_details.keys())
+ self.assertIn('namespace', ext_details.keys())
+ self.assertIn('links', ext_details.keys())
+ self.assertIn('alias', ext_details.keys())
+ self.assertEqual(ext_details['name'], ext_name)
+ self.assertEqual(ext_details['alias'], ext_alias)
+ self.assertEqual(ext_details, ext)
+ # Verify if expected extensions are present in the actual list
+ # of extensions returned
+ for e in expected_alias:
+ self.assertIn(e, actual_alias)
+
+
+class ExtensionsTestXML(ExtensionsTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 119e3e0..f2915f7 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -34,6 +34,15 @@
self.assertEqual(202, resp.status)
self.client.wait_for_resource_deletion(volume_id)
+ def _is_true(self, val):
+ # NOTE(jdg): Temporary conversion method to get cinder patch
+ # merged. Then we'll make this strict again and
+ #specifically check "true" or "false"
+ if val in ['true', 'True', True]:
+ return True
+ else:
+ return False
+
def _volume_create_get_update_delete(self, **kwargs):
# Create a volume, Get it's details and Delete the volume
volume = {}
@@ -69,10 +78,14 @@
fetched_volume['metadata'],
'The fetched Volume is different '
'from the created Volume')
+
+ # NOTE(jdg): Revert back to strict true/false checking
+ # after fix for bug #1227837 merges
+ boot_flag = self._is_true(fetched_volume['bootable'])
if 'imageRef' in kwargs:
- self.assertEqual(fetched_volume['bootable'], True)
+ self.assertEqual(boot_flag, True)
if 'imageRef' not in kwargs:
- self.assertEqual(fetched_volume['bootable'], False)
+ self.assertEqual(boot_flag, False)
# Update Volume
new_v_name = rand_name('new-Volume')
@@ -92,10 +105,14 @@
self.assertEqual(new_v_name, updated_volume['display_name'])
self.assertEqual(new_desc, updated_volume['display_description'])
self.assertEqual(metadata, updated_volume['metadata'])
+
+ # NOTE(jdg): Revert back to strict true/false checking
+ # after fix for bug #1227837 merges
+ boot_flag = self._is_true(updated_volume['bootable'])
if 'imageRef' in kwargs:
- self.assertEqual(updated_volume['bootable'], True)
+ self.assertEqual(boot_flag, True)
if 'imageRef' not in kwargs:
- self.assertEqual(updated_volume['bootable'], False)
+ self.assertEqual(boot_flag, False)
@attr(type='gate')
def test_volume_get_metadata_none(self):
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 7eeb1ee..a48cea2 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -15,6 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import uuid
+
from tempest.api.volume import base
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
@@ -29,37 +31,24 @@
super(VolumesNegativeTest, cls).setUpClass()
cls.client = cls.volumes_client
+ # Create a test shared instance and volume for attach/detach tests
+ vol_name = rand_name('Volume-')
+
+ cls.volume = cls.create_volume(size=1, display_name=vol_name)
+ cls.client.wait_for_volume_status(cls.volume['id'], 'available')
+ cls.mountpoint = "/dev/vdc"
+
@attr(type='gate')
def test_volume_get_nonexistant_volume_id(self):
- # Should not be able to get a non-existent volume
- # Creating a non-existent volume id
- volume_id_list = []
- resp, volumes = self.client.list_volumes()
- for i in range(len(volumes)):
- volume_id_list.append(volumes[i]['id'])
- while True:
- non_exist_id = rand_name('999')
- if non_exist_id not in volume_id_list:
- break
- # Trying to Get a non-existent volume
+ # Should not be able to get a non-existant volume
self.assertRaises(exceptions.NotFound, self.client.get_volume,
- non_exist_id)
+ str(uuid.uuid4()))
@attr(type='gate')
def test_volume_delete_nonexistant_volume_id(self):
- # Should not be able to delete a non-existent Volume
- # Creating non-existent volume id
- volume_id_list = []
- resp, volumes = self.client.list_volumes()
- for i in range(len(volumes)):
- volume_id_list.append(volumes[i]['id'])
- while True:
- non_exist_id = '12345678-abcd-4321-abcd-123456789098'
- if non_exist_id not in volume_id_list:
- break
- # Try to delete a non-existent volume
+ # Should not be able to delete a non-existant Volume
self.assertRaises(exceptions.NotFound, self.client.delete_volume,
- non_exist_id)
+ str(uuid.uuid4()))
@attr(type='gate')
def test_create_volume_with_invalid_size(self):
@@ -109,6 +98,26 @@
# Should not be able to delete volume when empty ID is passed
self.assertRaises(exceptions.NotFound, self.client.delete_volume, '')
+ @attr(type=['negative', 'gate'])
+ def test_attach_volumes_with_nonexistent_volume_id(self):
+ srv_name = rand_name('Instance-')
+ resp, server = self.servers_client.create_server(srv_name,
+ self.image_ref,
+ self.flavor_ref)
+ self.addCleanup(self.servers_client.delete_server, server['id'])
+ self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+ self.assertRaises(exceptions.NotFound,
+ self.client.attach_volume,
+ str(uuid.uuid4()),
+ server['id'],
+ self.mountpoint)
+
+ @attr(type=['negative', 'gate'])
+ def test_detach_volumes_with_invalid_volume_id(self):
+ self.assertRaises(exceptions.NotFound,
+ self.client.detach_volume,
+ 'xxx')
+
class VolumesNegativeTestXML(VolumesNegativeTest):
_interface = 'xml'
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index b72b99f..c01de83 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -374,8 +374,10 @@
**ruleset)
self.set_resource(sg_rule.id, sg_rule)
- def create_server(self, client, name=None, image=None, flavor=None,
+ def create_server(self, client=None, name=None, image=None, flavor=None,
create_kwargs={}):
+ if client is None:
+ client = self.compute_client
if name is None:
name = rand_name('scenario-server-')
if image is None:
@@ -484,7 +486,9 @@
cls.config.identity.password,
cls.config.identity.tenant_name).tenant_id
- def _create_security_group(self, client, namestart='secgroup-smoke-'):
+ def _create_security_group(self, client=None, namestart='secgroup-smoke-'):
+ if client is None:
+ client = self.compute_client
# Create security group
sg_name = rand_name(namestart)
sg_desc = sg_name + " description"
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index ce4d1bd..752ff6f 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -87,8 +87,7 @@
def nova_boot(self):
create_kwargs = {'key_name': self.keypair.name}
- self.server = self.create_server(self.compute_client,
- image=self.image,
+ self.server = self.create_server(image=self.image,
create_kwargs=create_kwargs)
def nova_list(self):
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index cf9ad64..6cd9fe8 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -164,8 +164,7 @@
name=rand_name('keypair-smoke-'))
def _create_security_groups(self):
- self.security_groups[self.tenant_id] = self._create_security_group(
- self.compute_client)
+ self.security_groups[self.tenant_id] = self._create_security_group()
def _create_networks(self):
network = self._create_network(self.tenant_id)
@@ -210,8 +209,7 @@
'key_name': keypair_name,
'security_groups': security_groups,
}
- server = self.create_server(self.compute_client, name=name,
- create_kwargs=create_kwargs)
+ server = self.create_server(name=name, create_kwargs=create_kwargs)
return server
def _create_servers(self):
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 26bc95e..853b1ba 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -48,7 +48,7 @@
@services('compute')
def test_resize_server_confirm(self):
# We create an instance for use in this test
- instance = self.create_server(self.compute_client)
+ instance = self.create_server()
instance_id = instance.id
resize_flavor = self.config.compute.flavor_ref_alt
LOG.debug("Resizing instance %s from flavor %s to flavor %s",
@@ -66,7 +66,7 @@
@services('compute')
def test_server_sequence_suspend_resume(self):
# We create an instance for use in this test
- instance = self.create_server(self.compute_client)
+ instance = self.create_server()
instance_id = instance.id
LOG.debug("Suspending instance %s. Current status: %s",
instance_id, instance.status)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 03e6e41..c32d49d 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -56,8 +56,7 @@
create_kwargs = {
'key_name': self.keypair.id
}
- instance = self.create_server(self.compute_client,
- create_kwargs=create_kwargs)
+ instance = self.create_server(create_kwargs=create_kwargs)
self.set_resource('instance', instance)
def pause_server(self):
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 8c2cc76..ba347e0 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -34,8 +34,7 @@
create_kwargs = {
'key_name': self.keypair.name
}
- return self.create_server(self.compute_client, image=image_id,
- create_kwargs=create_kwargs)
+ return self.create_server(image=image_id, create_kwargs=create_kwargs)
def _add_keypair(self):
self.keypair = self.create_keypair()
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index ab464e3..4f49d65 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -58,8 +58,7 @@
create_kwargs = {
'key_name': self.keypair.name
}
- return self.create_server(self.compute_client, image=image_id,
- create_kwargs=create_kwargs)
+ return self.create_server(image=image_id, create_kwargs=create_kwargs)
def _add_keypair(self):
self.keypair = self.create_keypair()
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 3572166..d12cd56 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -49,8 +49,7 @@
'block_device_mapping': bd_map,
'key_name': keypair.name
}
- return self.create_server(self.compute_client,
- create_kwargs=create_kwargs)
+ return self.create_server(create_kwargs=create_kwargs)
def _create_snapshot_from_volume(self, vol_id):
volume_snapshots = self.volume_client.volume_snapshots
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 81a7dbb..4b87b91 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -588,3 +588,15 @@
health_monitor_id)
resp, body = self.delete(uri, headers=self.headers)
return resp, body
+
+ def list_extensions(self):
+ uri = '%s/extensions' % (self.uri_prefix)
+ resp, body = self.get(uri, self.headers)
+ body = json.loads(body)
+ return resp, body
+
+ def show_extension_details(self, ext_alias):
+ uri = '%s/extensions/%s' % (self.uri_prefix, ext_alias)
+ resp, body = self.get(uri, headers=self.headers)
+ body = json.loads(body)
+ return resp, body
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index a9b5512..1523ed0 100755
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -428,6 +428,19 @@
health_monitor_id)
return self.delete(uri, self.headers)
+ def list_extensions(self):
+ url = '%s/extensions' % (self.uri_prefix)
+ resp, body = self.get(url, self.headers)
+ extensions = self._parse_array(etree.fromstring(body))
+ extensions = {"extensions": extensions}
+ return resp, extensions
+
+ def show_extension_details(self, ext_alias):
+ uri = '%s/extensions/%s' % (self.uri_prefix, str(ext_alias))
+ resp, body = self.get(uri, self.headers)
+ body = _root_tag_fetcher_and_xml_to_json_parse(body)
+ return resp, body
+
def _root_tag_fetcher_and_xml_to_json_parse(xml_returned_body):
body = ET.fromstring(xml_returned_body)
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index e518d28..d959543 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -13,6 +13,7 @@
# limitations under the License.
import multiprocessing
+import os
import signal
import time
@@ -87,12 +88,22 @@
"""
Goes through the process list and terminates all child processes.
"""
+ log_check_interval = int(admin_manager.config.stress.log_check_interval)
for process in processes:
if process['process'].is_alive():
try:
process['process'].terminate()
except Exception:
pass
+ time.sleep(log_check_interval)
+ for process in processes:
+ if process['process'].is_alive():
+ try:
+ pid = process['process'].pid
+ LOG.warn("Process %d hangs. Send SIGKILL." % pid)
+ os.kill(pid, signal.SIGKILL)
+ except Exception:
+ pass
process['process'].join()
diff --git a/tempest/stress/stressaction.py b/tempest/stress/stressaction.py
index 45a628d..61e46fa 100644
--- a/tempest/stress/stressaction.py
+++ b/tempest/stress/stressaction.py
@@ -30,7 +30,10 @@
self.stop_on_error = stop_on_error
def _shutdown_handler(self, signal, frame):
- self.tearDown()
+ try:
+ self.tearDown()
+ except Exception:
+ self.logger.exception("Error while tearDown")
sys.exit(0)
@property
diff --git a/tox.ini b/tox.ini
index 1b8a0fd..abc9e42 100644
--- a/tox.ini
+++ b/tox.ini
@@ -86,7 +86,7 @@
sitepackages = True
setenv = VIRTUAL_ENV={envdir}
commands =
- python -m tempest/stress/run_stress -a -d 3600
+ python -m tempest/stress/run_stress -a -d 3600 -S
[testenv:venv]
commands = {posargs}