Merge "API2.69,Add tests for manila recycle bin."
diff --git a/manila_tempest_tests/common/waiters.py b/manila_tempest_tests/common/waiters.py
index 056dab6..24b30de 100644
--- a/manila_tempest_tests/common/waiters.py
+++ b/manila_tempest_tests/common/waiters.py
@@ -179,3 +179,41 @@
' the required time (%s s).' %
(resource_id, client.build_timeout))
raise exceptions.TimeoutException(message)
+
+
+def wait_for_soft_delete(client, share_id, version=LATEST_MICROVERSION):
+ """Wait for a share soft delete to recycle bin."""
+ share = client.get_share(share_id, version=version)['share']
+ start = int(time.time())
+ while not share['is_soft_deleted']:
+ time.sleep(client.build_interval)
+ share = client.get_share(share_id, version=version)['share']
+ if share['is_soft_deleted']:
+ break
+ elif int(time.time()) - start >= client.build_timeout:
+ message = ('Share %(share_id)s failed to be soft deleted to '
+ 'recycle bin within the required time '
+ '%(timeout)s.' % {
+ 'share_id': share['id'],
+ 'timeout': client.build_timeout,
+ })
+ raise exceptions.TimeoutException(message)
+
+
+def wait_for_restore(client, share_id, version=LATEST_MICROVERSION):
+ """Wait for a share restore from recycle bin."""
+ share = client.get_share(share_id, version=version)['share']
+ start = int(time.time())
+ while share['is_soft_deleted']:
+ time.sleep(client.build_interval)
+ share = client.get_share(share_id, version=version)['share']
+ if not share['is_soft_deleted']:
+ break
+ elif int(time.time()) - start >= client.build_timeout:
+ message = ('Share %(share_id)s failed to restore from '
+ 'recycle bin within the required time '
+ '%(timeout)s.' % {
+ 'share_id': share['id'],
+ 'timeout': client.build_timeout,
+ })
+ raise exceptions.TimeoutException(message)
diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py
index 92cf7ba..f5022af 100644
--- a/manila_tempest_tests/config.py
+++ b/manila_tempest_tests/config.py
@@ -40,7 +40,7 @@
"This value is only used to validate the versions "
"response from Manila."),
cfg.StrOpt("max_api_microversion",
- default="2.65",
+ default="2.69",
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 293c24a..3837049 100644
--- a/manila_tempest_tests/services/share/v2/json/shares_client.py
+++ b/manila_tempest_tests/services/share/v2/json/shares_client.py
@@ -299,6 +299,20 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
+ def list_shares_in_recycle_bin(self, detailed=False,
+ params=None, version=LATEST_MICROVERSION,
+ experimental=False):
+ """Get list of shares in recycle bin with w/o filters."""
+ headers = EXPERIMENTAL if experimental else None
+ uri = 'shares/detail' if detailed else 'shares'
+ uri += '?is_soft_deleted=true'
+ uri += '&%s' % parse.urlencode(params) if params else ''
+ resp, body = self.get(uri, headers=headers, extra_headers=experimental,
+ version=version)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
def list_shares_with_detail(self, params=None,
version=LATEST_MICROVERSION,
experimental=False):
@@ -342,6 +356,22 @@
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
+ def soft_delete_share(self, share_id, version=LATEST_MICROVERSION):
+ post_body = {"soft_delete": None}
+ body = json.dumps(post_body)
+ resp, body = self.post(
+ "shares/%s/action" % share_id, body, version=version)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def restore_share(self, share_id, version=LATEST_MICROVERSION):
+ post_body = {"restore": None}
+ body = json.dumps(post_body)
+ resp, body = self.post(
+ "shares/%s/action" % share_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):
diff --git a/manila_tempest_tests/tests/api/test_shares_actions.py b/manila_tempest_tests/tests/api/test_shares_actions.py
index 3232d50..779bfb6 100644
--- a/manila_tempest_tests/tests/api/test_shares_actions.py
+++ b/manila_tempest_tests/tests/api/test_shares_actions.py
@@ -687,6 +687,51 @@
)
self.assertEqual(new_size, share_get['size'], msg)
+ @utils.skip_if_microversion_not_supported("2.69")
+ @decorators.idempotent_id('7a19fb58-b645-44cc-a6d7-b3508ff8754d')
+ @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+ def test_soft_delete_and_restore_share(self):
+ share = self.create_share(share_type_id=self.share_type_id)
+
+ # list shares
+ shares = self.shares_v2_client.list_shares()['shares']
+
+ # check the share in share list
+ share_ids = [sh['id'] for sh in shares]
+ self.assertIn(share['id'], share_ids)
+
+ # soft delete the share
+ self.shares_v2_client.soft_delete_share(share['id'])
+ waiters.wait_for_soft_delete(self.shares_v2_client, share['id'])
+
+ # list shares again
+ shares1 = self.shares_v2_client.list_shares()['shares']
+ share_ids1 = [sh['id'] for sh in shares1]
+
+ # list shares in recycle bin
+ shares2 = self.shares_v2_client.list_shares_in_recycle_bin()['shares']
+ share_ids2 = [sh['id'] for sh in shares2]
+
+ # check share has been soft delete to recycle bin
+ self.assertNotIn(share['id'], share_ids1)
+ self.assertIn(share['id'], share_ids2)
+
+ # restore share from recycle bin
+ self.shares_v2_client.restore_share(share['id'])
+ waiters.wait_for_restore(self.shares_v2_client, share['id'])
+
+ # list shares again
+ shares3 = self.shares_v2_client.list_shares()['shares']
+ share_ids3 = [sh['id'] for sh in shares3]
+
+ # list shares in recycle bin again
+ shares4 = self.shares_v2_client.list_shares_in_recycle_bin()['shares']
+ share_ids4 = [sh['id'] for sh in shares4]
+
+ # check share has restored from recycle bin
+ self.assertNotIn(share['id'], share_ids4)
+ self.assertIn(share['id'], share_ids3)
+
class SharesRenameTest(base.BaseSharesMixedTest):
diff --git a/manila_tempest_tests/tests/api/test_shares_actions_negative.py b/manila_tempest_tests/tests/api/test_shares_actions_negative.py
index 64818fd..715668c 100644
--- a/manila_tempest_tests/tests/api/test_shares_actions_negative.py
+++ b/manila_tempest_tests/tests/api/test_shares_actions_negative.py
@@ -21,6 +21,7 @@
import testtools
from testtools import testcase as tc
+from manila_tempest_tests.common import waiters
from manila_tempest_tests.tests.api import base
from manila_tempest_tests import utils
@@ -294,3 +295,68 @@
self.assertRaises(lib_exc.NotFound,
self.alt_shares_v2_client.get_share,
self.share['id'])
+
+ @utils.skip_if_microversion_not_supported("2.69")
+ @decorators.idempotent_id('36cbe23b-08d2-49d9-bb42-f9eb2a804cb1')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_soft_delete_share_has_been_soft_deleted(self):
+ share = self.create_share(share_type_id=self.share_type_id,
+ cleanup_in_class=False)
+
+ # soft delete the share
+ self.shares_v2_client.soft_delete_share(share['id'])
+
+ # try soft delete the share again
+ self.assertRaises(lib_exc.Forbidden,
+ self.shares_v2_client.soft_delete_share,
+ share['id'])
+
+ # restore the share for resource_cleanup
+ self.shares_v2_client.restore_share(share['id'])
+ waiters.wait_for_restore(self.shares_v2_client, share['id'])
+
+ @utils.skip_if_microversion_not_supported("2.69")
+ @decorators.idempotent_id('cf675ac9-0970-49fc-a051-8a94555c73b5')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_soft_delete_share_with_invalid_share_state(self):
+ share = self.create_share(share_type_id=self.share_type_id,
+ cleanup_in_class=False)
+
+ # set "error_deleting" state
+ self.admin_client.reset_state(share['id'], status="error_deleting")
+
+ # try soft delete the share
+ self.assertRaises(lib_exc.Forbidden,
+ self.shares_v2_client.soft_delete_share,
+ share['id'])
+
+ # rollback to available status
+ self.admin_client.reset_state(share['id'], status="available")
+
+ @utils.skip_if_microversion_not_supported("2.69")
+ @decorators.idempotent_id('f6106ee4-1a01-444f-b623-912a5e751d49')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+ def test_soft_delete_share_from_other_project(self):
+ share = self.create_share(share_type_id=self.share_type_id,
+ cleanup_in_class=False)
+
+ # try soft delete the share
+ self.assertRaises(lib_exc.Forbidden,
+ self.alt_shares_v2_client.soft_delete_share,
+ share['id'])
+
+ @utils.skip_if_microversion_not_supported("2.69")
+ @decorators.idempotent_id('0ccd44dd-2fda-403e-bc23-7ce428550f36')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_soft_delete_share_with_wrong_id(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.alt_shares_v2_client.soft_delete_share,
+ "wrong_share_id")
+
+ @utils.skip_if_microversion_not_supported("2.69")
+ @decorators.idempotent_id('87345725-f187-4d7d-86b1-62284e8c75ae')
+ @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+ def test_restore_share_with_wrong_id(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.alt_shares_v2_client.restore_share,
+ "wrong_share_id")