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