Merge "Correct reraising of exception"
diff --git a/manila_tempest_tests/common/constants.py b/manila_tempest_tests/common/constants.py
index bef35a5..abef181 100644
--- a/manila_tempest_tests/common/constants.py
+++ b/manila_tempest_tests/common/constants.py
@@ -34,3 +34,19 @@
 RULE_STATE_ACTIVE = 'active'
 RULE_STATE_OUT_OF_SYNC = 'out_of_sync'
 RULE_STATE_ERROR = 'error'
+
+TASK_STATE_MIGRATION_STARTING = 'migration_starting'
+TASK_STATE_MIGRATION_IN_PROGRESS = 'migration_in_progress'
+TASK_STATE_MIGRATION_COMPLETING = 'migration_completing'
+TASK_STATE_MIGRATION_SUCCESS = 'migration_success'
+TASK_STATE_MIGRATION_ERROR = 'migration_error'
+TASK_STATE_MIGRATION_CANCELLED = 'migration_cancelled'
+TASK_STATE_MIGRATION_DRIVER_STARTING = 'migration_driver_starting'
+TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS = 'migration_driver_in_progress'
+TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE = 'migration_driver_phase1_done'
+TASK_STATE_DATA_COPYING_STARTING = 'data_copying_starting'
+TASK_STATE_DATA_COPYING_IN_PROGRESS = 'data_copying_in_progress'
+TASK_STATE_DATA_COPYING_COMPLETING = 'data_copying_completing'
+TASK_STATE_DATA_COPYING_COMPLETED = 'data_copying_completed'
+TASK_STATE_DATA_COPYING_CANCELLED = 'data_copying_cancelled'
+TASK_STATE_DATA_COPYING_ERROR = 'data_copying_error'
diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py
index 4203a71..f0c8dc7 100644
--- a/manila_tempest_tests/config.py
+++ b/manila_tempest_tests/config.py
@@ -34,7 +34,7 @@
                help="The minimum api microversion is configured to be the "
                     "value of the minimum microversion supported by Manila."),
     cfg.StrOpt("max_api_microversion",
-               default="2.21",
+               default="2.22",
                help="The maximum api microversion is configured to be the "
                     "value of the latest microversion supported by Manila."),
     cfg.StrOpt("region",
@@ -168,9 +168,19 @@
                 help="Defines whether to run replication tests or not. "
                      "Enable this feature if the driver is configured "
                      "for replication."),
