Merge "Return complete resp from volumes_extensions_client"
diff --git a/.gitignore b/.gitignore
index f584532..efba45e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,4 +19,4 @@
 .coverage*
 !.coveragerc
 cover/
-doc/source/_static/tempest.conf
+doc/source/_static/tempest.conf.sample
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 3ec25ea..f85899b 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -15,18 +15,6 @@
 import os
 import subprocess
 
-# Build a tempest sample config file:
-def build_sample_config(app):
-    root_dir = os.path.dirname(
-        os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-    subprocess.call(["oslo-config-generator", "--config-file",
-                     "tools/config/config-generator.tempest.conf",
-                     "--output-file", "doc/source/_static/tempest.conf"],
-                    cwd=root_dir)
-
-def setup(app):
-    app.connect('builder-inited', build_sample_config)
-
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
@@ -42,9 +30,13 @@
 extensions = ['sphinx.ext.autodoc',
               'sphinx.ext.todo',
               'sphinx.ext.viewcode',
-              'oslosphinx'
+              'oslosphinx',
+              'oslo_config.sphinxconfiggen',
              ]
 
+config_generator_config_file = '../../tools/config/config-generator.tempest.conf'
+sample_config_basename = '_static/tempest'
+
 todo_include_todos = True
 
 # Add any paths that contain templates here, relative to this directory.
diff --git a/doc/source/sampleconf.rst b/doc/source/sampleconf.rst
index 2a72971..c290140 100644
--- a/doc/source/sampleconf.rst
+++ b/doc/source/sampleconf.rst
@@ -8,7 +8,6 @@
 if you are having issues with an option, please compare your version of
 Tempest with the version of this documentation.
 
-The sample configuration can also be viewed in `file form <_static/tempest.conf>`_.
+The sample configuration can also be viewed in `file form <_static/tempest.conf.sample>`_.
 
-.. include:: _static/tempest.conf
-   :code:
+.. literalinclude:: _static/tempest.conf.sample
diff --git a/requirements.txt b/requirements.txt
index 5ebcb65..b25b9d3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-pbr<2.0,>=1.4
+pbr<2.0,>=1.6
 cliff>=1.14.0 # Apache-2.0
 anyjson>=0.3.3
 httplib2>=0.7.5
@@ -9,11 +9,11 @@
 testtools>=1.4.0
 boto>=2.32.1
 paramiko>=1.13.0
-netaddr>=0.7.12,!=0.7.16
+netaddr!=0.7.16,>=0.7.12
 testrepository>=0.0.18
 pyOpenSSL>=0.14
 oslo.concurrency>=2.3.0 # Apache-2.0
-oslo.config>=2.1.0 # Apache-2.0
+oslo.config>=2.3.0 # Apache-2.0
 oslo.i18n>=1.5.0 # Apache-2.0
 oslo.log>=1.8.0 # Apache-2.0
 oslo.serialization>=1.4.0 # Apache-2.0
diff --git a/tempest/api_schema/response/compute/v2_1/versions.py b/tempest/api_schema/response/compute/v2_1/versions.py
index a01dd41..f08695c 100644
--- a/tempest/api_schema/response/compute/v2_1/versions.py
+++ b/tempest/api_schema/response/compute/v2_1/versions.py
@@ -12,6 +12,33 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+_version = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'links': {
+            'type': 'array',
+            'items': {
+                'type': 'object',
+                'properties': {
+                    'href': {'type': 'string', 'format': 'uri'},
+                    'rel': {'type': 'string'},
+                },
+                'required': ['href', 'rel'],
+                'additionalProperties': False
+            }
+        },
+        'status': {'type': 'string'},
+        'updated': {'type': 'string', 'format': 'date-time'},
+        'version': {'type': 'string'},
+        'min_version': {'type': 'string'}
+    },
+    # NOTE: version and min_version have been added since Kilo,
+    # so they should not be required.
+    'required': ['id', 'links', 'status', 'updated'],
+    'additionalProperties': False
+}
+
 list_versions = {
     'status_code': [200],
     'response_body': {
@@ -19,33 +46,7 @@
         'properties': {
             'versions': {
                 'type': 'array',
-                'items': {
-                    'type': 'object',
-                    'properties': {
-                        'id': {'type': 'string'},
-                        'links': {
-                            'type': 'array',
-                            'items': {
-                                'type': 'object',
-                                'properties': {
-                                    'href': {'type': 'string',
-                                             'format': 'uri'},
-                                    'rel': {'type': 'string'},
-                                },
-                                'required': ['href', 'rel'],
-                                'additionalProperties': False
-                            }
-                        },
-                        'status': {'type': 'string'},
-                        'updated': {'type': 'string', 'format': 'date-time'},
-                        'version': {'type': 'string'},
-                        'min_version': {'type': 'string'}
-                    },
-                    # NOTE: version and min_version have been added since Kilo,
-                    # so they should not be required.
-                    'required': ['id', 'links', 'status', 'updated'],
-                    'additionalProperties': False
-                }
+                'items': _version
             }
         },
         'required': ['versions'],
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 3d24547..49d5f8c 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -90,7 +90,7 @@
 import yaml
 
 from tempest import config
-from tempest import exceptions
+from tempest import exceptions as exc
 from tempest.services.identity.v2.json import identity_client
 from tempest.services.network.json import network_client
 import tempest_lib.auth
@@ -155,11 +155,10 @@
         for r in u.get('roles', ()):
             try:
                 role = filter(lambda r_: r_['name'] == r, roles)[0]
-                u['role_ids'] += [role['id']]
             except IndexError:
-                raise exceptions.TempestException(
-                    "Role: %s - doesn't exist" % r
-                )
+                msg = "Role: %s doesn't exist" % r
+                raise exc.InvalidConfiguration(msg)
+            u['role_ids'] += [role['id']]
     existing = [x['name'] for x in identity_admin.list_tenants()['tenants']]
     for tenant in resources['tenants']:
         if tenant not in existing:
@@ -285,17 +284,18 @@
             {'number': 1,
              'prefix': 'alt',
              'roles': (CONF.auth.tempest_roles +
-                       [CONF.object_storage.operator_role])},
-            {'number': 1,
-             'prefix': 'swift_admin',
-             'roles': (CONF.auth.tempest_roles +
-                       [CONF.object_storage.operator_role,
-                        CONF.object_storage.reseller_admin_role])},
-            {'number': 1,
-             'prefix': 'stack_owner',
-             'roles': (CONF.auth.tempest_roles +
-                       [CONF.orchestration.stack_owner_role])},
-            ]
+                       [CONF.object_storage.operator_role])}]
+    if CONF.service_available.swift:
+        spec.append({'number': 1,
+                     'prefix': 'swift_admin',
+                     'roles': (CONF.auth.tempest_roles +
+                               [CONF.object_storage.operator_role,
+                                CONF.object_storage.reseller_admin_role])})
+    if CONF.service_available.heat:
+        spec.append({'number': 1,
+                     'prefix': 'stack_owner',
+                     'roles': (CONF.auth.tempest_roles +
+                               [CONF.orchestration.stack_owner_role])})
     if opts.admin:
         spec.append({
             'number': 1,
diff --git a/tempest/config.py b/tempest/config.py
index 5721c27..7655c75 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -117,9 +117,7 @@
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
                help="The admin endpoint type to use for OpenStack Identity "
-                    "(Keystone) API v2",
-               deprecated_opts=[cfg.DeprecatedOpt('endpoint_type',
-                                                  group='identity')]),
+                    "(Keystone) API v2"),
     cfg.StrOpt('v2_public_endpoint_type',
                default='publicURL',
                choices=['public', 'admin', 'internal',
@@ -133,9 +131,7 @@
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
                help="The endpoint type to use for OpenStack Identity "
-                    "(Keystone) API v3",
-               deprecated_opts=[cfg.DeprecatedOpt('endpoint_type',
-                                                  group='identity')]),
+                    "(Keystone) API v3"),
     cfg.StrOpt('username',
                help="Username to use for Nova API requests."),
     cfg.StrOpt('tenant_name',
diff --git a/tempest/tests/fake_tempest_plugin.py b/tempest/tests/fake_tempest_plugin.py
new file mode 100644
index 0000000..f718d0b
--- /dev/null
+++ b/tempest/tests/fake_tempest_plugin.py
@@ -0,0 +1,40 @@
+# Copyright (c) 2015 Deutsche Telekom AG
+# 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.test_discover import plugins
+
+
+class FakePlugin(plugins.TempestPlugin):
+    expected_load_test = ["my/test/path", "/home/dir"]
+
+    def load_tests(self):
+        return self.expected_load_test
+
+    def register_opts(self, conf):
+        return
+
+    def get_opt_lists(self):
+        return []
+
+
+class FakeStevedoreObj(object):
+    obj = FakePlugin()
+
+    @property
+    def name(self):
+        return self._name
+
+    def __init__(self, name='Test1'):
+        self._name = name
diff --git a/tempest/tests/services/compute/test_baremetal_nodes_client.py b/tempest/tests/services/compute/test_baremetal_nodes_client.py
new file mode 100644
index 0000000..edf9014
--- /dev/null
+++ b/tempest/tests/services/compute/test_baremetal_nodes_client.py
@@ -0,0 +1,74 @@
+# Copyright 2015 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.
+
+import copy
+
+from tempest.services.compute.json import baremetal_nodes_client
+from tempest.tests import fake_auth_provider
+from tempest.tests.services.compute import base
+
+
+class TestBareMetalNodesClient(base.BaseComputeServiceTest):
+
+    FAKE_NODE_INFO = {'cpus': '8',
+                      'disk_gb': '64',
+                      'host': '10.0.2.15',
+                      'id': 'Identifier',
+                      'instance_uuid': "null",
+                      'interfaces': [
+                          {
+                              "address": "20::01",
+                              "datapath_id": "null",
+                              "id": 1,
+                              "port_no": None
+                          }
+                      ],
+                      'memory_mb': '8192',
+                      'task_state': None}
+
+    def setUp(self):
+        super(TestBareMetalNodesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.baremetal_nodes_client = (baremetal_nodes_client.
+                                       BaremetalNodesClient
+                                       (fake_auth, 'compute',
+                                        'regionOne'))
+
+    def _test_bareMetal_nodes(self, operation='list', bytes_body=False):
+        if operation != 'list':
+            expected = {"node": self.FAKE_NODE_INFO}
+            function = self.baremetal_nodes_client.show_baremetal_node
+        else:
+            node_info = copy.deepcopy(self.FAKE_NODE_INFO)
+            del node_info['instance_uuid']
+            expected = {"nodes": [node_info]}
+            function = self.baremetal_nodes_client.list_baremetal_nodes
+
+        self.check_service_client_function(
+            function,
+            'tempest.common.service_client.ServiceClient.get',
+            expected, bytes_body, 200,
+            baremetal_node_id='Identifier')
+
+    def test_list_bareMetal_nodes_with_str_body(self):
+        self._test_bareMetal_nodes()
+
+    def test_list_bareMetal_nodes_with_bytes_body(self):
+        self._test_bareMetal_nodes(bytes_body=True)
+
+    def test_show_bareMetal_node_with_str_body(self):
+        self._test_bareMetal_nodes('show')
+
+    def test_show_bareMetal_node_with_bytes_body(self):
+        self._test_bareMetal_nodes('show', True)
diff --git a/tempest/tests/services/compute/test_floating_ip_pools_client.py b/tempest/tests/services/compute/test_floating_ip_pools_client.py
new file mode 100644
index 0000000..0d19b7b
--- /dev/null
+++ b/tempest/tests/services/compute/test_floating_ip_pools_client.py
@@ -0,0 +1,47 @@
+# Copyright 2015 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.services.compute.json import floating_ip_pools_client
+from tempest.tests import fake_auth_provider
+from tempest.tests.services.compute import base
+
+
+class TestFloatingIPPoolsClient(base.BaseComputeServiceTest):
+
+    FAKE_FLOATING_IP_POOLS = {
+        "floating_ip_pools":
+        [
+            {"name": u'\u3042'},
+            {"name": u'\u3044'}
+        ]
+    }
+
+    def setUp(self):
+        super(TestFloatingIPPoolsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = floating_ip_pools_client.FloatingIPPoolsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def test_list_floating_ip_pools_with_str_body(self):
+        self.check_service_client_function(
+            self.client.list_floating_ip_pools,
+            'tempest.common.service_client.ServiceClient.get',
+            self.FAKE_FLOATING_IP_POOLS)
+
+    def test_list_floating_ip_pools_with_bytes_body(self):
+        self.check_service_client_function(
+            self.client.list_floating_ip_pools,
+            'tempest.common.service_client.ServiceClient.get',
+            self.FAKE_FLOATING_IP_POOLS, to_utf=True)
diff --git a/tempest/tests/services/compute/test_security_group_default_rules_client.py b/tempest/tests/services/compute/test_security_group_default_rules_client.py
new file mode 100644
index 0000000..75fa1cb
--- /dev/null
+++ b/tempest/tests/services/compute/test_security_group_default_rules_client.py
@@ -0,0 +1,88 @@
+# Copyright 2015 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.services.compute.json import security_group_default_rules_client
+from tempest.tests import fake_auth_provider
+from tempest.tests.services.compute import base
+
+
+class TestSecurityGroupDefaultRulesClient(base.BaseComputeServiceTest):
+    FAKE_RULE = {
+        "from_port": 80,
+        "id": 1,
+        "ip_protocol": "TCP",
+        "ip_range": {
+            "cidr": "10.10.10.0/24"
+        },
+        "to_port": 80
+    }
+
+    def setUp(self):
+        super(TestSecurityGroupDefaultRulesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = (security_group_default_rules_client.
+                       SecurityGroupDefaultRulesClient(fake_auth, 'compute',
+                                                       'regionOne'))
+
+    def _test_list_security_group_default_rules(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_security_group_default_rules,
+            'tempest.common.service_client.ServiceClient.get',
+            {"security_group_default_rules": [self.FAKE_RULE]},
+            to_utf=bytes_body)
+
+    def test_list_security_group_default_rules_with_str_body(self):
+        self._test_list_security_group_default_rules()
+
+    def test_list_security_group_default_rules_with_bytes_body(self):
+        self._test_list_security_group_default_rules(bytes_body=True)
+
+    def _test_show_security_group_default_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_security_group_default_rule,
+            'tempest.common.service_client.ServiceClient.get',
+            {"security_group_default_rule": self.FAKE_RULE},
+            to_utf=bytes_body,
+            security_group_default_rule_id=1)
+
+    def test_show_security_group_default_rule_with_str_body(self):
+        self._test_show_security_group_default_rule()
+
+    def test_show_security_group_default_rule_with_bytes_body(self):
+        self._test_show_security_group_default_rule(bytes_body=True)
+
+    def _test_create_security_default_group_rule(self, bytes_body=False):
+        request_body = {
+            "to_port": 80,
+            "from_port": 80,
+            "ip_protocol": "TCP",
+            "cidr": "10.10.10.0/24"
+        }
+        self.check_service_client_function(
+            self.client.create_security_default_group_rule,
+            'tempest.common.service_client.ServiceClient.post',
+            {"security_group_default_rule": self.FAKE_RULE},
+            to_utf=bytes_body, **request_body)
+
+    def test_create_security_default_group_rule_with_str_body(self):
+        self._test_create_security_default_group_rule()
+
+    def test_create_security_default_group_rule_with_bytes_body(self):
+        self._test_create_security_default_group_rule(bytes_body=True)
+
+    def test_delete_security_group_default_rule(self):
+        self.check_service_client_function(
+            self.client.delete_security_group_default_rule,
+            'tempest.common.service_client.ServiceClient.delete',
+            {}, status=204, security_group_default_rule_id=1)
diff --git a/tempest/tests/services/compute/test_security_groups_client.py b/tempest/tests/services/compute/test_security_groups_client.py
new file mode 100644
index 0000000..7a39048
--- /dev/null
+++ b/tempest/tests/services/compute/test_security_groups_client.py
@@ -0,0 +1,113 @@
+# Copyright 2015 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 oslotest import mockpatch
+from tempest_lib import exceptions as lib_exc
+
+from tempest.services.compute.json import security_groups_client
+from tempest.tests import fake_auth_provider
+from tempest.tests.services.compute import base
+
+
+class TestSecurityGroupsClient(base.BaseComputeServiceTest):
+
+    FAKE_SECURITY_GROUP_INFO = [{
+        "description": "default",
+        "id": "3fb26eb3-581b-4420-9963-b0879a026506",
+        "name": "default",
+        "rules": [],
+        "tenant_id": "openstack"
+    }]
+
+    def setUp(self):
+        super(TestSecurityGroupsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = security_groups_client.SecurityGroupsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_security_groups(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_security_groups,
+            'tempest.common.service_client.ServiceClient.get',
+            {"security_groups": self.FAKE_SECURITY_GROUP_INFO},
+            to_utf=bytes_body)
+
+    def test_list_security_groups_with_str_body(self):
+        self._test_list_security_groups()
+
+    def test_list_security_groups_with_bytes_body(self):
+        self._test_list_security_groups(bytes_body=True)
+
+    def _test_show_security_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_security_group,
+            'tempest.common.service_client.ServiceClient.get',
+            {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]},
+            to_utf=bytes_body,
+            security_group_id='fake-id')
+
+    def test_show_security_group_with_str_body(self):
+        self._test_show_security_group()
+
+    def test_show_security_group_with_bytes_body(self):
+        self._test_show_security_group(bytes_body=True)
+
+    def _test_create_security_group(self, bytes_body=False):
+        post_body = {"name": "test", "description": "test_group"}
+        self.check_service_client_function(
+            self.client.create_security_group,
+            'tempest.common.service_client.ServiceClient.post',
+            {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]},
+            to_utf=bytes_body,
+            kwargs=post_body)
+
+    def test_create_security_group_with_str_body(self):
+        self._test_create_security_group()
+
+    def test_create_security_group_with_bytes_body(self):
+        self._test_create_security_group(bytes_body=True)
+
+    def _test_update_security_group(self, bytes_body=False):
+        req_body = {"name": "test", "description": "test_group"}
+        self.check_service_client_function(
+            self.client.update_security_group,
+            'tempest.common.service_client.ServiceClient.put',
+            {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]},
+            to_utf=bytes_body,
+            security_group_id='fake-id',
+            kwargs=req_body)
+
+    def test_update_security_group_with_str_body(self):
+        self._test_update_security_group()
+
+    def test_update_security_group_with_bytes_body(self):
+        self._test_update_security_group(bytes_body=True)
+
+    def test_delete_security_group(self):
+        self.check_service_client_function(
+            self.client.delete_security_group,
+            'tempest.common.service_client.ServiceClient.delete',
+            {}, status=202, security_group_id='fake-id')
+
+    def test_is_resource_deleted_true(self):
+        mod = ('tempest.services.compute.json.security_groups_client.'
+               'SecurityGroupsClient.show_security_group')
+        self.useFixture(mockpatch.Patch(mod, side_effect=lib_exc.NotFound))
+        self.assertTrue(self.client.is_resource_deleted('fake-id'))
+
+    def test_is_resource_deleted_false(self):
+        mod = ('tempest.services.compute.json.security_groups_client.'
+               'SecurityGroupsClient.show_security_group')
+        self.useFixture(mockpatch.Patch(mod, return_value='success'))
+        self.assertFalse(self.client.is_resource_deleted('fake-id'))
diff --git a/tempest/tests/services/compute/test_services_client.py b/tempest/tests/services/compute/test_services_client.py
index fe63de9..e5a25ab 100644
--- a/tempest/tests/services/compute/test_services_client.py
+++ b/tempest/tests/services/compute/test_services_client.py
@@ -81,14 +81,14 @@
         fake_service["service"]["status"] = "disable"
 
         self.check_service_client_function(
-            self.client.enable_service,
+            self.client.disable_service,
             'tempest.common.service_client.ServiceClient.put',
             fake_service,
             bytes_body,
             host_name="nova-conductor", binary="controller")
 
     def test_disable_service_with_str_body(self):
