Merge "Added 2 user related testcases for Keystone V3API"
diff --git a/.gitignore b/.gitignore
index c154603..f5f51ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
 ChangeLog
 *.pyc
 etc/tempest.conf
+etc/logging.conf
 include/swift_objects/swift_small
 include/swift_objects/swift_medium
 include/swift_objects/swift_large
diff --git a/run_tests.sh b/run_tests.sh
index 6fcdd90..56a6e6e 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -109,7 +109,7 @@
 
 function run_pep8 {
   echo "Running pep8 ..."
-  ${wrapper} tools/check_source.sh
+  ${wrapper} flake8
 }
 
 function run_coverage_start {
diff --git a/stress/driver.py b/stress/driver.py
index 9604318..533c000 100644
--- a/stress/driver.py
+++ b/stress/driver.py
@@ -18,7 +18,7 @@
 import datetime
 import random
 import time
-from urlparse import urlparse
+import urlparse
 
 from config import StressConfig
 from state import ClusterState
@@ -174,7 +174,7 @@
     keypath = stress_config.host_private_key_path
     user = stress_config.host_admin_user
     logdir = stress_config.nova_logdir
-    host = urlparse(manager.config.identity.uri).hostname
+    host = urlparse.urlparse(manager.config.identity.uri).hostname
     computes = _get_compute_nodes(keypath, user, host)
     stress.utils.execute_on_all(keypath, user, computes,
                                 "rm -f %s/*.log" % logdir)
diff --git a/stress/test_floating_ips.py b/stress/test_floating_ips.py
index 6774e81..c5bad95 100755
--- a/stress/test_floating_ips.py
+++ b/stress/test_floating_ips.py
@@ -15,8 +15,8 @@
 import random
 import telnetlib
 
-import pending_action
-import test_case
+from stress import pending_action
+from stress import test_case
 
 
 class TestChangeFloatingIp(test_case.StressTestCase):
diff --git a/stress/test_server_actions.py b/stress/test_server_actions.py
index f4ddf23..3a7094d 100644
--- a/stress/test_server_actions.py
+++ b/stress/test_server_actions.py
@@ -19,10 +19,10 @@
 
 import random
 
-import pending_action
+from stress import pending_action
+from stress import test_case
 import stress.utils
 from tempest.exceptions import Duplicate
-import test_case
 
 
 class TestRebootVM(test_case.StressTestCase):
diff --git a/stress/test_servers.py b/stress/test_servers.py
index 25cbbb0..1dd72f1 100644
--- a/stress/test_servers.py
+++ b/stress/test_servers.py
@@ -19,8 +19,8 @@
 
 import random
 
-import pending_action
-import test_case
+from stress import pending_action
+from stress import test_case
 
 
 class TestCreateVM(test_case.StressTestCase):
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index 0902239..4ddaf17 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -136,7 +136,7 @@
 
         # Read body into string if it isn't obviously image data
         if resp.getheader('content-type', None) != 'application/octet-stream':
-            body_str = ''.join([chunk for chunk in body_iter])
+            body_str = ''.join([body_chunk for body_chunk in body_iter])
             body_iter = StringIO.StringIO(body_str)
             self._log_response(resp, None)
         else:
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index 263cf3f..448708e 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -16,7 +16,7 @@
 #    under the License.
 
 
-from cStringIO import StringIO
+import cStringIO
 import select
 import socket
 import time
@@ -28,7 +28,6 @@
 with warnings.catch_warnings():
     warnings.simplefilter("ignore")
     import paramiko
-    from paramiko import RSAKey
 
 
 class Client(object):
@@ -39,7 +38,8 @@
         self.username = username
         self.password = password
         if isinstance(pkey, basestring):
-            pkey = RSAKey.from_private_key(StringIO(str(pkey)))
+            pkey = paramiko.RSAKey.from_private_key(
+                cStringIO.StringIO(str(pkey)))
         self.pkey = pkey
         self.look_for_keys = look_for_keys
         self.key_filename = key_filename
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 9e71f3d..3569b50 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -390,3 +390,17 @@
     def unrescue_server(self, server_id):
         """Unrescue the provided server."""
         return self.action(server_id, 'unrescue', None)
+
+    def list_instance_actions(self, server_id):
+        """List the provided server action."""
+        resp, body = self.get("servers/%s/os-instance-actions" %
+                              str(server_id))
+        body = json.loads(body)
+        return resp, body['instanceActions']
+
+    def get_instance_action(self, server_id, request_id):
+        """Returns the action details of the provided server."""
+        resp, body = self.get("servers/%s/os-instance-actions/%s" %
+                              (str(server_id), str(request_id)))
+        body = json.loads(body)
+        return resp, body['instanceAction']
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 331d560..f7e8915 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -527,3 +527,17 @@
         resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
                                  (server_id, volume_id), headers)
         return resp, body
