Merge "Py3: don't access the `unicode` type directly."
diff --git a/HACKING.rst b/HACKING.rst
index 480650c..432db7d 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -160,11 +160,17 @@
 
 Negative Tests
 --------------
-TODO: Write the guideline related to negative tests.
+Error handling is an important aspect of API design and usage. Negative
+tests are a way to ensure that an application can gracefully handle
+invalid or unexpected input. However, as a black box integration test
+suite, Tempest is not suitable for handling all negative test cases, as
+the wide variety and complexity of negative tests can lead to long test
+runs and knowledge of internal implementation details. The bulk of
+negative testing should be handled with project function tests. The
+exception to this rule is API tests used for interoperability testing.
 
 Test skips because of Known Bugs
 --------------------------------
-
 If a test is broken because of a bug it is appropriate to skip the test until
 bug has been fixed. You should use the skip_because decorator so that
 Tempest's skip tracking tool can watch the bug status.
diff --git a/doc/source/index.rst b/doc/source/index.rst
index c73fac3..27f4fab 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -14,6 +14,7 @@
    plugin-registry
    library
    microversion_testing
+   test-removal
 
 ------------
 Field Guides
diff --git a/doc/source/test-removal.rst b/doc/source/test-removal.rst
new file mode 100644
index 0000000..6570bb7
--- /dev/null
+++ b/doc/source/test-removal.rst
@@ -0,0 +1,168 @@
+Tempest Test Removal Procedure
+==============================
+
+Historically tempest was the only way of doing functional testing and
+integration testing in OpenStack. This was mostly only an artifact of tempest
+being the only proven pattern for doing this, not an artifact of a design
+decision. However, moving forward as functional testing is being spun up in
+each individual project we really only want tempest to be the integration test
+suite it was intended to be; testing the high level interactions between
+projects through REST API requests. In this model there are probably existing
+tests that aren't the best fit living in tempest. However, since tempest is
+largely still the only gating test suite in this space we can't carelessly rip
+out everything from the tree. This document outlines the procedure which was
+developed to ensure we minimize the risk for removing something of value from
+the tempest tree.
+
+This procedure might seem overly conservative and slow paced, but this is by
+design to try and ensure we don't remove something that is actually providing
+value. Having potential duplication between testing is not a big deal
+especially compared to the alternative of removing something which is actually
+providing value and is actively catching bugs, or blocking incorrect patches
+from landing.
+
+Proposing a test removal
+------------------------
+
+3 prong rule for removal
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+In the proposal etherpad we'll be looking for answers to 3 questions
+
+ #. The tests proposed for removal must have equiv. coverage in a different
+    project's test suite (whether this is another gating test project, or an in
+    tree funcitonal test suite) For API tests preferably the other project will
+    have a similar source of friction in place to prevent breaking api changes
+    so that we don't regress and let breaking api changes slip through the
+    gate.
+ #. The test proposed for removal has a failure rate <  0.50% in the gate over
+    the past release (the value and interval will likely be adjusted in the
+    future)
+ #. There must not be an external user/consumer of tempest that depends on the
+    test proposed for removal
+
+The answers to 1 and 2 are easy to verify. For 1 just provide a link to the new
+test location. If you are linking to the tempest removal patch please also put
+a Depends-On in the commit message for the commit which moved the test into
+another repo.
+
+For prong 2 you can use OpenStack-Health:
+
+Using OpenStack-Health
+""""""""""""""""""""""
+
+Go to: http://status.openstack.org/openstack-health and then navigate to a per
+test page for six months. You'll end up with a page that will graph the success
+and failure rates on the bottom graph. For example, something like `this URL`_.
+
+.. _this URL: http://status.openstack.org/openstack-health/#/test/tempest.scenario.test_volume_boot_pattern.TestVolumeBootPatternV2.test_volume_boot_pattern?groupKey=project&resolutionKey=day&duration=P6M
+
+The Old Way using subunit2sql directly
+""""""""""""""""""""""""""""""""""""""
+
+SELECT * from tests where test_id like "%test_id%";
+(where $test_id is the full test_id, but truncated to the class because of
+setupclass or teardownclass failures)
+
+You can access the infra mysql subunit2sql db w/ read-only permissions with:
+
+ * hostname: logstash.openstack.org
+ * username: query
+ * password: query
+ * db_name: subunit2sql
+
+For example if you were trying to remove the test with the id:
+tempest.api.compute.admin.test_flavors_negative.FlavorsAdminNegativeTestJSON.test_get_flavor_details_for_deleted_flavor
+you would run the following:
+
+ #. run: "mysql -u query -p -h logstash.openstack.org subunit2sql" to connect
+    to the subunit2sql db
+ #. run the query: MySQL [subunit2sql]> select * from tests where test_id like
+    "tempest.api.compute.admin.test_flavors_negative.FlavorsAdminNegativeTestJSON%";
+    which will return a table of all the tests in the class (but it will also
+    catch failures in setupClass and tearDownClass)
+ #. paste the output table with numbers and the mysql command you ran to
+    generate it into the etherpad.
+
+Eventually a cli interface will be created to make that a bit more friendly.
+Also a dashboard is in the works so we don't need to manually run the command.
+
+The intent of the 2nd prong is to verify that moving the test into a project
+specific testing is preventing bugs (assuming the tempest tests were catching
+issues) from bubbling up a layer into tempest jobs. If we're seeing failure
+rates above a certain threshold in the gate checks that means the functional
+testing isn't really being effective in catching that bug (and therefore
+blocking it from landing) and having the testing run in tempest still has
+value.
+
+However for the 3rd prong verification is a bit more subjective. The original
+intent of this prong was mostly for refstack/defcore and also for things that
+running on the stable branches. We don't want to remove any tests if that
+would break our api consistency checking between releases, or something that
+defcore/refstack is depending on being in tempest. It's worth pointing out
+that if a test is used in defcore as part of interop testing then it will
+probably have continuing value being in tempest as part of the
+integration/integrated tests in general. This is one area where some overlap
+is expected between testing in projects and tempest, which is not a bad thing.
+
+Discussing the 3rd prong
+""""""""""""""""""""""""
+
+There are 2 approaches to addressing the 3rd prong. Either it can be raised
+during a qa meeting during the tempest discussion. Please put it on the agenda
+well ahead of the scheduled meeting. Since the meeting time will be well known
+ahead of time anyone who depends on the tests will have ample time beforehand
+to outline any concerns on the before the meeting. To give ample time for
+people to respond to removal proposals please add things to the agend by the
+Monday before the meeting.
+
+The other option is to raise the removal on the openstack-dev mailing list.
+(for example see: http://lists.openstack.org/pipermail/openstack-dev/2016-February/086218.html )
+This will raise the issue to the wider community and attract at least the same
+(most likely more) attention than discussing it during the irc meeting. The
+only downside is that it might take more time to get a response, given the
+nature of ML.
+
+Exceptions to this procedure
+----------------------------
+
+For the most part all tempest test removals have to go through this procedure
+there are a couple of exceptions though:
+
+ #. The class of testing has been decided to be outside the scope of tempest.
+ #. A revert for a patch which added a broken test, or testing which didn't
+    actually run in the gate (basically any revert for something which
+    shouldn't have been added)
+
+For the first exception type the only types of testing in tree which have been
+declared out of scope at this point are:
+
+ * The CLI tests (which should be completely removed at this point)
+ * Neutron Adv. Services testing (which should be completely removed at this
+   point)
+ * XML API Tests (which should be completely removed at this point)
+ * EC2 API/boto tests (which should be completely removed at this point)
+
+For tests that fit into this category the only criteria for removal is that
+there is equivalent testing elsewhere.
+
+Tempest Scope
+^^^^^^^^^^^^^
+
+Also starting in the liberty cycle tempest has defined a set of projects which
+are defined as in scope for direct testing in tempest. As of today that list
+is:
+
+ * Keystone
+ * Nova
+ * Glance
+ * Cinder
+ * Neutron
+ * Swift
+
+anything that lives in tempest which doesn't test one of these projects can be
+removed assuming there is equivalent testing elsewhere. Preferably using the
+`tempest plugin mechanism`_
+to mantain continuity after migrating the tests out of tempest
+
+.. _tempest plugin mechanism: http://docs.openstack.org/developer/tempest/plugin.html
diff --git a/releasenotes/notes/remove-input-scenarios-functionality-01308e6d4307f580.yaml b/releasenotes/notes/remove-input-scenarios-functionality-01308e6d4307f580.yaml
new file mode 100644
index 0000000..4ee883f
--- /dev/null
+++ b/releasenotes/notes/remove-input-scenarios-functionality-01308e6d4307f580.yaml
@@ -0,0 +1,11 @@
+---
+upgrade:
+  - The input scenarios functionality no longer exists in tempest. This caused
+    a large number of issues for limited benefit and was only used by a single
+    test, test_server_basic_ops. If you were using this functionality you'll
+    now have to do it manually with a script and/or tempest workspaces
+deprecations:
+  - All the options in the input-scenario group are now deprecated. These were
+    only used in tree by the now removed input scenarios functionality in
+    test_server_basic_ops. They were only deprecated because there could be
+    external consumers via plugins. They will be removed during the Ocata cycle.
diff --git a/releasenotes/notes/tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml b/releasenotes/notes/tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
new file mode 100644
index 0000000..eeda921
--- /dev/null
+++ b/releasenotes/notes/tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
@@ -0,0 +1,12 @@
+---
+upgrade:
+  - The location on disk that the *tempest init* command looks for has changed.
+    Previously it would attempt to use python packaging's data files to guess
+    where setuptools/distutils were installing data files, which was incredibly
+    unreliable and depended on how you installed tempest and which versions of
+    setuptools, distutils, and python you had installed. Instead, now it will
+    use either /etc/tempest, $XDG_CONFIG_PATH/.config/tempest, or
+    ~/.tempest/etc (attempted in that order). If none of these exist it will
+    create an empty ~/.tempest/etc directory. If you were relying on the
+    previous behavior and none of these directories were being used you will
+    need to move the files to live in one of these directories.
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 3a4428a..0d06119 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -375,17 +375,19 @@
             'backup_type': "daily",
             'instance_uuid': self.server_id,
         }
+        params = {
+            'status': 'active',
+            'sort_key': 'created_at',
+            'sort_dir': 'asc'
+        }
         if CONF.image_feature_enabled.api_v1:
-            params = dict(
-                properties=properties, status='active',
-                sort_key='created_at', sort_dir='asc',)
+            for key, value in properties.items():
+                params['property-%s' % key] = value
             image_list = glance_client.list_images(
                 detail=True,
                 **params)['images']
         else:
             # Additional properties are flattened in glance v2.
-            params = dict(
-                status='active', sort_key='created_at', sort_dir='asc')
             params.update(properties)
             image_list = glance_client.list_images(params)['images']
 
diff --git a/tempest/api/data_processing/test_node_group_templates.py b/tempest/api/data_processing/test_node_group_templates.py
index 388bb58..c2dae85 100644
--- a/tempest/api/data_processing/test_node_group_templates.py
+++ b/tempest/api/data_processing/test_node_group_templates.py
@@ -25,10 +25,6 @@
         if cls.default_plugin is None:
             raise cls.skipException("No Sahara plugins configured")
 
-    @classmethod
-    def resource_setup(cls):
-        super(NodeGroupTemplateTest, cls).resource_setup()
-
     def _create_node_group_template(self, template_name=None):
         """Creates Node Group Template with optional name specified.
 
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index e6a22b0..7a1e3a5 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -81,14 +81,6 @@
         cls.non_admin_tenants_client = cls.os.tenants_public_client
         cls.non_admin_users_client = cls.os.users_public_client
 
