Merge "fix test_flavors_extra_specs failure"
diff --git a/.testr.conf b/.testr.conf
index 510f4c9..05b12c4 100644
--- a/.testr.conf
+++ b/.testr.conf
@@ -1,7 +1,7 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
- OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-250} \
+ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \
${PYTHON:-python} -m subunit.run discover -t ./ ./tempest $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list
diff --git a/run_tests.sh b/run_tests.sh
index 856ce54..d672b62 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -124,7 +124,11 @@
}
function run_pep8 {
- echo "Running pep8 ..."
+ echo "Running flake8 ..."
+ if [ $never_venv -eq 1 ]; then
+ echo "**WARNING**:" >&2
+ echo "Running flake8 without virtual env may miss OpenStack HACKING detection" >&2
+ fi
${wrapper} flake8
}
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 5f31084..7efd3c1 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -296,6 +296,24 @@
_test_string_variations(['t', 'true', 'yes', '1'],
flavor_name_public)
+ @attr(type='gate')
+ def test_create_flavor_using_string_ram(self):
+ flavor_name = rand_name(self.flavor_name_prefix)
+ new_flavor_id = rand_int_id(start=1000)
+
+ ram = " 1024 "
+ resp, flavor = self.client.create_flavor(flavor_name,
+ ram, self.vcpus,
+ self.disk,
+ new_flavor_id)
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ self.assertEqual(200, resp.status)
+ self.assertEqual(flavor['name'], flavor_name)
+ self.assertEqual(flavor['vcpus'], self.vcpus)
+ self.assertEqual(flavor['disk'], self.disk)
+ self.assertEqual(flavor['ram'], int(ram))
+ self.assertEqual(int(flavor['id']), new_flavor_id)
+
@attr(type=['negative', 'gate'])
def test_invalid_is_public_string(self):
self.assertRaises(exceptions.BadRequest,
@@ -319,6 +337,26 @@
self.user_client.delete_flavor,
self.flavor_ref_alt)
+ @attr(type=['negative', 'gate'])
+ def test_create_flavor_using_invalid_ram(self):
+ flavor_name = rand_name(self.flavor_name_prefix)
+ new_flavor_id = rand_int_id(start=1000)
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ flavor_name, -1, self.vcpus,
+ self.disk, new_flavor_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_flavor_using_invalid_vcpus(self):
+ flavor_name = rand_name(self.flavor_name_prefix)
+ new_flavor_id = rand_int_id(start=1000)
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ flavor_name, self.ram, 0,
+ self.disk, new_flavor_id)
+
class FlavorsAdminTestXML(FlavorsAdminTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 0052a30..06e9ab2 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -64,7 +64,7 @@
cls.alt_manager = clients.AltManager()
cls.alt_client = cls.alt_manager.images_client
- @testtools.skip("Until Bug #1006725 is fixed")
+ @testtools.skip("Skipped until the Bug #1006725 is resolved.")
@attr(type=['negative', 'gate'])
def test_create_image_specify_multibyte_character_image_name(self):
# Return an error if the image name has multi-byte characters
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index ade7604..8d31598 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -205,7 +205,7 @@
self.assertNotIn(self.s2_name, map(lambda x: x['name'], servers))
self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
- @testtools.skip('Until Bug #1170718 is resolved.')
+ @testtools.skip('Skipped until the Bug #1170718 is resolved.')
@attr(type='gate')
def test_list_servers_filtered_by_ip(self):
# Filter servers by ip
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 25df6e6..76ef461 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -86,7 +86,7 @@
new_boot_time = linux_client.get_boot_time()
self.assertGreater(new_boot_time, boot_time)
- @testtools.skip('Until Bug #1014647 is dealt with.')
+ @testtools.skip('Skipped until the Bug #1014647 is resolved.')
@attr(type='smoke')
def test_reboot_server_soft(self):
# The server should be signaled to reboot gracefully
@@ -238,7 +238,7 @@
self.servers_client.get_console_output,
'!@#$%^&*()', 10)
- @testtools.skip('Until tempest Bug #1014683 is fixed.')
+ @testtools.skip('Skipped until the Bug #1014683 is resolved.')
@attr(type='gate')
def test_get_console_output_server_id_in_reboot_status(self):
# Positive test:Should be able to GET the console output
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index e5ea30e..b743a85 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -37,8 +37,8 @@
resp, server = cls.create_server(wait_until='ACTIVE')
cls.server_id = server['id']
- @testtools.skipIf(CONF.service_available.neutron, "This feature is not " +
- "implemented by Neutron. See bug: #1183436")
+ @testtools.skipIf(CONF.service_available.neutron, "Not implemented by " +
+ "Neutron. Skipped until the Bug #1183436 is resolved.")
@attr(type='gate')
def test_list_virtual_interfaces(self):
# Positive test:Should be able to GET the virtual interfaces list
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 7f49452..f3d1485 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -75,7 +75,7 @@
for n in created_networks:
self.assertNotIn(n['id'], networks_list)
- @attr(type='gate')
+ @attr(type='smoke')
def test_create_update_delete_network_subnet(self):
# Creates a network
name = rand_name('network-')
@@ -116,7 +116,7 @@
resp, body = self.client.delete_network(net_id)
self.assertEqual('204', resp['status'])
- @attr(type='gate')
+ @attr(type='smoke')
def test_show_network(self):
# Verifies the details of a network
resp, body = self.client.show_network(self.network['id'])
@@ -125,7 +125,7 @@
self.assertEqual(self.network['id'], network['id'])
self.assertEqual(self.name, network['name'])
- @attr(type='gate')
+ @attr(type='smoke')
def test_list_networks(self):
# Verify the network exists in the list of all networks
resp, body = self.client.list_networks()
@@ -138,7 +138,7 @@
msg = "Network list doesn't contain created network"
self.assertIsNotNone(found, msg)
- @attr(type='gate')
+ @attr(type='smoke')
def test_show_subnet(self):
# Verifies the details of a subnet
resp, body = self.client.show_subnet(self.subnet['id'])
@@ -147,7 +147,7 @@
self.assertEqual(self.subnet['id'], subnet['id'])
self.assertEqual(self.cidr, subnet['cidr'])
- @attr(type='gate')
+ @attr(type='smoke')
def test_list_subnets(self):
# Verify the subnet exists in the list of all subnets
resp, body = self.client.list_subnets()
@@ -160,7 +160,7 @@
msg = "Subnet list doesn't contain created subnet"
self.assertIsNotNone(found, msg)
- @attr(type='gate')
+ @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'])
@@ -176,7 +176,7 @@
resp, body = self.client.delete_port(port['id'])
self.assertEqual('204', resp['status'])
- @attr(type='gate')
+ @attr(type='smoke')
def test_show_port(self):
# Verify the details of port
resp, body = self.client.show_port(self.port['id'])
@@ -184,7 +184,7 @@
port = body['port']
self.assertEqual(self.port['id'], port['id'])
- @attr(type='gate')
+ @attr(type='smoke')
def test_list_ports(self):
# Verify the port exists in the list of all ports
resp, body = self.client.list_ports()
@@ -196,19 +196,19 @@
found = n['id']
self.assertIsNotNone(found, "Port list doesn't contain created port")
- @attr(type=['negative', 'gate'])
+ @attr(type=['negative', 'smoke'])
def test_show_non_existent_network(self):
non_exist_id = rand_name('network')
self.assertRaises(exceptions.NotFound, self.client.show_network,
non_exist_id)
- @attr(type=['negative', 'gate'])
+ @attr(type=['negative', 'smoke'])
def test_show_non_existent_subnet(self):
non_exist_id = rand_name('subnet')
self.assertRaises(exceptions.NotFound, self.client.show_subnet,
non_exist_id)
- @attr(type='gate')
+ @attr(type='smoke')
def test_bulk_create_delete_network(self):
# Creates 2 networks in one request
network_names = [rand_name('network-'), rand_name('network-')]
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 4f687b0..9f8c742 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -49,7 +49,7 @@
router_id, port_id)
self.assertEqual('200', resp['status'])
- @attr(type='gate')
+ @attr(type='smoke')
def test_create_show_list_update_delete_router(self):
# Create a router
name = rand_name('router-')
@@ -90,7 +90,7 @@
create_body['router']['id'])
self.assertEqual(show_body['router']['name'], updated_name)
- @attr(type='gate')
+ @attr(type='smoke')
def test_add_remove_router_interface_with_subnet_id(self):
network = self.create_network()
subnet = self.create_subnet(network)
@@ -111,7 +111,7 @@
self.assertEqual(show_port_body['port']['device_id'],
create_body['router']['id'])
- @attr(type='gate')
+ @attr(type='smoke')
def test_add_remove_router_interface_with_port_id(self):
network = self.create_network()
self.create_subnet(network)
diff --git a/tempest/api/object_storage/test_container_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
new file mode 100644
index 0000000..d07697a
--- /dev/null
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -0,0 +1,94 @@
+# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
+#
+# Author: Joe H. Rahme <joe.hakim.rahme@enovance.com>
+#
+# 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.object_storage import base
+from tempest.common.utils.data_utils import arbitrary_string
+from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
+
+
+class StaticWebTest(base.BaseObjectTest):
+
+ @classmethod
+ def setUpClass(cls):
+ super(StaticWebTest, cls).setUpClass()
+ cls.container_name = rand_name(name="TestContainer")
+
+ # This header should be posted on the container before every test
+ cls.headers_public_read_acl = {'Read': '.r:*'}
+
+ # Create test container and create one object in it
+ cls.container_client.create_container(cls.container_name)
+ cls.object_name = rand_name(name="TestObject")
+ cls.object_data = arbitrary_string()
+ cls.object_client.create_object(cls.container_name,
+ cls.object_name,
+ cls.object_data)
+
+ cls.container_client.update_container_metadata(
+ cls.container_name,
+ metadata=cls.headers_public_read_acl,
+ metadata_prefix="X-Container-")
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.delete_containers([cls.container_name])
+ cls.data.teardown_all()
+ super(StaticWebTest, cls).tearDownClass()
+
+ @attr('gate')
+ def test_web_index(self):
+ headers = {'web-index': self.object_name}
+
+ self.container_client.update_container_metadata(
+ self.container_name, metadata=headers)
+
+ # test GET on http://account_url/container_name
+ # we should retrieve the self.object_name file
+ resp, body = self.custom_account_client.request("GET",
+ self.container_name)
+ self.assertEqual(resp['status'], '200')
+ self.assertEqual(body, self.object_data)
+
+ # clean up before exiting
+ self.container_client.update_container_metadata(self.container_name,
+ {'web-index': ""})
+
+ _, body = self.container_client.list_container_metadata(
+ self.container_name)
+ self.assertNotIn('x-container-meta-web-index', body)
+
+ @attr('gate')
+ def test_web_listing(self):
+ headers = {'web-listings': 'true'}
+
+ self.container_client.update_container_metadata(
+ self.container_name, metadata=headers)
+
+ # test GET on http://account_url/container_name
+ # we should retrieve a listing of objects
+ resp, body = self.custom_account_client.request("GET",
+ self.container_name)
+ self.assertEqual(resp['status'], '200')
+ self.assertIn(self.object_name, body)
+
+ # clean up before exiting
+ self.container_client.update_container_metadata(self.container_name,
+ {'web-listings': ""})
+
+ _, body = self.container_client.list_container_metadata(
+ self.container_name)
+ self.assertNotIn('x-container-meta-web-listings', body)
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index d18c2ad..66a74e4 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -52,7 +52,7 @@
cls.delete_containers(cls.containers, client[0], client[1])
super(ContainerSyncTest, cls).tearDownClass()
- @testtools.skip('Until Bug #1093743 is resolved.')
+ @testtools.skip('Skipped until the Bug #1093743 is resolved.')
@attr(type='gate')
def test_container_synchronization(self):
# container to container synchronization
diff --git a/tempest/api/object_storage/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index 8703480..889436d 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -43,7 +43,7 @@
cls.delete_containers([cls.container_name])
super(ObjectExpiryTest, cls).tearDownClass()
- @testtools.skip('Until Bug #1069849 is resolved.')
+ @testtools.skip('Skipped until the Bug #1069849 is resolved.')
@attr(type='gate')
def test_get_object_after_expiry_time(self):
# TODO(harika-vakadi): similar test case has to be created for
diff --git a/tempest/api/orchestration/stacks/test_instance_cfn_init.py b/tempest/api/orchestration/stacks/test_server_cfn_init.py
similarity index 90%
rename from tempest/api/orchestration/stacks/test_instance_cfn_init.py
rename to tempest/api/orchestration/stacks/test_server_cfn_init.py
index fe55ecf..ffe8def 100644
--- a/tempest/api/orchestration/stacks/test_instance_cfn_init.py
+++ b/tempest/api/orchestration/stacks/test_server_cfn_init.py
@@ -26,7 +26,7 @@
LOG = logging.getLogger(__name__)
-class InstanceCfnInitTestJSON(base.BaseOrchestrationTest):
+class ServerCfnInitTestJSON(base.BaseOrchestrationTest):
_interface = 'json'
existing_keypair = (tempest.config.TempestConfig().
orchestration.keypair_name is not None)
@@ -37,11 +37,11 @@
Template which uses a wait condition to confirm that a minimal
cfn-init and cfn-signal has worked
Parameters:
- KeyName:
+ key_name:
Type: String
- InstanceType:
+ flavor:
Type: String
- ImageId:
+ image:
Type: String
Resources:
CfnUser:
@@ -58,7 +58,7 @@
Properties:
UserName: {Ref: CfnUser}
SmokeServer:
- Type: AWS::EC2::Instance
+ Type: OS::Nova::Server
Metadata:
AWS::CloudFormation::Init:
config:
@@ -83,12 +83,12 @@
owner: root
group: root
Properties:
- ImageId: {Ref: ImageId}
- InstanceType: {Ref: InstanceType}
- KeyName: {Ref: KeyName}
- SecurityGroups:
+ image: {Ref: image}
+ flavor: {Ref: flavor}
+ key_name: {Ref: key_name}
+ security_groups:
- {Ref: SmokeSecurityGroup}
- UserData:
+ user_data:
Fn::Base64:
Fn::Join:
- ''
@@ -118,12 +118,12 @@
SmokeServerIp:
Description: IP address of server
Value:
- Fn::GetAtt: [SmokeServer, PublicIp]
+ Fn::GetAtt: [SmokeServer, first_private_address]
"""
@classmethod
def setUpClass(cls):
- super(InstanceCfnInitTestJSON, cls).setUpClass()
+ super(ServerCfnInitTestJSON, cls).setUpClass()
if not cls.orchestration_cfg.image_ref:
raise cls.skipException("No image available to test")
cls.client = cls.orchestration_client
@@ -140,9 +140,9 @@
stack_name,
cls.template,
parameters={
- 'KeyName': keypair_name,
- 'InstanceType': cls.orchestration_cfg.instance_type,
- 'ImageId': cls.orchestration_cfg.image_ref
+ 'key_name': keypair_name,
+ 'flavor': cls.orchestration_cfg.instance_type,
+ 'image': cls.orchestration_cfg.image_ref
})
@attr(type='slow')
@@ -187,7 +187,7 @@
# This is an assert of great significance, as it means the following
# has happened:
# - cfn-init read the provided metadata and wrote out a file
- # - a user was created and credentials written to the instance
+ # - a user was created and credentials written to the server
# - a cfn-signal was built which was signed with provided credentials
# - the wait condition was fulfilled and the stack has changed state
wait_status = json.loads(
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 5d5fd7e..8c39e08 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -32,6 +32,19 @@
_interface = 'json'
+ def assertVolumesIn(self, fetched_list, expected_list):
+ missing_vols = [v for v in expected_list if v not in fetched_list]
+ if len(missing_vols) == 0:
+ return
+
+ def str_vol(vol):
+ return "%s:%s" % (vol['id'], vol['display_name'])
+
+ raw_msg = "Could not find volumes %s in expected list %s; fetched %s"
+ self.fail(raw_msg % ([str_vol(v) for v in missing_vols],
+ [str_vol(v) for v in expected_list],
+ [str_vol(v) for v in fetched_list]))
+
@classmethod
def setUpClass(cls):
super(VolumesListTest, cls).setUpClass()
@@ -82,12 +95,7 @@
# Fetch all volumes
resp, fetched_list = self.client.list_volumes()
self.assertEqual(200, resp.status)
- # Now check if all the volumes created in setup are in fetched list
- missing_vols = [v for v in self.volume_list if v not in fetched_list]
- self.assertFalse(missing_vols,
- "Failed to find volume %s in fetched list" %
- ', '.join(m_vol['display_name']
- for m_vol in missing_vols))
+ self.assertVolumesIn(fetched_list, self.volume_list)
@attr(type='gate')
def test_volume_list_with_details(self):
@@ -95,12 +103,7 @@
# Fetch all Volumes
resp, fetched_list = self.client.list_volumes_with_detail()
self.assertEqual(200, resp.status)
- # Verify that all the volumes are returned
- missing_vols = [v for v in self.volume_list if v not in fetched_list]
- self.assertFalse(missing_vols,
- "Failed to find volume %s in fetched list" %
- ', '.join(m_vol['display_name']
- for m_vol in missing_vols))
+ self.assertVolumesIn(fetched_list, self.volume_list)
class VolumeListTestXML(VolumesListTest):
diff --git a/tempest/scenario/orchestration/test_autoscaling.py b/tempest/scenario/orchestration/test_autoscaling.py
index 17870a1..78025ee 100644
--- a/tempest/scenario/orchestration/test_autoscaling.py
+++ b/tempest/scenario/orchestration/test_autoscaling.py
@@ -12,16 +12,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.openstack.common import log as logging
from tempest.scenario import manager
from tempest.test import attr
from tempest.test import call_until_true
import time
-LOG = logging.getLogger(__name__)
-
-
class AutoScalingTest(manager.OrchestrationScenarioTest):
def setUp(self):
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 95d2862..003c264 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -15,13 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.openstack.common import log as logging
from tempest.scenario import manager
-LOG = logging.getLogger(__name__)
-
-
class TestSnapshotPattern(manager.OfficialClientTest):
"""
This test is for snapshotting an instance and booting with it.
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 3cbd1fa..5af4bb2 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -143,7 +143,7 @@
got_timestamp = ssh_client.exec_command('sudo cat /mnt/timestamp')
self.assertEqual(self.timestamp, got_timestamp)
- @testtools.skip("Until Bug #1205344 is fixed")
+ @testtools.skip("Skipped until the Bug #1205344 is resolved.")
def test_stamp_pattern(self):
# prepare for booting a instance
self._add_keypair()
diff --git a/tempest/scenario/test_volume_snapshot_pattern.py b/tempest/scenario/test_volume_snapshot_pattern.py
index 8fa177e..d873d30 100644
--- a/tempest/scenario/test_volume_snapshot_pattern.py
+++ b/tempest/scenario/test_volume_snapshot_pattern.py
@@ -12,13 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.openstack.common import log as logging
-
from tempest.common.utils.data_utils import rand_name
from tempest.scenario import manager
-LOG = logging.getLogger(__name__)
-
class TestVolumeSnapshotPattern(manager.OfficialClientTest):
diff --git a/tempest/stress/actions/ssh_floating.py b/tempest/stress/actions/ssh_floating.py
new file mode 100644
index 0000000..36ef023
--- /dev/null
+++ b/tempest/stress/actions/ssh_floating.py
@@ -0,0 +1,189 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import socket
+import subprocess
+
+from tempest.common.utils.data_utils import rand_name
+import tempest.stress.stressaction as stressaction
+import tempest.test
+
+
+class FloatingStress(stressaction.StressAction):
+
+ # from the scenario manager
+ def ping_ip_address(self, ip_address):
+ cmd = ['ping', '-c1', '-w1', ip_address]
+
+ proc = subprocess.Popen(cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ proc.wait()
+ success = proc.returncode == 0
+ self.logger.info("%s(%s): %s", self.server_id, self.floating['ip'],
+ "pong!" if success else "no pong :(")
+ return success
+
+ def tcp_connect_scan(self, addr, port):
+ # like tcp
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ try:
+ s.connect((addr, port))
+ except socket.error as exc:
+ self.logger.info("%s(%s): %s", self.server_id, self.floating['ip'],
+ str(exc))
+ return False
+ self.logger.info("%s(%s): Connected :)", self.server_id,
+ self.floating['ip'])
+ s.close()
+ return True
+
+ def check_port_ssh(self):
+ def func():
+ return self.tcp_connect_scan(self.floating['ip'], 22)
+ if not tempest.test.call_until_true(func, self.check_timeout,
+ self.check_interval):
+ raise RuntimeError("Cannot connect to the ssh port.")
+
+ def check_icmp_echo(self):
+ def func():
+ return self.ping_ip_address(self.floating['ip'])
+ if not tempest.test.call_until_true(func, self.check_timeout,
+ self.check_interval):
+ raise RuntimeError("Cannot ping the machine.")
+
+ def _create_vm(self):
+ self.name = name = rand_name("instance")
+ servers_client = self.manager.servers_client
+ self.logger.info("creating %s" % name)
+ vm_args = self.vm_extra_args.copy()
+ vm_args['security_groups'] = [{'name': self.sec_grp}]
+ resp, server = servers_client.create_server(name, self.image,
+ self.flavor,
+ **vm_args)
+ self.server_id = server['id']
+ assert(resp.status == 202)
+ if self.wait_after_vm_create:
+ self.manager.servers_client.wait_for_server_status(self.server_id,
+ 'ACTIVE')
+
+ def _destroy_vm(self):
+ self.logger.info("deleting %s" % self.server_id)
+ resp, _ = self.manager.servers_client.delete_server(self.server_id)
+ assert(resp.status == 204) # It cannot be 204 if I had to wait..
+ self.manager.servers_client.wait_for_server_termination(self.server_id)
+ self.logger.info("deleted %s" % self.server_id)
+
+ def _create_sec_group(self):
+ sec_grp_cli = self.manager.security_groups_client
+ s_name = rand_name('sec_grp-')
+ s_description = rand_name('desc-')
+ _, _sec_grp = sec_grp_cli.create_security_group(s_name,
+ s_description)
+ self.sec_grp = _sec_grp['id']
+ create_rule = sec_grp_cli.create_security_group_rule
+ create_rule(self.sec_grp, 'tcp', 22, 22)
+ create_rule(self.sec_grp, 'icmp', -1, -1)
+
+ def _destroy_sec_grp(self):
+ sec_grp_cli = self.manager.security_groups_client
+ sec_grp_cli.delete_security_group(self.sec_grp)
+
+ def _create_floating_ip(self):
+ floating_cli = self.manager.floating_ips_client
+ _, self.floating = floating_cli.create_floating_ip(self.floating_pool)
+
+ def _destroy_floating_ip(self):
+ cli = self.manager.floating_ips_client
+ cli.delete_floating_ip(self.floating['id'])
+ cli.wait_for_resource_deletion(self.floating['id'])
+ self.logger.info("Deleted Floating IP %s", str(self.floating['ip']))
+
+ def setUp(self, **kwargs):
+ self.image = self.manager.config.compute.image_ref
+ self.flavor = self.manager.config.compute.flavor_ref
+ self.vm_extra_args = kwargs.get('vm_extra_args', {})
+ self.wait_after_vm_create = kwargs.get('wait_after_vm_create',
+ True)
+ self.new_vm = kwargs.get('new_vm', False)
+ self.new_sec_grp = kwargs.get('new_sec_group', False)
+ self.new_floating = kwargs.get('new_floating', False)
+ self.reboot = kwargs.get('reboot', False)
+ self.floating_pool = kwargs.get('floating_pool', None)
+ self.verify = kwargs.get('verify', ('check_port_ssh',
+ 'check_icmp_echo'))
+ self.check_timeout = kwargs.get('check_timeout', 120)
+ self.check_interval = kwargs.get('check_interval', 1)
+ self.wait_for_disassociate = kwargs.get('wait_for_disassociate',
+ True)
+
+ # allocate floating
+ if not self.new_floating:
+ self._create_floating_ip()
+ # add security group
+ if not self.new_sec_grp:
+ self._create_sec_group()
+ # create vm
+ if not self.new_vm:
+ self._create_vm()
+
+ def wait_disassociate(self):
+ cli = self.manager.floating_ips_client
+
+ def func():
+ _, floating = cli.get_floating_ip_details(self.floating['id'])
+ return floating['instance_id'] is None
+
+ if not tempest.test.call_until_true(func, self.check_timeout,
+ self.check_interval):
+ raise RuntimeError("IP disassociate timeout!")
+
+ def run_core(self):
+ cli = self.manager.floating_ips_client
+ cli.associate_floating_ip_to_server(self.floating['ip'],
+ self.server_id)
+ for method in self.verify:
+ m = getattr(self, method)
+ m()
+ cli.disassociate_floating_ip_from_server(self.floating['ip'],
+ self.server_id)
+ if self.wait_for_disassociate:
+ self.wait_disassociate()
+
+ def run(self):
+ if self.new_sec_grp:
+ self._create_sec_group()
+ if self.new_floating:
+ self._create_floating_ip()
+ if self.new_vm:
+ self._create_vm()
+ if self.reboot:
+ self.manager.servers_client.reboot(self.server_id, 'HARD')
+
+ self.run_core()
+
+ if self.new_vm:
+ self._destroy_vm()
+ if self.new_floating:
+ self._destroy_floating_ip()
+ if self.new_sec_grp:
+ self._destroy_sec_grp()
+
+ def tearDown(self):
+ if not self.new_vm:
+ self._destroy_vm()
+ if not self.new_floating:
+ self._destroy_floating_ip()
+ if not self.new_sec_grp:
+ self._destroy_sec_grp()
diff --git a/tempest/stress/etc/ssh_floating.json b/tempest/stress/etc/ssh_floating.json
new file mode 100644
index 0000000..0cb6776
--- /dev/null
+++ b/tempest/stress/etc/ssh_floating.json
@@ -0,0 +1,16 @@
+[{"action": "tempest.stress.actions.ssh_floating.FloatingStress",
+ "threads": 8,
+ "use_admin": false,
+ "use_isolated_tenants": false,
+ "kwargs": {"vm_extra_args": {},
+ "new_vm": true,
+ "new_sec_group": true,
+ "new_floating": true,
+ "verify": ["check_icmp_echo", "check_port_ssh"],
+ "check_timeout": 120,
+ "check_inerval": 1,
+ "wait_after_vm_create": true,
+ "wait_for_disassociate": true,
+ "reboot": false}
+}
+]
diff --git a/tempest/stress/run_stress.py b/tempest/stress/run_stress.py
index aab2afd..886d94b 100755
--- a/tempest/stress/run_stress.py
+++ b/tempest/stress/run_stress.py
@@ -17,16 +17,16 @@
# limitations under the License.
import argparse
+import inspect
import json
import sys
from testtools.testsuite import iterate_tests
from unittest import loader
-def discover_stress_tests(path="./", filter_attr=None):
+def discover_stress_tests(path="./", filter_attr=None, call_inherited=False):
"""Discovers all tempest tests and create action out of them
"""
-
tests = []
testloader = loader.TestLoader()
list = testloader.discover(path)
@@ -52,6 +52,11 @@
"class_setup_per": class_setup_per
}
}
+ if (not call_inherited and
+ getattr(test_func, "st_allow_inheritance") is not True):
+ class_structure = inspect.getmro(test_func.im_class)
+ if test_func.__name__ not in class_structure[0].__dict__:
+ continue
tests.append(action)
return tests
@@ -63,7 +68,8 @@
if not ns.all:
tests = json.load(open(ns.tests, 'r'))
else:
- tests = discover_stress_tests(filter_attr=ns.type)
+ tests = discover_stress_tests(filter_attr=ns.type,
+ call_inherited=ns.call_inherited)
if ns.serial:
for test in tests:
@@ -79,22 +85,25 @@
return result
-parser = argparse.ArgumentParser(description='Run stress tests. ')
+parser = argparse.ArgumentParser(description='Run stress tests')
parser.add_argument('-d', '--duration', default=300, type=int,
- help="Duration of test in secs.")
+ help="Duration of test in secs")
parser.add_argument('-s', '--serial', action='store_true',
- help="Trigger running tests serially.")
+ help="Trigger running tests serially")
parser.add_argument('-S', '--stop', action='store_true',
- default=False, help="Stop on first error.")
+ default=False, help="Stop on first error")
parser.add_argument('-n', '--number', type=int,
- help="How often an action is executed for each process.")
+ help="How often an action is executed for each process")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-a', '--all', action='store_true',
help="Execute all stress tests")
parser.add_argument('-T', '--type',
help="Filters tests of a certain type (e.g. gate)")
+parser.add_argument('-i', '--call-inherited', action='store_true',
+ default=False,
+ help="Call also inherited function with stress attribute")
group.add_argument('-t', "--tests", nargs='?',
- help="Name of the file with test description.")
+ help="Name of the file with test description")
if __name__ == "__main__":
sys.exit(main(parser.parse_args()))
diff --git a/tempest/test.py b/tempest/test.py
index ccb985a..decae94 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -67,12 +67,17 @@
``application``: once in the stress job lifetime
``process``: once in the worker process lifetime
``action``: on each action
+ @param allow_inheritance: allows inheritance of this attribute
"""
def decorator(f):
if 'class_setup_per' in kwargs:
setattr(f, "st_class_setup_per", kwargs['class_setup_per'])
else:
setattr(f, "st_class_setup_per", 'process')
+ if 'allow_inheritance' in kwargs:
+ setattr(f, "st_allow_inheritance", kwargs['allow_inheritance'])
+ else:
+ setattr(f, "st_allow_inheritance", False)
attr(type='stress')(f)
return f
return decorator
@@ -141,7 +146,7 @@
@classmethod
def tearDownClass(cls):
- at_exit_set.remove(cls)
+ at_exit_set.discard(cls)
if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
super(BaseTestCase, cls).tearDownClass()
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index 5007503..a848fc9 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -233,7 +233,7 @@
# NOTE(afazekas): doctored test case,
# with normal validation it would fail
- @testtools.skip("Until Bug #1182679 is fixed")
+ @testtools.skip("Skipped until the Bug #1182679 is resolved.")
@attr(type='smoke')
def test_integration_1(self):
# EC2 1. integration test (not strict)