+
+    def list_instance_actions(self, server_id):
+        """List the provided server action."""
+        resp, body = self.get("servers/%s/os-instance-actions" % server_id,
+                              self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
+
+    def get_instance_action(self, server_id, request_id):
+        """Returns the action details of the provided server."""
+        resp, body = self.get("servers/%s/os-instance-actions/%s" %
+                              (server_id, request_id), self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body
diff --git a/tempest/services/identity/v3/json/endpoints_client.py b/tempest/services/identity/v3/json/endpoints_client.py
index 3cb8f90..cf26d0a 100755
--- a/tempest/services/identity/v3/json/endpoints_client.py
+++ b/tempest/services/identity/v3/json/endpoints_client.py
@@ -16,7 +16,7 @@
 #    under the License.
 
 import json
-from urlparse import urlparse
+import urlparse
 
 from tempest.common.rest_client import RestClient
 
@@ -33,8 +33,8 @@
     def request(self, method, url, headers=None, body=None, wait=None):
         """Overriding the existing HTTP request in super class rest_client."""
         self._set_auth()
-        self.base_url = self.base_url.replace(urlparse(self.base_url).path,
-                                              "/v3")
+        self.base_url = self.base_url.replace(
+            urlparse.urlparse(self.base_url).path, "/v3")
         return super(EndPointClientJSON, self).request(method, url,
                                                        headers=headers,
                                                        body=body)
diff --git a/tempest/services/identity/v3/xml/endpoints_client.py b/tempest/services/identity/v3/xml/endpoints_client.py
index 8400976..f81fccf 100755
--- a/tempest/services/identity/v3/xml/endpoints_client.py
+++ b/tempest/services/identity/v3/xml/endpoints_client.py
@@ -14,7 +14,7 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
-from urlparse import urlparse
+import urlparse
 
 import httplib2
 from lxml import etree
@@ -52,8 +52,8 @@
         dscv = self.config.identity.disable_ssl_certificate_validation
         self.http_obj = httplib2.Http(disable_ssl_certificate_validation=dscv)
         self._set_auth()
-        self.base_url = self.base_url.replace(urlparse(self.base_url).path,
-                                              "/v3")
+        self.base_url = self.base_url.replace(
+            urlparse.urlparse(self.base_url).path, "/v3")
         return super(EndPointClientXML, self).request(method, url,
                                                       headers=headers,
                                                       body=body)
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 9626b6b..69df472 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -15,10 +15,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from hashlib import sha1
+import hashlib
 import hmac
 import httplib2
-from urlparse import urlparse
+import urlparse
 
 from tempest.common.rest_client import RestClient
 from tempest import exceptions
@@ -127,10 +127,10 @@
 
         self._set_auth()
         method = 'GET'
-        path = "%s/%s/%s" % (urlparse(self.base_url).path, container,
+        path = "%s/%s/%s" % (urlparse.urlparse(self.base_url).path, container,
                              object_name)
         hmac_body = '%s\n%s\n%s' % (method, expires, path)
-        sig = hmac.new(key, hmac_body, sha1).hexdigest()
+        sig = hmac.new(key, hmac_body, hashlib.sha1).hexdigest()
 
         url = "%s/%s?temp_url_sig=%s&temp_url_expires=%s" % (container,
                                                              object_name,
diff --git a/tempest/test.py b/tempest/test.py
index b0038e0..4db9827 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -120,7 +120,7 @@
     return False
 
 
-def status_timeout(things, thing_id, expected_status):
+def status_timeout(testcase, things, thing_id, expected_status):
     """
     Given a thing and an expected status, do a loop, sleeping
     for a configurable amount of time, checking for the
@@ -134,9 +134,9 @@
         thing = things.get(thing_id)
         new_status = thing.status
         if new_status == 'ERROR':
-            self.fail("%s failed to get to expected status."
-                      "In ERROR state."
-                      % thing)
+            testcase.fail("%s failed to get to expected status."
+                          "In ERROR state."
+                          % thing)
         elif new_status == expected_status:
             return True  # All good.
         LOG.debug("Waiting for %s to get to %s status. "
@@ -146,8 +146,8 @@
     if not call_until_true(check_status,
                            conf.compute.build_timeout,
                            conf.compute.build_interval):
-        self.fail("Timed out waiting for thing %s to become %s"
-                  % (thing_id, expected_status))
+        testcase.fail("Timed out waiting for thing %s to become %s"
+                      % (thing_id, expected_status))
 
 
 class DefaultClientSmokeTest(TestCase):
diff --git a/tempest/testboto.py b/tempest/testboto.py
index 8b819d9..9e652cb 100644
--- a/tempest/testboto.py
+++ b/tempest/testboto.py
@@ -15,7 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from contextlib import closing
+import contextlib
 import logging
 import os
 import re
@@ -413,7 +413,8 @@
         """Destroys the bucket and its content, just for teardown."""
         exc_num = 0
         try:
-            with closing(boto.connect_s3(**connection_data)) as conn:
+            with contextlib.closing(
+                    boto.connect_s3(**connection_data)) as conn:
                 if isinstance(bucket, basestring):
                     bucket = conn.lookup(bucket)
                     assert isinstance(bucket, s3.bucket.Bucket)
diff --git a/tempest/tests/boto/test_s3_objects.py b/tempest/tests/boto/test_s3_objects.py
index dcb7c86..9d4d79c 100644
--- a/tempest/tests/boto/test_s3_objects.py
+++ b/tempest/tests/boto/test_s3_objects.py
@@ -15,9 +15,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from contextlib import closing
+import contextlib
 
-from boto.s3.key import Key
+import boto.s3.key
 
 from tempest import clients
 from tempest.common.utils.data_utils import rand_name
@@ -46,7 +46,7 @@
                                 bucket_name)
 
         self.assertTrue(bucket.name == bucket_name)
-        with closing(Key(bucket)) as key:
+        with contextlib.closing(boto.s3.key.Key(bucket)) as key:
             key.key = object_name
             key.set_contents_from_string(content)
             readback = key.get_contents_as_string()
diff --git a/tempest/tests/boto/utils/s3.py b/tempest/tests/boto/utils/s3.py
index 4c3229b..ea9869b 100644
--- a/tempest/tests/boto/utils/s3.py
+++ b/tempest/tests/boto/utils/s3.py
@@ -15,24 +15,24 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from contextlib import closing
+import contextlib
 import logging
 import os
 import re
 
 import boto
-from boto.s3.key import Key
+import boto.s3.key
 
 LOG = logging.getLogger(__name__)
 
 
 def s3_upload_dir(bucket, path, prefix="", connection_data=None):
     if isinstance(bucket, basestring):
-        with closing(boto.connect_s3(**connection_data)) as conn:
+        with contextlib.closing(boto.connect_s3(**connection_data)) as conn:
             bucket = conn.lookup(bucket)
     for root, dirs, files in os.walk(path):
         for fil in files:
-            with closing(Key(bucket)) as key:
+            with contextlib.closing(boto.s3.key.Key(bucket)) as key:
                 source = root + os.sep + fil
                 target = re.sub("^" + re.escape(path) + "?/", prefix, source)
                 if os.sep != '/':
diff --git a/tempest/tests/boto/utils/wait.py b/tempest/tests/boto/utils/wait.py
index c2d4ea3..6cd17a9 100644
--- a/tempest/tests/boto/utils/wait.py
+++ b/tempest/tests/boto/utils/wait.py
@@ -19,7 +19,7 @@
 import re
 import time
 
-from boto.exception import BotoServerError
+import boto.exception
 from testtools import TestCase
 
 import tempest.config
@@ -87,7 +87,7 @@
     """Stops waiting on success."""
     start_time = time.time()
     if exc_matcher is not None:
-        exc_class = BotoServerError
+        exc_class = boto.exception.BotoServerError
 
     if exc_class is None:
         exc_class = BaseException
diff --git a/tempest/tests/compute/images/test_images_oneserver.py b/tempest/tests/compute/images/test_images_oneserver.py
index 7d4cefe..dfc16f4 100644
--- a/tempest/tests/compute/images/test_images_oneserver.py
+++ b/tempest/tests/compute/images/test_images_oneserver.py
@@ -128,6 +128,11 @@
         self.assertEqual(original_image['minRam'], image['minRam'])
         self.assertEqual(original_image['minDisk'], image['minDisk'])
 
+        # Verify the image was deleted correctly
+        resp, body = self.client.delete_image(image_id)
+        self.assertEqual('204', resp['status'])
+        self.assertRaises(exceptions.NotFound, self.client.get_image, image_id)
+
     @attr(type='negative')
     @testtools.skipUnless(compute.MULTI_USER,
                           'Need multiple users for this test.')
diff --git a/tempest/tests/compute/servers/test_instance_actions.py b/tempest/tests/compute/servers/test_instance_actions.py
new file mode 100644
index 0000000..e7e31e8
--- /dev/null
+++ b/tempest/tests/compute/servers/test_instance_actions.py
@@ -0,0 +1,69 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC Corporation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest import exceptions
+from tempest.test import attr
+from tempest.tests.compute import base
+
+
+class InstanceActionsTestJSON(base.BaseComputeTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(InstanceActionsTestJSON, cls).setUpClass()
+        cls.client = cls.servers_client
+        resp, server = cls.create_server(wait_until='ACTIVE')
+        cls.request_id = resp['x-compute-request-id']
+        cls.server_id = server['id']
+
+    @attr(type='positive')
+    def test_list_instance_actions(self):
+        # List actions of the provided server
+        resp, body = self.client.reboot(self.server_id, 'HARD')
+        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+
+        resp, body = self.client.list_instance_actions(self.server_id)
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(body) == 2)
+        self.assertTrue(any([i for i in body if i['action'] == 'create']))
+        self.assertTrue(any([i for i in body if i['action'] == 'reboot']))
+
+    @attr(type='positive')
+    def test_get_instance_action(self):
+        # Get the action details of the provided server
+        resp, body = self.client.get_instance_action(self.server_id,
+                                                     self.request_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(self.server_id, body['instance_uuid'])
+        self.assertEqual('create', body['action'])
+
+    @attr(type='negative')
+    def test_list_instance_actions_invalid_server(self):
+        # List actions of the invalid server id
+        self.assertRaises(exceptions.NotFound,
+                          self.client.list_instance_actions, 'server-999')
+
+    @attr(type='negative')
+    def test_get_instance_action_invalid_request(self):
+        # Get the action details of the provided server with invalid request
+        self.assertRaises(exceptions.NotFound, self.client.get_instance_action,
+                          self.server_id, '999')
+
+
+class InstanceActionsTestXML(InstanceActionsTestJSON):
+    _interface = 'xml'
diff --git a/tempest/tests/compute/servers/test_list_server_filters.py b/tempest/tests/compute/servers/test_list_server_filters.py
index 4d2b99f..852288e 100644
--- a/tempest/tests/compute/servers/test_list_server_filters.py
+++ b/tempest/tests/compute/servers/test_list_server_filters.py
@@ -22,6 +22,8 @@
 from tempest.tests.compute import base
 from tempest.tests import utils
 
+import testtools
+
 
 class ListServerFiltersTestJSON(base.BaseComputeTest):
     _interface = 'json'
@@ -180,6 +182,56 @@
         self.assertEqual(['ACTIVE'] * 3, [x['status'] for x in servers])
 
     @attr(type='positive')
+    def test_list_servers_filtered_by_name_wildcard(self):
+        # List all servers that contains 'server' in name
+        params = {'name': 'server'}
+        resp, body = self.client.list_servers(params)
+        servers = body['servers']
+
+        self.assertIn(self.s1_name, map(lambda x: x['name'], servers))
+        self.assertIn(self.s2_name, map(lambda x: x['name'], servers))
+        self.assertIn(self.s3_name, map(lambda x: x['name'], servers))
+
+        # Let's take random part of name and try to search it
+        part_name = self.s1_name[6:-1]
+
+        params = {'name': part_name}
+        resp, body = self.client.list_servers(params)
+        servers = body['servers']
+
+        self.assertIn(self.s1_name, map(lambda x: x['name'], servers))
+        self.assertNotIn(self.s2_name, map(lambda x: x['name'], servers))
+        self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
+
+    @testtools.skip('Until Bug #1170718 is resolved.')
+    @attr(type='positive', bug='lp1170718')
+    def test_list_servers_filtered_by_ip(self):
+        # Filter servers by ip
+        # Here should be listed 1 server
+        ip = self.s1['addresses']['private'][0]['addr']
+        params = {'ip': ip}
+        resp, body = self.client.list_servers(params)
+        servers = body['servers']
+
+        self.assertIn(self.s1_name, map(lambda x: x['name'], servers))
+        self.assertNotIn(self.s2_name, map(lambda x: x['name'], servers))
+        self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
+
+    @attr(type='positive')
+    def test_list_servers_filtered_by_ip_regex(self):
+        # Filter servers by regex ip
+        # List all servers filtered by part of ip address.
+        # Here should be listed all servers
+        ip = self.s1['addresses']['private'][0]['addr'][0:-3]
+        params = {'ip': ip}
+        resp, body = self.client.list_servers(params)
+        servers = body['servers']
+
+        self.assertIn(self.s1_name, map(lambda x: x['name'], servers))
+        self.assertIn(self.s2_name, map(lambda x: x['name'], servers))
+        self.assertIn(self.s3_name, map(lambda x: x['name'], servers))
+
+    @attr(type='positive')
     def test_list_servers_detailed_limit_results(self):
         # Verify only the expected number of detailed results are returned
         params = {'limit': 1}
diff --git a/tempest/tests/compute/servers/test_multiple_create.py b/tempest/tests/compute/servers/test_multiple_create.py
index ad5d604..47a38b1 100644
--- a/tempest/tests/compute/servers/test_multiple_create.py
+++ b/tempest/tests/compute/servers/test_multiple_create.py
@@ -48,7 +48,7 @@
         created_servers = self._get_created_servers(kwargs['name'])
         # NOTE(maurosr): append it to cls.servers list from base.BaseCompute
         # class.
-        self.servers.append(created_servers)
+        self.servers.extend(created_servers)
         # NOTE(maurosr): get a server list, check status of the ones with names
         # that match and wait for them become active. At a first look, since
         # they are building in parallel, wait inside the for doesn't seem be
diff --git a/tempest/tests/compute/servers/test_server_advanced_ops.py b/tempest/tests/compute/servers/test_server_advanced_ops.py
index 56222f9..8be9c54 100644
--- a/tempest/tests/compute/servers/test_server_advanced_ops.py
+++ b/tempest/tests/compute/servers/test_server_advanced_ops.py
@@ -66,16 +66,18 @@
 
         self.assertEqual(self.instance.status, 'BUILD')
         instance_id = self.get_resource('instance').id
-        test.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
+        test.status_timeout(
+            self, self.compute_client.servers, instance_id, 'ACTIVE')
         instance = self.get_resource('instance')
         instance_id = instance.id
         resize_flavor = self.config.compute.flavor_ref_alt
         LOG.debug("Resizing instance %s from flavor %s to flavor %s",
                   instance.id, instance.flavor, resize_flavor)
         instance.resize(resize_flavor)
-        test.status_timeout(self.compute_client.servers, instance_id,
+        test.status_timeout(self, self.compute_client.servers, instance_id,
                             'VERIFY_RESIZE')
 
         LOG.debug("Confirming resize of instance %s", instance_id)
         instance.confirm_resize()
-        test.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
+        test.status_timeout(
+            self, self.compute_client.servers, instance_id, 'ACTIVE')
diff --git a/tempest/tests/compute/servers/test_server_basic_ops.py b/tempest/tests/compute/servers/test_server_basic_ops.py
index 05cb39e..e4e246a 100644
--- a/tempest/tests/compute/servers/test_server_basic_ops.py
+++ b/tempest/tests/compute/servers/test_server_basic_ops.py
@@ -101,7 +101,8 @@
 
     def wait_on_active(self):
         instance_id = self.get_resource('instance').id
-        test.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
+        test.status_timeout(
+            self, self.compute_client.servers, instance_id, 'ACTIVE')
 
     def pause_server(self):
         instance = self.get_resource('instance')
@@ -109,7 +110,8 @@
         LOG.debug("Pausing instance %s. Current status: %s",
                   instance_id, instance.status)
         instance.pause()
-        test.status_timeout(self.compute_client.servers, instance_id, 'PAUSED')
+        test.status_timeout(
+            self, self.compute_client.servers, instance_id, 'PAUSED')
 
     def unpause_server(self):
         instance = self.get_resource('instance')
@@ -117,7 +119,8 @@
         LOG.debug("Unpausing instance %s. Current status: %s",
                   instance_id, instance.status)
         instance.unpause()
-        test.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
+        test.status_timeout(
+            self, self.compute_client.servers, instance_id, 'ACTIVE')
 
     def suspend_server(self):
         instance = self.get_resource('instance')
@@ -125,7 +128,7 @@
         LOG.debug("Suspending instance %s. Current status: %s",
                   instance_id, instance.status)
         instance.suspend()
-        test.status_timeout(self.compute_client.servers,
+        test.status_timeout(self, self.compute_client.servers,
                             instance_id, 'SUSPENDED')
 
     def resume_server(self):
@@ -134,7 +137,8 @@
         LOG.debug("Resuming instance %s. Current status: %s",
                   instance_id, instance.status)
         instance.resume()
-        test.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
+        test.status_timeout(
+            self, self.compute_client.servers, instance_id, 'ACTIVE')
 
     def terminate_instance(self):
         instance = self.get_resource('instance')
diff --git a/tempest/tests/compute/test_authorization.py b/tempest/tests/compute/test_authorization.py
index 91cf39f..6edc946 100644
--- a/tempest/tests/compute/test_authorization.py
+++ b/tempest/tests/compute/test_authorization.py
@@ -234,8 +234,7 @@
             # Reset the base_url...
             self.alt_security_client.base_url = self.saved_base_url
             if resp['status'] is not None:
-                #TODO(afazekas): body not defined
-                self.alt_security_client.delete_security_group(body['id'])
+                self.alt_security_client.delete_security_group(resp['id'])
                 self.fail("Create Security Group request should not happen if"
                           "the tenant id does not match the current user")
 
@@ -274,8 +273,7 @@
             # Reset the base_url...
             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
+                self.alt_security_client.delete_security_group_rule(resp['id'])
                 self.fail("Create security group rule request should not "
                           "happen if the tenant id does not match the"
                           " current user")
diff --git a/tempest/tests/identity/admin/test_users.py b/tempest/tests/identity/admin/test_users.py
index 0573b21..f9772ac 100644
--- a/tempest/tests/identity/admin/test_users.py
+++ b/tempest/tests/identity/admin/test_users.py
@@ -309,8 +309,8 @@
         for i in body:
             fetched_user_ids.append(i['id'])
         #verifying the user Id in the list
-        missing_users =\
-            [user for user in user_ids if user not in fetched_user_ids]
+        missing_users = [missing_user for missing_user in user_ids
+                         if missing_user not in fetched_user_ids]
         self.assertEqual(0, len(missing_users),
                          "Failed to find user %s in fetched list" %
                          ', '.join(m_user for m_user in missing_users))
diff --git a/tempest/tests/identity/admin/v3/test_endpoints.py b/tempest/tests/identity/admin/v3/test_endpoints.py
index 98fab57..3ad9b28 100755
--- a/tempest/tests/identity/admin/v3/test_endpoints.py
+++ b/tempest/tests/identity/admin/v3/test_endpoints.py
@@ -126,7 +126,6 @@
                                                 description=s_description)
         self.service_ids.append(self.service2['id'])
         #Updating endpoint with new values
-        service_id = self.service2['id']
         region2 = rand_name('region')
         url2 = rand_name('url')
         interface2 = 'internal'
diff --git a/tempest/tests/network/common.py b/tempest/tests/network/common.py
index 1cff2c4..6246f54 100644
--- a/tempest/tests/network/common.py
+++ b/tempest/tests/network/common.py
@@ -125,7 +125,6 @@
     @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,
@@ -246,10 +245,7 @@
             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
+        result = self.network_client.create_port(body=body)
         self.assertIsNotNone(result, 'Unable to allocate port')
         port = DeletablePort(client=self.network_client,
                              **result['port'])
@@ -273,7 +269,7 @@
             self.set_resource(name, server)
         except AttributeError:
             self.fail("Server not successfully created.")
-        test.status_timeout(client.servers, server.id, 'ACTIVE')
+        test.status_timeout(self, client.servers, server.id, 'ACTIVE')
         # The instance retrieved on creation is missing network
         # details, necessitating retrieval after it becomes active to
         # ensure correct details.
diff --git a/tempest/tests/object_storage/test_object_expiry.py b/tempest/tests/object_storage/test_object_expiry.py
index c12ec3d..e1b1dbd 100644
--- a/tempest/tests/object_storage/test_object_expiry.py
+++ b/tempest/tests/object_storage/test_object_expiry.py
@@ -21,7 +21,7 @@
 from tempest.test import attr
 from tempest.tests.object_storage import base
 import testtools
-from time import sleep
+import time
 
 
 class ObjectExpiryTest(base.BaseObjectTest):
@@ -88,7 +88,7 @@
         # Check data
         self.assertEqual(body, data)
         # Sleep for over 5 seconds, so that object is expired
-        sleep(5)
+        time.sleep(5)
         # Verification of raised exception after object gets expired
         self.assertRaises(exceptions.NotFound, self.object_client.get_object,
                           self.container_name, object_name)
diff --git a/tempest/tests/object_storage/test_object_services.py b/tempest/tests/object_storage/test_object_services.py
index 1edce92..4fcc617 100644
--- a/tempest/tests/object_storage/test_object_services.py
+++ b/tempest/tests/object_storage/test_object_services.py
@@ -21,7 +21,7 @@
 from tempest.test import attr
 from tempest.tests.object_storage import base
 import testtools
-from time import time
+import time
 
 
 class ObjectTest(base.BaseObjectTest):
@@ -617,7 +617,7 @@
             self.object_client.create_object(self.container_name,
                                              object_name, data)
 
-            expires = int(time() + 10)
+            expires = int(time.time() + 10)
 
             #Trying to GET object using temp URL with in expiry time
             _, body = self.object_client.get_object_using_temp_url(
diff --git a/tempest/tests/volume/base.py b/tempest/tests/volume/base.py
index 00e8668..978ec53 100644
--- a/tempest/tests/volume/base.py
+++ b/tempest/tests/volume/base.py
@@ -149,13 +149,13 @@
     def clear_volumes(cls):
         for volume in cls.volumes:
             try:
-                cls.volume_client.delete_volume(volume['id'])
+                cls.volumes_client.delete_volume(volume['id'])
             except Exception:
                 pass
 
         for volume in cls.volumes:
             try:
-                cls.servers_client.wait_for_resource_deletion(volume['id'])
+                cls.volumes_client.wait_for_resource_deletion(volume['id'])
             except Exception:
                 pass
 
diff --git a/tools/check_source.sh b/tools/check_source.sh
deleted file mode 100755
index 01724fa..0000000
--- a/tools/check_source.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-
-flake8 --ignore=E125,H302,H304,H404,F --show-source --exclude=.git,.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/install_venv.py b/tools/install_venv.py
index ef7b0a8..5d4b290 100644
--- a/tools/install_venv.py
+++ b/tools/install_venv.py
@@ -3,7 +3,7 @@
 # Copyright 2010 United States Government as represented by the
 # Administrator of the National Aeronautics and Space Administration.
 # All Rights Reserved.
-#
+# flake8: noqa
 # Copyright 2010 OpenStack, LLC
 # Copyright 2013 IBM Corp.
 #
diff --git a/tools/test-requires b/tools/test-requires
index 3d4c2d6..cba42a4 100644
--- a/tools/test-requires
+++ b/tools/test-requires
@@ -1,4 +1,8 @@
-flake8
-hacking
+# Install bounded pep8/pyflakes first, then let flake8 install
+pep8==1.4.5
+pyflakes==0.7.2
+flake8==2.0
+hacking>=0.5.3,<0.6
+#
 #TODO(afazekas): ensure pg_config installed
 psycopg2
diff --git a/tox.ini b/tox.ini
index 85a0d86..4a2f80e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -19,4 +19,9 @@
            python -m tools/tempest_coverage -c report --html
 
 [testenv:pep8]
-commands = bash tools/check_source.sh
+commands = flake8
+
+[flake8]
+ignore = E125,H302,H404
+show-source = True
+exclude = .git,.venv,.tox,dist,doc,openstack,*egg