-    cfg.BoolOpt("run_migration_tests",
+    cfg.BoolOpt("run_multiple_share_replicas_tests",
+                default=True,
+                help="Defines whether to run multiple replicas creation test "
+                     "or not. Enable this if the driver can create more than "
+                     "one replica for a share."),
+    cfg.BoolOpt("run_host_assisted_migration_tests",
+                deprecated_name="run_migration_tests",
                 default=False,
-                help="Enable or disable migration tests."),
+                help="Enable or disable host-assisted migration tests."),
+    cfg.BoolOpt("run_driver_assisted_migration_tests",
+                deprecated_name="run_migration_tests",
+                default=False,
+                help="Enable or disable driver-assisted migration tests."),
     cfg.BoolOpt("run_manage_unmanage_tests",
                 default=False,
                 help="Defines whether to run manage/unmanage tests or not. "
diff --git a/manila_tempest_tests/services/share/v2/json/shares_client.py b/manila_tempest_tests/services/share/v2/json/shares_client.py
index 46c0ce7..0ccc18b 100755
--- a/manila_tempest_tests/services/share/v2/json/shares_client.py
+++ b/manila_tempest_tests/services/share/v2/json/shares_client.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 import json
+import six
 import time
 
 from six.moves.urllib import parse as urlparse
@@ -688,8 +689,11 @@
 
 ###############
 
-    def list_share_types(self, params=None, version=LATEST_MICROVERSION):
+    def list_share_types(self, params=None, default=False,
+                         version=LATEST_MICROVERSION):
         uri = 'types'
+        if default:
+            uri += '/default'
         if params is not None:
             uri += '?%s' % urlparse.urlencode(params)
         resp, body = self.get(uri, version=version)
@@ -1013,22 +1017,25 @@
 
 ###############
 
-    def migrate_share(self, share_id, host, notify,
-                      version=LATEST_MICROVERSION, action_name=None):
-        if action_name is None:
-            if utils.is_microversion_lt(version, "2.7"):
-                action_name = 'os-migrate_share'
-            elif utils.is_microversion_lt(version, "2.15"):
-                action_name = 'migrate_share'
-            else:
-                action_name = 'migration_start'
-        post_body = {
-            action_name: {
+    def migrate_share(self, share_id, host,
+                      force_host_assisted_migration=False,
+                      new_share_network_id=None, writable=False,
+                      preserve_metadata=False, nondisruptive=False,
+                      new_share_type_id=None, version=LATEST_MICROVERSION):
+
+        body = {
+            'migration_start': {
                 'host': host,
-                'notify': notify,
+                'force_host_assisted_migration': force_host_assisted_migration,
+                'new_share_network_id': new_share_network_id,
+                'new_share_type_id': new_share_type_id,
+                'writable': writable,
+                'preserve_metadata': preserve_metadata,
+                'nondisruptive': nondisruptive,
             }
         }
-        body = json.dumps(post_body)
+
+        body = json.dumps(body)
         return self.post('shares/%s/action' % share_id, body,
                          headers=EXPERIMENTAL, extra_headers=True,
                          version=version)
@@ -1059,9 +1066,10 @@
             action_name: None,
         }
         body = json.dumps(post_body)
-        return self.post('shares/%s/action' % share_id, body,
-                         headers=EXPERIMENTAL, extra_headers=True,
-                         version=version)
+        result = self.post('shares/%s/action' % share_id, body,
+                           headers=EXPERIMENTAL, extra_headers=True,
+                           version=version)
+        return json.loads(result[1])
 
     def reset_task_state(
             self, share_id, task_state, version=LATEST_MICROVERSION,
@@ -1076,22 +1084,25 @@
                          headers=EXPERIMENTAL, extra_headers=True,
                          version=version)
 
-    def wait_for_migration_status(self, share_id, dest_host, status,
+    def wait_for_migration_status(self, share_id, dest_host, status_to_wait,
                                   version=LATEST_MICROVERSION):
         """Waits for a share to migrate to a certain host."""
+        statuses = ((status_to_wait,)
+                    if not isinstance(status_to_wait, (tuple, list, set))
+                    else status_to_wait)
         share = self.get_share(share_id, version=version)
         migration_timeout = CONF.share.migration_timeout
         start = int(time.time())
-        while share['task_state'] != status:
+        while share['task_state'] not in statuses:
             time.sleep(self.build_interval)
             share = self.get_share(share_id, version=version)
-            if share['task_state'] == status:
-                return share
+            if share['task_state'] in statuses:
+                break
             elif share['task_state'] == 'migration_error':
                 raise share_exceptions.ShareMigrationException(
                     share_id=share['id'], src=share['host'], dest=dest_host)
             elif int(time.time()) - start >= migration_timeout:
-                message = ('Share %(share_id)s failed to reach status '
+                message = ('Share %(share_id)s failed to reach a status in'
                            '%(status)s when migrating from host %(src)s to '
                            'host %(dest)s within the required time '
                            '%(timeout)s.' % {
@@ -1099,9 +1110,10 @@
                                'dest': dest_host,
                                'share_id': share['id'],
                                'timeout': self.build_timeout,
-                               'status': status,
+                               'status': six.text_type(statuses),
                            })
                 raise exceptions.TimeoutException(message)
+        return share
 
 ################
 
diff --git a/manila_tempest_tests/tests/api/admin/test_admin_actions.py b/manila_tempest_tests/tests/api/admin/test_admin_actions.py
index 40b2460..ac862d4 100644
--- a/manila_tempest_tests/tests/api/admin/test_admin_actions.py
+++ b/manila_tempest_tests/tests/api/admin/test_admin_actions.py
@@ -30,7 +30,7 @@
         super(AdminActionsTest, cls).resource_setup()
         cls.states = ["error", "available"]
         cls.task_states = ["migration_starting", "data_copying_in_progress",
-                           "migration_success"]
+                           "migration_success", None]
         cls.bad_status = "error_deleting"
         cls.sh = cls.create_share()
         cls.sh_instance = (
@@ -120,7 +120,7 @@
         self.shares_v2_client.wait_for_resource_deletion(snapshot_id=sn["id"])
 
     @test.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
-    @base.skip_if_microversion_lt("2.15")
+    @base.skip_if_microversion_lt("2.22")
     def test_reset_share_task_state(self):
         for task_state in self.task_states:
             self.shares_v2_client.reset_task_state(self.sh["id"], task_state)
diff --git a/manila_tempest_tests/tests/api/admin/test_admin_actions_negative.py b/manila_tempest_tests/tests/api/admin/test_admin_actions_negative.py
index 67444de..735c65d 100644
--- a/manila_tempest_tests/tests/api/admin/test_admin_actions_negative.py
+++ b/manila_tempest_tests/tests/api/admin/test_admin_actions_negative.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import ddt
 from tempest import config
 from tempest.lib import exceptions as lib_exc
 from tempest import test
@@ -124,20 +125,14 @@
                           self.sh['id'])
 
     @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
-    @base.skip_if_microversion_lt("2.15")
-    def test_reset_task_state_empty(self):
-        self.assertRaises(
-            lib_exc.BadRequest, self.admin_client.reset_task_state,
-            self.sh['id'], None)
-
-    @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
-    @base.skip_if_microversion_lt("2.15")
+    @base.skip_if_microversion_lt("2.22")
     def test_reset_task_state_invalid_state(self):
         self.assertRaises(
             lib_exc.BadRequest, self.admin_client.reset_task_state,
             self.sh['id'], 'fake_state')
 
 
+@ddt.ddt
 class AdminActionsAPIOnlyNegativeTest(base.BaseSharesMixedTest):
 
     @classmethod
@@ -153,7 +148,7 @@
                           self.member_client.list_share_instances)
 
     @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API])
-    @base.skip_if_microversion_lt("2.15")
+    @base.skip_if_microversion_lt("2.22")
     def test_reset_task_state_share_not_found(self):
         self.assertRaises(
             lib_exc.NotFound, self.admin_client.reset_task_state,
@@ -196,3 +191,20 @@
     def test_reset_nonexistent_snapshot_state(self):
         self.assertRaises(lib_exc.NotFound, self.admin_client.reset_state,
                           "fake", s_type="snapshots")
+
+    @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API])
+    @ddt.data('migrate_share', 'migration_complete', 'reset_task_state',
+              'migration_get_progress', 'migration_cancel')
+    def test_migration_API_invalid_microversion(self, method_name):
+        if method_name == 'migrate_share':
+            self.assertRaises(
+                lib_exc.NotFound, getattr(self.shares_v2_client, method_name),
+                'fake_share', 'fake_host', version='2.21')
+        elif method_name == 'reset_task_state':
+            self.assertRaises(
+                lib_exc.NotFound, getattr(self.shares_v2_client, method_name),
+                'fake_share', 'fake_task_state', version='2.21')
+        else:
+            self.assertRaises(
+                lib_exc.NotFound, getattr(self.shares_v2_client, method_name),
+                'fake_share', version='2.21')
diff --git a/manila_tempest_tests/tests/api/admin/test_export_locations.py b/manila_tempest_tests/tests/api/admin/test_export_locations.py
index d985d90..97db819 100644
--- a/manila_tempest_tests/tests/api/admin/test_export_locations.py
+++ b/manila_tempest_tests/tests/api/admin/test_export_locations.py
@@ -80,8 +80,8 @@
 
             # Check the format of ever-present summary keys
             self.assertTrue(uuidutils.is_uuid_like(export_location['id']))