-        self._test_enable_service()
+        self._test_disable_service()
 
     def test_disable_service_with_bytes_body(self):
-        self._test_enable_service(bytes_body=True)
+        self._test_disable_service(bytes_body=True)
diff --git a/tempest/tests/test_tempest_plugin.py b/tempest/tests/test_tempest_plugin.py
new file mode 100644
index 0000000..c07e98c
--- /dev/null
+++ b/tempest/tests/test_tempest_plugin.py
@@ -0,0 +1,44 @@
+# Copyright (c) 2015 Deutsche Telekom AG
+# 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.test_discover import plugins
+from tempest.tests import base
+from tempest.tests import fake_tempest_plugin as fake_plugin
+
+
+class TestPluginDiscovery(base.TestCase):
+    def test_load_tests_with_one_plugin(self):
+        # we can't mock stevedore since it's a singleton and already executed
+        # during test discovery. So basically this test covers the plugin loop
+        # and the abstract plugin interface.
+        manager = plugins.TempestTestPluginManager()
+        fake_obj = fake_plugin.FakeStevedoreObj()
+        manager.ext_plugins = [fake_obj]
+        result = manager.get_plugin_load_tests_tuple()
+
+        self.assertEqual(fake_plugin.FakePlugin.expected_load_test,
+                         result[fake_obj.name])
+
+    def test_load_tests_with_two_plugins(self):
+        manager = plugins.TempestTestPluginManager()
+        obj1 = fake_plugin.FakeStevedoreObj('fake01')
+        obj2 = fake_plugin.FakeStevedoreObj('fake02')
+        manager.ext_plugins = [obj1, obj2]
+        result = manager.get_plugin_load_tests_tuple()
+
+        self.assertEqual(fake_plugin.FakePlugin.expected_load_test,
+                         result['fake01'])
+        self.assertEqual(fake_plugin.FakePlugin.expected_load_test,
+                         result['fake02'])