Add functional tests for manage/unmanage of shares in DHSS=True

This patch adds functional tests for managing/unmanaging
share servers, shares, and snapshots in DHSS=True.

Change-Id: I452c2a99b186f53d737cb7fbd7eabfcfd9b249d6
Partially-implements: bp manage-unmanage-with-share-servers
diff --git a/manila_tempest_tests/common/constants.py b/manila_tempest_tests/common/constants.py
index c56712e..54d32fb 100644
--- a/manila_tempest_tests/common/constants.py
+++ b/manila_tempest_tests/common/constants.py
@@ -15,6 +15,10 @@
 STATUS_AVAILABLE = 'available'
 STATUS_ERROR_DELETING = 'error_deleting'
 STATUS_MIGRATING = 'migrating'
+STATUS_MANAGE_ERROR = 'manage_error'
+STATUS_MIGRATING_TO = 'migrating_to'
+STATUS_CREATING = 'creating'
+STATUS_DELETING = 'deleting'
 
 TEMPEST_MANILA_PREFIX = 'tempest-manila'
 
@@ -85,3 +89,13 @@
 }
 
 MIN_SHARE_ACCESS_METADATA_MICROVERSION = '2.45'
+
+# Share servers
+SERVER_STATE_ACTIVE = 'active'
+SERVER_STATE_CREATING = 'creating'
+SERVER_STATE_DELETING = 'deleting'
+SERVER_STATE_ERROR = 'error'
+SERVER_STATE_MANAGE_ERROR = 'manage_error'
+SERVER_STATE_MANAGE_STARTING = 'manage_starting'
+SERVER_STATE_UNMANAGE_ERROR = 'unmanage_error'
+SERVER_STATE_UNMANAGE_STARTING = 'unmanage_starting'
diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py
index 9d891ec..9c734f4 100644
--- a/manila_tempest_tests/config.py
+++ b/manila_tempest_tests/config.py
@@ -30,7 +30,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.48",
+               default="2.49",
                help="The maximum api microversion is configured to be the "
                     "value of the latest microversion supported by Manila."),
     cfg.StrOpt("region",
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 7a146b6..e0eec7c 100644
--- a/manila_tempest_tests/services/share/v2/json/shares_client.py
+++ b/manila_tempest_tests/services/share/v2/json/shares_client.py
@@ -436,7 +436,7 @@
     def manage_share(self, service_host, protocol, export_path,
                      share_type_id, name=None, description=None,
                      is_public=False, version=LATEST_MICROVERSION,
-                     url=None):
+                     url=None, share_server_id=None):
         post_body = {
             "share": {
                 "export_path": export_path,
@@ -448,6 +448,8 @@
                 "is_public": is_public,
             }
         }
+        if share_server_id is not None:
+            post_body['share']['share_server_id'] = share_server_id
         if url is None:
             if utils.is_microversion_gt(version, "2.6"):
                 url = 'shares/manage'
@@ -548,6 +550,8 @@
             time.sleep(self.build_interval)
             body = self.get_snapshot(snapshot_id, version=version)
             snapshot_status = body['status']
+            if snapshot_status == status:
+                return
             if 'error' in snapshot_status:
                 raise (share_exceptions.
                        SnapshotBuildErrorException(snapshot_id=snapshot_id))
@@ -596,6 +600,12 @@
         self.expected_success(202, resp.status)
         return body
 