-            self.assertTrue(isinstance(export_location['path'],
-                                       six.string_types))
+            self.assertIsInstance(export_location['path'],
+                                  six.string_types)
 
             if utils.is_microversion_ge(version, '2.14'):
                 self.assertIn(export_location['preferred'], (True, False))
diff --git a/manila_tempest_tests/tests/api/admin/test_migration.py b/manila_tempest_tests/tests/api/admin/test_migration.py
index a5ceaa4..0f55598 100644
--- a/manila_tempest_tests/tests/api/admin/test_migration.py
+++ b/manila_tempest_tests/tests/api/admin/test_migration.py
@@ -13,19 +13,41 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import six
+
+import ddt
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest import test
 
+from manila_tempest_tests.common import constants
 from manila_tempest_tests.tests.api import base
 from manila_tempest_tests import utils
 
 CONF = config.CONF
 
 
+@ddt.ddt
 class MigrationNFSTest(base.BaseSharesAdminTest):
-    """Tests Share Migration.
+    """Tests Share Migration for NFS shares.
 
-    Tests migration in multi-backend environment.
+    Tests share migration in multi-backend environment.
+
+    This class covers:
+    1) Driver-assisted migration: force_host_assisted_migration, nondisruptive,
+    writable and preserve-metadata are False.
+    2) Host-assisted migration: force_host_assisted_migration is True,
+    nondisruptive, writable and preserve-metadata are False.
+    3) 2-phase migration of both Host-assisted and Driver-assisted.
+
+    No need to test with writable, preserve-metadata and non-disruptive as
+    True, values are supplied to the driver which decides what to do. Test
+    should be positive, so not being writable, not preserving metadata and
+    being disruptive is less restrictive for drivers, which would abort if they
+    cannot handle them.
+
+    Drivers that implement driver-assisted migration should enable the
+    configuration flag to be tested.
     """
 
     protocol = "nfs"
@@ -34,60 +56,116 @@
     def resource_setup(cls):
         super(MigrationNFSTest, cls).resource_setup()
         if cls.protocol not in CONF.share.enable_protocols:
-            message = "%s tests are disabled" % cls.protocol
+            message = "%s tests are disabled." % cls.protocol
             raise cls.skipException(message)
-        if not CONF.share.run_migration_tests:
-            raise cls.skipException("Migration tests disabled. Skipping.")
+        if not (CONF.share.run_host_assisted_migration_tests or
+                CONF.share.run_driver_assisted_migration_tests):
+            raise cls.skipException("Share migration tests are disabled.")
+
+        extra_specs = {
+            'storage_protocol': CONF.share.capability_storage_protocol,
+            'driver_handles_share_servers': (
+                CONF.share.multitenancy_enabled),
+            'snapshot_support': six.text_type(
+                CONF.share.capability_snapshot_support),
+        }
+        cls.new_type = cls.create_share_type(
+            name=data_utils.rand_name('new_share_type_for_migration'),
+            cleanup_in_class=True,
+            extra_specs=extra_specs)
 
     @test.attr(type=[base.TAG_POSITIVE, base.TAG_BACKEND])
-    @base.skip_if_microversion_lt("2.5")
-    def test_migration_empty_v2_5(self):
+    @base.skip_if_microversion_lt("2.22")
+    @ddt.data(True, False)
+    def test_migration_cancel(self, force_host_assisted):
 
-        share, dest_pool = self._setup_migration()
-
-        old_exports = share['export_locations']
-
-        share = self.migrate_share(share['id'], dest_pool, version='2.5')
-
-        self._validate_migration_successful(dest_pool, share, old_exports,
-                                            version='2.5')
-
-    @test.attr(type=[base.TAG_POSITIVE, base.TAG_BACKEND])
-    @base.skip_if_microversion_lt("2.15")
-    def test_migration_completion_empty_v2_15(self):
+        self._check_migration_enabled(force_host_assisted)
 
         share, dest_pool = self._setup_migration()
 
         old_exports = self.shares_v2_client.list_share_export_locations(
-            share['id'], version='2.15')
+            share['id'])
         self.assertNotEmpty(old_exports)
         old_exports = [x['path'] for x in old_exports
                        if x['is_admin_only'] is False]
         self.assertNotEmpty(old_exports)
 
+        task_state = (constants.TASK_STATE_DATA_COPYING_COMPLETED
+                      if force_host_assisted
+                      else constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE)
+
         share = self.migrate_share(
-            share['id'], dest_pool, version='2.15', notify=False,
-            wait_for_status='data_copying_completed')
+            share['id'], dest_pool, wait_for_status=task_state,
+            force_host_assisted_migration=force_host_assisted)
 
-        self._validate_migration_successful(dest_pool, share,
-                                            old_exports, '2.15', notify=False)
+        self._validate_migration_successful(
+            dest_pool, share, task_state, complete=False)
 
-        share = self.migration_complete(share['id'], dest_pool, version='2.15')
+        share = self.migration_cancel(share['id'], dest_pool)
 
-        self._validate_migration_successful(dest_pool, share, old_exports,
-                                            version='2.15')
+        self._validate_migration_successful(
+            dest_pool, share, constants.TASK_STATE_MIGRATION_CANCELLED,
+            complete=False)
+
+    @test.attr(type=[base.TAG_POSITIVE, base.TAG_BACKEND])
+    @base.skip_if_microversion_lt("2.22")
+    @ddt.data(True, False)
+    def test_migration_2phase(self, force_host_assisted):
+
+        self._check_migration_enabled(force_host_assisted)
+
+        share, dest_pool = self._setup_migration()
+
+        old_exports = self.shares_v2_client.list_share_export_locations(
+            share['id'])
+        self.assertNotEmpty(old_exports)
+        old_exports = [x['path'] for x in old_exports
+                       if x['is_admin_only'] is False]
+        self.assertNotEmpty(old_exports)
+
+        task_state = (constants.TASK_STATE_DATA_COPYING_COMPLETED
+                      if force_host_assisted
+                      else constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE)
+
+        old_share_network_id = share['share_network_id']
+        new_share_network_id = self._create_secondary_share_network(
+            old_share_network_id)
+        old_share_type_id = share['share_type']
+        new_share_type_id = self.new_type['share_type']['id']
+
+        share = self.migrate_share(
+            share['id'], dest_pool,
+            force_host_assisted_migration=force_host_assisted,
+            wait_for_status=task_state, new_share_type_id=new_share_type_id,
+            new_share_network_id=new_share_network_id)
+
+        self._validate_migration_successful(
+            dest_pool, share, task_state, complete=False,
+            share_network_id=old_share_network_id,
+            share_type_id=old_share_type_id)
+
+        progress = self.shares_v2_client.migration_get_progress(share['id'])
+
+        self.assertEqual(task_state, progress['task_state'])
+        self.assertEqual(100, progress['total_progress'])
+
+        share = self.migration_complete(share['id'], dest_pool)
+
+        self._validate_migration_successful(
+            dest_pool, share, constants.TASK_STATE_MIGRATION_SUCCESS,
+            complete=True, share_network_id=new_share_network_id,
+            share_type_id=new_share_type_id)
 
     def _setup_migration(self):
 
-        pools = self.shares_client.list_pools()['pools']
+        pools = self.shares_v2_client.list_pools(detail=True)['pools']
 
         if len(pools) < 2:
-            raise self.skipException("At least two different pool entries "
-                                     "are needed to run migration tests. "
-                                     "Skipping.")
+            raise self.skipException("At least two different pool entries are "
+                                     "needed to run share migration tests.")
 
         share = self.create_share(self.protocol)
-        share = self.shares_client.get_share(share['id'])
+        share = self.shares_v2_client.get_share(share['id'])
 
         self.shares_v2_client.create_access_rule(
             share['id'], access_to="50.50.50.50", access_level="rw")
@@ -101,8 +179,10 @@
         self.shares_v2_client.wait_for_share_status(
             share['id'], 'active', status_attr='access_rules_status')
 
-        dest_pool = next((x for x in pools if x['name'] != share['host']),
-                         None)
+        default_type = self.shares_v2_client.list_share_types(
+            default=True)['share_type']
+
+        dest_pool = utils.choose_matching_backend(share, pools, default_type)
 
         self.assertIsNotNone(dest_pool)
         self.assertIsNotNone(dest_pool.get('name'))
@@ -111,28 +191,62 @@
 
         return share, dest_pool
 
-    def _validate_migration_successful(self, dest_pool, share,
-                                       old_exports, version, notify=True):
-        if utils.is_microversion_lt(version, '2.9'):
-            new_exports = share['export_locations']
-            self.assertNotEmpty(new_exports)
-        else:
-            new_exports = self.shares_v2_client.list_share_export_locations(
-                share['id'], version='2.9')
-            self.assertNotEmpty(new_exports)
-            new_exports = [x['path'] for x in new_exports if
-                           x['is_admin_only'] is False]
-            self.assertNotEmpty(new_exports)
+    def _validate_migration_successful(self, dest_pool, share, status_to_wait,
+                                       version=CONF.share.max_api_microversion,
+                                       complete=True, share_network_id=None,
+                                       share_type_id=None):
+
+        statuses = ((status_to_wait,)
+                    if not isinstance(status_to_wait, (tuple, list, set))
+                    else status_to_wait)
+
+        new_exports = self.shares_v2_client.list_share_export_locations(
+            share['id'], version=version)
+        self.assertNotEmpty(new_exports)
+        new_exports = [x['path'] for x in new_exports if
+                       x['is_admin_only'] is False]
+        self.assertNotEmpty(new_exports)
+
+        self.assertIn(share['task_state'], statuses)
+        if share_network_id:
+            self.assertEqual(share_network_id, share['share_network_id'])
+        if share_type_id:
+            self.assertEqual(share_type_id, share['share_type'])
 
         # Share migrated
-        if notify:
+        if complete:
             self.assertEqual(dest_pool, share['host'])
-            for export in old_exports:
-                self.assertFalse(export in new_exports)
-            self.assertEqual('migration_success', share['task_state'])
+            self.shares_v2_client.delete_share(share['id'])
+            self.shares_v2_client.wait_for_resource_deletion(
+                share_id=share['id'])
         # Share not migrated yet
         else:
             self.assertNotEqual(dest_pool, share['host'])
-            for export in old_exports:
-                self.assertTrue(export in new_exports)
-            self.assertEqual('data_copying_completed', share['task_state'])
+
+    def _check_migration_enabled(self, force_host_assisted):
+
+        if force_host_assisted:
+            if not CONF.share.run_host_assisted_migration_tests:
+                raise self.skipException(
+                    "Host-assisted migration tests are disabled.")
+        else:
+            if not CONF.share.run_driver_assisted_migration_tests:
+                raise self.skipException(
+                    "Driver-assisted migration tests are disabled.")
+
+    def _create_secondary_share_network(self, old_share_network_id):
+        if (utils.is_microversion_ge(
+                CONF.share.max_api_microversion, "2.22") and
+                CONF.share.multitenancy_enabled):
+
+            old_share_network = self.shares_v2_client.get_share_network(
+                old_share_network_id)
+
+            new_share_network = self.create_share_network(
+                cleanup_in_class=True,
+                neutron_net_id=old_share_network['neutron_net_id'],
+                neutron_subnet_id=old_share_network['neutron_subnet_id'])
+
+            return new_share_network['id']
+        else:
+            return None
diff --git a/manila_tempest_tests/tests/api/admin/test_migration_negative.py b/manila_tempest_tests/tests/api/admin/test_migration_negative.py
index 908abec..5d7a578 100644
--- a/manila_tempest_tests/tests/api/admin/test_migration_negative.py
+++ b/manila_tempest_tests/tests/api/admin/test_migration_negative.py
@@ -13,88 +13,199 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import six
+
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import exceptions as lib_exc
 from tempest import test
 import testtools
 
+from manila_tempest_tests.common import constants
+from manila_tempest_tests import share_exceptions
 from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
 
 CONF = config.CONF
 
 
-class MigrationNFSTest(base.BaseSharesAdminTest):
+class MigrationTest(base.BaseSharesAdminTest):
     """Tests Share Migration.
 