-    @classmethod
-    def resource_setup(cls):
-        super(BaseIdentityV2Test, cls).resource_setup()
-
-    @classmethod
-    def resource_cleanup(cls):
-        super(BaseIdentityV2Test, cls).resource_cleanup()
-
 
 class BaseIdentityV2AdminTest(BaseIdentityV2Test):
 
@@ -137,10 +129,6 @@
         cls.non_admin_token = cls.os.token_v3_client
         cls.non_admin_projects_client = cls.os.projects_client
 
-    @classmethod
-    def resource_cleanup(cls):
-        super(BaseIdentityV3Test, cls).resource_cleanup()
-
 
 class BaseIdentityV3AdminTest(BaseIdentityV3Test):
 
@@ -227,27 +215,24 @@
             name=data_utils.rand_name('test_role'))['role']
         self.roles.append(self.role)
 
-    @staticmethod
-    def _try_wrapper(func, item, **kwargs):
-        try:
-            test_utils.call_and_ignore_notfound_exc(func, item['id'], **kwargs)
-        except Exception:
-            LOG.exception("Unexpected exception occurred in %s deletion. "
-                          "But ignored here." % item['id'])
-
     def teardown_all(self):
         for user in self.users:
-            self._try_wrapper(self.users_client.delete_user, user)
+            test_utils.call_and_ignore_notfound_exc(
+                self.users_client.delete_user, user)
         for tenant in self.tenants:
