Merge "delete servers in setUpClass if ecxeption raised"
diff --git a/cli/__init__.py b/cli/__init__.py
index 5d986c0..7a92260 100644
--- a/cli/__init__.py
+++ b/cli/__init__.py
@@ -71,6 +71,11 @@
return self.cmd_with_auth(
'keystone', action, flags, params, admin, fail_ok)
+ def glance(self, action, flags='', params='', admin=True, fail_ok=False):
+ """Executes glance command for the given action."""
+ return self.cmd_with_auth(
+ 'glance', action, flags, params, admin, fail_ok)
+
def cmd_with_auth(self, cmd, action, flags='', params='',
admin=True, fail_ok=False):
"""Executes given command with auth attributes appended."""
diff --git a/cli/simple_read_only/test_compute.py b/cli/simple_read_only/test_compute.py
index c904882..d301d38 100644
--- a/cli/simple_read_only/test_compute.py
+++ b/cli/simple_read_only/test_compute.py
@@ -22,7 +22,6 @@
import testtools
import cli
-from tempest import config
CONF = cfg.CONF
diff --git a/cli/simple_read_only/test_compute_manage.py b/cli/simple_read_only/test_compute_manage.py
index 650ef10..bbcc5b1 100644
--- a/cli/simple_read_only/test_compute_manage.py
+++ b/cli/simple_read_only/test_compute_manage.py
@@ -18,8 +18,6 @@
import logging
import subprocess
-import testtools
-
import cli
diff --git a/cli/simple_read_only/test_glance.py b/cli/simple_read_only/test_glance.py
new file mode 100644
index 0000000..f9822cc
--- /dev/null
+++ b/cli/simple_read_only/test_glance.py
@@ -0,0 +1,66 @@
+# 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.
+
+import logging
+import re
+import subprocess
+
+import cli
+
+
+LOG = logging.getLogger(__name__)
+
+
+class SimpleReadOnlyGlanceClientTest(cli.ClientTestBase):
+ """Basic, read-only tests for Glance CLI client.
+
+ Checks return values and output of read-only commands.
+ These tests do not presume any content, nor do they create
+ their own. They only verify the structure of output if present.
+ """
+
+ def test_glance_fake_action(self):
+ self.assertRaises(subprocess.CalledProcessError,
+ self.glance,
+ 'this-does-not-exist')
+
+ def test_glance_image_list(self):
+ out = self.glance('image-list')
+ endpoints = self.parser.listing(out)
+ self.assertTableStruct(endpoints, [
+ 'ID', 'Name', 'Disk Format', 'Container Format',
+ 'Size', 'Status'])
+
+ def test_glance_help(self):
+ help_text = self.glance('help')
+ lines = help_text.split('\n')
+ self.assertTrue(lines[0].startswith('usage: glance'))
+
+ commands = []
+ cmds_start = lines.index('Positional arguments:')
+ cmds_end = lines.index('Optional arguments:')
+ command_pattern = re.compile('^ {4}([a-z0-9\-\_]+)')
+ for line in lines[cmds_start:cmds_end]:
+ match = command_pattern.match(line)
+ if match:
+ commands.append(match.group(1))
+ commands = set(commands)
+ wanted_commands = set(('image-create', 'image-delete', 'help',
+ 'image-download', 'image-show', 'image-update',
+ 'member-add', 'member-create', 'member-delete',
+ 'member-list'))
+ self.assertFalse(wanted_commands - commands)
diff --git a/run_tests.sh b/run_tests.sh
index 93edfaf..3f394e3 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -95,14 +95,7 @@
function run_pep8 {
echo "Running pep8 ..."
- srcfiles="`find tempest -type f -name "*.py"`"
- srcfiles+=" `find tools -type f -name "*.py"`"
- srcfiles+=" `find stress -type f -name "*.py"`"
- srcfiles+=" setup.py"
-
- ignore='--ignore=E121,E122,E125,E126'
-
- ${wrapper} python tools/hacking.py ${ignore} ${srcfiles}
+ ${wrapper} tools/check_source.sh
}
function run_coverage_start {
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index d68b9ed..88c8886 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -320,6 +320,10 @@
except KeyError:
ctype = 'application/json'
+ # It is not an error response
+ if resp.status < 400:
+ return
+
JSON_ENC = ['application/json; charset=UTF-8', 'application/json',
'application/json; charset=utf-8']
# NOTE(mtreinish): This is for compatibility with Glance and swift
diff --git a/tempest/services/compute/json/security_groups_client.py b/tempest/services/compute/json/security_groups_client.py
index 7f430d8..1dc11c4 100644
--- a/tempest/services/compute/json/security_groups_client.py
+++ b/tempest/services/compute/json/security_groups_client.py
@@ -19,6 +19,7 @@
import urllib
from tempest.common.rest_client import RestClient
+from tempest import exceptions
class SecurityGroupsClientJSON(RestClient):
diff --git a/tempest/services/compute/xml/images_client.py b/tempest/services/compute/xml/images_client.py
index 3b01efb..27bb979 100644
--- a/tempest/services/compute/xml/images_client.py
+++ b/tempest/services/compute/xml/images_client.py
@@ -77,6 +77,19 @@
data['images'].append(self._parse_image(image))
return data
+ def _parse_key_value(self, node):
+ """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
+ data = {}
+ for node in node.getchildren():
+ data[node.get('key')] = node.text
+ return data
+
+ def _parse_metadata(self, node):
+ """Parse the response body without children."""
+ data = {}
+ data[node.get('key')] = node.text
+ return data
+
def create_image(self, server_id, name, meta=None):
"""Creates an image of the original server."""
post_body = Element('createImage', name=name)
@@ -153,51 +166,53 @@
if int(time.time()) - start >= self.build_timeout:
raise exceptions.TimeoutException
+ def _metadata_body(self, meta):
+ post_body = Element('metadata')
+ for k, v in meta.items():
+ data = Element('meta', key=k)
+ data.append(Text(v))
+ post_body.append(data)
+ return post_body
+
def list_image_metadata(self, image_id):
"""Lists all metadata items for an image."""
resp, body = self.get("images/%s/metadata" % str(image_id),
self.headers)
- body = xml_to_json(etree.fromstring(body))
- return resp, body['metadata']
-
- def _metadata_body(image_id, meta):
- post_body = Document('metadata')
- for k, v in meta:
- text = Text(v)
- metadata = Element('meta', text, key=k)
- post_body.append(metadata)
- return post_body
+ body = self._parse_key_value(etree.fromstring(body))
+ return resp, body
def set_image_metadata(self, image_id, meta):
"""Sets the metadata for an image."""
- post_body = self._metadata_body(image_id, meta)
- resp, body = self.put('images/%s/metadata' % str(image_id),
- post_body, self.headers)
- body = xml_to_json(etree.fromstring(body))
- return resp, body['metadata']
+ post_body = self._metadata_body(meta)
+ resp, body = self.put('images/%s/metadata' % image_id,
+ str(Document(post_body)), self.headers)
+ body = self._parse_key_value(etree.fromstring(body))
+ return resp, body
def update_image_metadata(self, image_id, meta):
"""Updates the metadata for an image."""
- post_body = self._metadata_body(image_id, meta)
+ post_body = self._metadata_body(meta)
resp, body = self.post('images/%s/metadata' % str(image_id),
- post_body, self.headers)
- body = xml_to_json(etree.fromstring(body))
- return resp, body['metadata']
+ str(Document(post_body)), self.headers)
+ body = self._parse_key_value(etree.fromstring(body))
+ return resp, body
def get_image_metadata_item(self, image_id, key):
"""Returns the value for a specific image metadata key."""
resp, body = self.get("images/%s/metadata/%s.xml" %
(str(image_id), key), self.headers)
- body = xml_to_json(etree.fromstring(body))
- return resp, body['meta']
+ body = self._parse_metadata(etree.fromstring(body))
+ return resp, body
def set_image_metadata_item(self, image_id, key, meta):
"""Sets the value for a specific image metadata key."""
- post_body = Document('meta', Text(meta), key=key)
- resp, body = self.post('images/%s/metadata/%s' % (str(image_id), key),
- post_body, self.headers)
+ for k, v in meta.items():
+ post_body = Element('meta', key=key)
+ post_body.append(Text(v))
+ resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
+ str(Document(post_body)), self.headers)
body = xml_to_json(etree.fromstring(body))
- return resp, body['meta']
+ return resp, body
def update_image_metadata_item(self, image_id, key, meta):
"""Sets the value for a specific image metadata key."""
@@ -209,6 +224,5 @@
def delete_image_metadata_item(self, image_id, key):
"""Deletes a single image metadata key/value pair."""
- resp, body = self.delete("images/%s/metadata/%s" % (str(image_id), key,
- self.headers))
- return resp, body
+ return self.delete("images/%s/metadata/%s" % (str(image_id), key),
+ self.headers)
diff --git a/tempest/services/compute/xml/security_groups_client.py b/tempest/services/compute/xml/security_groups_client.py
index 7db60a1..10f1a42 100644
--- a/tempest/services/compute/xml/security_groups_client.py
+++ b/tempest/services/compute/xml/security_groups_client.py
@@ -19,6 +19,7 @@
import urllib
from tempest.common.rest_client import RestClientXML
+from tempest import exceptions
from tempest.services.compute.xml.common import Document
from tempest.services.compute.xml.common import Element
from tempest.services.compute.xml.common import Text
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 008417b..15dfd74 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -94,6 +94,11 @@
json['addresses'] = json_addresses
else:
json = xml_to_json(xml_dom)
+ diskConfig = '{http://docs.openstack.org/compute/ext/disk_config/api/v1.1'\
+ '}diskConfig'
+ if diskConfig in json:
+ json['OS-DCF:diskConfig'] = json[diskConfig]
+ del json[diskConfig]
return json
@@ -234,6 +239,11 @@
if attr in kwargs:
server.add_attr(attr, kwargs[attr])
+ if 'disk_config' in kwargs:
+ server.add_attr('xmlns:OS-DCF', "http://docs.openstack.org/"
+ "compute/ext/disk_config/api/v1.1")
+ server.add_attr('OS-DCF:diskConfig', kwargs['disk_config'])
+
if 'security_groups' in kwargs:
secgroups = Element("security_groups")
server.append(secgroups)
@@ -357,6 +367,12 @@
def rebuild(self, server_id, image_ref, **kwargs):
kwargs['imageRef'] = image_ref
+ if 'disk_config' in kwargs:
+ kwargs['OS-DCF:diskConfig'] = kwargs['disk_config']
+ del kwargs['disk_config']
+ kwargs['xmlns:OS-DCF'] = "http://docs.openstack.org/"\
+ "compute/ext/disk_config/api/v1.1"
+ kwargs['xmlns:atom'] = "http://www.w3.org/2005/Atom"
if 'xmlns' not in kwargs:
kwargs['xmlns'] = XMLNS_11
@@ -381,8 +397,11 @@
def resize(self, server_id, flavor_ref, **kwargs):
if 'disk_config' in kwargs:
- raise NotImplementedError("Sorry, disk_config not "
- "supported via XML yet")
+ kwargs['OS-DCF:diskConfig'] = kwargs['disk_config']
+ del kwargs['disk_config']
+ kwargs['xmlns:OS-DCF'] = "http://docs.openstack.org/"\
+ "compute/ext/disk_config/api/v1.1"
+ kwargs['xmlns:atom'] = "http://www.w3.org/2005/Atom"
kwargs['flavorRef'] = flavor_ref
return self.action(server_id, 'resize', None, **kwargs)
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index 77c9cd2..9c7269f 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -18,13 +18,17 @@
import copy
import errno
import json
+import logging
import os
+import time
import urllib
from tempest.common import glance_http
from tempest.common.rest_client import RestClient
from tempest import exceptions
+LOG = logging.getLogger(__name__)
+
class ImageClientJSON(RestClient):
@@ -59,8 +63,13 @@
def _image_meta_to_headers(self, fields):
headers = {}
fields_copy = copy.deepcopy(fields)
+ copy_from = fields_copy.pop('copy_from', None)
+ if copy_from is not None:
+ headers['x-glance-api-copy-from'] = copy_from
for key, value in fields_copy.pop('properties', {}).iteritems():
headers['x-image-meta-property-%s' % key] = str(value)
+ for key, value in fields_copy.pop('api', {}).iteritems():
+ headers['x-glance-api-property-%s' % key] = str(value)
for key, value in fields_copy.iteritems():
headers['x-image-meta-%s' % key] = str(value)
return headers
@@ -130,7 +139,7 @@
headers = {}
- for option in ['is_public', 'location', 'properties']:
+ for option in ['is_public', 'location', 'properties', 'copy_from']:
if option in kwargs:
params[option] = kwargs.get(option)
@@ -187,10 +196,15 @@
body = json.loads(body)
return resp, body['images']
+ def get_image_meta(self, image_id):
+ url = 'v1/images/%s' % image_id
+ resp, __ = self.head(url)
+ body = self._image_meta_from_headers(resp)
+ return resp, body
+
def get_image(self, image_id):
url = 'v1/images/%s' % image_id
- resp, __ = self.get(url)
- body = self._image_meta_from_headers(resp)
+ resp, body = self.get(url)
return resp, body
def is_resource_deleted(self, id):
@@ -231,3 +245,34 @@
resp, data = self.put(url, body, self.headers)
data = json.loads(data)
return resp, data
+
+ #NOTE(afazekas): just for the wait function
+ def _get_image_status(self, image_id):
+ resp, meta = self.get_image_meta(image_id)
+ status = meta['status']
+ return status
+
+ #NOTE(afazkas): Wait reinvented again. It is not in the correct layer
+ def wait_for_image_status(self, image_id, status):
+ """Waits for a Image to reach a given status."""
+ start_time = time.time()
+ old_value = value = self._get_image_status(image_id)
+ while True:
+ dtime = time.time() - start_time
+ time.sleep(self.build_interval)
+ if value != old_value:
+ LOG.info('Value transition from "%s" to "%s"'
+ 'in %d second(s).', old_value,
+ value, dtime)
+ if (value == status):
+ return value
+
+ if dtime > self.build_timeout:
+ message = ('Time Limit Exceeded! (%ds)'
+ 'while waiting for %s, '
+ 'but we got %s.' %
+ (self.build_timeout, status, value))
+ raise exceptions.TimeoutException(message)
+ time.sleep(self.build_interval)
+ old_value = value
+ value = self._get_image_status(image_id)
diff --git a/tempest/tests/boto/test_s3_objects.py b/tempest/tests/boto/test_s3_objects.py
index c735215..dcb7c86 100644
--- a/tempest/tests/boto/test_s3_objects.py
+++ b/tempest/tests/boto/test_s3_objects.py
@@ -18,7 +18,6 @@
from contextlib import closing
from boto.s3.key import Key
-import testtools
from tempest import clients
from tempest.common.utils.data_utils import rand_name
diff --git a/tempest/tests/compute/admin/test_flavors.py b/tempest/tests/compute/admin/test_flavors.py
index 7fabf7a..32b06f8 100644
--- a/tempest/tests/compute/admin/test_flavors.py
+++ b/tempest/tests/compute/admin/test_flavors.py
@@ -39,6 +39,7 @@
raise cls.skipException(msg)
cls.client = cls.os_adm.flavors_client
+ cls.user_client = cls.os.flavors_client
cls.flavor_name_prefix = 'test_flavor_'
cls.ram = 512
cls.vcpus = 1
@@ -315,7 +316,22 @@
self.client.list_flavors_with_detail,
{'is_public': 'invalid'})
-#TODO(afazekas): Negative tests with regular user
+ @attr(type='negative')
+ def test_create_flavor_as_user(self):
+ flavor_name = rand_name(self.flavor_name_prefix)
+ new_flavor_id = rand_int_id(start=1000)
+
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.create_flavor,
+ flavor_name, self.ram, self.vcpus, self.disk,
+ new_flavor_id, ephemeral=self.ephemeral,
+ swap=self.swap, rxtx=self.rxtx)
+
+ @attr(type='negative')
+ def test_delete_flavor_as_user(self):
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.delete_flavor,
+ self.flavor_ref_alt)
class FlavorsAdminTestXML(FlavorsAdminTestJSON):
diff --git a/tempest/tests/compute/images/test_image_metadata.py b/tempest/tests/compute/images/test_image_metadata.py
index 918075c..5d6439b 100644
--- a/tempest/tests/compute/images/test_image_metadata.py
+++ b/tempest/tests/compute/images/test_image_metadata.py
@@ -21,12 +21,12 @@
from tempest.tests.compute import base
-class ImagesMetadataTest(base.BaseComputeTest):
+class ImagesMetadataTestJSON(base.BaseComputeTest):
_interface = 'json'
@classmethod
def setUpClass(cls):
- super(ImagesMetadataTest, cls).setUpClass()
+ super(ImagesMetadataTestJSON, cls).setUpClass()
cls.servers_client = cls.servers_client
cls.client = cls.images_client
@@ -44,10 +44,10 @@
@classmethod
def tearDownClass(cls):
cls.client.delete_image(cls.image_id)
- super(ImagesMetadataTest, cls).tearDownClass()
+ super(ImagesMetadataTestJSON, cls).tearDownClass()
def setUp(self):
- super(ImagesMetadataTest, self).setUp()
+ super(ImagesMetadataTestJSON, self).setUp()
meta = {'key1': 'value1', 'key2': 'value2'}
resp, _ = self.client.set_image_metadata(self.image_id, meta)
self.assertEqual(resp.status, 200)
@@ -143,3 +143,7 @@
# item from nonexistant image
self.assertRaises(exceptions.NotFound,
self.client.delete_image_metadata_item, 999, 'key1')
+
+
+class ImagesMetadataTestXML(ImagesMetadataTestJSON):
+ _interface = 'xml'
diff --git a/tempest/tests/compute/images/test_list_image_filters.py b/tempest/tests/compute/images/test_list_image_filters.py
index 472f7fb..e668aca 100644
--- a/tempest/tests/compute/images/test_list_image_filters.py
+++ b/tempest/tests/compute/images/test_list_image_filters.py
@@ -22,12 +22,12 @@
from tempest.tests.compute import base
-class ListImageFiltersTest(base.BaseComputeTest):
+class ListImageFiltersTestJSON(base.BaseComputeTest):
_interface = 'json'
@classmethod
def setUpClass(cls):
- super(ListImageFiltersTest, cls).setUpClass()
+ super(ListImageFiltersTestJSON, cls).setUpClass()
cls.client = cls.images_client
resp, cls.server1 = cls.create_server()
@@ -65,7 +65,7 @@
cls.client.delete_image(cls.image1_id)
cls.client.delete_image(cls.image2_id)
cls.client.delete_image(cls.image3_id)
- super(ListImageFiltersTest, cls).tearDownClass()
+ super(ListImageFiltersTestJSON, cls).tearDownClass()
@attr(type='negative')
def test_get_image_not_existing(self):
@@ -140,7 +140,9 @@
# Verify only the expected number of results are returned
params = {'limit': '1'}
resp, images = self.client.list_images(params)
- self.assertEqual(1, len(images))
+ #when _interface='xml', one element for images_links in images
+ #ref: Question #224349
+ self.assertEqual(1, len([x for x in images if 'id' in x]))
@attr(type='positive')
def test_list_images_filter_by_changes_since(self):
@@ -226,3 +228,7 @@
def test_get_nonexistant_image(self):
# Negative test: GET on non existant image should fail
self.assertRaises(exceptions.NotFound, self.client.get_image, 999)
+
+
+class ListImageFiltersTestXML(ListImageFiltersTestJSON):
+ _interface = 'xml'
diff --git a/tempest/tests/compute/images/test_list_images.py b/tempest/tests/compute/images/test_list_images.py
index d583a95..ec9e7bc 100644
--- a/tempest/tests/compute/images/test_list_images.py
+++ b/tempest/tests/compute/images/test_list_images.py
@@ -19,17 +19,17 @@
from tempest.tests.compute import base
-class ListImagesTest(base.BaseComputeTest):
+class ListImagesTestJSON(base.BaseComputeTest):
_interface = 'json'
@classmethod
def setUpClass(cls):
- super(ListImagesTest, cls).setUpClass()
+ super(ListImagesTestJSON, cls).setUpClass()
cls.client = cls.images_client
@classmethod
def tearDownClass(cls):
- super(ListImagesTest, cls).tearDownClass()
+ super(ListImagesTestJSON, cls).tearDownClass()
@attr(type='smoke')
def test_get_image(self):
@@ -50,3 +50,7 @@
resp, images = self.client.list_images_with_detail()
found = any([i for i in images if i['id'] == self.image_ref])
self.assertTrue(found)
+
+
+class ListImagesTestXML(ListImagesTestJSON):
+ _interface = 'xml'
diff --git a/tempest/tests/compute/servers/test_create_server.py b/tempest/tests/compute/servers/test_create_server.py
index aaab9fa..b522992 100644
--- a/tempest/tests/compute/servers/test_create_server.py
+++ b/tempest/tests/compute/servers/test_create_server.py
@@ -33,7 +33,7 @@
class ServersTestJSON(base.BaseComputeTest):
_interface = 'json'
run_ssh = tempest.config.TempestConfig().compute.run_ssh
- disk_config = None
+ disk_config = 'AUTO'
@classmethod
def setUpClass(cls):
@@ -118,18 +118,6 @@
@attr(type='positive')
-class ServersTestAutoDisk(ServersTestJSON):
- disk_config = 'AUTO'
-
- @classmethod
- def setUpClass(cls):
- if not compute.DISK_CONFIG_ENABLED:
- msg = "DiskConfig extension not enabled."
- raise cls.skipException(msg)
- super(ServersTestAutoDisk, cls).setUpClass()
-
-
-@attr(type='positive')
class ServersTestManualDisk(ServersTestJSON):
disk_config = 'MANUAL'
diff --git a/tempest/tests/compute/servers/test_disk_config.py b/tempest/tests/compute/servers/test_disk_config.py
index 2fbb876..fe1c271 100644
--- a/tempest/tests/compute/servers/test_disk_config.py
+++ b/tempest/tests/compute/servers/test_disk_config.py
@@ -22,7 +22,7 @@
from tempest.tests.compute import base
-class TestServerDiskConfig(base.BaseComputeTest):
+class ServerDiskConfigTestJSON(base.BaseComputeTest):
_interface = 'json'
@classmethod
@@ -30,7 +30,7 @@
if not compute.DISK_CONFIG_ENABLED:
msg = "DiskConfig extension not enabled."
raise cls.skipException(msg)
- super(TestServerDiskConfig, cls).setUpClass()
+ super(ServerDiskConfigTestJSON, cls).setUpClass()
cls.client = cls.os.servers_client
@attr(type='positive')
@@ -120,3 +120,7 @@
#Delete the server
resp, body = self.client.delete_server(server['id'])
+
+
+class ServerDiskConfigTestXML(ServerDiskConfigTestJSON):
+ _interface = 'xml'
diff --git a/tempest/tests/compute/servers/test_list_servers_negative.py b/tempest/tests/compute/servers/test_list_servers_negative.py
index 01b11e0..0559206 100644
--- a/tempest/tests/compute/servers/test_list_servers_negative.py
+++ b/tempest/tests/compute/servers/test_list_servers_negative.py
@@ -22,12 +22,12 @@
from tempest.tests.compute import base
-class ListServersNegativeTest(base.BaseComputeTest):
+class ListServersNegativeTestJSON(base.BaseComputeTest):
_interface = 'json'
@classmethod
def setUpClass(cls):
- super(ListServersNegativeTest, cls).setUpClass()
+ super(ListServersNegativeTestJSON, cls).setUpClass()
cls.client = cls.servers_client
cls.servers = []
@@ -138,7 +138,8 @@
# List servers by specifying limits
resp, body = self.client.list_servers({'limit': 1})
self.assertEqual('200', resp['status'])
- self.assertEqual(1, len(body['servers']))
+ #when _interface='xml', one element for servers_links in servers
+ self.assertEqual(1, len([x for x in body['servers'] if 'id' in x]))
def test_list_servers_by_limits_greater_than_actual_count(self):
# List servers by specifying a greater value for limit
@@ -187,3 +188,7 @@
if srv['id'] in deleted_ids]
self.assertEqual('200', resp['status'])
self.assertEqual([], actual)
+
+
+class ListServersNegativeTestXML(ListServersNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/tests/compute/servers/test_server_addresses.py b/tempest/tests/compute/servers/test_server_addresses.py
index cb8e85e..05fa320 100644
--- a/tempest/tests/compute/servers/test_server_addresses.py
+++ b/tempest/tests/compute/servers/test_server_addresses.py
@@ -15,7 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
from tempest.test import attr
from tempest.tests.compute import base
diff --git a/tempest/tests/compute/servers/test_server_rescue.py b/tempest/tests/compute/servers/test_server_rescue.py
index 0777163..29c9944 100644
--- a/tempest/tests/compute/servers/test_server_rescue.py
+++ b/tempest/tests/compute/servers/test_server_rescue.py
@@ -15,17 +15,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-import base64
-import time
-
import testtools
from tempest.common.utils.data_utils import rand_name
-from tempest.common.utils.linux.remote_client import RemoteClient
import tempest.config
from tempest import exceptions
from tempest.test import attr
-from tempest.tests import compute
from tempest.tests.compute import base
@@ -141,8 +136,6 @@
@attr(type='negative')
def test_rescued_vm_attach_volume(self):
- client = self.volumes_extensions_client
-
# Rescue the server
self.servers_client.rescue_server(self.server_id, self.password)
self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
diff --git a/tempest/tests/compute/test_authorization.py b/tempest/tests/compute/test_authorization.py
index 4ca197a..91cf39f 100644
--- a/tempest/tests/compute/test_authorization.py
+++ b/tempest/tests/compute/test_authorization.py
@@ -15,8 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
-
from tempest import clients
from tempest.common.utils.data_utils import parse_image_id
from tempest.common.utils.data_utils import rand_name
@@ -25,7 +23,7 @@
from tempest.tests.compute import base
-class AuthorizationTest(base.BaseComputeTest):
+class AuthorizationTestJSON(base.BaseComputeTest):
_interface = 'json'
@classmethod
@@ -34,7 +32,7 @@
msg = "Need >1 user"
raise cls.skipException(msg)
- super(AuthorizationTest, cls).setUpClass()
+ super(AuthorizationTestJSON, cls).setUpClass()
cls.client = cls.os.servers_client
cls.images_client = cls.os.images_client
@@ -73,18 +71,15 @@
name = rand_name('security')
description = rand_name('description')
- resp, cls.security_group = \
- cls.security_client.create_security_group(name, description)
+ resp, cls.security_group = cls.security_client.create_security_group(
+ name, description)
parent_group_id = cls.security_group['id']
ip_protocol = 'tcp'
from_port = 22
to_port = 22
- resp, cls.rule =\
- cls.security_client.create_security_group_rule(
- parent_group_id,
- ip_protocol, from_port,
- to_port)
+ resp, cls.rule = cls.security_client.create_security_group_rule(
+ parent_group_id, ip_protocol, from_port, to_port)
@classmethod
def tearDownClass(cls):
@@ -92,7 +87,7 @@
cls.images_client.delete_image(cls.image['id'])
cls.keypairs_client.delete_keypair(cls.keypairname)
cls.security_client.delete_security_group(cls.security_group['id'])
- super(AuthorizationTest, cls).tearDownClass()
+ super(AuthorizationTestJSON, cls).tearDownClass()
def test_get_server_for_alt_account_fails(self):
# A GET request for a server on another user's account should fail
@@ -280,7 +275,7 @@
self.alt_security_client.base_url = self.saved_base_url
if resp['status'] is not None:
self.alt_security_client.delete_security_group_rule(
- body['id']) # BUG
+ body['id']) # BUG
self.fail("Create security group rule request should not "
"happen if the tenant id does not match the"
" current user")
@@ -354,3 +349,7 @@
self.assertRaises(exceptions.NotFound,
self.alt_client.get_console_output,
self.server['id'], 10)
+
+
+class AuthorizationTestXML(AuthorizationTestJSON):
+ _interface = 'xml'
diff --git a/tempest/tests/compute/volumes/test_attach_volume.py b/tempest/tests/compute/volumes/test_attach_volume.py
index d9abe41..1b52ccf 100644
--- a/tempest/tests/compute/volumes/test_attach_volume.py
+++ b/tempest/tests/compute/volumes/test_attach_volume.py
@@ -17,7 +17,6 @@
import testtools
-from tempest.common.utils.data_utils import rand_name
from tempest.common.utils.linux.remote_client import RemoteClient
import tempest.config
from tempest.test import attr
@@ -92,7 +91,7 @@
self.assertTrue(self.device in partitions)
self._detach(server['id'], volume['id'])
- attached = False
+ self.attached = False
self.servers_client.stop(server['id'])
self.servers_client.wait_for_server_status(server['id'], 'SHUTOFF')
diff --git a/tempest/tests/image/base.py b/tempest/tests/image/base.py
index 65d81b6..f12e957 100644
--- a/tempest/tests/image/base.py
+++ b/tempest/tests/image/base.py
@@ -15,7 +15,6 @@
# under the License.
import logging
-import time
from tempest import clients
from tempest.common.utils.data_utils import rand_name
diff --git a/tempest/tests/image/v1/test_images.py b/tempest/tests/image/v1/test_images.py
index af09b79..1b6fa10 100644
--- a/tempest/tests/image/v1/test_images.py
+++ b/tempest/tests/image/v1/test_images.py
@@ -17,7 +17,6 @@
import cStringIO as StringIO
-from tempest import clients
from tempest import exceptions
from tempest.test import attr
from tempest.tests.image import base
@@ -68,13 +67,55 @@
container_format='bare',
disk_format='raw', is_public=True,
location='http://example.com'
- '/someimage.iso')
+ '/someimage.iso',
+ properties={'key1': 'value1',
+ 'key2': 'value2'})
self.assertTrue('id' in body)
image_id = body.get('id')
self.created_images.append(image_id)
self.assertEqual('New Remote Image', body.get('name'))
self.assertTrue(body.get('is_public'))
self.assertEqual('active', body.get('status'))
+ properties = body.get('properties')
+ self.assertEqual(properties['key1'], 'value1')
+ self.assertEqual(properties['key2'], 'value2')
+
+ def test_register_http_image(self):
+ container_client = self.os.container_client
+ object_client = self.os.object_client
+ container_name = "image_container"
+ object_name = "test_image.img"
+ container_client.create_container(container_name)
+ self.addCleanup(container_client.delete_container, container_name)
+ cont_headers = {'X-Container-Read': '.r:*'}
+ resp, _ = container_client.update_container_metadata(
+ container_name,
+ metadata=cont_headers,
+ metadata_prefix='')
+ self.assertEqual(resp['status'], '204')
+
+ data = "TESTIMAGE"
+ resp, _ = object_client.create_object(container_name,
+ object_name, data)
+ self.addCleanup(object_client.delete_object, container_name,
+ object_name)
+ self.assertEqual(resp['status'], '201')
+ object_url = '/'.join((object_client.base_url,
+ container_name,
+ object_name))
+ resp, body = self.create_image(name='New Http Image',
+ container_format='bare',
+ disk_format='raw', is_public=True,
+ copy_from=object_url)
+ self.assertTrue('id' in body)
+ image_id = body.get('id')
+ self.created_images.append(image_id)
+ self.assertEqual('New Http Image', body.get('name'))
+ self.assertTrue(body.get('is_public'))
+ self.client.wait_for_image_status(image_id, 'active')
+ resp, body = self.client.get_image(image_id)
+ self.assertEqual(resp['status'], '200')
+ self.assertEqual(body, data)
class ListImagesTest(base.BaseV1ImageTest):
diff --git a/tempest/tests/image/v2/test_images.py b/tempest/tests/image/v2/test_images.py
index 19a7a95..eddeb78 100644
--- a/tempest/tests/image/v2/test_images.py
+++ b/tempest/tests/image/v2/test_images.py
@@ -19,7 +19,6 @@
import cStringIO as StringIO
import random
-from tempest import clients
from tempest import exceptions
from tempest.test import attr
from tempest.tests.image import base
diff --git a/tempest/tests/network/common.py b/tempest/tests/network/common.py
new file mode 100644
index 0000000..0bb806f
--- /dev/null
+++ b/tempest/tests/network/common.py
@@ -0,0 +1,316 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Hewlett-Packard Development Company, L.P.
+# 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 subprocess
+
+import netaddr
+
+from quantumclient.common import exceptions as exc
+from tempest.common.utils.data_utils import rand_name
+from tempest import smoke
+from tempest import test
+
+
+class AttributeDict(dict):
+
+ """
+ Provide attribute access (dict.key) to dictionary values.
+ """
+
+ def __getattr__(self, name):
+ """Allow attribute access for all keys in the dict."""
+ if name in self:
+ return self[name]
+ return super(AttributeDict, self).__getattribute__(name)
+
+
+class DeletableResource(AttributeDict):
+
+ """
+ Support deletion of quantum resources (networks, subnets) via a
+ delete() method, as is supported by keystone and nova resources.
+ """
+
+ def __init__(self, *args, **kwargs):
+ self.client = kwargs.pop('client', None)
+ super(DeletableResource, self).__init__(*args, **kwargs)
+
+ def __str__(self):
+ return '<%s id="%s" name="%s">' % (self.__class__.__name__,
+ self.id, self.name)
+
+ def delete(self):
+ raise NotImplemented()
+
+
+class DeletableNetwork(DeletableResource):
+
+ def delete(self):
+ self.client.delete_network(self.id)
+
+
+class DeletableSubnet(DeletableResource):
+
+ _router_ids = set()
+
+ def add_to_router(self, router_id):
+ self._router_ids.add(router_id)
+ body = dict(subnet_id=self.id)
+ self.client.add_interface_router(router_id, body=body)
+
+ def delete(self):
+ for router_id in self._router_ids.copy():
+ body = dict(subnet_id=self.id)
+ self.client.remove_interface_router(router_id, body=body)
+ self._router_ids.remove(router_id)
+ self.client.delete_subnet(self.id)
+
+
+class DeletableRouter(DeletableResource):
+
+ def add_gateway(self, network_id):
+ body = dict(network_id=network_id)
+ self.client.add_gateway_router(self.id, body=body)
+
+ def delete(self):
+ self.client.remove_gateway_router(self.id)
+ self.client.delete_router(self.id)
+
+
+class DeletableFloatingIp(DeletableResource):
+
+ def delete(self):
+ self.client.delete_floatingip(self.id)
+
+
+class DeletablePort(DeletableResource):
+
+ def delete(self):
+ self.client.delete_port(self.id)
+
+
+class TestNetworkSmokeCommon(smoke.DefaultClientSmokeTest):
+ """
+ Base class for network smoke tests
+ """
+
+ @classmethod
+ def check_preconditions(cls):
+ if (cls.config.network.quantum_available):
+ cls.enabled = True
+ #verify that quantum_available is telling the truth
+ try:
+ cls.network_client.list_networks()
+ except exc.EndpointNotFound:
+ cls.enabled = False
+ raise
+ else:
+ cls.enabled = False
+ msg = 'Quantum not available'
+ raise cls.skipException(msg)
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestNetworkSmokeCommon, cls).setUpClass()
+ cfg = cls.config.network
+ cls.tenant_id = cls.manager._get_identity_client(
+ cls.config.identity.username,
+ cls.config.identity.password,
+ cls.config.identity.tenant_name).tenant_id
+
+ def _create_keypair(self, client, namestart='keypair-smoke-'):
+ kp_name = rand_name(namestart)
+ keypair = client.keypairs.create(kp_name)
+ try:
+ self.assertEqual(keypair.id, kp_name)
+ self.set_resource(kp_name, keypair)
+ except AttributeError:
+ self.fail("Keypair object not successfully created.")
+ return keypair
+
+ def _create_security_group(self, client, namestart='secgroup-smoke-'):
+ # Create security group
+ sg_name = rand_name(namestart)
+ sg_desc = sg_name + " description"
+ secgroup = client.security_groups.create(sg_name, sg_desc)
+ try:
+ self.assertEqual(secgroup.name, sg_name)
+ self.assertEqual(secgroup.description, sg_desc)
+ self.set_resource(sg_name, secgroup)
+ except AttributeError:
+ self.fail("SecurityGroup object not successfully created.")
+
+ # Add rules to the security group
+ rulesets = [
+ {
+ # ssh
+ 'ip_protocol': 'tcp',
+ 'from_port': 22,
+ 'to_port': 22,
+ 'cidr': '0.0.0.0/0',
+ 'group_id': secgroup.id
+ },
+ {
+ # ping
+ 'ip_protocol': 'icmp',
+ 'from_port': -1,
+ 'to_port': -1,
+ 'cidr': '0.0.0.0/0',
+ 'group_id': secgroup.id
+ }
+ ]
+ for ruleset in rulesets:
+ try:
+ client.security_group_rules.create(secgroup.id, **ruleset)
+ except Exception:
+ self.fail("Failed to create rule in security group.")
+
+ return secgroup
+
+ def _create_network(self, tenant_id, namestart='network-smoke-'):
+ name = rand_name(namestart)
+ body = dict(
+ network=dict(
+ name=name,
+ tenant_id=tenant_id,
+ ),
+ )
+ result = self.network_client.create_network(body=body)
+ network = DeletableNetwork(client=self.network_client,
+ **result['network'])
+ self.assertEqual(network.name, name)
+ self.set_resource(name, network)
+ return network
+
+ def _list_networks(self):
+ nets = self.network_client.list_networks()
+ return nets['networks']
+
+ def _list_subnets(self):
+ subnets = self.network_client.list_subnets()
+ return subnets['subnets']
+
+ def _list_routers(self):
+ routers = self.network_client.list_routers()
+ return routers['routers']
+
+ def _create_subnet(self, network, namestart='subnet-smoke-'):
+ """
+ Create a subnet for the given network within the cidr block
+ configured for tenant networks.
+ """
+ cfg = self.config.network
+ tenant_cidr = netaddr.IPNetwork(cfg.tenant_network_cidr)
+ result = None
+ # Repeatedly attempt subnet creation with sequential cidr
+ # blocks until an unallocated block is found.
+ for subnet_cidr in tenant_cidr.subnet(cfg.tenant_network_mask_bits):
+ body = dict(
+ subnet=dict(
+ ip_version=4,
+ network_id=network.id,
+ tenant_id=network.tenant_id,
+ cidr=str(subnet_cidr),
+ ),
+ )
+ try:
+ result = self.network_client.create_subnet(body=body)
+ break
+ except exc.QuantumClientException as e:
+ is_overlapping_cidr = 'overlaps with another subnet' in str(e)
+ if not is_overlapping_cidr:
+ raise
+ self.assertIsNotNone(result, 'Unable to allocate tenant network')
+ subnet = DeletableSubnet(client=self.network_client,
+ **result['subnet'])
+ self.assertEqual(subnet.cidr, str(subnet_cidr))
+ self.set_resource(rand_name(namestart), subnet)
+ return subnet
+
+ def _create_port(self, network, namestart='port-quotatest-'):
+ name = rand_name(namestart)
+ body = dict(
+ port=dict(name=name,
+ network_id=network.id,
+ tenant_id=network.tenant_id))
+ try:
+ result = self.network_client.create_port(body=body)
+ except Exception as e:
+ raise
+ self.assertIsNotNone(result, 'Unable to allocate port')
+ port = DeletablePort(client=self.network_client,
+ **result['port'])
+ self.set_resource(name, port)
+ return port
+
+ def _create_server(self, client, network, name, key_name, security_groups):
+ flavor_id = self.config.compute.flavor_ref
+ base_image_id = self.config.compute.image_ref
+ create_kwargs = {
+ 'nics': [
+ {'net-id': network.id},
+ ],
+ 'key_name': key_name,
+ 'security_groups': security_groups,
+ }
+ server = client.servers.create(name, base_image_id, flavor_id,
+ **create_kwargs)
+ try:
+ self.assertEqual(server.name, name)
+ self.set_resource(name, server)
+ except AttributeError:
+ self.fail("Server not successfully created.")
+ self.status_timeout(client.servers, server.id, 'ACTIVE')
+ # The instance retrieved on creation is missing network
+ # details, necessitating retrieval after it becomes active to
+ # ensure correct details.
+ server = client.servers.get(server.id)
+ self.set_resource(name, server)
+ return server
+
+ def _create_floating_ip(self, server, external_network_id):
+ result = self.network_client.list_ports(device_id=server.id)
+ ports = result.get('ports', [])
+ self.assertEqual(len(ports), 1,
+ "Unable to determine which port to target.")
+ port_id = ports[0]['id']
+ body = dict(
+ floatingip=dict(
+ floating_network_id=external_network_id,
+ port_id=port_id,
+ tenant_id=server.tenant_id,
+ )
+ )
+ result = self.network_client.create_floatingip(body=body)
+ floating_ip = DeletableFloatingIp(client=self.network_client,
+ **result['floatingip'])
+ self.set_resource(rand_name('floatingip-'), floating_ip)
+ return floating_ip
+
+ def _ping_ip_address(self, ip_address):
+ cmd = ['ping', '-c1', '-w1', ip_address]
+
+ def ping():
+ proc = subprocess.Popen(cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ proc.wait()
+ if proc.returncode == 0:
+ return True
+
+ # TODO(mnewby) Allow configuration of execution and sleep duration.
+ return test.call_until_true(ping, 20, 1)
diff --git a/tempest/tests/network/test_network_basic_ops.py b/tempest/tests/network/test_network_basic_ops.py
index aed368e..a38a5c0 100644
--- a/tempest/tests/network/test_network_basic_ops.py
+++ b/tempest/tests/network/test_network_basic_ops.py
@@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack, LLC
+# Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -15,94 +16,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
-import subprocess
-
-import netaddr
-
-from quantumclient.common import exceptions as exc
-
from tempest.common.utils.data_utils import rand_name
-from tempest import smoke
-from tempest import test
+from tempest.tests.network.common import DeletableRouter
+from tempest.tests.network.common import TestNetworkSmokeCommon
-LOG = logging.getLogger(__name__)
-
-
-class AttributeDict(dict):
-
- """
- Provide attribute access (dict.key) to dictionary values.
- """
-
- def __getattr__(self, name):
- """Allow attribute access for all keys in the dict."""
- if name in self:
- return self[name]
- return super(AttributeDict, self).__getattribute__(name)
-
-
-class DeletableResource(AttributeDict):
-
- """
- Support deletion of quantum resources (networks, subnets) via a
- delete() method, as is supported by keystone and nova resources.
- """
-
- def __init__(self, *args, **kwargs):
- self.client = kwargs.pop('client', None)
- super(DeletableResource, self).__init__(*args, **kwargs)
-
- def __str__(self):
- return '<%s id="%s" name="%s">' % (self.__class__.__name__,
- self.id, self.name)
-
- def delete(self):
- raise NotImplemented()
-
-
-class DeletableNetwork(DeletableResource):
-
- def delete(self):
- self.client.delete_network(self.id)
-
-
-class DeletableSubnet(DeletableResource):
-
- _router_ids = set()
-
- def add_to_router(self, router_id):
- self._router_ids.add(router_id)
- body = dict(subnet_id=self.id)
- self.client.add_interface_router(router_id, body=body)
-
- def delete(self):
- for router_id in self._router_ids.copy():
- body = dict(subnet_id=self.id)
- self.client.remove_interface_router(router_id, body=body)
- self._router_ids.remove(router_id)
- self.client.delete_subnet(self.id)
-
-
-class DeletableRouter(DeletableResource):
-
- def add_gateway(self, network_id):
- body = dict(network_id=network_id)
- self.client.add_gateway_router(self.id, body=body)
-
- def delete(self):
- self.client.remove_gateway_router(self.id)
- self.client.delete_router(self.id)
-
-
-class DeletableFloatingIp(DeletableResource):
-
- def delete(self):
- self.client.delete_floatingip(self.id)
-
-
-class TestNetworkBasicOps(smoke.DefaultClientSmokeTest):
+class TestNetworkBasicOps(TestNetworkSmokeCommon):
"""
This smoke test suite assumes that Nova has been configured to
@@ -165,19 +84,12 @@
@classmethod
def check_preconditions(cls):
+ super(TestNetworkBasicOps, cls).check_preconditions()
cfg = cls.config.network
- msg = None
if not (cfg.tenant_networks_reachable or cfg.public_network_id):
msg = ('Either tenant_networks_reachable must be "true", or '
'public_network_id must be defined.')
- else:
- try:
- cls.network_client.list_networks()
- except exc.QuantumClientException:
- msg = 'Unable to connect to Quantum service.'
-
- cls.enabled = not bool(msg)
- if msg:
+ cls.enabled = False
raise cls.skipException(msg)
@classmethod
@@ -198,55 +110,6 @@
cls.servers = []
cls.floating_ips = {}
- def _create_keypair(self, client):
- kp_name = rand_name('keypair-smoke-')
- keypair = client.keypairs.create(kp_name)
- try:
- self.assertEqual(keypair.id, kp_name)
- self.set_resource(kp_name, keypair)
- except AttributeError:
- self.fail("Keypair object not successfully created.")
- return keypair
-
- def _create_security_group(self, client):
- # Create security group
- sg_name = rand_name('secgroup-smoke-')
- sg_desc = sg_name + " description"
- secgroup = client.security_groups.create(sg_name, sg_desc)
- try:
- self.assertEqual(secgroup.name, sg_name)
- self.assertEqual(secgroup.description, sg_desc)
- self.set_resource(sg_name, secgroup)
- except AttributeError:
- self.fail("SecurityGroup object not successfully created.")
-
- # Add rules to the security group
- rulesets = [
- {
- # ssh
- 'ip_protocol': 'tcp',
- 'from_port': 22,
- 'to_port': 22,
- 'cidr': '0.0.0.0/0',
- 'group_id': secgroup.id
- },
- {
- # ping
- 'ip_protocol': 'icmp',
- 'from_port': -1,
- 'to_port': -1,
- 'cidr': '0.0.0.0/0',
- 'group_id': secgroup.id
- }
- ]
- for ruleset in rulesets:
- try:
- client.security_group_rules.create(secgroup.id, **ruleset)
- except Exception:
- self.fail("Failed to create rule in security group.")
-
- return secgroup
-
def _get_router(self, tenant_id):
"""Retrieve a router for the given tenant id.
@@ -270,8 +133,8 @@
raise Exception("Neither of 'public_router_id' or "
"'public_network_id' has been defined.")
- def _create_router(self, tenant_id):
- name = rand_name('router-smoke-')
+ def _create_router(self, tenant_id, namestart='router-smoke-'):
+ name = rand_name(namestart)
body = dict(
router=dict(
name=name,
@@ -286,124 +149,6 @@
self.set_resource(name, router)
return router
- def _create_network(self, tenant_id):
- name = rand_name('network-smoke-')
- body = dict(
- network=dict(
- name=name,
- tenant_id=tenant_id,
- ),
- )
- result = self.network_client.create_network(body=body)
- network = DeletableNetwork(client=self.network_client,
- **result['network'])
- self.assertEqual(network.name, name)
- self.set_resource(name, network)
- return network
-
- def _list_networks(self):
- nets = self.network_client.list_networks()
- return nets['networks']
-
- def _list_subnets(self):
- subnets = self.network_client.list_subnets()
- return subnets['subnets']
-
- def _list_routers(self):
- routers = self.network_client.list_routers()
- return routers['routers']
-
- def _create_subnet(self, network):
- """
- Create a subnet for the given network within the cidr block
- configured for tenant networks.
- """
- cfg = self.config.network
- tenant_cidr = netaddr.IPNetwork(cfg.tenant_network_cidr)
- result = None
- # Repeatedly attempt subnet creation with sequential cidr
- # blocks until an unallocated block is found.
- for subnet_cidr in tenant_cidr.subnet(cfg.tenant_network_mask_bits):
- body = dict(
- subnet=dict(
- ip_version=4,
- network_id=network.id,
- tenant_id=network.tenant_id,
- cidr=str(subnet_cidr),
- ),
- )
- try:
- result = self.network_client.create_subnet(body=body)
- break
- except exc.QuantumClientException as e:
- is_overlapping_cidr = 'overlaps with another subnet' in str(e)
- if not is_overlapping_cidr:
- raise
- self.assertIsNotNone(result, 'Unable to allocate tenant network')
- subnet = DeletableSubnet(client=self.network_client,
- **result['subnet'])
- self.assertEqual(subnet.cidr, str(subnet_cidr))
- self.set_resource(rand_name('subnet-smoke-'), subnet)
- return subnet
-
- def _create_server(self, client, network, name, key_name, security_groups):
- flavor_id = self.config.compute.flavor_ref
- base_image_id = self.config.compute.image_ref
- create_kwargs = {
- 'nics': [
- {'net-id': network.id},
- ],
- 'key_name': key_name,
- 'security_groups': security_groups,
- }
- server = client.servers.create(name, base_image_id, flavor_id,
- **create_kwargs)
- try:
- self.assertEqual(server.name, name)
- self.set_resource(name, server)
- except AttributeError:
- self.fail("Server not successfully created.")
- self.status_timeout(client.servers, server.id, 'ACTIVE')
- # The instance retrieved on creation is missing network
- # details, necessitating retrieval after it becomes active to
- # ensure correct details.
- server = client.servers.get(server.id)
- self.set_resource(name, server)
- return server
-
- def _create_floating_ip(self, server, external_network_id):
- result = self.network_client.list_ports(device_id=server.id)
- ports = result.get('ports', [])
- self.assertEqual(len(ports), 1,
- "Unable to determine which port to target.")
- port_id = ports[0]['id']
- body = dict(
- floatingip=dict(
- floating_network_id=external_network_id,
- port_id=port_id,
- tenant_id=server.tenant_id,
- )
- )
- result = self.network_client.create_floatingip(body=body)
- floating_ip = DeletableFloatingIp(client=self.network_client,
- **result['floatingip'])
- self.set_resource(rand_name('floatingip-'), floating_ip)
- return floating_ip
-
- def _ping_ip_address(self, ip_address):
- cmd = ['ping', '-c1', '-w1', ip_address]
-
- def ping():
- proc = subprocess.Popen(cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- proc.wait()
- if proc.returncode == 0:
- return True
-
- # TODO(mnewby) Allow configuration of execution and sleep duration.
- return test.call_until_true(ping, 20, 1)
-
def test_001_create_keypairs(self):
self.keypairs[self.tenant_id] = self._create_keypair(
self.compute_client)
@@ -428,31 +173,21 @@
seen_names = [n['name'] for n in seen_nets]
seen_ids = [n['id'] for n in seen_nets]
for mynet in self.networks:
- assert mynet.name in seen_names, \
- "Did not see expected network with name %s" % mynet.name
- assert mynet.id in seen_ids, \
- "Did not see expected network with id %s" % mynet.id
+ self.assertIn(mynet.name, seen_names)
+ self.assertIn(mynet.id, seen_ids)
seen_subnets = self._list_subnets()
seen_net_ids = [n['network_id'] for n in seen_subnets]
seen_subnet_ids = [n['id'] for n in seen_subnets]
for mynet in self.networks:
- assert mynet.id in seen_net_ids, \
- "Did not see subnet belonging to network %s/%s" % \
- (mynet.name, mynet.id)
+ self.assertIn(mynet.id, seen_net_ids)
for mysubnet in self.subnets:
- assert mysubnet.id in seen_subnet_ids, \
- "Did not see expected subnet with id %s" % \
- mysubnet.id
+ self.assertIn(mysubnet.id, seen_subnet_ids)
seen_routers = self._list_routers()
seen_router_ids = [n['id'] for n in seen_routers]
seen_router_names = [n['name'] for n in seen_routers]
for myrouter in self.routers:
- assert myrouter.name in seen_router_names, \
- "Did not see expected router with name %s" % \
- myrouter.name
- assert myrouter.id in seen_router_ids, \
- "Did not see expected router with id %s" % \
- myrouter.id
+ self.assertIn(myrouter.name, seen_router_names)
+ self.assertIn(myrouter.id, seen_router_ids)
def test_005_create_servers(self):
if not (self.keypairs or self.security_groups or self.networks):
diff --git a/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py b/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py
index f528cec..13fcbbf 100644
--- a/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py
+++ b/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py
@@ -15,7 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
import uuid
from tempest.common.utils.data_utils import rand_name
diff --git a/tempest/tests/volume/admin/test_volume_types_negative.py b/tempest/tests/volume/admin/test_volume_types_negative.py
index 1b11d68..daf804d 100644
--- a/tempest/tests/volume/admin/test_volume_types_negative.py
+++ b/tempest/tests/volume/admin/test_volume_types_negative.py
@@ -15,7 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
import uuid
from tempest import exceptions
diff --git a/tools/check_source.sh b/tools/check_source.sh
new file mode 100755
index 0000000..089ad70
--- /dev/null
+++ b/tools/check_source.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+python tools/hacking.py --ignore=E122,E125,E126 --repeat --show-source --exclude=.venv,.tox,dist,doc,openstack,*egg .
+pep8_ret=$?
+
+pyflakes tempest stress setup.py tools cli bin | grep "imported but unused"
+unused_ret=$?
+
+ret=0
+if [ $pep8_ret != 0 ]; then
+ echo "hacking.py/pep8 test FAILED!" >&2
+ (( ret += 1 ))
+else
+ echo "hacking.py/pep8 test OK!" >&2
+fi
+
+if [ $unused_ret == 0 ]; then
+ echo "Unused import test FAILED!" >&2
+ (( ret += 2 ))
+else
+ echo "Unused import test OK!" >&2
+fi
+
+exit $ret
diff --git a/tools/hacking.py b/tools/hacking.py
index 528424a..7e46b74 100755
--- a/tools/hacking.py
+++ b/tools/hacking.py
@@ -21,7 +21,6 @@
built on top of pep8.py
"""
-import fnmatch
import inspect
import logging
import os
diff --git a/tools/install_venv.py b/tools/install_venv.py
index 20dcefa..ef7b0a8 100644
--- a/tools/install_venv.py
+++ b/tools/install_venv.py
@@ -21,9 +21,7 @@
"""Installation script for Tempest's development virtualenv."""
-import optparse
import os
-import subprocess
import sys
import install_venv_common as install_venv
diff --git a/tools/pip-requires b/tools/pip-requires
index e85cced..758442c 100644
--- a/tools/pip-requires
+++ b/tools/pip-requires
@@ -14,3 +14,6 @@
keyring
testrepository
oslo.config>=1.1.0
+# Needed for whitebox testing
+sqlalchemy
+MySQL-python
diff --git a/tools/tempest_coverage.py b/tools/tempest_coverage.py
index a46d0fb..c385eae 100755
--- a/tools/tempest_coverage.py
+++ b/tools/tempest_coverage.py
@@ -16,7 +16,6 @@
import json
import os
-import re
import shutil
import sys
@@ -24,7 +23,6 @@
from tempest.common.rest_client import RestClient
from tempest import config
-from tempest.tests.compute import base
CONF = config.TempestConfig()
diff --git a/tools/test-requires b/tools/test-requires
index 4801391..f701dab 100644
--- a/tools/test-requires
+++ b/tools/test-requires
@@ -1,6 +1,5 @@
pep8==1.3.3
pylint==0.19
-# Needed for whitebox testing
-sqlalchemy
-MySQL-python
+#TODO(afazekas): ensure pg_config installed
psycopg2
+pyflakes
diff --git a/tox.ini b/tox.ini
index 92ce6bc..85a0d86 100644
--- a/tox.ini
+++ b/tox.ini
@@ -19,4 +19,4 @@
python -m tools/tempest_coverage -c report --html
[testenv:pep8]
-commands = python tools/hacking.py --ignore=E122,E125,E126 --repeat --show-source --exclude=.venv,.tox,dist,doc,openstack,*egg .
+commands = bash tools/check_source.sh