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")