-            self._try_wrapper(self.projects_client.delete_tenant, tenant)
-        for project in self.projects:
-            self._try_wrapper(self.projects_client.delete_project, project)
+            test_utils.call_and_ignore_notfound_exc(
+                self.projects_client.delete_tenant, tenant)
+        for project in reversed(self.projects):
+            test_utils.call_and_ignore_notfound_exc(
+                self.projects_client.delete_project, project)
         for role in self.roles:
-            self._try_wrapper(self.roles_client.delete_role, role)
+            test_utils.call_and_ignore_notfound_exc(
+                self.roles_client.delete_role, role)
         for domain in self.domains:
-            self._try_wrapper(self.domains_client.update_domain, domain,
-                              enabled=False)
-            self._try_wrapper(self.domains_client.delete_domain, domain)
+            test_utils.call_and_ignore_notfound_exc(
+                self.domains_client.update_domain, domain, enabled=False)
+            test_utils.call_and_ignore_notfound_exc(
+                self.domains_client.delete_domain, domain)
 
 
 class DataGeneratorV2(BaseDataGenerator):
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 1d96439..bf80ff5 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -565,9 +565,9 @@
         subnets = [sub['id'] for sub in body['subnets']
                    if sub['network_id'] == network['id']]
         test_subnet_ids = [sub['id'] for sub in (subnet1, subnet2)]
