Merge "Define v3 projects_client as library"
diff --git a/README.rst b/README.rst
index 13f4f61..3c0463b 100644
--- a/README.rst
+++ b/README.rst
@@ -124,6 +124,9 @@
 
 Release Versioning
 ------------------
+`Tempest Release Notes <http://docs.openstack.org/releasenotes/tempest>`_
+shows what changes have been released on each version.
+
 Tempest's released versions are broken into 2 sets of information. Depending on
 how you intend to consume tempest you might need
 
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 5ff5d58..db6e682 100755
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -34,6 +34,7 @@
         cls.client = cls.os_adm.servers_client
         cls.non_adm_client = cls.servers_client
         cls.flavors_client = cls.os_adm.flavors_client
+        cls.quotas_client = cls.os_adm.quotas_client
 
     @classmethod
     def resource_setup(cls):
@@ -64,8 +65,8 @@
         self.useFixture(fixtures.LockFixture('compute_quotas'))
         flavor_name = data_utils.rand_name("flavor")
         flavor_id = self._get_unused_flavor_id()
-        quota_set = (self.quotas_client.show_default_quota_set(self.tenant_id)
-                     ['quota_set'])
+        quota_set = self.quotas_client.show_quota_set(
+            self.tenant_id)['quota_set']
         ram = int(quota_set['ram'])
         if ram == -1:
             raise self.skipException("default ram quota set is -1,"
@@ -93,8 +94,8 @@
         flavor_name = data_utils.rand_name("flavor")
         flavor_id = self._get_unused_flavor_id()
         ram = 512
-        quota_set = (self.quotas_client.show_default_quota_set(self.tenant_id)
-                     ['quota_set'])
+        quota_set = self.quotas_client.show_quota_set(
+            self.tenant_id)['quota_set']
         vcpus = int(quota_set['cores'])
         if vcpus == -1:
             raise self.skipException("default cores quota set is -1,"
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index e8df52d..78f0db4 100755
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -139,7 +139,7 @@
         hostname = linux_client.get_hostname()
         msg = ('Failed while verifying servername equals hostname. Expected '
                'hostname "%s" but got "%s".' % (self.name, hostname))
-        self.assertEqual(self.name, hostname, msg)
+        self.assertEqual(self.name.lower(), hostname, msg)
 
     @test.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
     def test_create_server_with_scheduler_hint_group(self):
diff --git a/tempest/api/volume/test_volumes_clone.py b/tempest/api/volume/test_volumes_clone.py
index f38a068..7529dc2 100644
--- a/tempest/api/volume/test_volumes_clone.py
+++ b/tempest/api/volume/test_volumes_clone.py
@@ -23,6 +23,12 @@
 
 class VolumesCloneTest(base.BaseVolumeTest):
 
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesCloneTest, cls).skip_checks()
+        if not CONF.volume_feature_enabled.clone:
+            raise cls.skipException("Cinder volume clones are disabled")
+
     @test.idempotent_id('9adae371-a257-43a5-9555-dc7c88e66e0e')
     def test_create_from_volume(self):
         # Creates a volume from another volume passing a size different from
diff --git a/tempest/api/volume/test_volumes_clone_negative.py b/tempest/api/volume/test_volumes_clone_negative.py
index ee51e00..d1bedb4 100644
--- a/tempest/api/volume/test_volumes_clone_negative.py
+++ b/tempest/api/volume/test_volumes_clone_negative.py
@@ -24,6 +24,12 @@
 
 class VolumesCloneTest(base.BaseVolumeTest):
 
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesCloneTest, cls).skip_checks()
+        if not CONF.volume_feature_enabled.clone:
+            raise cls.skipException("Cinder volume clones are disabled")
+
     @test.idempotent_id('9adae371-a257-43a5-459a-dc7c88e66e0e')
     def test_create_from_volume_decreasing_size(self):
         # Creates a volume from another volume passing a size different from
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index c9b9db1..04c9645 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -401,9 +401,18 @@
             except lib_exc.NotFound:
                 LOG.warning("user with name: %s not found for delete" %
                             creds.username)