+    def snapshot_reset_state(self, snapshot_id,
+                             status=constants.STATUS_AVAILABLE,
+                             version=LATEST_MICROVERSION):
+        self.reset_state(snapshot_id, status=status, s_type='snapshots',
+                         version=version)
+
 ###############
 
     def revert_to_snapshot(self, share_id, snapshot_id,
@@ -1370,6 +1380,64 @@
 
 ###############
 
+    def manage_share_server(self, host, share_network_id, identifier,
+                            driver_options=None, version=LATEST_MICROVERSION):
+        body = {
+            'share_server': {
+                'host': host,
+                'share_network_id': share_network_id,
+                'identifier': identifier,
+                'driver_options': driver_options if driver_options else {},
+            }
+        }
+
+        body = json.dumps(body)
+        resp, body = self.post('share-servers/manage', body,
+                               extra_headers=True, version=version)
+        self.expected_success(202, resp.status)
+        return self._parse_resp(body)
+
+    def unmanage_share_server(self, share_server_id,
+                              version=LATEST_MICROVERSION):
+        body = json.dumps({'unmanage': None})
+        resp, body = self.post('share-servers/%s/action' % share_server_id,
+                               body, extra_headers=True, version=version)
+        self.expected_success(202, resp.status)
+        return self._parse_resp(body)
+
+    def wait_for_share_server_status(self, server_id, status,
+                                     status_attr='status'):
+        """Waits for a share to reach a given status."""
+        body = self.show_share_server(server_id)
+        server_status = body[status_attr]
+        start = int(time.time())
+
+        while server_status != status:
+            time.sleep(self.build_interval)
+            body = self.show_share_server(server_id)
+            server_status = body[status_attr]
+            if server_status == status:
+                return
+            elif constants.STATUS_ERROR in server_status.lower():
+                raise share_exceptions.ShareServerBuildErrorException(
+                    server_id=server_id)
+
+            if int(time.time()) - start >= self.build_timeout:
+                message = ("Share server's %(status_attr)s failed to "
+                           "transition to %(status)s within the required "
+                           "time %(seconds)s." %
+                           {"status_attr": status_attr, "status": status,
+                            "seconds": self.build_timeout})
+                raise exceptions.TimeoutException(message)
+
+    def share_server_reset_state(self, share_server_id,
+                                 status=constants.SERVER_STATE_ACTIVE,
+                                 version=LATEST_MICROVERSION):
+        self.reset_state(share_server_id, status=status,
+                         s_type='share-servers', version=version)
+
+###############
+
     def migrate_share(self, share_id, host,
                       force_host_assisted_migration=False,
                       new_share_network_id=None, writable=False,
diff --git a/manila_tempest_tests/share_exceptions.py b/manila_tempest_tests/share_exceptions.py
index a309b84..67b4fca 100644
--- a/manila_tempest_tests/share_exceptions.py
+++ b/manila_tempest_tests/share_exceptions.py
@@ -75,3 +75,8 @@
 class ShareReplicationTypeException(exceptions.TempestException):
     message = ("Option backend_replication_type is set to incorrect value: "
                "%(replication_type)s")
+
+
+class ShareServerBuildErrorException(exceptions.TempestException):
+    message = ("Share server %(server_id)s failed to build and is in ERROR "
+               "status")
diff --git a/manila_tempest_tests/tests/api/admin/test_share_manage.py b/manila_tempest_tests/tests/api/admin/test_share_manage.py
index 7efdb49..a7337fe 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_manage.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_manage.py
@@ -13,19 +13,20 @@
 #    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.lib import exceptions as lib_exc
 import testtools
 from testtools import testcase as tc
 
+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 ManageNFSShareTest(base.BaseSharesAdminTest):
     protocol = 'nfs'
 
@@ -34,36 +35,23 @@
     # won't be deleted.
 
     @classmethod
-    @testtools.skipIf(
-        CONF.share.multitenancy_enabled,
-        "Only for driver_handles_share_servers = False driver mode.")
     @testtools.skipUnless(
         CONF.share.run_manage_unmanage_tests,
         "Manage/unmanage tests are disabled.")
     def resource_setup(cls):
-        super(ManageNFSShareTest, cls).resource_setup()
         if cls.protocol not in CONF.share.enable_protocols:
             message = "%s tests are disabled" % cls.protocol
             raise cls.skipException(message)
 
-        # Create share types
+        utils.skip_if_manage_not_supported_for_version()
+
+        super(ManageNFSShareTest, cls).resource_setup()
+
+        # Create share type
         cls.st_name = data_utils.rand_name("manage-st-name")
-        cls.st_name_invalid = data_utils.rand_name("manage-st-name-invalid")
         cls.extra_specs = {
             'storage_protocol': CONF.share.capability_storage_protocol,
-            'driver_handles_share_servers': False,
-            'snapshot_support': six.text_type(
-                CONF.share.capability_snapshot_support),
-            'create_share_from_snapshot_support': six.text_type(
-                CONF.share.capability_create_share_from_snapshot_support)
-        }
-        cls.extra_specs_invalid = {
-            'storage_protocol': CONF.share.capability_storage_protocol,
-            'driver_handles_share_servers': True,
-            'snapshot_support': six.text_type(
-                CONF.share.capability_snapshot_support),
-            'create_share_from_snapshot_support': six.text_type(
-                CONF.share.capability_create_share_from_snapshot_support),
+            'driver_handles_share_servers': CONF.share.multitenancy_enabled,
         }
 
         cls.st = cls.create_share_type(
@@ -71,15 +59,12 @@
             cleanup_in_class=True,
             extra_specs=cls.extra_specs)
 
-        cls.st_invalid = cls.create_share_type(
-            name=cls.st_name_invalid,
-            cleanup_in_class=True,
-            extra_specs=cls.extra_specs_invalid)
-
     def _test_manage(self, is_public=False,
                      version=CONF.share.max_api_microversion,
                      check_manage=False):
 
+        utils.skip_if_manage_not_supported_for_version(version)
+
         share = self._create_share_for_manage()
 
         name = "Name for 'managed' share that had ID %s" % share['id']
@@ -97,16 +82,19 @@
             self.assertNotIn(share['id'], share_ids)
 
         # Manage share
-        managed_share = self.shares_v2_client.manage_share(
-            service_host=share['host'],
-            export_path=share['export_locations'][0],
-            protocol=share['share_proto'],
-            share_type_id=self.st['share_type']['id'],
-            name=name,
-            description=description,
-            is_public=is_public,
-            version=version,
-        )
+        manage_params = {
+            'service_host': share['host'],
+            'export_path': share['export_locations'][0],
+            'protocol': share['share_proto'],
+            'share_type_id': self.st['share_type']['id'],
+            'name': name,
+            'description': description,
+            'is_public': is_public,
+            'version': version,
+        }
+        if CONF.share.multitenancy_enabled:
+            manage_params['share_server_id'] = share['share_server_id']
+        managed_share = self.shares_v2_client.manage_share(**manage_params)
 
         # Add managed share to cleanup queue
         self.method_resources.insert(
@@ -115,7 +103,7 @@
 
         # Wait for success
         self.shares_v2_client.wait_for_share_status(managed_share['id'],
-                                                    'available')
+                                                    constants.STATUS_AVAILABLE)
 
         # Verify data of managed share
         self.assertEqual(name, managed_share['name'])
@@ -141,31 +129,7 @@
             self.assertNotIn('user_id', managed_share)
 
         # Delete share
-        self.shares_v2_client.delete_share(managed_share['id'])
-        self.shares_v2_client.wait_for_resource_deletion(
-            share_id=managed_share['id'])
-        self.assertRaises(lib_exc.NotFound,
-                          self.shares_v2_client.get_share,
-                          managed_share['id'])
-
-    def _create_share_for_manage(self):
-        creation_data = {
-            'share_type_id': self.st['share_type']['id'],
-            'share_protocol': self.protocol,
-        }
-
-        share = self.create_share(**creation_data)
-        share = self.shares_v2_client.get_share(share['id'])
-
-        if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
-            el = self.shares_v2_client.list_share_export_locations(share["id"])
-            share["export_locations"] = el
-
-        return share
-
-    def _unmanage_share_and_wait(self, share):
-        self.shares_v2_client.unmanage_share(share['id'])
-        self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
+        self._delete_share_and_wait(managed_share)
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
     @base.skip_if_microversion_not_supported("2.5")
@@ -186,55 +150,6 @@
     def test_manage(self):
         self._test_manage(check_manage=True)
 
-    @testtools.skipUnless(
-        CONF.share.multitenancy_enabled,
-        "Will be re-enabled along with the updated tests of Manage-Unmanage "
-        "with Share Server patch")
-    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
-    def test_manage_invalid(self):
-        # Try to manage share with invalid parameters, it should not succeed
-        # because the scheduler will reject it. If it succeeds, then this test
-        # case failed. Then, in order to remove the resource from backend, we
-        # need to manage it again, properly, so we can delete it. Consequently
-        # the second part of this test also tests that manage operation with a
-        # proper share type works.
-
-        def _delete_share(share_id):
-            self.shares_v2_client.reset_state(share_id)
-            self.shares_v2_client.delete_share(share_id)
-            self.shares_v2_client.wait_for_resource_deletion(share_id=share_id)
-            self.assertRaises(lib_exc.NotFound,
-                              self.shares_v2_client.get_share,
-                              share_id)
-
-        share = self._create_share_for_manage()
-
-        self._unmanage_share_and_wait(share)
-
-        managed_share = self.shares_v2_client.manage_share(
-            service_host=share['host'],
-            export_path=share['export_locations'][0],
-            protocol=share['share_proto'],
-            share_type_id=self.st_invalid['share_type']['id'])
-        self.addCleanup(_delete_share, managed_share['id'])
-
-        self.shares_v2_client.wait_for_share_status(
-            managed_share['id'], 'manage_error')
-        managed_share = self.shares_v2_client.get_share(managed_share['id'])
-        self.assertEqual(1, int(managed_share['size']))
-
-        # Delete resource from backend. We need to manage the share properly
-        # so it can be removed.
-        managed_share = self.shares_v2_client.manage_share(
-            service_host=share['host'],
-            export_path=share['export_locations'][0],
-            protocol=share['share_proto'],
-            share_type_id=self.st['share_type']['id'])
-        self.addCleanup(_delete_share, managed_share['id'])
-
-        self.shares_v2_client.wait_for_share_status(
-            managed_share['id'], 'available')
-
 
 class ManageCIFSShareTest(ManageNFSShareTest):
     protocol = 'cifs'
diff --git a/manila_tempest_tests/tests/api/admin/test_share_manage_negative.py b/manila_tempest_tests/tests/api/admin/test_share_manage_negative.py
new file mode 100644
index 0000000..76e1d5f
--- /dev/null
+++ b/manila_tempest_tests/tests/api/admin/test_share_manage_negative.py
@@ -0,0 +1,306 @@
+# Copyright 2019 NetApp Inc.
+# 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 import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
+import testtools
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+CONF = config.CONF
+
+
+class ManageNFSShareNegativeTest(base.BaseSharesAdminTest):
+    protocol = 'nfs'
+
+    # NOTE(lseki): be careful running these tests using generic driver
+    # because cinder volumes will stay attached to service Nova VM and
+    # won't be deleted.
+
+    @classmethod
+    @testtools.skipUnless(
+        CONF.share.run_manage_unmanage_tests,
+        "Manage/unmanage tests are disabled.")
+    def resource_setup(cls):
+        if cls.protocol not in CONF.share.enable_protocols:
+            message = "%s tests are disabled" % cls.protocol
+            raise cls.skipException(message)
+
+        utils.skip_if_manage_not_supported_for_version()
+
+        super(ManageNFSShareNegativeTest, cls).resource_setup()
+
+        # Create share type
+        cls.st_name = data_utils.rand_name("manage-st-name")
+        cls.extra_specs = {
+            'storage_protocol': CONF.share.capability_storage_protocol,
+            'driver_handles_share_servers': CONF.share.multitenancy_enabled,
+            'snapshot_support': CONF.share.capability_snapshot_support,
+        }
+
+        cls.st = cls.create_share_type(
+            name=cls.st_name,
+            cleanup_in_class=True,
+            extra_specs=cls.extra_specs)
+
+    def _manage_share_for_cleanup_and_wait(self, params,
+                                           state=constants.STATUS_AVAILABLE):
+        # Manage the share, schedule its deletion upon tearDown and wait for
+        # the expected state.
+        # Return the managed share object.
+        managed_share = self.shares_v2_client.manage_share(**params)
+        self.addCleanup(self._reset_state_and_delete_share,
+                        managed_share)
+        self.shares_v2_client.wait_for_share_status(
+            managed_share['id'], state)
+
+        return managed_share
+
+    def _get_manage_params_from_share(self, share, invalid_params=None):
+        valid_params = {
+            'service_host': share['host'],
+            'protocol': share['share_proto'],
+            'share_type_id': share['share_type'],
+        }
+        if CONF.share.multitenancy_enabled:
+            valid_params['share_server_id'] = share['share_server_id']
+
+        if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
+            el = self.shares_v2_client.list_share_export_locations(share["id"])
+            valid_params['export_path'] = el[0]['path']
+
+        if invalid_params:
+            valid_params.update(invalid_params)
+
+        return valid_params
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_manage_invalid_param_raises_exception(self):
+        # Try to manage share with invalid parameters, it should not succeed
+        # because the api will reject it. If it succeeds, then this test case
+        # failed. Then, in order to remove the resource from backend, we need
+        # to manage it again, properly, so we can delete it. Consequently the
+        # second part of this test also tests that manage operation with a
+        # proper share type that works.
+
+        share = self._create_share_for_manage()
+
+        valid_params = self._get_manage_params_from_share(share)
+        self._unmanage_share_and_wait(share)
+
+        test_set = [
+            ('service_host', 'invalid_host#invalid_pool', lib_exc.NotFound),
+            ('share_type_id', 'invalid_share_type_id', lib_exc.NotFound),
+        ]
+        if CONF.share.multitenancy_enabled:
+            test_set.append(
+                ('share_server_id', 'invalid_server_id', lib_exc.BadRequest)
+            )
+
+        for invalid_key, invalid_value, expected_exception in test_set:
+            # forge a bad param
+            invalid_params = valid_params.copy()
+            invalid_params.update({
+                invalid_key: invalid_value
+            })
+
+            # the attempt to manage with bad param should fail and raise an
+            # exception
+            self.assertRaises(
+                expected_exception,
+                self.shares_v2_client.manage_share,
+                **invalid_params
+            )
+
+        # manage it properly and schedule cleanup upon tearDown
+        self._manage_share_for_cleanup_and_wait(valid_params)
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_manage_invalid_param_manage_error(self):
+        # Try to manage share with invalid parameters, it should not succeed.
+        # If it succeeds, then this test case failed. Then, in order to remove
+        # the resource from backend, we need to manage it again, properly, so
+        # we can delete it. Consequently the second part of this test also
+        # tests that manage operation with a proper share type works.
+        share = self._create_share_for_manage()
+
+        valid_params = self._get_manage_params_from_share(share)
+        self._unmanage_share_and_wait(share)
+
+        for invalid_key, invalid_value in (
+            ('export_path', 'invalid_export'),
+            ('protocol', 'invalid_protocol'),
+        ):
+
+            # forge a bad param
+            invalid_params = valid_params.copy()
+            invalid_params.update({invalid_key: invalid_value})
+
+            # the attempt to manage the share with invalid params should fail
+            # and leave it in manage_error state
+            invalid_share = self.shares_v2_client.manage_share(
+                **invalid_params
+            )
+            self.shares_v2_client.wait_for_share_status(
+                invalid_share['id'], constants.STATUS_MANAGE_ERROR)
+
+            # cleanup
+            self._unmanage_share_and_wait(invalid_share)
+
+        # manage it properly and schedule cleanup upon tearDown
+        self._manage_share_for_cleanup_and_wait(valid_params)
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_manage_share_duplicate(self):
+        share = self._create_share_for_manage()
+
+        manage_params = self._get_manage_params_from_share(share)
+        self._unmanage_share_and_wait(share)
+
+        # manage the share for the first time
+        managed_share = self._manage_share_for_cleanup_and_wait(manage_params)
+
+        # update managed share's reference
+        managed_share = self.shares_v2_client.get_share(managed_share['id'])
+        manage_params = self._get_manage_params_from_share(managed_share)
+
+        # the second attempt to manage the same share should fail
+        self.assertRaises(
+            lib_exc.Conflict,
+            self.shares_v2_client.manage_share,
+            **manage_params
+        )
+
+    @testtools.skipUnless(CONF.share.multitenancy_enabled,
+                          'Multitenancy tests are disabled.')
+    @utils.skip_if_microversion_not_supported("2.49")
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_manage_share_without_share_server_id(self):
+        share = self._create_share_for_manage()
+        manage_params = self._get_manage_params_from_share(share)
+        share_server_id = manage_params.pop('share_server_id')
+        self._unmanage_share_and_wait(share)
+
+        self.assertRaises(
+            lib_exc.BadRequest,
+            self.shares_v2_client.manage_share,
+            **manage_params)
+
+        manage_params['share_server_id'] = share_server_id
+        self._manage_share_for_cleanup_and_wait(manage_params)
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_delete_share_in_manage_error(self):
+        share = self._create_share_for_manage()
+
+        valid_params = self._get_manage_params_from_share(share)
+
+        # forge bad param to have a share in manage_error state
+        invalid_params = valid_params.copy()
+        invalid_params.update({'export_path': 'invalid'})
+        invalid_share = self.shares_v2_client.manage_share(**invalid_params)
+
+        self.shares_v2_client.wait_for_share_status(
+            invalid_share['id'], constants.STATUS_MANAGE_ERROR)
+        self._unmanage_share_and_wait(share)
+
+        # the attempt to delete a share in manage_error should raise an
+        # exception
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.shares_v2_client.delete_share,
+            invalid_share['id']
+        )
+
+        # cleanup
+        self.shares_v2_client.unmanage_share(invalid_share['id'])
+        self._manage_share_for_cleanup_and_wait(valid_params)
+
+    @testtools.skipUnless(CONF.share.run_snapshot_tests,
+                          'Snapshot tests are disabled.')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_unmanage_share_with_snapshot(self):
+        # A share with snapshot cannot be unmanaged
+        share = self._create_share_for_manage()
+
+        snap = self.create_snapshot_wait_for_active(share["id"])
+        snap = self.shares_v2_client.get_snapshot(snap['id'])
+
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.shares_v2_client.unmanage_share,
+            share['id']
+        )
+
+        # cleanup
+        self._delete_snapshot_and_wait(snap)
+        self._delete_share_and_wait(share)
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_unmanage_share_transitional_state(self):
+        # A share in transitional state cannot be unmanaged
+        share = self._create_share_for_manage()
+        for state in (constants.STATUS_CREATING,
+                      constants.STATUS_DELETING,
+                      constants.STATUS_MIGRATING,
+                      constants.STATUS_MIGRATING_TO):
+            self.shares_v2_client.reset_state(share['id'], state)
+
+            self.assertRaises(
+                lib_exc.Forbidden,
+                self.shares_v2_client.unmanage_share,
+                share['id']
+            )
+
+        # cleanup
+        self._reset_state_and_delete_share(share)
+
+    @testtools.skipUnless(CONF.share.multitenancy_enabled,
+                          'Multitenancy tests are disabled.')
+    @utils.skip_if_microversion_not_supported("2.48")
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_unmanage_share_with_server_unsupported(self):
+        share = self._create_share_for_manage()
+
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.shares_v2_client.unmanage_share,
+            share['id'], version="2.48")
+
+        self._delete_share_and_wait(share)
+
+
+class ManageCIFSShareNegativeTest(ManageNFSShareNegativeTest):
+    protocol = 'cifs'
+
+
+class ManageGLUSTERFSShareNegativeTest(ManageNFSShareNegativeTest):
+    protocol = 'glusterfs'
+
+
+class ManageHDFSShareNegativeTest(ManageNFSShareNegativeTest):
+    protocol = 'hdfs'
+
+
+class ManageCephFSShareNegativeTest(ManageNFSShareNegativeTest):
+    protocol = 'cephfs'
+
+
+class ManageMapRFSShareNegativeTest(ManageNFSShareNegativeTest):
+    protocol = 'maprfs'
diff --git a/manila_tempest_tests/tests/api/admin/test_share_servers.py b/manila_tempest_tests/tests/api/admin/test_share_servers.py
index b9376fc..c5975a4 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_servers.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_servers.py
@@ -22,7 +22,9 @@
 import testtools
 from testtools import testcase as tc
 