-    Tests migration in multi-backend environment.
+    Tests share migration in multi-backend environment.
     """
 
     protocol = "nfs"
 
     @classmethod
     def resource_setup(cls):
-        super(MigrationNFSTest, cls).resource_setup()
-        if not CONF.share.run_migration_tests:
-            raise cls.skipException("Migration tests disabled. Skipping.")
+        super(MigrationTest, cls).resource_setup()
+        if cls.protocol not in CONF.share.enable_protocols:
+            message = "%s tests are disabled." % cls.protocol
+            raise cls.skipException(message)
+        if not (CONF.share.run_host_assisted_migration_tests or
+                CONF.share.run_driver_assisted_migration_tests):
+            raise cls.skipException("Share migration tests are disabled.")
 
-        cls.share = cls.create_share(cls.protocol)
-        cls.share = cls.shares_client.get_share(cls.share['id'])
-        pools = cls.shares_client.list_pools()['pools']
+        pools = cls.shares_client.list_pools(detail=True)['pools']
 
         if len(pools) < 2:
             raise cls.skipException("At least two different pool entries "
-                                    "are needed to run migration tests. "
-                                    "Skipping.")
-        cls.dest_pool = next((x for x in pools
-                              if x['name'] != cls.share['host']), None)
+                                    "are needed to run share migration tests.")
+
+        cls.share = cls.create_share(cls.protocol)
+        cls.share = cls.shares_client.get_share(cls.share['id'])
+
+        default_type = cls.shares_v2_client.list_share_types(
+            default=True)['share_type']
+
+        dest_pool = utils.choose_matching_backend(
+            cls.share, pools, default_type)
+
+        if not dest_pool or dest_pool.get('name') is None:
+            raise share_exceptions.ShareMigrationException(
+                "No valid pool entries to run share migration tests.")
+
+        cls.dest_pool = dest_pool['name']
+
+        extra_specs = {
+            'storage_protocol': CONF.share.capability_storage_protocol,
+            'driver_handles_share_servers': CONF.share.multitenancy_enabled,
+            'snapshot_support': six.text_type(
+                not CONF.share.capability_snapshot_support),
+        }
+        cls.new_type = cls.create_share_type(
+            name=data_utils.rand_name(
+                'new_invalid_share_type_for_migration'),
+            cleanup_in_class=True,
+            extra_specs=extra_specs)
 
     @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
-    @base.skip_if_microversion_lt("2.15")
+    @base.skip_if_microversion_lt("2.22")
     def test_migration_cancel_invalid(self):
         self.assertRaises(
             lib_exc.BadRequest, self.shares_v2_client.migration_cancel,
             self.share['id'])
 
     @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
-    @base.skip_if_microversion_lt("2.15")
-    def test_migration_get_progress_invalid(self):
+    @base.skip_if_microversion_lt("2.22")
+    def test_migration_get_progress_None(self):
+        self.shares_v2_client.reset_task_state(self.share["id"], None)
+        self.shares_v2_client.wait_for_share_status(
+            self.share["id"], None, 'task_state')
         self.assertRaises(
             lib_exc.BadRequest, self.shares_v2_client.migration_get_progress,
             self.share['id'])
 
     @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
-    @base.skip_if_microversion_lt("2.15")
+    @base.skip_if_microversion_lt("2.22")
     def test_migration_complete_invalid(self):
         self.assertRaises(
             lib_exc.BadRequest, self.shares_v2_client.migration_complete,
             self.share['id'])
 
     @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
-    @base.skip_if_microversion_lt("2.5")
+    @base.skip_if_microversion_lt("2.22")
+    def test_migration_cancel_not_found(self):
+        self.assertRaises(
+            lib_exc.NotFound, self.shares_v2_client.migration_cancel,
+            'invalid_share_id')
+
+    @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+    @base.skip_if_microversion_lt("2.22")
+    def test_migration_get_progress_not_found(self):
+        self.assertRaises(
+            lib_exc.NotFound, self.shares_v2_client.migration_get_progress,
+            'invalid_share_id')
+
+    @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+    @base.skip_if_microversion_lt("2.22")
+    def test_migration_complete_not_found(self):
+        self.assertRaises(
+            lib_exc.NotFound, self.shares_v2_client.migration_complete,
+            'invalid_share_id')
+
+    @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+    @base.skip_if_microversion_lt("2.22")
     @testtools.skipUnless(CONF.share.run_snapshot_tests,
                           "Snapshot tests are disabled.")
-    def test_migrate_share_with_snapshot_v2_5(self):
+    def test_migrate_share_with_snapshot(self):
         snap = self.create_snapshot_wait_for_active(self.share['id'])
         self.assertRaises(
             lib_exc.BadRequest, self.shares_v2_client.migrate_share,
-            self.share['id'], self.dest_pool, True, version='2.5')
+            self.share['id'], self.dest_pool)
         self.shares_client.delete_snapshot(snap['id'])
         self.shares_client.wait_for_resource_deletion(snapshot_id=snap["id"])
 
     @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
-    @base.skip_if_microversion_lt("2.5")
-    def test_migrate_share_same_host_v2_5(self):
+    @base.skip_if_microversion_lt("2.22")
+    def test_migrate_share_same_host(self):
         self.assertRaises(
             lib_exc.BadRequest, self.shares_v2_client.migrate_share,
-            self.share['id'], self.share['host'], True, version='2.5')
+            self.share['id'], self.share['host'])
 
     @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
-    @base.skip_if_microversion_lt("2.5")
-    def test_migrate_share_not_available_v2_5(self):
-        self.shares_client.reset_state(self.share['id'], 'error')
-        self.shares_client.wait_for_share_status(self.share['id'], 'error')
+    @base.skip_if_microversion_lt("2.22")
+    def test_migrate_share_host_invalid(self):
+        self.assertRaises(
+            lib_exc.NotFound, self.shares_v2_client.migrate_share,
+            self.share['id'], 'invalid_host')
+
+    @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+    @base.skip_if_microversion_lt("2.22")
+    def test_migrate_share_host_assisted_not_allowed(self):
+        self.shares_v2_client.migrate_share(
+            self.share['id'], self.dest_pool,
+            force_host_assisted_migration=True, writable=True,
+            preserve_metadata=True)
+        self.shares_v2_client.wait_for_migration_status(
+            self.share['id'], self.dest_pool,
+            constants.TASK_STATE_MIGRATION_ERROR)
+
+    @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+    @base.skip_if_microversion_lt("2.22")
+    def test_migrate_share_change_type_no_valid_host(self):
+        self.shares_v2_client.migrate_share(
+            self.share['id'], self.dest_pool,
+            new_share_type_id=self.new_type['share_type']['id'])
+        self.shares_v2_client.wait_for_migration_status(
+            self.share['id'], self.dest_pool,
+            constants.TASK_STATE_MIGRATION_ERROR)
+
+    @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+    @base.skip_if_microversion_lt("2.22")
+    def test_migrate_share_not_found(self):
+        self.assertRaises(
+            lib_exc.NotFound, self.shares_v2_client.migrate_share,
+            'invalid_share_id', self.dest_pool)
+
+    @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+    @base.skip_if_microversion_lt("2.22")
+    def test_migrate_share_not_available(self):
+        self.shares_client.reset_state(self.share['id'],
+                                       constants.STATUS_ERROR)
+        self.shares_client.wait_for_share_status(self.share['id'],
+                                                 constants.STATUS_ERROR)
         self.assertRaises(
             lib_exc.BadRequest, self.shares_v2_client.migrate_share,
-            self.share['id'], self.dest_pool, True, version='2.5')
-        self.shares_client.reset_state(self.share['id'], 'available')
-        self.shares_client.wait_for_share_status(self.share['id'], 'available')
+            self.share['id'], self.dest_pool)
+        self.shares_client.reset_state(self.share['id'],
+                                       constants.STATUS_AVAILABLE)
+        self.shares_client.wait_for_share_status(self.share['id'],
+                                                 constants.STATUS_AVAILABLE)
+
+    @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+    @base.skip_if_microversion_lt("2.22")
+    def test_migrate_share_invalid_share_network(self):
+        self.assertRaises(
+            lib_exc.BadRequest, self.shares_v2_client.migrate_share,
+            self.share['id'], self.dest_pool,
+            new_share_network_id='invalid_net_id')
+
+    @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+    @base.skip_if_microversion_lt("2.22")
+    def test_migrate_share_invalid_share_type(self):
+        self.assertRaises(
+            lib_exc.BadRequest, self.shares_v2_client.migrate_share,
+            self.share['id'], self.dest_pool, True,
+            new_share_type_id='invalid_type_id')
diff --git a/manila_tempest_tests/tests/api/admin/test_replication.py b/manila_tempest_tests/tests/api/admin/test_replication.py
index f8b1741..ff2f3c6 100644
--- a/manila_tempest_tests/tests/api/admin/test_replication.py
+++ b/manila_tempest_tests/tests/api/admin/test_replication.py
@@ -86,6 +86,10 @@
         replica = self.create_share_replica(
             share["id"], self.replica_zone, cleanup=False,
             client=self.admin_client)
+        # Wait for replica state to update after creation
+        self.admin_client.wait_for_share_replica_status(
+            replica['id'], constants.REPLICATION_STATE_IN_SYNC,
+            status_attr='replica_state')
 
         # List replicas
         replica_list = self.admin_client.list_share_replicas(
diff --git a/manila_tempest_tests/tests/api/admin/test_replication_actions.py b/manila_tempest_tests/tests/api/admin/test_replication_actions.py
index a8f5eb7..41bfa86 100644
--- a/manila_tempest_tests/tests/api/admin/test_replication_actions.py
+++ b/manila_tempest_tests/tests/api/admin/test_replication_actions.py
@@ -58,7 +58,7 @@
             client=cls.admin_client)
         cls.share_type = share_type["share_type"]
         # Create share with above share_type
-        cls.share = cls.create_share(size=2,
+        cls.share = cls.create_share(size=CONF.share.share_size+1,
                                      share_type_id=cls.share_type["id"],
                                      availability_zone=cls.share_zone,
                                      client=cls.admin_client)
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index 9d15d48..a36f391 100644
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -401,13 +401,19 @@
         return share
 
     @classmethod
-    def migrate_share(cls, share_id, dest_host, client=None, notify=True,
-                      wait_for_status='migration_success', **kwargs):
+    def migrate_share(
+            cls, share_id, dest_host, wait_for_status, client=None,
+            force_host_assisted_migration=False, new_share_network_id=None,
+            new_share_type_id=None, **kwargs):
         client = client or cls.shares_v2_client
-        client.migrate_share(share_id, dest_host, notify, **kwargs)
+        client.migrate_share(
+            share_id, dest_host,
+            force_host_assisted_migration=force_host_assisted_migration,
+            new_share_network_id=new_share_network_id,
+            writable=False, preserve_metadata=False, nondisruptive=False,
+            new_share_type_id=new_share_type_id, **kwargs)
         share = client.wait_for_migration_status(
-            share_id, dest_host, wait_for_status,
-            version=kwargs.get('version'))
+            share_id, dest_host, wait_for_status, **kwargs)
         return share
 
     @classmethod
@@ -415,8 +421,15 @@
         client = client or cls.shares_v2_client
         client.migration_complete(share_id, **kwargs)
         share = client.wait_for_migration_status(
-            share_id, dest_host, 'migration_success',
-            version=kwargs.get('version'))
+            share_id, dest_host, 'migration_success', **kwargs)
+        return share
+
+    @classmethod
+    def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
+        client = client or cls.shares_v2_client
+        client.migration_cancel(share_id, **kwargs)
+        share = client.wait_for_migration_status(
+            share_id, dest_host, 'migration_cancelled', **kwargs)
         return share
 
     @classmethod
diff --git a/manila_tempest_tests/tests/api/test_replication.py b/manila_tempest_tests/tests/api/test_replication.py
index 27db099..d5ec21a 100644
--- a/manila_tempest_tests/tests/api/test_replication.py
+++ b/manila_tempest_tests/tests/api/test_replication.py
@@ -175,6 +175,8 @@
         self.delete_share_replica(share_replica["id"])
 
     @test.attr(type=[base.TAG_POSITIVE, base.TAG_BACKEND])
+    @testtools.skipUnless(CONF.share.run_multiple_share_replicas_tests,
+                          'Multiple share replicas tests are disabled.')
     def test_add_multiple_share_replicas(self):
         rep_domain, pools = self.get_pools_for_replication_domain()
         if len(pools) < 3:
diff --git a/manila_tempest_tests/tests/scenario/manager_share.py b/manila_tempest_tests/tests/scenario/manager_share.py
index 972654b..14fb34d 100644
--- a/manila_tempest_tests/tests/scenario/manager_share.py
+++ b/manila_tempest_tests/tests/scenario/manager_share.py
@@ -21,6 +21,7 @@
 from tempest.lib.common.utils import data_utils
 from tempest.scenario import manager
 
+from manila_tempest_tests.common import constants
 from manila_tempest_tests.services.share.json import shares_client
 from manila_tempest_tests.services.share.v2.json import (
     shares_client as shares_v2_client)
@@ -196,11 +197,19 @@
 
         return linux_client
 
-    def _migrate_share(self, share_id, dest_host, client=None):
+    def _migrate_share(self, share_id, dest_host, status, client=None):
         client = client or self.shares_admin_v2_client
-        client.migrate_share(share_id, dest_host, True)
-        share = client.wait_for_migration_status(share_id, dest_host,
-                                                 'migration_success')
+        client.migrate_share(share_id, dest_host, writable=False,
+                             preserve_metadata=False, nondisruptive=False)
+        share = client.wait_for_migration_status(share_id, dest_host, status)
+        return share
+
+    def _migration_complete(self, share_id, dest_host, client=None, **kwargs):
+        client = client or self.shares_admin_v2_client
+        client.migration_complete(share_id, **kwargs)
+        share = client.wait_for_migration_status(
+            share_id, dest_host, constants.TASK_STATE_MIGRATION_SUCCESS,
+            **kwargs)
         return share
 
     def _create_share_type(self, name, is_public=True, **kwargs):
diff --git a/manila_tempest_tests/tests/scenario/test_share_basic_ops.py b/manila_tempest_tests/tests/scenario/test_share_basic_ops.py
index dbe5599..532ddd5 100644
--- a/manila_tempest_tests/tests/scenario/test_share_basic_ops.py
+++ b/manila_tempest_tests/tests/scenario/test_share_basic_ops.py
@@ -20,7 +20,9 @@
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions
 from tempest import test
+import testtools
 
+from manila_tempest_tests.common import constants
 from manila_tempest_tests.tests.api import base
 from manila_tempest_tests.tests.scenario import manager_share as manager
 from manila_tempest_tests import utils
@@ -123,9 +125,10 @@
         data = ssh_client.exec_command("sudo cat /mnt/t1")
         return data.rstrip()
 
-    def migrate_share(self, share_id, dest_host):
-        share = self._migrate_share(share_id, dest_host,
+    def migrate_share(self, share_id, dest_host, status):
+        share = self._migrate_share(share_id, dest_host, status,
                                     self.shares_admin_v2_client)
+        share = self._migration_complete(share['id'], dest_host)
         return share
 
     def create_share_network(self):
@@ -244,47 +247,51 @@
 
     @test.services('compute', 'network')
     @test.attr(type=[base.TAG_POSITIVE, base.TAG_BACKEND])
+    @testtools.skipUnless(CONF.share.run_host_assisted_migration_tests or
+                          CONF.share.run_driver_assisted_migration_tests,
+                          "Share migration tests are disabled.")
     def test_migration_files(self):
 
-        if self.protocol == "CIFS":
-            raise self.skipException("Test for CIFS protocol not supported "
-                                     "at this moment. Skipping.")
+        if self.protocol != "NFS":
+            raise self.skipException("Only NFS protocol supported "
+                                     "at this moment.")
 
-        if not CONF.share.run_migration_tests:
-            raise self.skipException("Migration tests disabled. Skipping.")
-
-        pools = self.shares_admin_client.list_pools()['pools']
+        pools = self.shares_admin_v2_client.list_pools(detail=True)['pools']
 
         if len(pools) < 2:
-            raise self.skipException("At least two different pool entries "
-                                     "are needed to run migration tests. "
-                                     "Skipping.")
+            raise self.skipException("At least two different pool entries are "
+                                     "needed to run share migration tests.")
 
         instance = self.boot_instance(wait_until="BUILD")
         self.create_share()
         instance = self.wait_for_active_instance(instance["id"])
-        share = self.shares_client.get_share(self.share['id'])
+        self.share = self.shares_client.get_share(self.share['id'])
 
-        dest_pool = next((x for x in pools if x['name'] != share['host']),
-                         None)
+        default_type = self.shares_v2_client.list_share_types(
+            default=True)['share_type']
+
+        dest_pool = utils.choose_matching_backend(
+            self.share, pools, default_type)
 
         self.assertIsNotNone(dest_pool)
         self.assertIsNotNone(dest_pool.get('name'))
 
         dest_pool = dest_pool['name']
 
-        self.allow_access_ip(self.share['id'], instance=instance,
-                             cleanup=False)
+        self.allow_access_ip(
+            self.share['id'], instance=instance, cleanup=False)
         ssh_client = self.init_ssh(instance)
 
         if utils.is_microversion_lt(CONF.share.max_api_microversion, "2.9"):
-            locations = self.share['export_locations']
+            exports = self.share['export_locations']
         else:
             exports = self.shares_v2_client.list_share_export_locations(
                 self.share['id'])
-            locations = [x['path'] for x in exports]
+            self.assertNotEmpty(exports)
+            exports = [x['path'] for x in exports]
+            self.assertNotEmpty(exports)
 
-        self.mount_share(locations[0], ssh_client)
+        self.mount_share(exports[0], ssh_client)
 
         ssh_client.exec_command("mkdir -p /mnt/f1")
         ssh_client.exec_command("mkdir -p /mnt/f2")
@@ -307,21 +314,27 @@
 
         self.umount_share(ssh_client)
 
-        share = self.migrate_share(share['id'], dest_pool)
+        task_state = (constants.TASK_STATE_DATA_COPYING_COMPLETED,
+                      constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE)
+
+        self.share = self.migrate_share(
+            self.share['id'], dest_pool, task_state)
+
         if utils.is_microversion_lt(CONF.share.max_api_microversion, "2.9"):
-            new_locations = self.share['export_locations']
+            new_exports = self.share['export_locations']
+            self.assertNotEmpty(new_exports)
         else:
             new_exports = self.shares_v2_client.list_share_export_locations(
                 self.share['id'])
-            new_locations = [x['path'] for x in new_exports]
+            self.assertNotEmpty(new_exports)
+            new_exports = [x['path'] for x in new_exports]
+            self.assertNotEmpty(new_exports)
 
-        self.assertEqual(dest_pool, share['host'])
-        locations.sort()
-        new_locations.sort()
-        self.assertNotEqual(locations, new_locations)
-        self.assertEqual('migration_success', share['task_state'])
+        self.assertEqual(dest_pool, self.share['host'])
+        self.assertEqual(constants.TASK_STATE_MIGRATION_SUCCESS,
+                         self.share['task_state'])
 
-        self.mount_share(new_locations[0], ssh_client)
+        self.mount_share(new_exports[0], ssh_client)
 
         output = ssh_client.exec_command("ls -lRA --ignore=lost+found /mnt")
 
diff --git a/manila_tempest_tests/utils.py b/manila_tempest_tests/utils.py
index dea51ab..277130e 100644
--- a/manila_tempest_tests/utils.py
+++ b/manila_tempest_tests/utils.py
@@ -100,3 +100,18 @@
     TEST_NET_3 = '203.0.113.'
     final_octet = six.text_type(random.randint(0, 255))
     return TEST_NET_3 + final_octet
+
+
+def choose_matching_backend(share, pools, share_type):
+    extra_specs = {}
+    # fix extra specs with string values instead of boolean
+    for k, v in share_type['extra_specs'].items():
+        extra_specs[k] = (True if six.text_type(v).lower() == 'true'
+                          else False if six.text_type(v).lower() == 'false'
+                          else v)
+    selected_pool = next(
+        (x for x in pools if (x['name'] != share['host'] and all(
+            y in x['capabilities'].items() for y in extra_specs.items()))),
+        None)
+
+    return selected_pool