-        self.assertItemsEqual(subnets,
-                              test_subnet_ids,
-                              'Subnet are not in the same network')
+        six.assertCountEqual(self, subnets,
+                             test_subnet_ids,
+                             'Subnet are not in the same network')
 
 
 class NetworksIpV6TestAttrs(NetworksIpV6Test):
diff --git a/tempest/api/orchestration/stacks/test_stacks.py b/tempest/api/orchestration/stacks/test_stacks.py
index 28463ab..f13a2d9 100644
--- a/tempest/api/orchestration/stacks/test_stacks.py
+++ b/tempest/api/orchestration/stacks/test_stacks.py
@@ -18,10 +18,6 @@
 class StacksTestJSON(base.BaseOrchestrationTest):
     empty_template = "HeatTemplateFormatVersion: '2012-12-12'\n"
 
-    @classmethod
-    def resource_setup(cls):
-        super(StacksTestJSON, cls).resource_setup()
-
     @test.attr(type='smoke')
     @test.idempotent_id('d35d628c-07f6-4674-85a1-74db9919e986')
     def test_stack_list_responds(self):
diff --git a/tempest/api/volume/admin/test_volumes_list.py b/tempest/api/volume/admin/test_volumes_list.py
new file mode 100644
index 0000000..64041b8
--- /dev/null
+++ b/tempest/api/volume/admin/test_volumes_list.py
@@ -0,0 +1,59 @@
+# Copyright 2016 Red Hat, Inc.
+# 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 operator
+
+from tempest.api.volume import base
+from tempest.common import waiters
+from tempest import test
+
+
+class VolumesListAdminV2TestJSON(base.BaseVolumeAdminTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesListAdminV2TestJSON, cls).resource_setup()
+        # Create 3 test volumes
+        cls.volume_list = []
+        for i in range(3):
+            volume = cls.create_volume()
+            # Fetch volume details
+            volume_details = cls.volumes_client.show_volume(
+                volume['id'])['volume']
+            cls.volume_list.append(volume_details)
+
+    @test.idempotent_id('5866286f-3290-4cfd-a414-088aa6cdc469')
+    def test_volume_list_param_tenant(self):
+        # Test to list volumes from single tenant
+        # Create a volume in admin tenant
+        adm_vol = self.admin_volume_client.create_volume()['volume']
+        waiters.wait_for_volume_status(self.admin_volume_client,
+                                       adm_vol['id'], 'available')
+        self.addCleanup(self.admin_volume_client.delete_volume, adm_vol['id'])
+        params = {'all_tenants': 1,
+                  'project_id': self.volumes_client.tenant_id}
+        # Getting volume list from primary tenant using admin credentials
+        fetched_list = self.admin_volume_client.list_volumes(
+            detail=True, params=params)['volumes']
+        # Verifying fetched volume ids list is related to primary tenant
+        fetched_list_ids = map(operator.itemgetter('id'), fetched_list)
+        expected_list_ids = map(operator.itemgetter('id'), self.volume_list)
+        self.assertEqual(sorted(expected_list_ids), sorted(fetched_list_ids))
+        # Verifying tenant id of volumes fetched list is related to
+        # primary tenant
+        fetched_tenant_id = map(operator.itemgetter(
+            'os-vol-tenant-attr:tenant_id'), fetched_list)
+        expected_tenant_id = [self.volumes_client.tenant_id] * 3
+        self.assertEqual(expected_tenant_id, fetched_tenant_id)
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index 289650f..80de6f5 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -71,9 +71,6 @@
 
 class TempestCleanup(command.Command):
 
-    def __init__(self, app, cmd):
-        super(TempestCleanup, self).__init__(app, cmd)
-
     def take_action(self, parsed_args):
         try:
             self.init(parsed_args)
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index 3b84599..e3788ab 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -40,45 +40,28 @@
 def get_tempest_default_config_dir():
     """Get default config directory of tempest
 
-    Returns the correct default config dir to support both cases of
-    tempest being or not installed in a virtualenv.
-    Cases considered:
-    - no virtual env, python2: real_prefix and base_prefix not set
-    - no virtual env, python3: real_prefix not set, base_prefix set and
-      identical to prefix
-    - virtualenv, python2: real_prefix and prefix are set and different
-    - virtualenv, python3: real_prefix not set, base_prefix and prefix are
-      set and identical
-    - pyvenv, any python version: real_prefix not set, base_prefix and prefix
-      are set and different
+    There are 3 dirs that get tried in priority order. First is /etc/tempest,
+    if that doesn't exist it looks for a tempest dir in the XDG_CONFIG_HOME
+    dir (defaulting to ~/.config/tempest) and last it tries for a
+    ~/.tempest/etc directory. If none of these exist a ~/.tempest/etc
+    directory will be created.
 
     :return: default config dir
     """