+from manila_tempest_tests.common import constants
 from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
 
 CONF = config.CONF
 
@@ -168,6 +170,9 @@
             "updated_at",
             "backend_details",
         ]
+        if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.49"):
+            keys.append("is_auto_deletable")
+            keys.append("identifier")
         # all expected keys are present
         for key in keys:
             self.assertIn(key, server.keys())
@@ -261,3 +266,65 @@
             if delete_share_network:
                 self.shares_v2_client.wait_for_resource_deletion(
                     sn_id=new_sn["id"])
+
+    @testtools.skipIf(CONF.share.share_network_id != "",
+                      "This test is not suitable for pre-existing "
+                      "share_network.")
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @utils.skip_if_microversion_not_supported("2.49")
+    def test_share_server_reset_state(self):
+        # Get network and subnet from existing share_network and reuse it
+        # to be able to delete share_server after test ends.
+        new_sn = self.create_share_network(
+            neutron_net_id=self.share_network['neutron_net_id'],
+            neutron_subnet_id=self.share_network['neutron_subnet_id'])
+        share = self.create_share(
+            share_type_id=self.share_type_id,
+            share_network_id=new_sn['id']
+        )
+        share = self.shares_v2_client.get_share(share['id'])
+
+        # obtain share server
+        share_server = self.shares_v2_client.show_share_server(
+            share['share_server_id']
+        )
+
+        for state in (constants.SERVER_STATE_ACTIVE,
+                      constants.SERVER_STATE_CREATING,
+                      constants.SERVER_STATE_DELETING,
+                      constants.SERVER_STATE_ERROR,
+                      constants.SERVER_STATE_MANAGE_ERROR,
+                      constants.SERVER_STATE_MANAGE_STARTING,
+                      constants.SERVER_STATE_UNMANAGE_ERROR,
+                      constants.SERVER_STATE_UNMANAGE_STARTING):
+
+            # leave it in a new state
+            self.shares_v2_client.share_server_reset_state(
+                share_server['id'],
+                status=state,
+            )
+            self.shares_v2_client.wait_for_share_server_status(
+                share_server['id'],
+                status=state
+            )
+
+        # bring the share server back in the active state
+        self.shares_v2_client.share_server_reset_state(
+            share_server['id'],
+            status=constants.SERVER_STATE_ACTIVE,
+        )
+        self.shares_v2_client.wait_for_share_server_status(
+            share_server['id'],
+            status=constants.SERVER_STATE_ACTIVE
+        )
+
+        # delete share
+        self.shares_v2_client.delete_share(share["id"])
+        self.shares_v2_client.wait_for_resource_deletion(
+            share_id=share["id"]
+        )
+
+        # delete share network. This will trigger share server deletion
+        self.shares_v2_client.delete_share_network(new_sn["id"])
+        self.shares_v2_client.wait_for_resource_deletion(
+            sn_id=new_sn['id'])
diff --git a/manila_tempest_tests/tests/api/admin/test_share_servers_manage.py b/manila_tempest_tests/tests/api/admin/test_share_servers_manage.py
new file mode 100644
index 0000000..ae4f007
--- /dev/null
+++ b/manila_tempest_tests/tests/api/admin/test_share_servers_manage.py
@@ -0,0 +1,140 @@
+# Copyright 2019 NetApp Inc.
+# 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 import config
+from tempest.lib.common.utils import data_utils
+import testtools
+from testtools import testcase as tc
+
+from manila_tempest_tests.tests.api import base
+
+CONF = config.CONF
+
+
+@base.skip_if_microversion_lt("2.49")
+@testtools.skipUnless(
+    CONF.share.multitenancy_enabled,
+    'Multitenancy tests are disabled.')
+@testtools.skipUnless(
+    CONF.share.run_manage_unmanage_tests,
+    'Manage/unmanage tests are disabled.')
+class ManageShareServersTest(base.BaseSharesAdminTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(ManageShareServersTest, cls).resource_setup()
+
+        # create share type
+        cls.st_name = data_utils.rand_name("manage-st-name")
+        cls.extra_specs = {
+            'storage_protocol': CONF.share.capability_storage_protocol,
+            'driver_handles_share_servers': CONF.share.multitenancy_enabled,
+        }
+        cls.share_type = cls.create_share_type(
+            name=cls.st_name,
+            cleanup_in_class=True,
+            extra_specs=cls.extra_specs)
+
+    @testtools.skipIf(CONF.share.share_network_id != "",
+                      "This test is not suitable for pre-existing "
+                      "share_network.")
+    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+    def test_manage_share_server(self):
+
+        # create a new share network to make sure that a new share server
+        # will be created
+        original_share_network = self.shares_v2_client.get_share_network(
+            self.shares_v2_client.share_network_id
+        )
+        share_network = self.create_share_network(
+            neutron_net_id=original_share_network['neutron_net_id'],
+            neutron_subnet_id=original_share_network['neutron_subnet_id'],
+            cleanup_in_class=True
+        )
+
+        # create share
+        share = self.create_share(
+            share_type_id=self.share_type['share_type']['id'],
+            share_network_id=share_network['id']
+        )
+        share = self.shares_v2_client.get_share(share['id'])
+        el = self.shares_v2_client.list_share_export_locations(share['id'])
+        share['export_locations'] = el
+        share_server = self.shares_v2_client.show_share_server(
+            share['share_server_id']
+        )
+
+        keys = [
+            "id",
+            "host",
+            "project_id",
+            "status",
+            "share_network_name",
+            "created_at",
+            "updated_at",
+            "backend_details",
+            "is_auto_deletable",
+            "identifier",
+        ]
+        # all expected keys are present
+        for key in keys:
+            self.assertIn(key, share_server)
+
+        # check that the share server is initially auto-deletable
+        self.assertIs(True, share_server["is_auto_deletable"])
+        self.assertIsNotNone(share_server["identifier"])
+
+        self._unmanage_share_and_wait(share)
+
+        # Starting from microversion 2.49, any share server that has ever had
+        # an unmanaged share will never be auto-deleted.
+        share_server = self.shares_v2_client.show_share_server(
+            share_server['id']
+        )
+        self.assertIs(False, share_server['is_auto_deletable'])
+
+        # unmanage share server and manage it again
+        self._unmanage_share_server_and_wait(share_server)
+        managed_share_server = self._manage_share_server(share_server)
+        managed_share = self._manage_share(
+            share,
+            name="managed share that had ID %s" % share['id'],
+            description="description for managed share",
+            share_server_id=managed_share_server['id']
+        )
+
+        # check managed share server
+        managed_share_server = self.shares_v2_client.show_share_server(
+            managed_share_server['id']
+        )
+
+        # all expected keys are present in the managed share server
+        for key in keys:
+            self.assertIn(key, managed_share_server)
+
+        # check that managed share server is used by the managed share
+        self.assertEqual(
+            managed_share['share_server_id'],
+            managed_share_server['id']
+        )
+
+        # check that the managed share server is still not auto-deletable
+        self.assertIs(False, managed_share_server["is_auto_deletable"])
+
+        # delete share
+        self._delete_share_and_wait(managed_share)
+
+        # delete share server
+        self._delete_share_server_and_wait(managed_share_server['id'])
diff --git a/manila_tempest_tests/tests/api/admin/test_share_servers_manage_negative.py b/manila_tempest_tests/tests/api/admin/test_share_servers_manage_negative.py
new file mode 100644
index 0000000..5cd39e7
--- /dev/null
+++ b/manila_tempest_tests/tests/api/admin/test_share_servers_manage_negative.py
@@ -0,0 +1,320 @@
+# Copyright 2019 NetApp Inc.
+# 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 ddt
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
+import testtools
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests import share_exceptions
+from manila_tempest_tests.tests.api import base
+
+CONF = config.CONF
+
+
+@base.skip_if_microversion_lt("2.49")
+@testtools.skipUnless(
+    CONF.share.multitenancy_enabled,
+    'Multitenancy tests are disabled')
+@testtools.skipUnless(
+    CONF.share.run_manage_unmanage_tests,
+    'Manage/unmanage tests are disabled.')
+@ddt.ddt
+class ManageShareServersNegativeTest(base.BaseSharesAdminTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(ManageShareServersNegativeTest, cls).resource_setup()
+
+        # create share type
+        cls.st_name = data_utils.rand_name("manage-st-name")
+        cls.extra_specs = {
+            'storage_protocol': CONF.share.capability_storage_protocol,
+            'driver_handles_share_servers': CONF.share.multitenancy_enabled,
+        }
+        cls.share_type = cls.create_share_type(
+            name=cls.st_name,
+            cleanup_in_class=True,
+            extra_specs=cls.extra_specs)
+        cls.original_share_network = cls.shares_v2_client.get_share_network(
+            cls.shares_v2_client.share_network_id)
+
+    def _create_share_with_new_share_network(self):
+        share_network = self.create_share_network(
+            neutron_net_id=self.original_share_network['neutron_net_id'],
+            neutron_subnet_id=self.original_share_network['neutron_subnet_id'],
+            cleanup_in_class=True
+        )
+        share = self.create_share(
+            share_type_id=self.share_type['share_type']['id'],
+            share_network_id=share_network['id']
+        )
+        return self.shares_v2_client.get_share(share['id'])
+
+    @ddt.data(
+        ('host', 'invalid_host'),
+        ('share_network_id', 'invalid_share_network_id'),
+    )
+    @ddt.unpack
+    @testtools.skipIf(CONF.share.share_network_id != "",
+                      "This test is not suitable for pre-existing "
+                      "share_network.")
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_manage_share_server_invalid_params(self, param, invalid_value):
+
+        # create share
+        share = self._create_share_with_new_share_network()
+        el = self.shares_v2_client.list_share_export_locations(share['id'])
+        share['export_locations'] = el
+        share_server = self.shares_v2_client.show_share_server(
+            share['share_server_id']
+        )
+
+        self._unmanage_share_and_wait(share)
+        self._unmanage_share_server_and_wait(share_server)
+
+        # forge invalid params
+        invalid_params = share_server.copy()
+        invalid_params[param] = invalid_value
+
+        # try to manage in the wrong way
+        self.assertRaises(
+            lib_exc.BadRequest,
+            self._manage_share_server,
+            share_server,
+            invalid_params
+        )
+
+        # manage in the correct way
+        managed_share_server = self._manage_share_server(share_server)
+        managed_share = self._manage_share(
+            share,
+            name="managed share that had ID %s" % share['id'],
+            description="description for managed share",
+            share_server_id=managed_share_server['id']
+        )
+
+        # delete share
+        self._delete_share_and_wait(managed_share)
+
+        # delete share server
+        self._delete_share_server_and_wait(managed_share_server['id'])
+
+    @testtools.skipIf(CONF.share.share_network_id != "",
+                      "This test is not suitable for pre-existing "
+                      "share_network.")
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_delete_share_server_invalid_state(self):
+
+        # create share
+        share = self._create_share_with_new_share_network()
+
+        for state in (constants.SERVER_STATE_MANAGE_STARTING,
+                      constants.SERVER_STATE_CREATING,
+                      constants.SERVER_STATE_DELETING):
+            # leave it in the wrong state
+            self.shares_v2_client.share_server_reset_state(
+                share['share_server_id'],
+                status=state,
+            )
+
+            # try to delete
+            self.assertRaises(
+                lib_exc.Forbidden,
+                self.shares_v2_client.delete_share_server,
+                share['share_server_id'],
+            )
+
+            # put it in the correct state
+            self.shares_v2_client.share_server_reset_state(
+                share['share_server_id'],
+                status=constants.SERVER_STATE_ACTIVE,
+            )
+            self.shares_v2_client.wait_for_share_server_status(
+                share['share_server_id'],
+                constants.SERVER_STATE_ACTIVE,
+            )
+
+        # delete share
+        self._delete_share_and_wait(share)
+
+        # delete share server
+        self._delete_share_server_and_wait(share['share_server_id'])
+
+    @testtools.skipIf(CONF.share.share_network_id != "",
+                      "This test is not suitable for pre-existing "
+                      "share_network.")
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_unmanage_share_server_invalid_state(self):
+
+        # create share
+        share = self._create_share_with_new_share_network()
+
+        for state in (constants.SERVER_STATE_MANAGE_STARTING,
+                      constants.SERVER_STATE_CREATING,
+                      constants.SERVER_STATE_DELETING):
+            # leave it in the wrong state
+            self.shares_v2_client.share_server_reset_state(
+                share['share_server_id'],
+                status=state,
+            )
+
+            # try to unmanage
+            self.assertRaises(
+                lib_exc.BadRequest,
+                self.shares_v2_client.unmanage_share_server,
+                share['share_server_id'],
+            )
+
+            # put it in the correct state
+            self.shares_v2_client.share_server_reset_state(
+                share['share_server_id'],
+                status=constants.SERVER_STATE_ACTIVE,
+            )
+            self.shares_v2_client.wait_for_share_server_status(
+                share['share_server_id'],
+                constants.SERVER_STATE_ACTIVE,
+            )
+
+        # delete share
+        self._delete_share_and_wait(share)
+
+        # delete share server
+        self._delete_share_server_and_wait(share['share_server_id'])
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_share_server_reset_state_invalid_state(self):
+
+        # create share
+        share = self.create_share(
+            share_type_id=self.share_type['share_type']['id'])
+        share = self.shares_v2_client.get_share(share['id'])
+
+        # try to change it to wrong state
+        self.assertRaises(
+            lib_exc.BadRequest,
+            self.shares_v2_client.share_server_reset_state,
+            share['share_server_id'],
+            status='invalid_state',
+        )
+
+        # delete share
+        self._delete_share_and_wait(share)
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_unmanage_share_server_with_share(self):
+
+        # create share
+        share = self.create_share(
+            share_type_id=self.share_type['share_type']['id'])
+        share = self.shares_v2_client.get_share(share['id'])
+
+        # try to unmanage
+        self.assertRaises(
+            lib_exc.BadRequest,
+            self.shares_v2_client.unmanage_share_server,
+            share['share_server_id'],
+        )
+
+        # delete share
+        self._delete_share_and_wait(share)
+
+    @testtools.skipIf(CONF.share.share_network_id != "",
+                      "This test is not suitable for pre-existing "
+                      "share_network.")
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_manage_share_server_invalid_identifier(self):
+        # create share
+        share = self._create_share_with_new_share_network()
+        el = self.shares_v2_client.list_share_export_locations(share['id'])
+        share['export_locations'] = el
+        share_server = self.shares_v2_client.show_share_server(
+            share['share_server_id']
+        )
+
+        self._unmanage_share_and_wait(share)
+        self._unmanage_share_server_and_wait(share_server)
+
+        # forge invalid params
+        invalid_params = share_server.copy()
+        invalid_params['identifier'] = 'invalid_id'
+
+        self.assertRaises(
+            share_exceptions.ShareServerBuildErrorException,
+            self._manage_share_server,
+            invalid_params
+        )
+
+        # manage in the correct way
+        managed_share_server = self._manage_share_server(share_server)
+        managed_share_server = self.shares_v2_client.show_share_server(
+            managed_share_server['id']
+        )
+        managed_share = self._manage_share(
+            share,
+            name="managed share that had ID %s" % share['id'],
+            description="description for managed share",
+            share_server_id=managed_share_server['id']
+        )
+
+        # delete share
+        self._delete_share_and_wait(managed_share)
+
+        # delete share server
+        self._delete_share_server_and_wait(managed_share_server['id'])
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_manage_share_server_double_manage(self):
+
+        # create share
+        share = self.create_share(
+            share_type_id=self.share_type['share_type']['id'])
+        share = self.shares_v2_client.get_share(share['id'])
+
+        share_server = self.shares_v2_client.show_share_server(
+            share['share_server_id'])
+
+        # try with more data around the identifier
+        invalid_params = share_server.copy()
+        invalid_params['identifier'] = (
+            'foo_' + share_server['identifier'] + '_bar')
+
+        self.assertRaises(
+            lib_exc.BadRequest,
+            self._manage_share_server,
+            invalid_params)
+
+        # try with part of the identifier
+        invalid_params['identifier'] = share_server['identifier'].split("-")[2]
+
+        self.assertRaises(
+            lib_exc.BadRequest,
+            self._manage_share_server,
+            invalid_params)
+
+        # try with same identifier but underscores
+        invalid_params['identifier'] = (
+            share_server['identifier'].replace("-", "_"))
+
+        self.assertRaises(
+            lib_exc.BadRequest,
+            self._manage_share_server,
+            invalid_params)
+
+        # delete share
+        self._delete_share_and_wait(share)
diff --git a/manila_tempest_tests/tests/api/admin/test_snapshot_manage.py b/manila_tempest_tests/tests/api/admin/test_snapshot_manage.py
index 8798212..aa7ccc8 100644
--- a/manila_tempest_tests/tests/api/admin/test_snapshot_manage.py
+++ b/manila_tempest_tests/tests/api/admin/test_snapshot_manage.py
@@ -14,13 +14,13 @@
 #    under the License.
 
 import ddt
-import six
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import exceptions as lib_exc
 import testtools
 from testtools import testcase as tc
 
+from manila_tempest_tests.common import constants
 from manila_tempest_tests.tests.api import base
 from manila_tempest_tests import utils
 
@@ -36,27 +36,24 @@
 
     @classmethod
     @base.skip_if_microversion_lt("2.12")
-    @testtools.skipIf(
-        CONF.share.multitenancy_enabled,
-        "Only for driver_handles_share_servers = False driver mode.")
     @testtools.skipUnless(
         CONF.share.run_manage_unmanage_snapshot_tests,
         "Manage/unmanage snapshot tests are disabled.")
     def resource_setup(cls):
-        super(ManageNFSSnapshotTest, cls).resource_setup()
         if cls.protocol not in CONF.share.enable_protocols:
             message = "%s tests are disabled" % cls.protocol
             raise cls.skipException(message)
 
+        utils.skip_if_manage_not_supported_for_version()
+
+        super(ManageNFSSnapshotTest, cls).resource_setup()
+
         # Create share type
         cls.st_name = data_utils.rand_name("tempest-manage-st-name")
         cls.extra_specs = {
             'storage_protocol': CONF.share.capability_storage_protocol,
-            'driver_handles_share_servers': False,
-            'snapshot_support': six.text_type(
-                CONF.share.capability_snapshot_support),
-            'create_share_from_snapshot_support': six.text_type(
-                CONF.share.capability_create_share_from_snapshot_support)
+            'driver_handles_share_servers': CONF.share.multitenancy_enabled,
+            'snapshot_support': CONF.share.capability_snapshot_support,
         }
 
         cls.st = cls.create_share_type(
@@ -76,6 +73,8 @@
                 snapshot['id'])
         description = "Description for 'managed' snapshot"
 
+        utils.skip_if_manage_not_supported_for_version(version)
+
         # Manage snapshot
         share_id = snapshot['share_id']
         snapshot = self.shares_v2_client.manage_snapshot(
@@ -96,8 +95,10 @@
                 'client': self.shares_v2_client})
 
         # Wait for success
-        self.shares_v2_client.wait_for_snapshot_status(snapshot['id'],
-                                                       'available')
+        self.shares_v2_client.wait_for_snapshot_status(
+            snapshot['id'],
+            constants.STATUS_AVAILABLE
+        )
 
         # Verify manage snapshot API response
         expected_keys = ["status", "links", "share_id", "name",
@@ -135,6 +136,8 @@
         version as well as versions 2.12 (when the API was introduced) and
         2.16.
         """
+        utils.skip_if_manage_not_supported_for_version(version)
+
         # Skip in case specified version is not supported
         self.skip_if_microversion_not_supported(version)
 
diff --git a/manila_tempest_tests/tests/api/admin/test_snapshot_manage_negative.py b/manila_tempest_tests/tests/api/admin/test_snapshot_manage_negative.py
index 575060c..287654e 100644
--- a/manila_tempest_tests/tests/api/admin/test_snapshot_manage_negative.py
+++ b/manila_tempest_tests/tests/api/admin/test_snapshot_manage_negative.py
@@ -20,7 +20,9 @@
 import testtools
 from testtools import testcase as tc
 
+from manila_tempest_tests.common import constants
 from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
 
 CONF = config.CONF
 
@@ -30,27 +32,25 @@
 
     @classmethod
     @base.skip_if_microversion_lt("2.12")
-    @testtools.skipIf(
-        CONF.share.multitenancy_enabled,
-        "Only for driver_handles_share_servers = False driver mode.")
     @testtools.skipUnless(
         CONF.share.run_manage_unmanage_snapshot_tests,
         "Manage/unmanage snapshot tests are disabled.")
     def resource_setup(cls):
-        super(ManageNFSSnapshotNegativeTest, cls).resource_setup()
         if cls.protocol not in CONF.share.enable_protocols:
             message = "%s tests are disabled" % cls.protocol
             raise cls.skipException(message)
 
+        utils.skip_if_manage_not_supported_for_version()
+
+        super(ManageNFSSnapshotNegativeTest, cls).resource_setup()
+
         # Create share type
         cls.st_name = data_utils.rand_name("tempest-manage-st-name")
         cls.extra_specs = {
             'storage_protocol': CONF.share.capability_storage_protocol,
-            'driver_handles_share_servers': False,
+            'driver_handles_share_servers': CONF.share.multitenancy_enabled,
             'snapshot_support': six.text_type(
                 CONF.share.capability_snapshot_support),
-            'create_share_from_snapshot_support': six.text_type(
-                CONF.share.capability_create_share_from_snapshot_support),
         }
 
         cls.st = cls.create_share_type(
@@ -66,12 +66,13 @@
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
     def test_manage_not_found(self):
-        # Manage snapshot fails
-        self.assertRaises(lib_exc.NotFound,
-                          self.shares_v2_client.manage_snapshot,
-                          'fake-share-id',
-                          'fake-vol-snap-id',
-                          driver_options={})
+        # Manage non-existing snapshot fails
+        self.assertRaises(
+            lib_exc.NotFound,
+            self.shares_v2_client.manage_snapshot,
+            'fake-share-id',
+            'fake-provider-location',
+        )
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
     def test_manage_already_exists(self):
@@ -79,24 +80,74 @@
 
         # Create snapshot
         snap = self.create_snapshot_wait_for_active(self.share['id'])
-        get_snap = self.shares_v2_client.get_snapshot(snap['id'])
-        self.assertEqual(self.share['id'], get_snap['share_id'])
-        self.assertIsNotNone(get_snap['provider_location'])
+        snap = self.shares_v2_client.get_snapshot(snap['id'])
+        self.assertEqual(self.share['id'], snap['share_id'])
+        self.assertIsNotNone(snap['provider_location'])
 
         # Manage snapshot fails
-        self.assertRaises(lib_exc.Conflict,
-                          self.shares_v2_client.manage_snapshot,
-                          self.share['id'],
-                          get_snap['provider_location'],
-                          driver_options={})
+        self.assertRaises(
+            lib_exc.Conflict,
+            self.shares_v2_client.manage_snapshot,
+            self.share['id'],
+            snap['provider_location']
+        )
 
         # Delete snapshot
-        self.shares_v2_client.delete_snapshot(get_snap['id'])
+        self._delete_snapshot_and_wait(snap)
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_manage_invalid_provider_location(self):
+        # Manage a snapshot with wrong provider location fails
+
+        # Create snapshot
+        snap = self.create_snapshot_wait_for_active(self.share['id'])
+        snap = self.shares_v2_client.get_snapshot(snap['id'])
+
+        # Unmanage snapshot
+        self.shares_v2_client.unmanage_snapshot(snap['id'])
         self.shares_client.wait_for_resource_deletion(
-            snapshot_id=get_snap['id'])
-        self.assertRaises(lib_exc.NotFound,
-                          self.shares_v2_client.get_snapshot,
-                          get_snap['id'])
+            snapshot_id=snap['id']
+        )
+
+        # Manage snapshot with invalid provider location leaves it in
+        # manage_error state
+        invalid_snap = self.shares_v2_client.manage_snapshot(
+            self.share['id'],
+            'invalid_provider_location',
+            driver_options={}
+        )
+        self.shares_v2_client.wait_for_snapshot_status(
+            invalid_snap['id'],
+            constants.STATUS_MANAGE_ERROR
+        )
+        self.shares_v2_client.unmanage_snapshot(invalid_snap['id'])
+
+        # Manage it properly and delete
+        managed_snap = self.shares_v2_client.manage_snapshot(
+            self.share['id'],
+            snap['provider_location']
+        )
+        self.shares_v2_client.wait_for_snapshot_status(
+            managed_snap['id'],
+            constants.STATUS_AVAILABLE
+        )
+        self._delete_snapshot_and_wait(managed_snap)
+
+    @testtools.skipUnless(CONF.share.multitenancy_enabled,
+                          'Multitenancy tests are disabled.')
+    @utils.skip_if_microversion_not_supported("2.48")
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_unmanage_snapshot_with_server_unsupported(self):
+        share = self._create_share_for_manage()
+        snap = self.create_snapshot_wait_for_active(share["id"])
+
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.shares_v2_client.unmanage_snapshot,
+            snap['id'], version="2.48")
+
+        self._delete_snapshot_and_wait(snap)
+        self._delete_share_and_wait(share)
 
 
 class ManageCIFSSnapshotNegativeTest(ManageNFSSnapshotNegativeTest):
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index fefdc81..06b9d3f 100644
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -1115,6 +1115,88 @@
             name=share_group_type_name, share_types=[cls.share_type_id],
             client=cls.admin_shares_v2_client)
 
+    def _create_share_for_manage(self):
+        creation_data = {
+            'share_type_id': self.st['share_type']['id'],
+            'share_protocol': self.protocol,
+        }
+
+        share = self.create_share(**creation_data)
+        share = self.shares_v2_client.get_share(share['id'])
+
+        if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
+            el = self.shares_v2_client.list_share_export_locations(share["id"])
+            share["export_locations"] = el
+
+        return share
+
+    def _unmanage_share_and_wait(self, share):
+        self.shares_v2_client.unmanage_share(share['id'])
+        self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
+
+    def _reset_state_and_delete_share(self, share):
+        self.shares_v2_client.reset_state(share['id'])
+        self._delete_share_and_wait(share)
+
+    def _delete_snapshot_and_wait(self, snap):
+        self.shares_v2_client.delete_snapshot(snap['id'])
+        self.shares_v2_client.wait_for_resource_deletion(
+            snapshot_id=snap['id']
+        )
+        self.assertRaises(exceptions.NotFound,
+                          self.shares_v2_client.get_snapshot,
+                          snap['id'])
+
+    def _delete_share_and_wait(self, share):
+        self.shares_v2_client.delete_share(share['id'])
+        self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
+        self.assertRaises(exceptions.NotFound,
+                          self.shares_v2_client.get_share,
+                          share['id'])
+
+    def _manage_share(self, share, name, description, share_server_id):
+        managed_share = self.shares_v2_client.manage_share(
+            service_host=share['host'],
+            export_path=share['export_locations'][0],
+            protocol=share['share_proto'],
+            share_type_id=self.share_type['share_type']['id'],
+            name=name,
+            description=description,
+            share_server_id=share_server_id
+        )
+        self.shares_v2_client.wait_for_share_status(
+            managed_share['id'], constants.STATUS_AVAILABLE
+        )
+
+        return managed_share
+
+    def _unmanage_share_server_and_wait(self, server):
+        self.shares_v2_client.unmanage_share_server(server['id'])
+        self.shares_v2_client.wait_for_resource_deletion(
+            server_id=server['id']
+        )
+
+    def _manage_share_server(self, share_server, fields=None):
+        params = fields or {}
+        managed_share_server = self.shares_v2_client.manage_share_server(
+            params.get('host', share_server['host']),
+            params.get('share_network_id', share_server['share_network_id']),
+            params.get('identifier', share_server['identifier']),
+        )
+        self.shares_v2_client.wait_for_share_server_status(
+            managed_share_server['id'],
+            constants.SERVER_STATE_ACTIVE,
+        )
+
+        return managed_share_server
+
+    def _delete_share_server_and_wait(self, share_server_id):
+        self.shares_v2_client.delete_share_server(
+            share_server_id
+        )
+        self.shares_v2_client.wait_for_resource_deletion(
+            server_id=share_server_id)
+
 
 class BaseSharesMixedTest(BaseSharesTest):
     """Base test case class for all Shares API tests with all user roles."""
diff --git a/manila_tempest_tests/utils.py b/manila_tempest_tests/utils.py
index 9f08cac..c2c7ec6 100644
--- a/manila_tempest_tests/utils.py
+++ b/manila_tempest_tests/utils.py
@@ -168,3 +168,12 @@
             CONF.share.capability_create_share_from_snapshot_support)
 
     return extra_specs
+
+
+def skip_if_manage_not_supported_for_version(
+        version=CONF.share.max_api_microversion):
+    if (is_microversion_lt(version, "2.49")
+            and CONF.share.multitenancy_enabled):
+        raise testtools.TestCase.skipException(
+            "Share manage tests with multitenancy are disabled for "
+            "microversion < 2.49")