Merge "Add a test for deleting a suspended server"
diff --git a/HACKING.rst b/HACKING.rst
index 025bf74..29d5bf4 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -227,3 +227,48 @@
2. The unit tests cannot use setUpClass, instead fixtures and testresources
should be used for shared state between tests.
+
+
+.. _TestDocumentation:
+
+Test Documentation
+------------------
+For tests being added we need to require inline documentation in the form of
+docstings to explain what is being tested. In API tests for a new API a class
+level docstring should be added to an API reference doc. If one doesn't exist
+a TODO comment should be put indicating that the reference needs to be added.
+For individual API test cases a method level docstring should be used to
+explain the functionality being tested if the test name isn't descriptive
+enough. For example::
+
+ def test_get_role_by_id(self):
+ """Get a role by its id."""
+
+the docstring there is superfluous and shouldn't be added. but for a method
+like::
+
+ def test_volume_backup_create_get_detailed_list_restore_delete(self):
+ pass
+
+a docstring would be useful because while the test title is fairly descriptive
+the operations being performed are complex enough that a bit more explanation
+will help people figure out the intent of the test.
+
+For scenario tests a class level docstring describing the steps in the scenario
+is required. If there is more than one test case in the class individual
+docstrings for the workflow in each test methods can be used instead. A good
+example of this would be::
+
+ class TestVolumeBootPattern(manager.OfficialClientTest):
+ """
+ This test case attempts to reproduce the following steps:
+
+ * Create in Cinder some bootable volume importing a Glance image
+ * Boot an instance from the bootable volume
+ * Write content to the volume
+ * Delete an instance and Boot a new instance from the volume
+ * Check written content in the instance
+ * Create a volume snapshot while the instance is running
+ * Boot an additional instance from the new snapshot based volume
+ * Check written content in the instance booted from snapshot
+ """
diff --git a/REVIEWING.rst b/REVIEWING.rst
index d6dc83e..74bd2ad 100644
--- a/REVIEWING.rst
+++ b/REVIEWING.rst
@@ -51,6 +51,15 @@
whether to skip or not.
+Test Documentation
+------------------
+When a new test is being added refer to the :ref:`TestDocumentation` section in
+hacking to see if the requirements are being met. With the exception of a class
+level docstring linking to the API ref doc in the API tests and a docstring for
+scenario tests this is up to the reviewers discretion whether a docstring is
+required or not.
+
+
When to approve
---------------
* Every patch needs two +2s before being approved.
diff --git a/requirements.txt b/requirements.txt
index 9a3b74d..708ede3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,25 +1,28 @@
+# The order of packages is significant, because pip processes them in the order
+# of appearance. Changing the order has an impact on the overall integration
+# process, which may cause wedges in the gate later.
pbr>=0.6,!=0.7,<1.0
anyjson>=0.3.3
httplib2>=0.7.5
jsonschema>=2.0.0,<3.0.0
testtools>=0.9.34
lxml>=2.3
-boto>=2.12.0,!=2.13.0
+boto>=2.32.1
paramiko>=1.13.0
-netaddr>=0.7.6
+netaddr>=0.7.12
python-ceilometerclient>=1.0.6
-python-glanceclient>=0.13.1
-python-keystoneclient>=0.9.0
-python-novaclient>=2.17.0
-python-neutronclient>=2.3.5,<3
-python-cinderclient>=1.0.7
+python-glanceclient>=0.14.0
+python-keystoneclient>=0.10.0
+python-novaclient>=2.18.0
+python-neutronclient>=2.3.6,<3
+python-cinderclient>=1.1.0
python-heatclient>=0.2.9
-python-ironicclient
-python-saharaclient>=0.6.0
-python-swiftclient>=2.0.2
+python-ironicclient>=0.2.1
+python-saharaclient>=0.7.3
+python-swiftclient>=2.2.0
testresources>=0.2.4
testrepository>=0.0.18
-oslo.config>=1.2.1
+oslo.config>=1.4.0 # Apache-2.0
six>=1.7.0
iso8601>=0.1.9
fixtures>=0.3.14
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 25f24b9..a984ade 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -64,7 +64,8 @@
def resource_cleanup(cls):
# Deleting the floating IP which is created in this method
cls.floating_ips_client.delete_floating_ip(cls.floating_ip_id)
- cls.delete_volume(cls.volume['id'])
+ if getattr(cls, 'volume', None):
+ cls.delete_volume(cls.volume['id'])
resp, cls.sg = cls.security_groups_client.delete_security_group(
cls.sg_id)
super(ServerRescueTestJSON, cls).resource_cleanup()
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index aa406f7..0d29968 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -56,7 +56,8 @@
@classmethod
def resource_cleanup(cls):
- cls.delete_volume(cls.volume['id'])
+ if getattr(cls, 'volume', None):
+ cls.delete_volume(cls.volume['id'])
super(ServerRescueNegativeTestJSON, cls).resource_cleanup()
def _detach(self, server_id, volume_id):
diff --git a/tempest/api/network/test_fwaas_extensions.py b/tempest/api/network/test_fwaas_extensions.py
index 193bf76..11588d6 100644
--- a/tempest/api/network/test_fwaas_extensions.py
+++ b/tempest/api/network/test_fwaas_extensions.py
@@ -86,7 +86,6 @@
(fw_id, target_states))
raise exceptions.TimeoutException(m)
- @test.attr(type='smoke')
def test_list_firewall_rules(self):
# List firewall rules
_, fw_rules = self.client.list_firewall_rules()
@@ -104,7 +103,6 @@
m['ip_version'],
m['enabled']) for m in fw_rules])
- @test.attr(type='smoke')
def test_create_update_delete_firewall_rule(self):
# Create firewall rule
_, body = self.client.create_firewall_rule(
@@ -125,14 +123,12 @@
self.assertNotIn(fw_rule_id,
[m['id'] for m in fw_rules['firewall_rules']])
- @test.attr(type='smoke')
def test_show_firewall_rule(self):
# show a created firewall rule
_, fw_rule = self.client.show_firewall_rule(self.fw_rule['id'])
for key, value in fw_rule['firewall_rule'].iteritems():
self.assertEqual(self.fw_rule[key], value)
- @test.attr(type='smoke')
def test_list_firewall_policies(self):
_, fw_policies = self.client.list_firewall_policies()
fw_policies = fw_policies['firewall_policies']
@@ -143,7 +139,6 @@
m['name'],
m['firewall_rules']) for m in fw_policies])
- @test.attr(type='smoke')
def test_create_update_delete_firewall_policy(self):
# Create firewall policy
_, body = self.client.create_firewall_policy(
@@ -166,7 +161,6 @@
fw_policies = fw_policies['firewall_policies']
self.assertNotIn(fw_policy_id, [m['id'] for m in fw_policies])
- @test.attr(type='smoke')
def test_show_firewall_policy(self):
# show a created firewall policy
_, fw_policy = self.client.show_firewall_policy(self.fw_policy['id'])
@@ -174,7 +168,6 @@
for key, value in fw_policy.iteritems():
self.assertEqual(self.fw_policy[key], value)
- @test.attr(type='smoke')
def test_create_show_delete_firewall(self):
# Create tenant network resources required for an ACTIVE firewall
network = self.create_network()
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index bf0cc70..27e388d 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -525,7 +525,8 @@
for volume in volumes:
client = client_for_user(volume['owner'])
volume_id = _get_volume_by_name(client, volume['name'])['id']
- r, body = client.volumes.delete_volume(volume_id)
+ client.volumes.detach_volume(volume_id)
+ client.volumes.delete_volume(volume_id)
def attach_volumes(volumes):
diff --git a/tempest/scenario/utils.py b/tempest/scenario/utils.py
index e2adb34..c20f20c 100644
--- a/tempest/scenario/utils.py
+++ b/tempest/scenario/utils.py
@@ -40,33 +40,33 @@
self.non_ssh_image_pattern = \
CONF.input_scenario.non_ssh_image_regex
# Setup clients
- ocm = clients.OfficialClientManager(
- auth.get_default_credentials('user'))
- self.client = ocm.compute_client
+ os = clients.Manager()
+ self.images_client = os.images_client
+ self.flavors_client = os.flavors_client
def ssh_user(self, image_id):
- _image = self.client.images.get(image_id)
+ _, _image = self.images_client.get_image(image_id)
for regex, user in self.ssh_users:
# First match wins
- if re.match(regex, _image.name) is not None:
+ if re.match(regex, _image['name']) is not None:
return user
else:
return self.default_ssh_user
def _is_sshable_image(self, image):
return not re.search(pattern=self.non_ssh_image_pattern,
- string=str(image.name))
+ string=str(image['name']))
def is_sshable_image(self, image_id):
- _image = self.client.images.get(image_id)
+ _, _image = self.images_client.get_image(image_id)
return self._is_sshable_image(_image)
def _is_flavor_enough(self, flavor, image):
- return image.minDisk <= flavor.disk
+ return image['minDisk'] <= flavor['disk']
def is_flavor_enough(self, flavor_id, image_id):
- _image = self.client.images.get(image_id)
- _flavor = self.client.flavors.get(flavor_id)
+ _, _image = self.images_client.get_image(image_id)
+ _, _flavor = self.flavors_client.get_flavor_details(flavor_id)
return self._is_flavor_enough(_flavor, _image)
@@ -81,7 +81,7 @@
load_tests = testscenarios.load_tests_apply_scenarios
- class TestInputScenario(manager.OfficialClientTest):
+ class TestInputScenario(manager.ScenarioTest):
scenario_utils = utils.InputScenarioUtils()
scenario_flavor = scenario_utils.scenario_flavors
@@ -91,17 +91,18 @@
def test_create_server_metadata(self):
name = rand_name('instance')
- _ = self.compute_client.servers.create(name=name,
- flavor=self.flavor_ref,
- image=self.image_ref)
+ self.servers_client.create_server(name=name,
+ flavor_ref=self.flavor_ref,
+ image_ref=self.image_ref)
"""
validchars = "-_.{ascii}{digit}".format(ascii=string.ascii_letters,
digit=string.digits)
def __init__(self):
- ocm = clients.OfficialClientManager(
+ os = clients.Manager(
auth.get_default_credentials('user', fill_in=False))
- self.client = ocm.compute_client
+ self.images_client = os.images_client
+ self.flavors_client = os.flavors_client
self.image_pattern = CONF.input_scenario.image_regex
self.flavor_pattern = CONF.input_scenario.flavor_regex
@@ -118,10 +119,11 @@
if not CONF.service_available.glance:
return []
if not hasattr(self, '_scenario_images'):
- images = self.client.images.list(detailed=False)
+ _, images = self.images_client.list_images()
self._scenario_images = [
- (self._normalize_name(i.name), dict(image_ref=i.id))
- for i in images if re.search(self.image_pattern, str(i.name))
+ (self._normalize_name(i['name']), dict(image_ref=i['id']))
+ for i in images if re.search(self.image_pattern,
+ str(i['name']))
]
return self._scenario_images
@@ -131,10 +133,11 @@
:return: a scenario with name and uuid of flavors
"""
if not hasattr(self, '_scenario_flavors'):
- flavors = self.client.flavors.list(detailed=False)
+ _, flavors = self.flavors_client.list_flavors()
self._scenario_flavors = [
- (self._normalize_name(f.name), dict(flavor_ref=f.id))
- for f in flavors if re.search(self.flavor_pattern, str(f.name))
+ (self._normalize_name(f['name']), dict(flavor_ref=f['id']))
+ for f in flavors if re.search(self.flavor_pattern,
+ str(f['name']))
]
return self._scenario_flavors
diff --git a/test-requirements.txt b/test-requirements.txt
index cd8154b..ba70259 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,10 +1,13 @@
+# The order of packages is significant, because pip processes them in the order
+# of appearance. Changing the order has an impact on the overall integration
+# process, which may cause wedges in the gate later.
hacking>=0.9.2,<0.10
# needed for doc build
sphinx>=1.1.2,!=1.2.0,<1.3
python-subunit>=0.0.18
-oslosphinx
+oslosphinx>=2.2.0 # Apache-2.0
mox>=0.5.3
mock>=1.0
coverage>=3.6
-oslotest
-stevedore>=0.14
+oslotest>=1.1.0 # Apache-2.0
+stevedore>=1.0.0 # Apache-2.0