-    real_prefix = getattr(sys, 'real_prefix', None)
-    base_prefix = getattr(sys, 'base_prefix', None)
-    prefix = sys.prefix
     global_conf_dir = '/etc/tempest'
-    if (real_prefix is None and
-            (base_prefix is None or base_prefix == prefix) and
-            os.path.isdir(global_conf_dir)):
-        # Probably not running in a virtual environment.
-        # NOTE(andreaf) we cannot distinguish this case from the case of
-        # a virtual environment created with virtualenv, and running python3.
-        # Also if it appears we are not in virtual env and fail to find
-        # global config: '/etc/tempest', fall back to
-        # '[sys.prefix]/etc/tempest'
+    xdg_config = os.environ.get('XDG_CONFIG_HOME',
+                                os.path.expanduser('~/.config'))
+    user_xdg_global_path = os.path.join(xdg_config, 'tempest')
+    user_global_path = os.path.join(os.path.expanduser('~'), '.tempest/etc')
+    if os.path.isdir(global_conf_dir):
         return global_conf_dir
+    elif os.path.isdir(user_xdg_global_path):
+        return user_xdg_global_path
+    elif os.path.isdir(user_global_path):
+        return user_global_path
     else:
-        conf_dir = os.path.join(prefix, 'etc/tempest')
-        if os.path.isdir(conf_dir):
-            return conf_dir
-        else:
-            # NOTE: The prefix is gotten from the path which pyconfig.h is
-            # installed under. Some envs contain it under /usr/include, not
-            # /user/local/include. Then prefix becomes /usr on such envs.
-            # However, etc/tempest is installed under /usr/local and the bove
-            # path logic mismatches. This is a workaround for such envs.
-            return os.path.join(prefix, 'local/etc/tempest')
+        os.makedirs(user_global_path)
+        return user_global_path
 
 
 class TempestInit(command.Command):
diff --git a/tempest/config.py b/tempest/config.py
index a9cf537..b3d409f 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -1035,23 +1035,28 @@
 
 input_scenario_group = cfg.OptGroup(name="input-scenario",
                                     title="Filters and values for"
-                                          " input scenarios")
+                                          " input scenarios[DEPRECATED]")
+
 
 InputScenarioGroup = [
     cfg.StrOpt('image_regex',
                default='^cirros-0.3.1-x86_64-uec$',
-               help="Matching images become parameters for scenario tests"),
+               help="Matching images become parameters for scenario tests",
+               deprecated_for_removal=True),
     cfg.StrOpt('flavor_regex',
                default='^m1.nano$',
-               help="Matching flavors become parameters for scenario tests"),
+               help="Matching flavors become parameters for scenario tests",
+               deprecated_for_removal=True),
     cfg.StrOpt('non_ssh_image_regex',
                default='^.*[Ww]in.*$',
                help="SSH verification in tests is skipped"
-                    "for matching images"),
+                    "for matching images",
+               deprecated_for_removal=True),
     cfg.StrOpt('ssh_user_regex',
                default="[[\"^.*[Cc]irros.*$\", \"cirros\"]]",
                help="List of user mapped to regex "
-                    "to matching image names."),
+                    "to matching image names.",
+               deprecated_for_removal=True),
 ]
 
 
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index f9f0c83..7fcec8c 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -154,7 +154,7 @@
     This generates a string with an arbitrary number of characters, generated
     by looping the base_text string. If the size is smaller than the size of
     base_text, returning string is shrinked to the size.
-    :param int size: a returning charactors size
+    :param int size: a returning characters size
     :param str base_text: a string you want to repeat
     :return: size string
     :rtype: string
diff --git a/tempest/lib/services/compute/base_compute_client.py b/tempest/lib/services/compute/base_compute_client.py
index 433c94c..a706a79 100644
--- a/tempest/lib/services/compute/base_compute_client.py
+++ b/tempest/lib/services/compute/base_compute_client.py
@@ -36,11 +36,6 @@
 
     api_microversion_header_name = 'X-OpenStack-Nova-API-Version'
 
