Share Migration Ocata Improvements

Implemented several improvements to share migration
according to spec [1].

Summary of changes:
- Snapshot restriction in API has been changed to return error only
when parameter force-host-assisted-migration is True
- Added preserve_snapshot to API and migration_check_compatibility
driver interface
- Changed all driver-assisted API parameters to be mandatory
- Added validation to prevent 'force_host_assisted_migration' to be
used alongside driver-assisted parameters
- Changed "same host" validation to reject only if the combination
of "host", "new_share_network" and "new_share_type" is the same as
the source
- Updated migration driver interfaces to support snapshots
- Updated zfsonlinux driver, defaulting preserve_snapshots to False
- Updated dummy driver to support preserve_snapshots

Spec update with latest changes since [1] merged
can be found in [2].

APIImpact
DocImpact

[1] I5717e902373d79ed0d55372afdedfaa98134c24e
[2] If02180ec3b5ae05c9ff18c9f5a054c33f13edcdf

Change-Id: I764b389816319ed0ac5178cadbf809cb632035b4
Partially-implements: blueprint ocata-migration-improvements
diff --git a/manila_tempest_tests/tests/api/admin/test_migration.py b/manila_tempest_tests/tests/api/admin/test_migration.py
index dc58a16..5a30bae 100644
--- a/manila_tempest_tests/tests/api/admin/test_migration.py
+++ b/manila_tempest_tests/tests/api/admin/test_migration.py
@@ -36,7 +36,8 @@
     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.
+    nondisruptive, writable, preserve-metadata and preserve-snapshots are
+    False.
     3) 2-phase migration of both Host-assisted and Driver-assisted.
     4) Cancelling migration past first phase.
     5) Changing driver modes through migration.
@@ -75,7 +76,7 @@
                 variation='opposite_driver_modes'))
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
-    @base.skip_if_microversion_lt("2.22")
+    @base.skip_if_microversion_lt("2.29")
     @ddt.data(True, False)
     def test_migration_cancel(self, force_host_assisted):
 
@@ -112,7 +113,7 @@
             complete=False)
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
-    @base.skip_if_microversion_lt("2.22")
+    @base.skip_if_microversion_lt("2.29")
     @ddt.data(True, False)
     def test_migration_opposite_driver_modes(self, force_host_assisted):
 
@@ -172,7 +173,7 @@
             share_type_id=new_share_type_id)
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
-    @base.skip_if_microversion_lt("2.22")
+    @base.skip_if_microversion_lt("2.29")
     @ddt.data(True, False)
     def test_migration_2phase(self, force_host_assisted):
 
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 59d97f4..9d25fff 100644
--- a/manila_tempest_tests/tests/api/admin/test_migration_negative.py
+++ b/manila_tempest_tests/tests/api/admin/test_migration_negative.py
@@ -30,7 +30,7 @@
 
 
 @ddt.ddt
