Merge "Fix update_host API response schema"
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index ffdd1de..e555fd9 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -26,17 +26,6 @@
     credentials = [['operator', CONF.object_storage.operator_role],
                    ['operator_alt', CONF.object_storage.operator_role]]
 
-    @classmethod
-    def setup_credentials(cls):
-        super(ObjectTestACLs, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
-        cls.os_operator = cls.os_roles_operator_alt
-
-    @classmethod
-    def resource_setup(cls):
-        super(ObjectTestACLs, cls).resource_setup()
-        cls.test_auth_data = cls.os_operator.auth_provider.auth_data
-
     def setUp(self):
         super(ObjectTestACLs, self).setUp()
         self.container_name = self.create_container()
@@ -49,24 +38,26 @@
     def test_read_object_with_rights(self):
         # attempt to read object using authorized user
         # update X-Container-Read metadata ACL
-        tenant_name = self.os_operator.credentials.tenant_name
-        username = self.os_operator.credentials.username
+        tenant_name = self.os_roles_operator_alt.credentials.tenant_name
+        username = self.os_roles_operator_alt.credentials.username
         cont_headers = {'X-Container-Read': tenant_name + ':' + username}
-        resp_meta, body = self.container_client.update_container_metadata(
-            self.container_name, metadata=cont_headers,
-            metadata_prefix='')
+        resp_meta, body = self.os_roles_operator.container_client.\
+            update_container_metadata(
+                self.container_name, metadata=cont_headers,
+                metadata_prefix='')
         self.assertHeaders(resp_meta, 'Container', 'POST')
         # create object
         object_name = data_utils.rand_name(name='Object')
-        resp, _ = self.object_client.create_object(self.container_name,
-                                                   object_name, 'data')
+        resp, _ = self.os_roles_operator.object_client.create_object(
+            self.container_name, object_name, 'data')
         self.assertHeaders(resp, 'Object', 'PUT')
-        # Trying to read the object with rights
-        self.object_client.auth_provider.set_alt_auth_data(
+        # set alternative authentication data; cannot simply use the
+        # other object client.
+        self.os_roles_operator.object_client.auth_provider.set_alt_auth_data(
             request_part='headers',
-            auth_data=self.test_auth_data
-        )
-        resp, _ = self.object_client.get_object(
+            auth_data=self.os_roles_operator_alt.object_client.auth_provider.
+            auth_data)
+        resp, _ = self.os_roles_operator.object_client.get_object(
             self.container_name, object_name)
         self.assertHeaders(resp, 'Object', 'GET')
 
@@ -74,20 +65,23 @@
     def test_write_object_with_rights(self):
         # attempt to write object using authorized user
         # update X-Container-Write metadata ACL
-        tenant_name = self.os_operator.credentials.tenant_name
-        username = self.os_operator.credentials.username
+        tenant_name = self.os_roles_operator_alt.credentials.tenant_name
+        username = self.os_roles_operator_alt.credentials.username
         cont_headers = {'X-Container-Write': tenant_name + ':' + username}
-        resp_meta, body = self.container_client.update_container_metadata(
-            self.container_name, metadata=cont_headers,
-            metadata_prefix='')
+        resp_meta, body = self.os_roles_operator.container_client.\
+            update_container_metadata(self.container_name,
+                                      metadata=cont_headers,
+                                      metadata_prefix='')
         self.assertHeaders(resp_meta, 'Container', 'POST')
-        # Trying to write the object with rights
-        self.object_client.auth_provider.set_alt_auth_data(
+        # set alternative authentication data; cannot simply use the
+        # other object client.
+        self.os_roles_operator.object_client.auth_provider.set_alt_auth_data(
             request_part='headers',
-            auth_data=self.test_auth_data
-        )
+            auth_data=self.os_roles_operator_alt.object_client.auth_provider.
+            auth_data)
+        # Trying to write the object with rights
         object_name = data_utils.rand_name(name='Object')
-        resp, _ = self.object_client.create_object(
+        resp, _ = self.os_roles_operator.object_client.create_object(
             self.container_name,
             object_name, 'data', headers={})
         self.assertHeaders(resp, 'Object', 'PUT')
diff --git a/tempest/api/volume/admin/test_volume_retype_with_migration.py b/tempest/api/volume/admin/test_volume_retype_with_migration.py
new file mode 100644
index 0000000..8a69ea3
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_retype_with_migration.py
@@ -0,0 +1,109 @@
+#    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 oslo_log import log as logging
+
+from tempest.api.volume import base
+from tempest.common import waiters
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+class VolumeRetypeWithMigrationV2Test(base.BaseVolumeAdminTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumeRetypeWithMigrationV2Test, cls).skip_checks()
+
+        if not CONF.volume_feature_enabled.multi_backend:
+            raise cls.skipException("Cinder multi-backend feature disabled.")
+
+        if len(set(CONF.volume.backend_names)) < 2:
+            raise cls.skipException("Requires at least two different "
+                                    "backend names")
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumeRetypeWithMigrationV2Test, cls).resource_setup()
+        # read backend name from a list.
+        cls.backend_src = CONF.volume.backend_names[0]
+        cls.backend_dst = CONF.volume.backend_names[1]
+
+        extra_specs_src = {"volume_backend_name": cls.backend_src}
+        extra_specs_dst = {"volume_backend_name": cls.backend_dst}
+
+        cls.src_vol_type = cls.create_volume_type(extra_specs=extra_specs_src)
+        cls.dst_vol_type = cls.create_volume_type(extra_specs=extra_specs_dst)
+
+        cls.src_vol = cls.create_volume(volume_type=cls.src_vol_type['name'])
+
+    @classmethod
+    def resource_cleanup(cls):
+        # When retyping a volume, Cinder creates an internal volume in the
+        # target backend. The volume in the source backend is deleted after
+        # the migration, so we need to wait for Cinder delete this volume
+        # before deleting the types we've created.
+
+        # This list should return 2 volumes until the copy and cleanup
+        # process is finished.
+        fetched_list = cls.admin_volume_client.list_volumes(
+            params={'all_tenants': True,
+                    'display_name': cls.src_vol['name']})['volumes']
+
+        for fetched_vol in fetched_list:
+            if fetched_vol['id'] != cls.src_vol['id']:
+                # This is the Cinder internal volume
+                LOG.debug('Waiting for internal volume %s deletion',
+                          fetched_vol['id'])
+                cls.admin_volume_client.wait_for_resource_deletion(
+                    fetched_vol['id'])
+                break
+
+        super(VolumeRetypeWithMigrationV2Test, cls).resource_cleanup()
+
+    @test.idempotent_id('a1a41f3f-9dad-493e-9f09-3ff197d477cd')
+    def test_available_volume_retype_with_migration(self):
+
+        keys_with_no_change = ('id', 'size', 'description', 'name', 'user_id',
+                               'os-vol-tenant-attr:tenant_id')
+        keys_with_change = ('volume_type', 'os-vol-host-attr:host')
+
+        volume_source = self.admin_volume_client.show_volume(
+            self.src_vol['id'])['volume']
+
+        # TODO(erlon): change this to volumes_client client after Bug
+        # #1657806 is fixed
+        self.admin_volume_client.retype_volume(
+            self.src_vol['id'],
+            new_type=self.dst_vol_type['name'],
+            migration_policy='on-demand')
+
+        waiters.wait_for_volume_retype(self.volumes_client, self.src_vol['id'],
+                                       self.dst_vol_type['name'])
+        volume_dest = self.admin_volume_client.show_volume(
+            self.src_vol['id'])['volume']
+
+        # Check the volume information after the migration.
+        self.assertEqual('success',
+                         volume_dest['os-vol-mig-status-attr:migstat'])
+        self.assertEqual('success', volume_dest['migration_status'])
+
+        for key in keys_with_no_change:
+            self.assertEqual(volume_source[key], volume_dest[key])
+
+        for key in keys_with_change:
+            self.assertNotEqual(volume_source[key], volume_dest[key])
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 6b2acc6..3d44bbe 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -42,7 +42,7 @@
         extra_specs = {"storage_protocol": proto,
                        "vendor_name": vendor}
         # Create two volume_types
-        for i in range(2):
+        for _ in range(2):
             vol_type = self.create_volume_type(
                 extra_specs=extra_specs)
             volume_types.append(vol_type)
diff --git a/tempest/api/volume/admin/v2/test_volumes_list.py b/tempest/api/volume/admin/v2/test_volumes_list.py
index cdd9df9..fd36d0a 100644
--- a/tempest/api/volume/admin/v2/test_volumes_list.py
+++ b/tempest/api/volume/admin/v2/test_volumes_list.py
@@ -32,7 +32,7 @@
         # NOTE(zhufl): When using pre-provisioned credentials, the project
         # may have volumes other than those created below.
         cls.volume_list = cls.volumes_client.list_volumes()['volumes']
-        for i in range(3):
+        for _ in range(3):
             volume = cls.create_volume()
             # Fetch volume details
             volume_details = cls.volumes_client.show_volume(
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 030ea6c..5e3f49f 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -61,7 +61,7 @@
         # Create 3 test volumes
         cls.volume_list = []
         cls.metadata = {'Type': 'work'}
-        for i in range(3):
+        for _ in range(3):
             volume = cls.create_volume(metadata=cls.metadata)
             volume = cls.volumes_client.show_volume(volume['id'])['volume']
             cls.volume_list.append(volume)
diff --git a/tempest/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
index 4416bef..b831252 100644
--- a/tempest/api/volume/test_volumes_snapshots_list.py
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -105,12 +105,6 @@
         # List returns zero elements
         self._list_snapshots_by_param_limit(limit=0, expected_elements=0)
 
-    def cleanup_snapshot(self, snapshot):
-        # Delete the snapshot
-        self.snapshots_client.delete_snapshot(snapshot['id'])
-        self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
-        self.snapshots.remove(snapshot)
-
 
 class VolumesV1SnapshotLimitTestJSON(VolumesV2SnapshotListTestJSON):
     _api_version = 1
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index fb8c65d..28ba941 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -42,7 +42,7 @@
         # may have volumes other than those created below.
         existing_volumes = cls.volumes_client.list_volumes()['volumes']
         cls.volume_id_list = [vol['id'] for vol in existing_volumes]
-        for i in range(3):
+        for _ in range(3):
             volume = cls.create_volume(metadata=cls.metadata)
             cls.volume_id_list.append(volume['id'])
 
diff --git a/tempest/lib/cmd/skip_tracker.py b/tempest/lib/cmd/skip_tracker.py
index d95aa46..07b811d 100755
--- a/tempest/lib/cmd/skip_tracker.py
+++ b/tempest/lib/cmd/skip_tracker.py
@@ -21,16 +21,18 @@
 """
 
 import argparse
-import logging
 import os
 import re
 
+from oslo_log import log as logging
+
 try:
     from launchpadlib import launchpad
 except ImportError:
     launchpad = None
 
 LPCACHEDIR = os.path.expanduser('~/.launchpadlib/cache')
+LOG = logging.getLogger(__name__)
 
 
 def parse_args():
@@ -40,11 +42,11 @@
 
 
 def info(msg, *args, **kwargs):
-    logging.info(msg, *args, **kwargs)
+    LOG.info(msg, *args, **kwargs)
 
 
 def debug(msg, *args, **kwargs):
-    logging.debug(msg, *args, **kwargs)
+    LOG.debug(msg, *args, **kwargs)
 
 
 def find_skips(start):
@@ -110,8 +112,6 @@
 
 
 def main():
-    logging.basicConfig(format='%(levelname)s: %(message)s',
-                        level=logging.INFO)
     parser = parse_args()
     results = find_skips(parser.test_path)
     unique_bugs = sorted(set([bug for (method, bug) in get_results(results)]))
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index 55f41a6..9735f77 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -20,23 +20,25 @@
 is fixed but a skip is still in the Tempest test code
 """
 
-import logging
 import os
 import re
 
 from launchpadlib import launchpad
+from oslo_log import log as logging
 
 BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
 TESTDIR = os.path.join(BASEDIR, 'tempest')
 LPCACHEDIR = os.path.expanduser('~/.launchpadlib/cache')
 
+LOG = logging.getLogger(__name__)
+
 
 def info(msg, *args, **kwargs):
-    logging.info(msg, *args, **kwargs)
+    LOG.info(msg, *args, **kwargs)
 
 
 def debug(msg, *args, **kwargs):
-    logging.debug(msg, *args, **kwargs)
+    LOG.debug(msg, *args, **kwargs)
 
 
 def find_skips(start=TESTDIR):
@@ -102,8 +104,6 @@
 
 
 if __name__ == '__main__':
-    logging.basicConfig(format='%(levelname)s: %(message)s',
-                        level=logging.INFO)
     results = find_skips()
     unique_bugs = sorted(set([bug for (method, bug) in get_results(results)]))
     unskips = []