-    def __init__(self, auth_provider, service, region,
-                 **kwargs):
-        super(BaseComputeClient, self).__init__(
-            auth_provider, service, region, **kwargs)
-
     def get_headers(self):
         headers = super(BaseComputeClient, self).get_headers()
         if COMPUTE_MICROVERSION:
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index e377c84..0d80a82 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -52,7 +52,7 @@
         """Create a new flavor or instance type.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#create-flavors
+                              api-ref-compute-v2.1.html#createFlavor
         """
         if kwargs.get('ephemeral'):
             kwargs['OS-FLV-EXT-DATA:ephemeral'] = kwargs.pop('ephemeral')
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index a9f2dff..80728dc 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -20,15 +20,12 @@
 from tempest import config
 from tempest import exceptions
 from tempest.scenario import manager
-from tempest.scenario import utils as test_utils
 from tempest import test
 
 CONF = config.CONF
 
 LOG = logging.getLogger(__name__)
 
-load_tests = test_utils.load_tests_input_scenario_utils
-
 
 class TestServerBasicOps(manager.ScenarioTest):
 
@@ -47,27 +44,10 @@
 
     def setUp(self):
         super(TestServerBasicOps, self).setUp()
-        # Setup image and flavor the test instance
-        # Support both configured and injected values
-        if not hasattr(self, 'image_ref'):
-            self.image_ref = CONF.compute.image_ref
-        if not hasattr(self, 'flavor_ref'):
-            self.flavor_ref = CONF.compute.flavor_ref
-        self.image_utils = test_utils.ImageUtils(self.manager)
-        if not self.image_utils.is_flavor_enough(self.flavor_ref,
-                                                 self.image_ref):
-            raise self.skipException(
-                '{image} does not fit in {flavor}'.format(
-                    image=self.image_ref, flavor=self.flavor_ref
-                )
-            )
-        self.run_ssh = CONF.validation.run_validation and \
-            self.image_utils.is_sshable_image(self.image_ref)
-        self.ssh_user = self.image_utils.ssh_user(self.image_ref)
-        LOG.debug('Starting test for i:{image}, f:{flavor}. '
-                  'Run ssh: {ssh}, user: {ssh_user}'.format(
-                      image=self.image_ref, flavor=self.flavor_ref,
-                      ssh=self.run_ssh, ssh_user=self.ssh_user))
+        self.image_ref = CONF.compute.image_ref
+        self.flavor_ref = CONF.compute.flavor_ref
+        self.run_ssh = CONF.validation.run_validation
+        self.ssh_user = CONF.validation.image_ssh_user
 
     def verify_ssh(self, keypair):
         if self.run_ssh:
@@ -76,7 +56,7 @@
             # Check ssh
             self.ssh_client = self.get_remote_client(
                 ip_address=self.fip,
-                username=self.image_utils.ssh_user(self.image_ref),
+                username=self.ssh_user,
                 private_key=keypair['private_key'])
 
     def verify_metadata(self):