-class MigrationTest(base.BaseSharesAdminTest):
+class MigrationNegativeTest(base.BaseSharesAdminTest):
     """Tests Share Migration.
 
     Tests share migration in multi-backend environment.
@@ -40,7 +40,7 @@
 
     @classmethod
     def resource_setup(cls):
-        super(MigrationTest, cls).resource_setup()
+        super(MigrationNegativeTest, cls).resource_setup()
         if cls.protocol not in CONF.share.enable_protocols:
             message = "%s tests are disabled." % cls.protocol
             raise cls.skipException(message)
@@ -57,11 +57,11 @@
         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(
+        cls.default_type = cls.shares_v2_client.list_share_types(
             default=True)['share_type']
 
         dest_pool = utils.choose_matching_backend(
-            cls.share, pools, default_type)
+            cls.share, pools, cls.default_type)
 
         if not dest_pool or dest_pool.get('name') is None:
             raise share_exceptions.ShareMigrationException(
@@ -121,44 +121,65 @@
             'invalid_share_id')
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
-    @base.skip_if_microversion_lt("2.22")
+    @base.skip_if_microversion_lt("2.29")
     @testtools.skipUnless(CONF.share.run_snapshot_tests,
                           "Snapshot tests are disabled.")
     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)
+            lib_exc.Conflict, self.shares_v2_client.migrate_share,
+            self.share['id'], self.dest_pool,
+            force_host_assisted_migration=True)
         self.shares_client.delete_snapshot(snap['id'])
         self.shares_client.wait_for_resource_deletion(snapshot_id=snap["id"])
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
-    @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'])
+    @base.skip_if_microversion_lt("2.29")
+    @ddt.data(True, False)
+    def test_migrate_share_same_host(self, specified):
+        new_share_type_id = None
+        new_share_network_id = None
+        if specified:
+            new_share_type_id = self.default_type['id']
+            new_share_network_id = self.share['share_network_id']
+        self.migrate_share(
+            self.share['id'], self.share['host'],
+            wait_for_status=constants.TASK_STATE_MIGRATION_SUCCESS,
+            new_share_type_id=new_share_type_id,
+            new_share_network_id=new_share_network_id)
+        # NOTE(ganso): No need to assert, it is already waiting for correct
+        # status (migration_success).
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
-    @base.skip_if_microversion_lt("2.22")
+    @base.skip_if_microversion_lt("2.29")
     def test_migrate_share_host_invalid(self):
         self.assertRaises(
             lib_exc.NotFound, self.shares_v2_client.migrate_share,
             self.share['id'], 'invalid_host')
 
     @tc.attr(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(
+    @base.skip_if_microversion_lt("2.29")
+    @ddt.data({'writable': False, 'preserve_metadata': False,
+               'preserve_snapshots': False, 'nondisruptive': True},
+              {'writable': False, 'preserve_metadata': False,
+               'preserve_snapshots': True, 'nondisruptive': False},
+              {'writable': False, 'preserve_metadata': True,
+               'preserve_snapshots': False, 'nondisruptive': False},
+              {'writable': True, 'preserve_metadata': False,
+               'preserve_snapshots': False, 'nondisruptive': False})
+    @ddt.unpack
+    def test_migrate_share_host_assisted_not_allowed_API(
+            self, writable, preserve_metadata, preserve_snapshots,
+            nondisruptive):
+        self.assertRaises(
+            lib_exc.BadRequest, 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)
+            force_host_assisted_migration=True, writable=writable,
+            preserve_metadata=preserve_metadata, nondisruptive=nondisruptive,
+            preserve_snapshots=preserve_snapshots)
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
-    @base.skip_if_microversion_lt("2.22")
+    @base.skip_if_microversion_lt("2.29")
     def test_migrate_share_change_type_no_valid_host(self):
         if not CONF.share.multitenancy_enabled:
             new_share_network_id = self.create_share_network(
@@ -176,14 +197,14 @@
             constants.TASK_STATE_MIGRATION_ERROR)
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
-    @base.skip_if_microversion_lt("2.22")
+    @base.skip_if_microversion_lt("2.29")
     def test_migrate_share_not_found(self):
         self.assertRaises(
             lib_exc.NotFound, self.shares_v2_client.migrate_share,
             'invalid_share_id', self.dest_pool)
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
-    @base.skip_if_microversion_lt("2.22")
+    @base.skip_if_microversion_lt("2.29")
     def test_migrate_share_not_available(self):
         self.shares_client.reset_state(self.share['id'],
                                        constants.STATUS_ERROR)
@@ -198,7 +219,7 @@
                                                  constants.STATUS_AVAILABLE)
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
-    @base.skip_if_microversion_lt("2.22")
+    @base.skip_if_microversion_lt("2.29")
     def test_migrate_share_invalid_share_network(self):
         self.assertRaises(
             lib_exc.BadRequest, self.shares_v2_client.migrate_share,
@@ -206,7 +227,7 @@
             new_share_network_id='invalid_net_id')
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
-    @base.skip_if_microversion_lt("2.22")
+    @base.skip_if_microversion_lt("2.29")
     def test_migrate_share_invalid_share_type(self):
         self.assertRaises(
             lib_exc.BadRequest, self.shares_v2_client.migrate_share,
@@ -214,7 +235,7 @@
             new_share_type_id='invalid_type_id')
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
-    @base.skip_if_microversion_lt("2.22")
+    @base.skip_if_microversion_lt("2.29")
     def test_migrate_share_opposite_type_share_network_invalid(self):
 
         extra_specs = utils.get_configured_extra_specs(
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index 398108d..48cd3ed 100644
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -424,14 +424,17 @@
     @classmethod
     def migrate_share(
             cls, share_id, dest_host, wait_for_status, client=None,
-            force_host_assisted_migration=False, new_share_network_id=None,
+            force_host_assisted_migration=False, writable=False,
+            nondisruptive=False, preserve_metadata=False,
+            preserve_snapshots=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,
             force_host_assisted_migration=force_host_assisted_migration,
+            writable=writable, preserve_metadata=preserve_metadata,
+            nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
             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, **kwargs)