+            # NOTE(zhufl): Only when neutron's security_group ext is
+            # enabled, _cleanup_default_secgroup will not raise error. But
+            # here cannot use test.is_extension_enabled for it will cause
+            # "circular dependency". So here just use try...except to
+            # ensure tenant deletion without big changes.
             try:
                 if CONF.service_available.neutron:
                     self._cleanup_default_secgroup(creds.tenant_id)
+            except lib_exc.NotFound:
+                LOG.warning("failed to cleanup tenant %s's secgroup" %
+                            creds.tenant_name)
+            try:
                 self.creds_client.delete_project(creds.tenant_id)
             except lib_exc.NotFound:
                 LOG.warning("tenant with name: %s not found for delete" %
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 1857a43..83aa405 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -689,6 +689,10 @@
         """Credentials are equal if attributes in self.ATTRIBUTES are equal"""
         return str(self) == str(other)
 
+    def __ne__(self, other):
+        """Contrary to the __eq__"""
+        return not self.__eq__(other)
+
     def __getattr__(self, key):
         # If an attribute is set, __getattr__ is not invoked
         # If an attribute is not set, and it is a known one, return None
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 349f0d3..8507f8a 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -235,8 +235,8 @@
             raise TypeError("'read_code' must be an int instead of (%s)"
                             % type(read_code))
 
-        assert_msg = ("This function only allowed to use for HTTP status"
-                      "codes which explicitly defined in the RFC 7231 & 4918."
+        assert_msg = ("This function only allowed to use for HTTP status "
+                      "codes which explicitly defined in the RFC 7231 & 4918. "
                       "{0} is not a defined Success Code!"
                       ).format(expected_code)
         if isinstance(expected_code, list):
diff --git a/tempest/lib/common/ssh.py b/tempest/lib/common/ssh.py
index a831dbd..8e83775 100644
--- a/tempest/lib/common/ssh.py
+++ b/tempest/lib/common/ssh.py
@@ -121,7 +121,6 @@
             channel.fileno()  # Register event pipe
             channel.exec_command(cmd)
             channel.shutdown_write()
-            exit_status = channel.recv_exit_status()
 
             # If the executing host is linux-based, poll the channel
             if self._can_system_poll():
@@ -162,6 +161,8 @@
                 out_data = out_data.decode(encoding)
                 err_data = err_data.decode(encoding)
 
+            exit_status = channel.recv_exit_status()
+
             if 0 != exit_status:
                 raise exceptions.SSHExecCommandFailed(
                     command=cmd, exit_status=exit_status,
diff --git a/tempest/scenario/test_baremetal_basic_ops.py b/tempest/scenario/test_baremetal_basic_ops.py
index c060c61..45c38f6 100644
--- a/tempest/scenario/test_baremetal_basic_ops.py
+++ b/tempest/scenario/test_baremetal_basic_ops.py
@@ -22,7 +22,7 @@
 
 
 class BaremetalBasicOps(manager.BaremetalScenarioTest):
-    """This smoke test tests the pxe_ssh Ironic driver.
+    """This test tests the pxe_ssh Ironic driver.
 
     It follows this basic set of operations:
         * Creates a keypair
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index e4b699e..a89147a 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -58,7 +58,7 @@
             security_groups = [{'name': security_group['name']}]
         network, subnet, router = self.create_networks()
         public_network_id = CONF.network.public_network_id
-        server_name = data_utils.rand_name('server-smoke')
+        server_name = data_utils.rand_name('server')
         server = self.create_server(
             name=server_name,
             networks=[{'uuid': network['id']}],
diff --git a/tempest/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
index 273dbbb..8130089 100755
--- a/tempest/services/volume/base/base_volumes_client.py
+++ b/tempest/services/volume/base/base_volumes_client.py
@@ -218,22 +218,6 @@
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def volume_begin_detaching(self, volume_id):
-        """Volume Begin Detaching."""
-        # ref cinder/api/contrib/volume_actions.py#L158
-        post_body = json.dumps({'os-begin_detaching': {}})
-        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def volume_roll_detaching(self, volume_id):
-        """Volume Roll Detaching."""
-        # cinder/api/contrib/volume_actions.py#L170
-        post_body = json.dumps({'os-roll_detaching': {}})
-        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
     def create_volume_transfer(self, **kwargs):
         """Create a volume transfer.