diff --git a/tempest/scenario/utils.py b/tempest/scenario/utils.py
deleted file mode 100644
index c7ba659..0000000
--- a/tempest/scenario/utils.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# Copyright 2013 Hewlett-Packard, Ltd.
-#
-#    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 re
-import string
-import unicodedata
-
-from oslo_serialization import jsonutils as json
-import testscenarios
-import testtools
-
-from tempest import clients
-from tempest.common import credentials_factory as credentials
-from tempest import config
-from tempest.lib.common.utils import misc
-from tempest.lib import exceptions as exc_lib
-
-CONF = config.CONF
-
-
-class ImageUtils(object):
-
-    default_ssh_user = 'root'
-
-    def __init__(self, os):
-        # Load configuration items
-        self.ssh_users = json.loads(CONF.input_scenario.ssh_user_regex)
-        self.non_ssh_image_pattern = \
-            CONF.input_scenario.non_ssh_image_regex
-        # Setup clients
-        self.compute_images_client = os.compute_images_client
-        self.flavors_client = os.flavors_client
-
-    def ssh_user(self, image_id):
-        _image = self.compute_images_client.show_image(image_id)['image']
-        for regex, user in self.ssh_users:
-            # First match wins
-            if re.match(regex, _image['name']) is not None:
-                return user
-        else:
-            return self.default_ssh_user
-
-    def _is_sshable_image(self, image):
-        return not re.search(pattern=self.non_ssh_image_pattern,
-                             string=str(image['name']))
-
-    def is_sshable_image(self, image_id):
-        _image = self.compute_images_client.show_image(image_id)['image']
-        return self._is_sshable_image(_image)
-
-    def _is_flavor_enough(self, flavor, image):
-        return image['minDisk'] <= flavor['disk']
-
-    def is_flavor_enough(self, flavor_id, image_id):
-        _image = self.compute_images_client.show_image(image_id)['image']
-        _flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
-        return self._is_flavor_enough(_flavor, _image)
-
-
-@misc.singleton
-class InputScenarioUtils(object):
-
-    """Example usage:
-
-    import testscenarios
-    (...)
-    load_tests = testscenarios.load_tests_apply_scenarios
-
-
-    class TestInputScenario(manager.ScenarioTest):
-
-        scenario_utils = utils.InputScenarioUtils()
-        scenario_flavor = scenario_utils.scenario_flavors
-        scenario_image = scenario_utils.scenario_images
-        scenarios = testscenarios.multiply_scenarios(scenario_image,
-                                                     scenario_flavor)
-
-        def test_create_server_metadata(self):
-            name = rand_name('instance')
-            self.servers_client.create_server(name=name,
-                                              flavorRef=self.flavor_ref,
-                                              imageRef=self.image_ref)
-    """
-    validchars = "-_.{ascii}{digit}".format(ascii=string.ascii_letters,
-                                            digit=string.digits)
-
-    def __init__(self):
-        network_resources = {
-            'network': False,
-            'router': False,
-            'subnet': False,
-            'dhcp': False,
-        }
-        self.cred_provider = credentials.get_credentials_provider(
-            name='InputScenarioUtils',
-            identity_version=CONF.identity.auth_version,
-            network_resources=network_resources)
-        os = clients.Manager(
-            self.cred_provider.get_primary_creds().credentials)
-        self.compute_images_client = os.compute_images_client
-        self.flavors_client = os.flavors_client
-        self.image_pattern = CONF.input_scenario.image_regex
-        self.flavor_pattern = CONF.input_scenario.flavor_regex
-
-    def _normalize_name(self, name):
-        nname = unicodedata.normalize('NFKD', name).encode('ASCII', 'ignore')
-        nname = ''.join(c for c in nname if c in self.validchars)
-        return nname
-
-    def clear_creds(self):
-        self.cred_provider.clear_creds()
-
-    @property
-    def scenario_images(self):
-        """:return: a scenario with name and uuid of images"""
-        if not CONF.service_available.glance:
-            return []
-        if not hasattr(self, '_scenario_images'):
-            try:
-                images = self.compute_images_client.list_images()['images']
-                self._scenario_images = [
-                    (self._normalize_name(i['name']), dict(image_ref=i['id']))
-                    for i in images if re.search(self.image_pattern,
-                                                 str(i['name']))
-                ]
-            except Exception:
-                self._scenario_images = []
-        return self._scenario_images
-
-    @property
-    def scenario_flavors(self):
-        """:return: a scenario with name and uuid of flavors"""
-        if not hasattr(self, '_scenario_flavors'):
-            try:
-                flavors = self.flavors_client.list_flavors()['flavors']
-                self._scenario_flavors = [
-                    (self._normalize_name(f['name']), dict(flavor_ref=f['id']))
-                    for f in flavors if re.search(self.flavor_pattern,
-                                                  str(f['name']))
-                ]
-            except Exception:
-                self._scenario_flavors = []
-        return self._scenario_flavors
-
-
-def load_tests_input_scenario_utils(*args):
-    """Wrapper for testscenarios to set the scenarios
-
-    The purpose is to avoid running a getattr on the CONF object at import.
-    """
-
-    if getattr(args[0], 'suiteClass', None) is not None:
-        loader, standard_tests, pattern = args
-    else:
-        standard_tests, module, loader = args
-    output = None
-    scenario_utils = None
-    try:
-        scenario_utils = InputScenarioUtils()
-        scenario_flavor = scenario_utils.scenario_flavors
-        scenario_image = scenario_utils.scenario_images
-    except (exc_lib.InvalidCredentials, TypeError):
-        output = standard_tests
-    finally:
-        if scenario_utils:
-            scenario_utils.clear_creds()
-    if output is not None:
-        return output
-    for test in testtools.iterate_tests(standard_tests):
-        setattr(test, 'scenarios', testscenarios.multiply_scenarios(
-            scenario_image,
-            scenario_flavor))
-    return testscenarios.load_tests_apply_scenarios(*args)
diff --git a/tempest/services/image/v1/json/images_client.py b/tempest/services/image/v1/json/images_client.py
index 5680668..ed0a676 100644
--- a/tempest/services/image/v1/json/images_client.py
+++ b/tempest/services/image/v1/json/images_client.py
@@ -130,10 +130,6 @@
         if detail:
             url += '/detail'
 
-        properties = kwargs.pop('properties', {})
-        for key, value in six.iteritems(properties):
-            kwargs['property-%s' % key] = value
-
         if kwargs.get('changes_since'):
             kwargs['changes-since'] = kwargs.pop('changes_since')
 
