Merge "SSH connection related cleanups"
diff --git a/HACKING.rst b/HACKING.rst
index 1083075..3fa1ff5 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -39,7 +39,7 @@
when the additional operations leads to another exception.
Just letting an exception to propagate, is not bad idea in a test case,
- at all.
+at all.
Try to avoid using any exception handling construct which can hide the errors
origin.
@@ -53,10 +53,10 @@
test fails part way through.
Use the ``self.assert*`` methods provided by the unit test framework
- the signal failures early.
+the signal failures early.
Avoid using the ``self.fail`` alone, it's stack trace will signal
- the ``self.fail`` line as the origin of the error.
+the ``self.fail`` line as the origin of the error.
Avoid constructing complex boolean expressions for assertion.
The ``self.assertTrue`` or ``self.assertFalse`` without a ``msg`` argument,
diff --git a/tempest/api/compute/admin/test_availability_zone.py b/tempest/api/compute/admin/test_availability_zone.py
index d6488c4..e1a1a5d 100644
--- a/tempest/api/compute/admin/test_availability_zone.py
+++ b/tempest/api/compute/admin/test_availability_zone.py
@@ -16,21 +16,20 @@
# under the License.
from tempest.api.compute import base
-from tempest import exceptions
from tempest.test import attr
-class AvailabilityZoneAdminTestJSON(base.BaseV2ComputeAdminTest):
+class AZAdminTestJSON(base.BaseV2ComputeAdminTest):
"""
- Tests Availability Zone API List that require admin privileges
+ Tests Availability Zone API List
"""
_interface = 'json'
@classmethod
def setUpClass(cls):
- super(AvailabilityZoneAdminTestJSON, cls).setUpClass()
+ super(AZAdminTestJSON, cls).setUpClass()
cls.client = cls.os_adm.availability_zone_client
cls.non_adm_client = cls.availability_zone_client
@@ -57,14 +56,6 @@
self.assertEqual(200, resp.status)
self.assertTrue(len(availability_zone) > 0)
- @attr(type=['negative', 'gate'])
- def test_get_availability_zone_list_detail_with_non_admin_user(self):
- # List of availability zones and available services with
- # non-administrator user
- self.assertRaises(
- exceptions.Unauthorized,
- self.non_adm_client.get_availability_zone_list_detail)
-
-class AvailabilityZoneAdminTestXML(AvailabilityZoneAdminTestJSON):
+class AZAdminTestXML(AZAdminTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/admin/test_availability_zone_negative.py b/tempest/api/compute/admin/test_availability_zone_negative.py
new file mode 100644
index 0000000..6ba8d58
--- /dev/null
+++ b/tempest/api/compute/admin/test_availability_zone_negative.py
@@ -0,0 +1,45 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 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.test import attr
+
+
+class AZAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+
+ """
+ Tests Availability Zone API List
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(AZAdminNegativeTestJSON, cls).setUpClass()
+ cls.non_adm_client = cls.availability_zone_client
+
+ @attr(type=['negative', 'gate'])
+ def test_get_availability_zone_list_detail_with_non_admin_user(self):
+ # List of availability zones and available services with
+ # non-administrator user
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_availability_zone_list_detail)
+
+
+class AZAdminNegativeTestXML(AZAdminNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index 81e4f87..0b20e90 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -18,7 +18,6 @@
import uuid
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest import exceptions
from tempest.test import attr
@@ -31,14 +30,6 @@
super(FlavorsNegativeTestJSON, cls).setUpClass()
cls.client = cls.flavors_client
- # Generating a nonexistent flavor id
- resp, flavors = cls.client.list_flavors()
- flavor_ids = [flavor['id'] for flavor in flavors]
- while True:
- cls.nonexistent_flavor_id = data_utils.rand_int_id(start=999)
- if cls.nonexistent_flavor_id not in flavor_ids:
- break
-
@attr(type=['negative', 'gate'])
def test_invalid_minRam_filter(self):
self.assertRaises(exceptions.BadRequest,
@@ -52,17 +43,11 @@
{'minDisk': 'invalid'})
@attr(type=['negative', 'gate'])
- def test_get_flavor_details_for_invalid_flavor_id(self):
- # Ensure 404 returned for invalid flavor ID
- invalid_flavor_id = str(uuid.uuid4())
- self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
- invalid_flavor_id)
-
- @attr(type=['negative', 'gate'])
def test_non_existent_flavor_id(self):
# flavor details are not returned for non-existent flavors
+ nonexistent_flavor_id = str(uuid.uuid4())
self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
- self.nonexistent_flavor_id)
+ nonexistent_flavor_id)
class FlavorsNegativeTestXML(FlavorsNegativeTestJSON):
diff --git a/tempest/api/compute/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index 50b6c77..b36595c 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -17,7 +17,7 @@
from tempest.api.compute import base
from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
class KeyPairsTestJSON(base.BaseV2ComputeTest):
@@ -28,14 +28,23 @@
super(KeyPairsTestJSON, cls).setUpClass()
cls.client = cls.keypairs_client
- @attr(type='gate')
+ def _delete_keypair(self, keypair_name):
+ resp, _ = self.client.delete_keypair(keypair_name)
+ self.assertEqual(202, resp.status)
+
+ def _create_keypair(self, keypair_name, pub_key=None):
+ resp, body = self.client.create_keypair(keypair_name, pub_key)
+ self.addCleanup(self._delete_keypair, keypair_name)
+ return resp, body
+
+ @test.attr(type='gate')
def test_keypairs_create_list_delete(self):
# Keypairs created should be available in the response list
# Create 3 keypairs
key_list = list()
for i in range(3):
k_name = data_utils.rand_name('keypair-')
- resp, keypair = self.client.create_keypair(k_name)
+ resp, keypair = self._create_keypair(k_name)
# Need to pop these keys so that our compare doesn't fail later,
# as the keypair dicts from list API doesn't have them.
keypair.pop('private_key')
@@ -57,16 +66,12 @@
self.assertFalse(missing_kps,
"Failed to find keypairs %s in fetched list"
% ', '.join(m_key['name'] for m_key in missing_kps))
- # Delete all the keypairs created
- for keypair in key_list:
- resp, _ = self.client.delete_keypair(keypair['name'])
- self.assertEqual(202, resp.status)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_keypair_create_delete(self):
# Keypair should be created, verified and deleted
k_name = data_utils.rand_name('keypair-')
- resp, keypair = self.client.create_keypair(k_name)
+ resp, keypair = self._create_keypair(k_name)
self.assertEqual(200, resp.status)
private_key = keypair['private_key']
key_name = keypair['name']
@@ -75,15 +80,12 @@
"to the requested name")
self.assertTrue(private_key is not None,
"Field private_key is empty or not found.")
- resp, _ = self.client.delete_keypair(k_name)
- self.assertEqual(202, resp.status)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_keypair_detail(self):
# Keypair should be created, Got details by name and deleted
k_name = data_utils.rand_name('keypair-')
- resp, keypair = self.client.create_keypair(k_name)
- self.addCleanup(self.client.delete_keypair, k_name)
+ resp, keypair = self._create_keypair(k_name)
resp, keypair_detail = self.client.get_keypair(k_name)
self.assertEqual(200, resp.status)
self.assertIn('name', keypair_detail)
@@ -95,7 +97,7 @@
self.assertTrue(public_key is not None,
"Field public_key is empty or not found.")
- @attr(type='gate')
+ @test.attr(type='gate')
def test_keypair_create_with_pub_key(self):
# Keypair should be created with a given public key
k_name = data_utils.rand_name('keypair-')
@@ -108,7 +110,7 @@
"LOeB1kYMOBaiUPLQTWXR3JpckqFIQwhIH0zoHlJvZE8hh90"
"XcPojYN56tI0OlrGqojbediJYD0rUsJu4weZpbn8vilb3JuDY+jws"
"snSA8wzBx3A/8y9Pp1B nova@ubuntu")
- resp, keypair = self.client.create_keypair(k_name, pub_key)
+ resp, keypair = self._create_keypair(k_name, pub_key)
self.assertEqual(200, resp.status)
self.assertFalse('private_key' in keypair,
"Field private_key is not empty!")
@@ -116,8 +118,6 @@
self.assertEqual(key_name, k_name,
"The created keypair name is not equal "
"to the requested name!")
- resp, _ = self.client.delete_keypair(k_name)
- self.assertEqual(202, resp.status)
class KeyPairsTestXML(KeyPairsTestJSON):
diff --git a/tempest/api/compute/keypairs/test_keypairs_negative.py b/tempest/api/compute/keypairs/test_keypairs_negative.py
index fad985e..621487c 100644
--- a/tempest/api/compute/keypairs/test_keypairs_negative.py
+++ b/tempest/api/compute/keypairs/test_keypairs_negative.py
@@ -19,7 +19,7 @@
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class KeyPairsNegativeTestJSON(base.BaseV2ComputeTest):
@@ -30,67 +30,71 @@
super(KeyPairsNegativeTestJSON, cls).setUpClass()
cls.client = cls.keypairs_client
- @attr(type=['negative', 'gate'])
+ def _create_keypair(self, keypair_name, pub_key=None):
+ self.client.create_keypair(keypair_name, pub_key)
+ self.addCleanup(self.client.delete_keypair, keypair_name)
+
+ @test.attr(type=['negative', 'gate'])
def test_keypair_create_with_invalid_pub_key(self):
# Keypair should not be created with a non RSA public key
k_name = data_utils.rand_name('keypair-')
pub_key = "ssh-rsa JUNK nova@ubuntu"
self.assertRaises(exceptions.BadRequest,
- self.client.create_keypair, k_name, pub_key)
+ self._create_keypair, k_name, pub_key)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_keypair_delete_nonexistant_key(self):
# Non-existant key deletion should throw a proper error
k_name = data_utils.rand_name("keypair-non-existant-")
self.assertRaises(exceptions.NotFound, self.client.delete_keypair,
k_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_with_empty_public_key(self):
# Keypair should not be created with an empty public key
k_name = data_utils.rand_name("keypair-")
pub_key = ' '
- self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
k_name, pub_key)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_when_public_key_bits_exceeds_maximum(self):
# Keypair should not be created when public key bits are too long
k_name = data_utils.rand_name("keypair-")
pub_key = 'ssh-rsa ' + 'A' * 2048 + ' openstack@ubuntu'
- self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
k_name, pub_key)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_with_duplicate_name(self):
# Keypairs with duplicate names should not be created
k_name = data_utils.rand_name('keypair-')
resp, _ = self.client.create_keypair(k_name)
self.assertEqual(200, resp.status)
# Now try the same keyname to create another key
- self.assertRaises(exceptions.Conflict, self.client.create_keypair,
+ self.assertRaises(exceptions.Conflict, self._create_keypair,
k_name)
resp, _ = self.client.delete_keypair(k_name)
self.assertEqual(202, resp.status)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_with_empty_name_string(self):
# Keypairs with name being an empty string should not be created
- self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
'')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_with_long_keynames(self):
# Keypairs with name longer than 255 chars should not be created
k_name = 'keypair-'.ljust(260, '0')
- self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
k_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_invalid_name(self):
# Keypairs with name being an invalid name should not be created
k_name = 'key_/.\@:'
- self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
k_name)
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index 8f1e446..b0bffc4 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -17,18 +17,31 @@
from tempest.api.compute import base
-from tempest.test import attr
+from tempest import test
+import testtools
class ExtensionsTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
- @attr(type='gate')
+ @testtools.skipIf(not test.is_extension_enabled('os-consoles', 'compute'),
+ 'os-consoles extension not enabled.')
+ @test.attr(type='gate')
def test_list_extensions(self):
# List of all extensions
resp, extensions = self.extensions_client.list_extensions()
self.assertIn("extensions", extensions)
self.assertEqual(200, resp.status)
+ self.assertTrue(self.extensions_client.is_enabled("Consoles"))
+
+ @testtools.skipIf(not test.is_extension_enabled('os-consoles', 'compute'),
+ 'os-consoles extension not enabled.')
+ @test.attr(type='gate')
+ def test_get_extension(self):
+ # get the specified extensions
+ resp, extension = self.extensions_client.get_extension('os-consoles')
+ self.assertEqual(200, resp.status)
+ self.assertEqual('os-consoles', extension['alias'])
class ExtensionsTestXML(ExtensionsTestJSON):
diff --git a/tempest/api/compute/v3/admin/test_availability_zone.py b/tempest/api/compute/v3/admin/test_availability_zone.py
index ff2765c..dca556f 100644
--- a/tempest/api/compute/v3/admin/test_availability_zone.py
+++ b/tempest/api/compute/v3/admin/test_availability_zone.py
@@ -16,21 +16,20 @@
# under the License.
from tempest.api.compute import base
-from tempest import exceptions
from tempest.test import attr
-class AvailabilityZoneAdminV3TestJSON(base.BaseV3ComputeAdminTest):
+class AZAdminV3TestJSON(base.BaseV3ComputeAdminTest):
"""
- Tests Availability Zone API List that require admin privileges
+ Tests Availability Zone API List
"""
_interface = 'json'
@classmethod
def setUpClass(cls):
- super(AvailabilityZoneAdminV3TestJSON, cls).setUpClass()
+ super(AZAdminV3TestJSON, cls).setUpClass()
cls.client = cls.availability_zone_admin_client
cls.non_adm_client = cls.availability_zone_client
@@ -57,14 +56,6 @@
self.assertEqual(200, resp.status)
self.assertTrue(len(availability_zone) > 0)
- @attr(type=['negative', 'gate'])
- def test_get_availability_zone_list_detail_with_non_admin_user(self):
- # List of availability zones and available services with
- # non-administrator user
- self.assertRaises(
- exceptions.Unauthorized,
- self.non_adm_client.get_availability_zone_list_detail)
-
-class AvailabilityZoneAdminV3TestXML(AvailabilityZoneAdminV3TestJSON):
+class AZAdminV3TestXML(AZAdminV3TestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_availability_zone_negative.py b/tempest/api/compute/v3/admin/test_availability_zone_negative.py
new file mode 100644
index 0000000..93a57e3
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_availability_zone_negative.py
@@ -0,0 +1,47 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.test import attr
+
+
+class AZAdminNegativeV3TestJSON(base.BaseV3ComputeAdminTest):
+
+ """
+ Tests Availability Zone API List
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(AZAdminNegativeV3TestJSON, cls).setUpClass()
+ cls.client = cls.availability_zone_admin_client
+ cls.non_adm_client = cls.availability_zone_client
+
+ @attr(type=['negative', 'gate'])
+ def test_get_availability_zone_list_detail_with_non_admin_user(self):
+ # List of availability zones and available services with
+ # non-administrator user
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_availability_zone_list_detail)
+
+
+class AZAdminNegativeV3TestXML(AZAdminNegativeV3TestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/test_extensions.py b/tempest/api/compute/v3/test_extensions.py
index d7269d1..2affd86 100644
--- a/tempest/api/compute/v3/test_extensions.py
+++ b/tempest/api/compute/v3/test_extensions.py
@@ -17,18 +17,26 @@
from tempest.api.compute import base
-from tempest.test import attr
+from tempest import test
class ExtensionsV3TestJSON(base.BaseV3ComputeTest):
_interface = 'json'
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_extensions(self):
# List of all extensions
resp, extensions = self.extensions_client.list_extensions()
self.assertIn("extensions", extensions)
self.assertEqual(200, resp.status)
+ self.assertTrue(self.extensions_client.is_enabled("Consoles"))
+
+ @test.attr(type='gate')
+ def test_get_extension(self):
+ # get the specified extensions
+ resp, extension = self.extensions_client.get_extension('servers')
+ self.assertEqual(200, resp.status)
+ self.assertEqual('servers', extension['alias'])
class ExtensionsV3TestXML(ExtensionsV3TestJSON):
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 44198f0..d2b40c9 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -88,3 +88,34 @@
raise exceptions.TimeoutException(message)
old_status = server_status
old_task_state = task_state
+
+
+def wait_for_image_status(client, image_id, status):
+ """Waits for an image to reach a given status.
+
+ The client should have a get_image(image_id) method to get the image.
+ The client should also have build_interval and build_timeout attributes.
+ """
+ resp, image = client.get_image(image_id)
+ start = int(time.time())
+
+ while image['status'] != status:
+ time.sleep(client.build_interval)
+ resp, image = client.get_image(image_id)
+ if image['status'] == 'ERROR':
+ raise exceptions.AddImageException(image_id=image_id)
+
+ # check the status again to avoid a false negative where we hit
+ # the timeout at the same time that the image reached the expected
+ # status
+ if image['status'] == status:
+ return
+
+ if int(time.time()) - start >= client.build_timeout:
+ message = ('Image %(image_id)s failed to reach %(status)s '
+ 'status within the required time (%(timeout)s s).' %
+ {'image_id': image_id,
+ 'status': status,
+ 'timeout': client.build_timeout})
+ message += ' Current status: %s.' % image['status']
+ raise exceptions.TimeoutException(message)
diff --git a/tempest/services/compute/json/extensions_client.py b/tempest/services/compute/json/extensions_client.py
index ce46a9b..ad5354c 100644
--- a/tempest/services/compute/json/extensions_client.py
+++ b/tempest/services/compute/json/extensions_client.py
@@ -37,3 +37,8 @@
_, extensions = self.list_extensions()
exts = extensions['extensions']
return any([e for e in exts if e['name'] == extension])
+
+ def get_extension(self, extension_alias):
+ resp, body = self.get('extensions/%s' % extension_alias)
+ body = json.loads(body)
+ return resp, body['extension']
diff --git a/tempest/services/compute/json/images_client.py b/tempest/services/compute/json/images_client.py
index 5f17894..c05571a 100644
--- a/tempest/services/compute/json/images_client.py
+++ b/tempest/services/compute/json/images_client.py
@@ -16,10 +16,10 @@
# under the License.
import json
-import time
import urllib
from tempest.common.rest_client import RestClient
+from tempest.common import waiters
from tempest import exceptions
@@ -82,18 +82,7 @@
def wait_for_image_status(self, image_id, status):
"""Waits for an image to reach a given status."""
- resp, image = self.get_image(image_id)
- start = int(time.time())
-
- while image['status'] != status:
- time.sleep(self.build_interval)
- resp, image = self.get_image(image_id)
-
- if image['status'] == 'ERROR':
- raise exceptions.AddImageException(image_id=image_id)
-
- if int(time.time()) - start >= self.build_timeout:
- raise exceptions.TimeoutException
+ waiters.wait_for_image_status(self, image_id, status)
def list_image_metadata(self, image_id):
"""Lists all metadata items for an image."""
diff --git a/tempest/services/compute/v3/json/extensions_client.py b/tempest/services/compute/v3/json/extensions_client.py
index 60c0217..6e0dc9d 100644
--- a/tempest/services/compute/v3/json/extensions_client.py
+++ b/tempest/services/compute/v3/json/extensions_client.py
@@ -38,3 +38,8 @@
_, extensions = self.list_extensions()
exts = extensions['extensions']
return any([e for e in exts if e['name'] == extension])
+
+ def get_extension(self, extension_alias):
+ resp, body = self.get('extensions/%s' % extension_alias)
+ body = json.loads(body)
+ return resp, body['extension']
diff --git a/tempest/services/compute/v3/xml/extensions_client.py b/tempest/services/compute/v3/xml/extensions_client.py
index e03251c..8f97692 100644
--- a/tempest/services/compute/v3/xml/extensions_client.py
+++ b/tempest/services/compute/v3/xml/extensions_client.py
@@ -43,3 +43,8 @@
_, extensions = self.list_extensions()
exts = extensions['extensions']
return any([e for e in exts if e['name'] == extension])
+
+ def get_extension(self, extension_alias):
+ resp, body = self.get('extensions/%s' % extension_alias, self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
diff --git a/tempest/services/compute/xml/extensions_client.py b/tempest/services/compute/xml/extensions_client.py
index 1395b5a..b17fc4f 100644
--- a/tempest/services/compute/xml/extensions_client.py
+++ b/tempest/services/compute/xml/extensions_client.py
@@ -43,3 +43,8 @@
_, extensions = self.list_extensions()
exts = extensions['extensions']
return any([e for e in exts if e['name'] == extension])
+
+ def get_extension(self, extension_alias):
+ resp, body = self.get('extensions/%s' % extension_alias, self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
diff --git a/tempest/services/compute/xml/images_client.py b/tempest/services/compute/xml/images_client.py
index b17ae78..20fcc9b 100644
--- a/tempest/services/compute/xml/images_client.py
+++ b/tempest/services/compute/xml/images_client.py
@@ -15,12 +15,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-import time
import urllib
from lxml import etree
from tempest.common.rest_client import RestClientXML
+from tempest.common import waiters
from tempest import exceptions
from tempest.services.compute.xml.common import Document
from tempest.services.compute.xml.common import Element
@@ -140,17 +140,7 @@
def wait_for_image_status(self, image_id, status):
"""Waits for an image to reach a given status."""
- resp, image = self.get_image(image_id)
- start = int(time.time())
-
- while image['status'] != status:
- time.sleep(self.build_interval)
- resp, image = self.get_image(image_id)
- if image['status'] == 'ERROR':
- raise exceptions.AddImageException(image_id=image_id)
-
- if int(time.time()) - start >= self.build_timeout:
- raise exceptions.TimeoutException
+ waiters.wait_for_image_status(self, image_id, status)
def _metadata_body(self, meta):
post_body = Element('metadata')