Merge "Change the expected status response from Forbidden to NotFound"
diff --git a/manila_tempest_tests/common/constants.py b/manila_tempest_tests/common/constants.py
index 3488bc5..416de56 100644
--- a/manila_tempest_tests/common/constants.py
+++ b/manila_tempest_tests/common/constants.py
@@ -108,3 +108,6 @@
SERVER_STATE_UNMANAGE_STARTING = 'unmanage_starting'
STATUS_SERVER_MIGRATING = 'server_migrating'
STATUS_SERVER_MIGRATING_TO = 'server_migrating_to'
+
+# Share transfer
+SHARE_TRANSFER_VERSION = "2.77"
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 fe3e31c..f4538d7 100644
--- a/manila_tempest_tests/services/share/v2/json/shares_client.py
+++ b/manila_tempest_tests/services/share/v2/json/shares_client.py
@@ -373,6 +373,61 @@
return rest_client.ResponseBody(resp, body)
###############
+ def create_share_transfer(self, share_id, name=None,
+ version=LATEST_MICROVERSION):
+ if name is None:
+ name = data_utils.rand_name("tempest-created-share-transfer")
+ post_body = {
+ "transfer": {
+ "share_id": share_id,
+ "name": name
+ }
+ }
+ body = json.dumps(post_body)
+ resp, body = self.post("share-transfers", body, version=version)
+ self.expected_success(202, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_share_transfer(self, transfer_id, version=LATEST_MICROVERSION):
+ resp, body = self.delete("share-transfers/%s" % transfer_id,
+ version=version)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_share_transfers(self, detailed=False, params=None,
+ version=LATEST_MICROVERSION):
+ """Get list of share transfers w/o filters."""
+ uri = 'share-transfers/detail' if detailed else 'share-transfers'
+ uri += '?%s' % parse.urlencode(params) if params else ''
+ resp, body = self.get(uri, version=version)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def get_share_transfer(self, transfer_id, version=LATEST_MICROVERSION):
+ resp, body = self.get("share-transfers/%s" % transfer_id,
+ version=version)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def accept_share_transfer(self, transfer_id, auth_key,
+ clear_access_rules=False,
+ version=LATEST_MICROVERSION):
+ post_body = {
+ "accept": {
+ "auth_key": auth_key,
+ "clear_access_rules": clear_access_rules
+ }
+ }
+ body = json.dumps(post_body)
+ resp, body = self.post("share-transfers/%s/accept" % transfer_id,
+ body, version=version)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+###############
def get_instances_of_share(self, share_id, version=LATEST_MICROVERSION):
resp, body = self.get("shares/%s/instances" % share_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
index 1e9073d..09a40d9 100644
--- 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
@@ -333,7 +333,8 @@
invalid_params)
# try with part of the identifier
- invalid_params['identifier'] = share_server['identifier'].split("-")[2]
+ invalid_params['identifier'] = (
+ share_server['identifier'].split("-")[-1])
self.assertRaises(
lib_exc.BadRequest,
diff --git a/manila_tempest_tests/tests/api/test_share_transfers.py b/manila_tempest_tests/tests/api/test_share_transfers.py
new file mode 100644
index 0000000..56f8e0c
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_share_transfers.py
@@ -0,0 +1,107 @@
+# Copyright (C) 2022 China Telecom Digital Intelligence.
+# 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 decorators
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.common import waiters
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+CONF = config.CONF
+
+
+class ShareTransferTest(base.BaseSharesMixedTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(ShareTransferTest, cls).skip_checks()
+ utils.check_skip_if_microversion_not_supported(
+ constants.SHARE_TRANSFER_VERSION)
+ if CONF.share.multitenancy_enabled:
+ raise cls.skipException(
+ 'Only for driver_handles_share_servers = False driver mode.')
+
+ @classmethod
+ def resource_setup(cls):
+ super(ShareTransferTest, cls).resource_setup()
+ # create share_type with dhss=False
+ extra_specs = cls.add_extra_specs_to_dict()
+ cls.share_type = cls.create_share_type(extra_specs=extra_specs)
+ cls.share_type_id = cls.share_type['id']
+
+ @decorators.idempotent_id('716e71a0-8265-4410-9170-08714095d9e8')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_and_delete_share_transfer(self):
+ # create share
+ share_name = data_utils.rand_name("tempest-share-name")
+ share = self.create_share(name=share_name,
+ share_type_id=self.share_type_id,
+ cleanup_in_class=False)
+
+ # create share transfer
+ transfer = self.shares_v2_client.create_share_transfer(
+ share['id'], name='tempest_share_transfer')['transfer']
+ waiters.wait_for_resource_status(
+ self.shares_client, share['id'], 'awaiting_transfer')
+
+ # check transfer exists and show transfer
+ transfer_show = self.shares_v2_client.get_share_transfer(
+ transfer['id'])['transfer']
+ self.assertEqual(transfer_show['name'], 'tempest_share_transfer')
+
+ # delete share transfer
+ self.shares_v2_client.delete_share_transfer(transfer['id'])
+ waiters.wait_for_resource_status(
+ self.shares_client, share['id'], 'available')
+
+ # check transfer not in transfer list
+ transfers = self.shares_v2_client.list_share_transfers()['transfers']
+ transfer_ids = [tf['id'] for tf in transfers]
+ self.assertNotIn(transfer['id'], transfer_ids)
+
+ @decorators.idempotent_id('3c2622ab-3368-4693-afb6-e60bd27e61ef')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_create_and_accept_share_transfer(self):
+ # create share
+ share_name = data_utils.rand_name("tempest-share-name")
+ share = self.create_share(name=share_name,
+ share_type_id=self.share_type_id)
+
+ # create share transfer
+ transfer = self.shares_v2_client.create_share_transfer(
+ share['id'])['transfer']
+ waiters.wait_for_resource_status(
+ self.shares_client, share['id'], 'awaiting_transfer')
+
+ # accept share transfer by alt project
+ self.alt_shares_v2_client.accept_share_transfer(transfer['id'],
+ transfer['auth_key'])
+ waiters.wait_for_resource_status(
+ self.alt_shares_client, share['id'], 'available')
+
+ # check share in alt project
+ shares = self.alt_shares_v2_client.list_shares(
+ detailed=True)['shares']
+ share_ids = [sh['id'] for sh in shares] if shares else []
+ self.assertIn(share['id'], share_ids)
+
+ # delete the share
+ self.alt_shares_v2_client.delete_share(share['id'])
+ self.alt_shares_v2_client.wait_for_resource_deletion(
+ share_id=share["id"])
diff --git a/manila_tempest_tests/tests/api/test_share_transfers_negative.py b/manila_tempest_tests/tests/api/test_share_transfers_negative.py
new file mode 100644
index 0000000..0672459
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_share_transfers_negative.py
@@ -0,0 +1,137 @@
+# Copyright (C) 2022 China Telecom Digital Intelligence.
+# 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 oslo_utils import uuidutils
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.common import waiters
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+CONF = config.CONF
+
+
+class ShareTransferNegativeTest(base.BaseSharesMixedTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(ShareTransferNegativeTest, cls).skip_checks()
+ utils.check_skip_if_microversion_not_supported(
+ constants.SHARE_TRANSFER_VERSION)
+ if CONF.share.multitenancy_enabled:
+ raise cls.skipException(
+ 'Only for driver_handles_share_servers = False driver mode.')
+
+ @classmethod
+ def resource_setup(cls):
+ super(ShareTransferNegativeTest, cls).resource_setup()
+ # create share_type with dhss=False
+ extra_specs = cls.add_extra_specs_to_dict()
+ cls.share_type = cls.create_share_type(extra_specs=extra_specs)
+ cls.share_type_id = cls.share_type['id']
+
+ def _create_share_transfer(self, share):
+ transfer = self.shares_v2_client.create_share_transfer(
+ share['id'])['transfer']
+ waiters.wait_for_resource_status(
+ self.shares_client, share['id'], 'awaiting_transfer')
+ self.addCleanup(waiters.wait_for_resource_status, self.shares_client,
+ share['id'], 'available')
+ self.addCleanup(self.shares_v2_client.delete_share_transfer,
+ transfer['id'])
+ return transfer
+
+ @decorators.idempotent_id('baf66f62-253e-40dd-a6a9-109bc7613e52')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_show_transfer_of_other_tenants(self):
+ # create share
+ share_name = data_utils.rand_name("tempest-share-name")
+ share = self.create_share(
+ name=share_name,
+ share_type_id=self.share_type_id)
+
+ # create share transfer
+ transfer = self._create_share_transfer(share)
+
+ self.assertRaises(lib_exc.NotFound,
+ self.alt_shares_v2_client.get_share_transfer,
+ transfer['id'])
+
+ @decorators.idempotent_id('4b9e75b1-4ac6-4111-b09e-e6dacd0ac2c3')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_show_nonexistent_transfer(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.shares_v2_client.get_share_transfer,
+ str(uuidutils.generate_uuid()))
+
+ @decorators.idempotent_id('b3e26356-5eb0-4f73-b5a7-d3594cc2f30e')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_delete_transfer_of_other_tenants(self):
+ # create share
+ share_name = data_utils.rand_name("tempest-share-name")
+ share = self.create_share(
+ name=share_name,
+ share_type_id=self.share_type_id)
+
+ # create share transfer
+ transfer = self._create_share_transfer(share)
+
+ self.assertRaises(lib_exc.NotFound,
+ self.alt_shares_v2_client.delete_share_transfer,
+ transfer['id'])
+
+ @decorators.idempotent_id('085d5971-fe6e-4497-93cb-f1eb176a10da')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_delete_nonexistent_transfer(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.shares_v2_client.delete_share_transfer,
+ str(uuidutils.generate_uuid()))
+
+ @decorators.idempotent_id('cc7af032-0504-417e-8ab9-73b37bed7f85')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_accept_transfer_without_auth_key(self):
+ # create share
+ share_name = data_utils.rand_name("tempest-share-name")
+ share = self.create_share(
+ name=share_name,
+ share_type_id=self.share_type_id)
+
+ # create share transfer
+ transfer = self._create_share_transfer(share)
+
+ self.assertRaises(lib_exc.BadRequest,
+ self.alt_shares_v2_client.accept_share_transfer,
+ transfer['id'], "")
+
+ @decorators.idempotent_id('05a6a345-7609-421f-be21-d79041970674')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_accept_transfer_with_incorrect_auth_key(self):
+ # create share
+ share_name = data_utils.rand_name("tempest-share-name")
+ share = self.create_share(
+ name=share_name,
+ share_type_id=self.share_type_id)
+
+ # create share transfer
+ transfer = self._create_share_transfer(share)
+
+ self.assertRaises(lib_exc.BadRequest,
+ self.alt_shares_v2_client.accept_share_transfer,
+ transfer['id'], "incorrect_auth_key")
diff --git a/manila_tempest_tests/tests/scenario/manager_share.py b/manila_tempest_tests/tests/scenario/manager_share.py
index a847217..6d6efc0 100644
--- a/manila_tempest_tests/tests/scenario/manager_share.py
+++ b/manila_tempest_tests/tests/scenario/manager_share.py
@@ -84,7 +84,7 @@
if CONF.share.image_with_share_tools == 'centos':
self.image_ref = self._create_centos_based_glance_image()
elif CONF.share.image_with_share_tools:
- images = self.compute_images_client.list_images()["images"]
+ images = self.image_client.list_images()["images"]
for img in images:
if img["name"] == CONF.share.image_with_share_tools:
self.image_id = img['id']
@@ -186,8 +186,7 @@
storage_net_nic[0]['addr']
)
# Attach a floating IP
- self.compute_floating_ips_client.associate_floating_ip_to_server(
- floating_ip['floating_ip_address'], instance['id'])
+ self.associate_floating_ip(floating_ip, instance)
self.assertIsNotNone(server_ip)
# Check ssh
diff --git a/zuul.d/manila-tempest-jobs.yaml b/zuul.d/manila-tempest-jobs.yaml
index 92ec2d8..3a48147 100644
--- a/zuul.d/manila-tempest-jobs.yaml
+++ b/zuul.d/manila-tempest-jobs.yaml
@@ -321,6 +321,7 @@
MANILA_USE_SERVICE_INSTANCE_PASSWORD: true
MANILA_DEFAULT_SHARE_TYPE_EXTRA_SPECS: 'snapshot_support=True create_share_from_snapshot_support=True'
TEMPEST_USE_TEST_ACCOUNTS: true
+ GLANCE_ENFORCE_SCOPE: false
devstack_services:
cinder: true
devstack_local_conf:
@@ -506,6 +507,105 @@
IP_VERSION: 4
- job:
+ name: manila-tempest-plugin-multinode-base
+ abstract: true
+ description: |
+ Base job for testing multinode with Manila. Manila is enabled in
+ the controller node; and we have an additional compute node.
+ parent: tempest-multinode-full-py3
+ timeout: 10800
+ irrelevant-files: *irrelevant-files
+ required-projects: *manila-tempest-required-projects
+ vars:
+ tox_envlist: all
+ tempest_test_regex: manila_tempest_tests
+ tempest_plugins:
+ - manila-tempest-plugin
+ tempest_concurrency: 8
+ devstack_services:
+ cinder: false
+ c-bak: false
+ s-account: false
+ s-container: false
+ s-object: false
+ s-proxy: false
+ horizon: false
+ tls-proxy: true
+ devstack_localrc:
+ MANILA_USE_DOWNGRADE_MIGRATIONS: false
+ MANILA_INSTALL_TEMPEST_PLUGIN_SYSTEMWIDE: false
+ MANILA_ALLOW_NAS_SERVER_PORTS_ON_HOST: true
+ MANILA_SHARE_MIGRATION_PERIOD_TASK_INTERVAL: 1
+ MANILA_SERVER_MIGRATION_PERIOD_TASK_INTERVAL: 10
+ MANILA_REPLICA_STATE_UPDATE_INTERVAL: 10
+ group-vars:
+ tempest:
+ devstack_plugins:
+ manila: https://opendev.org/openstack/manila
+ subnode:
+ devstack_services:
+ cinder: false
+ c-bak: false
+
+- job:
+ name: manila-tempest-plugin-multinode-cephfs-nfs-cephadm
+ description: Test CephFS NFS (DHSS=False) in a Multinode devstack env
+ parent: manila-tempest-plugin-multinode-base
+ required-projects:
+ - openstack/devstack-plugin-ceph
+ vars:
+ configure_swap_size: 8192
+ tempest_concurrency: 2
+ # TODO(gouthamr): some tests are disabled due to bugs
+ # IPv6 Tests: https://bugs.launchpad.net/manila/+bug/1998489
+ # snapshot clone fs sync: https://bugs.launchpad.net/manila/+bug/1989273
+ tempest_exclude_regex: "\
+ (^manila_tempest_tests.tests.scenario.*IPv6.*)|\
+ (^manila_tempest_tests.tests.scenario.test_share_basic_ops.TestShareBasicOpsNFS.test_write_data_to_share_created_from_snapshot)"
+ devstack_localrc:
+ MYSQL_REDUCE_MEMORY: True
+ CEPHADM_DEPLOY: True
+ CEPHADM_DEV_OSD: true
+ CEPH_LOOPBACK_DISK_SIZE: 40GB
+ ENABLED_SHARE_PROTOCOLS: NFS
+ ENABLE_CEPH_MANILA: True
+ ENABLE_CEPH_NOVA: False
+ MANILA_CEPH_DRIVER: cephfsnfs
+ MANILA_CONFIGURE_DEFAULT_TYPES: true
+ MANILA_DEFAULT_SHARE_TYPE_EXTRA_SPECS: 'snapshot_support=True create_share_from_snapshot_support=True'
+ MANILA_ENABLED_BACKENDS: cephfsnfs
+ MANILA_OPTGROUP_cephfsnfs_cephfs_auth_id: manila
+ MANILA_OPTGROUP_cephfsnfs_cephfs_conf_path: /etc/ceph/ceph.conf
+ MANILA_OPTGROUP_cephfsnfs_cephfs_nfs_cluster_id: cephfs
+ MANILA_OPTGROUP_cephfsnfs_cephfs_protocol_helper_type: NFS
+ MANILA_OPTGROUP_cephfsnfs_driver_handles_share_servers: false
+ MANILA_OPTGROUP_cephfsnfs_share_driver: manila.share.drivers.cephfs.driver.CephFSDriver
+ MANILA_SERVICE_IMAGE_ENABLED: True
+ MANILA_SETUP_IPV6: false
+ SHARE_DRIVER: manila.share.drivers.cephfs.driver.CephFSDriver
+ TARGET_DEV_OSD_DIR: /opt/stack
+ devstack_local_conf:
+ test-config:
+ $TEMPEST_CONFIG:
+ share:
+ backend_names: cephfsnfs
+ capability_storage_protocol: NFS
+ default_share_type_name: default
+ enable_protocols: nfs
+ image_password: manila
+ multitenancy_enabled: false
+ run_share_group_tests: false
+ group-vars:
+ subnode:
+ devstack_plugins:
+ devstack-plugin-ceph: https://opendev.org/openstack/devstack-plugin-ceph
+ devstack_localrc:
+ REMOTE_CEPH: True
+ tempest:
+ devstack_plugins:
+ devstack-plugin-ceph: https://opendev.org/openstack/devstack-plugin-ceph
+
+- job:
name: manila-tempest-plugin-dummy-no-dhss
description: Test the Dummy driver with DHSS=False
parent: manila-tempest-plugin-standalone-base
@@ -752,7 +852,7 @@
voting: false
- manila-tempest-plugin-cephfs-native-cephadm:
voting: false
- - manila-tempest-plugin-cephfs-nfs:
+ - manila-tempest-plugin-multinode-cephfs-nfs-cephadm:
voting: false
- manila-tempest-plugin-zfsonlinux:
voting: false