diff --git a/tempest/services/volume/base/base_backups_client.py b/tempest/services/volume/base/base_backups_client.py
index 780da7b..3842d66 100644
--- a/tempest/services/volume/base/base_backups_client.py
+++ b/tempest/services/volume/base/base_backups_client.py
@@ -26,7 +26,11 @@
     """Client class to send CRUD Volume backup API requests"""
 
     def create_backup(self, **kwargs):
-        """Creates a backup of volume."""
+        """Creates a backup of volume.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html#createBackup
+        """
         post_body = json.dumps({'backup': kwargs})
         resp, body = self.post('backups', post_body)
         body = json.loads(body)
@@ -34,7 +38,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def restore_backup(self, backup_id, **kwargs):
-        """Restore volume from backup."""
+        """Restore volume from backup.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html#restoreBackup
+        """
         post_body = json.dumps({'restore': kwargs})
         resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
         body = json.loads(body)
diff --git a/tempest/services/volume/base/base_snapshots_client.py b/tempest/services/volume/base/base_snapshots_client.py
index 68503dd..da7bb01 100644
--- a/tempest/services/volume/base/base_snapshots_client.py
+++ b/tempest/services/volume/base/base_snapshots_client.py
@@ -60,7 +60,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def update_snapshot(self, snapshot_id, **kwargs):
-        """Updates a snapshot."""
+        """Updates a snapshot.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html#updateSnapshot
+        """
         put_body = json.dumps({'snapshot': kwargs})
         resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
         body = json.loads(body)
@@ -123,11 +127,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def update_snapshot_metadata(self, snapshot_id, **kwargs):
-        """Update metadata for the snapshot."""
-        # TODO(piyush): Current api-site doesn't contain this API description.
-        # After fixing the api-site, we need to fix here also for putting the
-        # link to api-site.
-        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1529063
+        """Update metadata for the snapshot.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html#updateSnapshotMetadata
+        """
         put_body = json.dumps(kwargs)
         url = "snapshots/%s/metadata" % str(snapshot_id)
         resp, body = self.put(url, put_body)
diff --git a/tempest/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
old mode 100644
new mode 100755
index a3a4eb6..d694c53
--- a/tempest/services/volume/base/base_volumes_client.py
+++ b/tempest/services/volume/base/base_volumes_client.py
@@ -96,7 +96,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def update_volume(self, volume_id, **kwargs):
-        """Updates the Specified Volume."""
+        """Updates the Specified Volume.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#updateVolume
+        """
         put_body = json.dumps({'volume': kwargs})
         resp, body = self.put('volumes/%s' % volume_id, put_body)
         body = json.loads(body)
@@ -119,7 +123,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def attach_volume(self, volume_id, **kwargs):
-        """Attaches a volume to a given instance on a given mountpoint."""
+        """Attaches a volume to a given instance on a given mountpoint.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#attachVolume
+        """
         post_body = json.dumps({'os-attach': kwargs})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
@@ -171,7 +179,11 @@
         return 'volume'
 
     def extend_volume(self, volume_id, **kwargs):
-        """Extend a volume."""
+        """Extend a volume.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#extendVolume
+        """
         post_body = json.dumps({'os-extend': kwargs})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
@@ -179,7 +191,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def reset_volume_status(self, volume_id, **kwargs):
-        """Reset the Specified Volume's Status."""
+        """Reset the Specified Volume's Status.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#resetVolume
+        """
         post_body = json.dumps({'os-reset_status': kwargs})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
         self.expected_success(202, resp.status)
@@ -202,7 +218,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def create_volume_transfer(self, **kwargs):
-        """Create a volume transfer."""
+        """Create a volume transfer.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#createVolumeTransfer
+        """
         post_body = json.dumps({'transfer': kwargs})
         resp, body = self.post('os-volume-transfer', post_body)
         body = json.loads(body)
@@ -218,7 +238,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def list_volume_transfers(self, **params):
-        """List all the volume transfers created."""
+        """List all the volume transfers created.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#listVolumeTransfer
+        """
         url = 'os-volume-transfer'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -234,7 +258,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def accept_volume_transfer(self, transfer_id, **kwargs):
-        """Accept a volume transfer."""
+        """Accept a volume transfer.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#acceptVolumeTransfer
+        """
         url = 'os-volume-transfer/%s/accept' % transfer_id
         post_body = json.dumps({'accept': kwargs})
         resp, body = self.post(url, post_body)
@@ -300,7 +328,12 @@
         return rest_client.ResponseBody(resp, body)
 
     def update_volume_image_metadata(self, volume_id, **kwargs):
-        """Update image metadata for the volume."""
+        """Update image metadata for the volume.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html
+                              #setVolumeimagemetadata
+        """
         post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
         url = "volumes/%s/action" % (volume_id)
         resp, body = self.post(url, post_body)