diff --git a/manilaclient/api_versions.py b/manilaclient/api_versions.py index c3cbd2bd2..2b8c35e5c 100644 --- a/manilaclient/api_versions.py +++ b/manilaclient/api_versions.py @@ -27,7 +27,7 @@ from manilaclient import utils LOG = logging.getLogger(__name__) -MAX_VERSION = '2.81' +MAX_VERSION = '2.82' MIN_VERSION = '2.0' DEPRECATED_VERSION = '1.0' _VERSIONED_METHOD_MAP = {} diff --git a/manilaclient/osc/v2/resource_locks.py b/manilaclient/osc/v2/resource_locks.py index 6199631bf..6f0ca873e 100644 --- a/manilaclient/osc/v2/resource_locks.py +++ b/manilaclient/osc/v2/resource_locks.py @@ -48,6 +48,7 @@ LOCK_SUMMARY_ATTRIBUTES = [ RESOURCE_TYPE_MANAGERS = { 'share': 'shares', + 'access_rule': 'share_access_rules' } diff --git a/manilaclient/osc/v2/share_access_rules.py b/manilaclient/osc/v2/share_access_rules.py index bf72b08e2..8e69d2d05 100644 --- a/manilaclient/osc/v2/share_access_rules.py +++ b/manilaclient/osc/v2/share_access_rules.py @@ -84,6 +84,30 @@ class ShareAccessAllow(command.ShowOne): action='store_true', help=_("Wait for share access rule creation.") ) + parser.add_argument( + "--lock-visibility", + action='store_true', + default=False, + help=_("Whether the sensitive fields of the access rule redacted " + "to other users. Only available with API version >= 2.82.") + ) + parser.add_argument( + "--lock-deletion", + action='store_true', + default=False, + help=_("When enabled, a 'delete' lock will be placed against the " + "rule and the rule cannot be deleted while the lock " + "exists. Only available with API version >= 2.82.") + ) + parser.add_argument( + '--lock-reason', + metavar="", + type=str, + default=None, + help=_("Reason for locking the access rule. Should only be " + "provided alongside a deletion or visibility lock. " + "Only available with API version >= 2.82.") + ) return parser def take_action(self, parsed_args): @@ -91,6 +115,28 @@ class ShareAccessAllow(command.ShowOne): share = apiutils.find_resource(share_client.shares, parsed_args.share) + lock_kwargs = {} + if parsed_args.lock_visibility: + lock_kwargs['lock_visibility'] = parsed_args.lock_visibility + if parsed_args.lock_deletion: + lock_kwargs['lock_deletion'] = parsed_args.lock_deletion + if parsed_args.lock_reason: + lock_kwargs['lock_reason'] = parsed_args.lock_reason + + if (lock_kwargs + and share_client.api_version < api_versions.APIVersion( + "2.82")): + raise exceptions.CommandError( + 'Restricted access rules are only available starting ' + 'from API version 2.82.') + + if (lock_kwargs.get('lock_reason', None) + and not (lock_kwargs.get('lock_visibility', None) + or lock_kwargs.get('lock_deletion', None))): + raise exceptions.CommandError( + 'Lock reason can only be set while locking the deletion or ' + 'visibility.') + properties = {} if parsed_args.properties: if share_client.api_version >= api_versions.APIVersion("2.45"): @@ -104,7 +150,8 @@ class ShareAccessAllow(command.ShowOne): access_type=parsed_args.access_type, access=parsed_args.access_to, access_level=parsed_args.access_level, - metadata=properties + metadata=properties, + **lock_kwargs ) if parsed_args.wait: if not oscutils.wait_for_status( @@ -154,6 +201,13 @@ class ShareAccessDeny(command.Command): default=False, help=_("Wait for share access rule deletion") ) + parser.add_argument( + "--unrestrict", + action='store_true', + default=False, + help=_("Seek access rule deletion despite restrictions. Only " + "available with API version >= 2.82.") + ) return parser def take_action(self, parsed_args): @@ -161,9 +215,17 @@ class ShareAccessDeny(command.Command): share = apiutils.find_resource(share_client.shares, parsed_args.share) + kwargs = {} + if parsed_args.unrestrict: + if share_client.api_version < api_versions.APIVersion("2.82"): + raise exceptions.CommandError( + 'Restricted access rules are only available starting from ' + 'API version 2.82.') + kwargs['unrestrict'] = True + error = None try: - share.deny(parsed_args.id) + share.deny(parsed_args.id, **kwargs) if parsed_args.wait: if not oscutils.wait_for_delete( manager=share_client.share_access_rules, @@ -201,6 +263,30 @@ class ListShareAccess(command.Lister): 'OPTIONAL: Default=None. ' 'Available only for API microversion >= 2.45'), ) + parser.add_argument( + "--access-type", + metavar="", + default=None, + help=_("Filter access rules by the access type.") + ) + parser.add_argument( + "--access-key", + metavar="", + default=None, + help=_("Filter access rules by the access key.") + ) + parser.add_argument( + "--access-to", + metavar="", + default=None, + help=_("Filter access rules by the access to field.") + ) + parser.add_argument( + "--access-level", + metavar="", + default=None, + help=_("Filter access rules by the access level.") + ) return parser def take_action(self, parsed_args): @@ -208,9 +294,33 @@ class ListShareAccess(command.Lister): share = apiutils.find_resource(share_client.shares, parsed_args.share) + access_type = parsed_args.access_type + access_key = parsed_args.access_key + access_to = parsed_args.access_to + access_level = parsed_args.access_level + + extended_filter_keys = { + 'access_type': access_type, + 'access_key': access_key, + 'access_to': access_to, + 'access_level': access_level + } + + if (any(extended_filter_keys.values()) + and share_client.api_version < api_versions.APIVersion( + "2.82")): + raise exceptions.CommandError( + 'Filtering access rules by access_type, access_key, access_to ' + 'and access_level is available starting from API version ' + '2.82.') + + search_opts = {} + if share_client.api_version >= api_versions.APIVersion("2.82"): + for filter_key, filter_value in extended_filter_keys.items(): + if filter_value: + search_opts[filter_key] = filter_value if share_client.api_version >= api_versions.APIVersion("2.45"): - search_opts = {} if parsed_args.properties: search_opts = { 'metadata': utils.extract_properties( diff --git a/manilaclient/tests/functional/osc/base.py b/manilaclient/tests/functional/osc/base.py index 8d1c6a0ca..b3a204923 100644 --- a/manilaclient/tests/functional/osc/base.py +++ b/manilaclient/tests/functional/osc/base.py @@ -259,7 +259,11 @@ class OSCClientTestBase(base.ClientTestBase): def create_share_access_rule(self, share, access_type, access_to, properties=None, - access_level=None, wait=False): + access_level=None, wait=False, + lock_visibility=False, + lock_deletion=False, + lock_reason=None, + add_cleanup=False): cmd = f'access create {share} {access_type} {access_to} ' if access_level: @@ -267,7 +271,13 @@ class OSCClientTestBase(base.ClientTestBase): if properties: cmd += f'--properties {properties} ' if wait: - cmd += f'--wait' + cmd += '--wait ' + if lock_visibility: + cmd += '--lock-visibility ' + if lock_deletion: + cmd += '--lock-deletion ' + if lock_reason: + cmd += f'--lock-reason {lock_reason}' access_rule = self.dict_result('share', cmd) diff --git a/manilaclient/tests/functional/osc/test_resource_locks.py b/manilaclient/tests/functional/osc/test_resource_locks.py index e51724c77..1635b8081 100644 --- a/manilaclient/tests/functional/osc/test_resource_locks.py +++ b/manilaclient/tests/functional/osc/test_resource_locks.py @@ -37,7 +37,7 @@ LOCK_SUMMARY_ATTRIBUTES = [ ] -@utils.skip_if_microversion_not_supported('2.81') +@utils.skip_if_microversion_not_supported('2.82') class ResourceLockTests(base.OSCClientTestBase): """Lock CLI test cases""" diff --git a/manilaclient/tests/functional/osc/test_share_access_rules.py b/manilaclient/tests/functional/osc/test_share_access_rules.py index a6b2986ce..a23848942 100644 --- a/manilaclient/tests/functional/osc/test_share_access_rules.py +++ b/manilaclient/tests/functional/osc/test_share_access_rules.py @@ -10,11 +10,13 @@ # License for the specific language governing permissions and limitations # under the License. +import ddt +from tempest.lib import exceptions as tempest_exc + from manilaclient.tests.functional.osc import base -import ddt - +@ddt.ddt class ShareAccessAllowTestCase(base.OSCClientTestBase): def test_share_access_allow(self): @@ -52,23 +54,63 @@ class ShareAccessAllowTestCase(base.OSCClientTestBase): self.assertEqual(access_rule['properties'], 'foo : bar') self.assertEqual(access_rule['access_level'], 'ro') + @ddt.data( + {'lock_visibility': True, 'lock_deletion': True, + 'lock_reason': None}, + {'lock_visibility': False, 'lock_deletion': True, + 'lock_reason': None}, + {'lock_visibility': True, 'lock_deletion': False, + 'lock_reason': 'testing'}, + {'lock_visibility': True, 'lock_deletion': False, + 'lock_reason': 'testing'}, + ) + @ddt.unpack + def test_share_access_allow_restrict(self, lock_visibility, + lock_deletion, lock_reason): + share = self.create_share() + access_rule = self.create_share_access_rule( + share=share['id'], + access_type='ip', + access_to='0.0.0.0/0', + wait=True, + lock_visibility=lock_visibility, + lock_deletion=lock_deletion, + lock_reason=lock_reason) + if lock_deletion: + self.assertRaises( + tempest_exc.CommandFailed, + self.openstack, + 'share', + params=f'access delete {share["id"]} {access_rule["id"]}' + ) + self.openstack( + 'share', + params=f'access delete {share["id"]} {access_rule["id"]} ' + f'--unrestrict --wait') + + +@ddt.ddt class ShareAccessDenyTestCase(base.OSCClientTestBase): - def test_share_access_deny(self): + @ddt.data(True, False) + def test_share_access_deny(self, lock_deletion): share = self.create_share() access_rule = self.create_share_access_rule( share=share['name'], access_type='ip', access_to='0.0.0.0/0', - wait=True) + wait=True, + lock_deletion=lock_deletion) access_rules = self.listing_result('share', f'access list {share["id"]}') num_access_rules = len(access_rules) - self.openstack('share', - params=f'access delete ' - f'{share["name"]} {access_rule["id"]} --wait') + delete_params = ( + f'access delete {share["name"]} {access_rule["id"]} --wait') + if lock_deletion: + delete_params += ' --unrestrict' + self.openstack('share', params=delete_params) access_rules = self.listing_result('share', f'access list {share["id"]}') @@ -127,6 +169,28 @@ class ListShareAccessRulesTestCase(base.OSCClientTestBase): self.assertEqual(access_rule_properties['id'], access_rule_properties[0]['ID']) + def test_share_access_list_with_filters(self): + share = self.create_share() + access_to_filter = '20.0.0.0/0' + self.create_share_access_rule( + share=share['name'], + access_type='ip', + access_to='0.0.0.0/0', + wait=True) + self.create_share_access_rule( + share=share['name'], + access_type='ip', + access_to=access_to_filter, + wait=True) + + output = self.openstack( + 'share', + params=f'access list {share["id"]} --access-to {access_to_filter}', + flags=f'--os-share-api-version 2.82') + access_rule_list = self.parser.listing(output) + + self.assertTrue(len(access_rule_list) == 1) + class ShowShareAccessRulesTestCase(base.OSCClientTestBase): def test_share_access_show(self): diff --git a/manilaclient/tests/unit/osc/v2/test_share_access_rules.py b/manilaclient/tests/unit/osc/v2/test_share_access_rules.py index 916119a25..96d99c2f2 100644 --- a/manilaclient/tests/unit/osc/v2/test_share_access_rules.py +++ b/manilaclient/tests/unit/osc/v2/test_share_access_rules.py @@ -10,8 +10,10 @@ # License for the specific language governing permissions and limitations # under the License. # + from unittest import mock +import ddt from osc_lib import exceptions from osc_lib import utils as oscutils @@ -48,6 +50,7 @@ class TestShareAccess(manila_fakes.TestShare): self.access_rules_mock.reset_mock() +@ddt.ddt class TestShareAccessCreate(TestShareAccess): def setUp(self): @@ -84,7 +87,7 @@ class TestShareAccessCreate(TestShareAccess): access_type="user", access="demo", access_level=None, - metadata={} + metadata={}, ) self.assertEqual(ACCESS_RULE_ATTRIBUTES, columns) self.assertCountEqual(self.access_rule._info.values(), data) @@ -100,7 +103,7 @@ class TestShareAccessCreate(TestShareAccess): ("share", self.share.id), ("access_type", "user"), ("access_to", "demo"), - ('properties', ['key=value']) + ('properties', ['key=value']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -109,11 +112,92 @@ class TestShareAccessCreate(TestShareAccess): access_type="user", access="demo", access_level=None, - metadata={'key': 'value'} + metadata={'key': 'value'}, ) self.assertEqual(ACCESS_RULE_ATTRIBUTES, columns) self.assertCountEqual(self.access_rule._info.values(), data) + @ddt.data( + {'lock_visibility': True, 'lock_deletion': True, + 'lock_reason': 'testing resource locks'}, + {'lock_visibility': False, 'lock_deletion': True, 'lock_reason': None}, + {'lock_visibility': True, 'lock_deletion': False, 'lock_reason': None}, + ) + @ddt.unpack + def test_share_access_create_restrict(self, lock_visibility, + lock_deletion, lock_reason): + arglist = [ + self.share.id, + 'user', + 'demo', + '--properties', 'key=value' + ] + verifylist = [ + ("share", self.share.id), + ("access_type", "user"), + ("access_to", "demo"), + ('properties', ['key=value']), + ] + allow_call_kwargs = {} + if lock_visibility: + arglist.append('--lock-visibility') + verifylist.append(('lock_visibility', lock_visibility)) + allow_call_kwargs['lock_visibility'] = lock_visibility + if lock_deletion: + arglist.append('--lock-deletion') + verifylist.append(('lock_deletion', lock_deletion)) + allow_call_kwargs['lock_deletion'] = lock_deletion + if lock_reason: + arglist.append('--lock-reason') + arglist.append(lock_reason) + verifylist.append(('lock_reason', lock_reason)) + allow_call_kwargs['lock_reason'] = lock_reason + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.shares_mock.get.assert_called_with(self.share.id) + self.share.allow.assert_called_with( + access_type="user", + access="demo", + access_level=None, + metadata={'key': 'value'}, + **allow_call_kwargs + ) + self.assertEqual(ACCESS_RULE_ATTRIBUTES, columns) + self.assertCountEqual(self.access_rule._info.values(), data) + + @ddt.data( + {'lock_visibility': True, 'lock_deletion': False}, + {'lock_visibility': False, 'lock_deletion': True}, + ) + @ddt.unpack + def test_share_access_create_restrict_not_available( + self, lock_visibility, lock_deletion): + arglist = [ + self.share.id, + 'user', + 'demo', + ] + self.app.client_manager.share.api_version = api_versions.APIVersion( + "2.79") + verifylist = [ + ("share", self.share.id), + ("access_type", "user"), + ("access_to", "demo"), + ("lock_visibility", lock_visibility), + ("lock_deletion", lock_deletion), + ("lock_reason", None), + ] + if lock_visibility: + arglist.append('--lock-visibility') + if lock_deletion: + arglist.append('--lock-deletion') + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args) + def test_access_rule_create_access_level(self): arglist = [ self.share.id, @@ -125,7 +209,7 @@ class TestShareAccessCreate(TestShareAccess): ("share", self.share.id), ("access_type", "user"), ("access_to", "demo"), - ('access_level', 'ro') + ('access_level', 'ro'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -134,7 +218,7 @@ class TestShareAccessCreate(TestShareAccess): access_type="user", access="demo", access_level='ro', - metadata={} + metadata={}, ) self.assertEqual(ACCESS_RULE_ATTRIBUTES, columns) self.assertCountEqual(self.access_rule._info.values(), data) @@ -150,7 +234,7 @@ class TestShareAccessCreate(TestShareAccess): ("share", self.share.id), ("access_type", "user"), ("access_to", "demo"), - ("wait", True) + ("wait", True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -159,7 +243,7 @@ class TestShareAccessCreate(TestShareAccess): access_type="user", access="demo", access_level=None, - metadata={} + metadata={}, ) self.assertEqual(ACCESS_RULE_ATTRIBUTES, columns) self.assertCountEqual(self.access_rule._info.values(), data) @@ -176,7 +260,7 @@ class TestShareAccessCreate(TestShareAccess): ("share", self.share.id), ("access_type", "user"), ("access_to", "demo"), - ("wait", True) + ("wait", True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -188,7 +272,7 @@ class TestShareAccessCreate(TestShareAccess): access_type="user", access="demo", access_level=None, - metadata={} + metadata={}, ) mock_logger.error.assert_called_with( @@ -198,6 +282,7 @@ class TestShareAccessCreate(TestShareAccess): self.assertCountEqual(self.access_rule._info.values(), data) +@ddt.ddt class TestShareAccessDelete(TestShareAccess): def setUp(self): @@ -213,21 +298,47 @@ class TestShareAccessDelete(TestShareAccess): # Get the command object to test self.cmd = osc_share_access_rules.ShareAccessDeny(self.app, None) - def test_share_access_delete(self): + @ddt.data(True, False) + def test_share_access_delete(self, unrestrict): arglist = [ self.share.id, self.access_rule.id ] verifylist = [ ("share", self.share.id), - ("id", self.access_rule.id) + ("id", self.access_rule.id), ] + deny_kwargs = {} + if unrestrict: + arglist.append('--unrestrict') + verifylist.append(("unrestrict", unrestrict)) + deny_kwargs['unrestrict'] = unrestrict parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.shares_mock.get.assert_called_with(self.share.id) - self.share.deny.assert_called_with(self.access_rule.id) + self.share.deny.assert_called_with( + self.access_rule.id, **deny_kwargs) self.assertIsNone(result) + def test_share_access_delete_unrestrict_not_available(self): + self.app.client_manager.share.api_version = api_versions.APIVersion( + "2.79") + arglist = [ + self.share.id, + self.access_rule.id, + "--unrestrict" + ] + verifylist = [ + ("share", self.share.id), + ("id", self.access_rule.id), + ("unrestrict", True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args) + def test_share_access_delete_wait(self): arglist = [ self.share.id, @@ -237,7 +348,7 @@ class TestShareAccessDelete(TestShareAccess): verifylist = [ ("share", self.share.id), ("id", self.access_rule.id), - ('wait', True) + ('wait', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -246,7 +357,8 @@ class TestShareAccessDelete(TestShareAccess): result = self.cmd.take_action(parsed_args) self.shares_mock.get.assert_called_with(self.share.id) - self.share.deny.assert_called_with(self.access_rule.id) + self.share.deny.assert_called_with( + self.access_rule.id) self.assertIsNone(result) def test_share_access_delete_wait_error(self): @@ -270,6 +382,7 @@ class TestShareAccessDelete(TestShareAccess): ) +@ddt.ddt class TestShareAccessList(TestShareAccess): access_rules_columns = [ @@ -339,6 +452,58 @@ class TestShareAccessList(TestShareAccess): self.assertEqual(self.access_rules_columns, columns) self.assertEqual(tuple(self.values_list), tuple(data)) + @ddt.data( + {'access_to': '10.0.0.0/0', 'access_type': 'ip'}, + {'access_key': '10.0.0.0/0', 'access_level': 'rw'}, + ) + def test_access_rules_list_access_filters(self, filters): + arglist = [ + self.share.id, + ] + + verifylist = [ + ("share", self.share.id), + ] + for filter_key, filter_value in filters.items(): + filter_arg = filter_key.replace("_", "-") + arglist.append(f'--{filter_arg}') + arglist.append(filter_value) + verifylist.append((filter_key, filter_value)) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.shares_mock.get.assert_called_with(self.share.id) + self.access_rules_mock.access_list.assert_called_with( + self.share, + filters) + self.assertEqual(self.access_rules_columns, columns) + self.assertEqual(tuple(self.values_list), tuple(data)) + + @ddt.data( + {'access_to': '10.0.0.0/0', 'access_type': 'ip'}, + {'access_key': '10.0.0.0/0', 'access_level': 'rw'}, + ) + def test_access_rules_list_access_filters_command_error(self, filters): + self.app.client_manager.share.api_version = api_versions.APIVersion( + "2.81") + arglist = [ + self.share.id, + ] + verifylist = [ + ("share", self.share.id), + ] + for filter_key, filter_value in filters.items(): + filter_arg = filter_key.replace("_", "-") + arglist.append(f'--{filter_arg}') + arglist.append(filter_value) + verifylist.append((filter_key, filter_value)) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args) + class TestShareAccessShow(TestShareAccess): diff --git a/manilaclient/tests/unit/v2/test_shares.py b/manilaclient/tests/unit/v2/test_shares.py index a98eb30a1..d52b3a779 100644 --- a/manilaclient/tests/unit/v2/test_shares.py +++ b/manilaclient/tests/unit/v2/test_shares.py @@ -50,7 +50,7 @@ class SharesTest(utils.TestCase): self.share.allow(access_type, access_to, access_level) self.share.manager.allow.assert_called_once_with( - self.share, access_type, access_to, access_level, None) + self.share, access_type, access_to, access_level) # Testcases for class ShareManager diff --git a/manilaclient/v2/shares.py b/manilaclient/v2/shares.py index aae179c2d..a58fcf106 100644 --- a/manilaclient/v2/shares.py +++ b/manilaclient/v2/shares.py @@ -75,14 +75,13 @@ class Share(base.MetadataCapableResource): """Delete the specified share ignoring its current state.""" self.manager.force_delete(self) - def allow(self, access_type, access, access_level, metadata=None): + def allow(self, *args, **kwargs): """Allow access to a share.""" - return self.manager.allow( - self, access_type, access, access_level, metadata) + return self.manager.allow(self, *args, **kwargs) - def deny(self, id): + def deny(self, id, **kwargs): """Deny access from IP to a share.""" - return self.manager.deny(self, id) + return self.manager.deny(self, id, **kwargs) def access_list(self): """Get access list from a share.""" @@ -570,7 +569,8 @@ class ShareManager(base.MetadataCapableManager): raise exceptions.CommandError(msg) def _do_allow(self, share, access_type, access, access_level, action_name, - metadata=None): + metadata=None, lock_visibility=False, + lock_deletion=False, lock_reason=None): """Allow access to a share. :param share: either share object or text with its ID. @@ -587,6 +587,12 @@ class ShareManager(base.MetadataCapableManager): access_params['access_level'] = access_level if metadata: access_params['metadata'] = metadata + if lock_visibility: + access_params['lock_visibility'] = lock_visibility + if lock_deletion: + access_params['lock_deletion'] = lock_deletion + if lock_reason: + access_params['lock_reason'] = lock_reason access = self._action(action_name, share, access_params)[1]["access"] return access @@ -618,30 +624,53 @@ class ShareManager(base.MetadataCapableManager): return self._do_allow( share, access_type, access, access_level, "allow_access") - @api_versions.wraps("2.45") # noqa + @api_versions.wraps("2.45", "2.81") # noqa def allow(self, share, access_type, access, access_level, metadata=None): # noqa valid_access_types = ('ip', 'user', 'cert', 'cephx') self._validate_access(access_type, access, valid_access_types, enable_ipv6=True) return self._do_allow( - share, access_type, access, access_level, "allow_access", metadata) + share, access_type, access, access_level, "allow_access", + metadata=metadata) - def _do_deny(self, share, access_id, action_name): + @api_versions.wraps("2.82") # noqa + def allow(self, share, access_type, access, access_level, # pylint: disable=function-redefined # noqa F811 + metadata=None, lock_visibility=False, lock_deletion=False, + lock_reason=None): + valid_access_types = ('ip', 'user', 'cert', 'cephx') + self._validate_access(access_type, access, valid_access_types, + enable_ipv6=True) + return self._do_allow( + share, access_type, access, access_level, "allow_access", + metadata=metadata, lock_visibility=lock_visibility, + lock_deletion=lock_deletion, lock_reason=lock_reason) + + def _do_deny(self, share, access_id, action_name, unrestrict=False): """Deny access to a share. :param share: either share object or text with its ID. :param access_id: ID of share access rule """ - return self._action(action_name, share, {"access_id": access_id}) + body = { + "access_id": access_id, + } + if unrestrict: + body['unrestrict'] = True + return self._action(action_name, share, body) @api_versions.wraps("1.0", "2.6") def deny(self, share, access_id): return self._do_deny(share, access_id, "os-deny_access") - @api_versions.wraps("2.7") # noqa + @api_versions.wraps("2.7", "2.81") # noqa def deny(self, share, access_id): # noqa return self._do_deny(share, access_id, "deny_access") + @api_versions.wraps("2.82") # noqa + def deny(self, share, access_id, unrestrict=False): # noqa + return self._do_deny(share, access_id, "deny_access", + unrestrict=unrestrict) + def _do_access_list(self, share, action_name): """Get access list to a share. diff --git a/releasenotes/notes/add-access-visibility-and-deletion-locks-69978f052e25334c.yaml b/releasenotes/notes/add-access-visibility-and-deletion-locks-69978f052e25334c.yaml new file mode 100644 index 000000000..88cb2004d --- /dev/null +++ b/releasenotes/notes/add-access-visibility-and-deletion-locks-69978f052e25334c.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + It is now possible to restrict the visibility of access rules' sensitive + fields, as well as lock the access rule deletion while allowing access to + a share. A lock reason can also be provided. + - | + It is now possible to filter access rules while listing them by its + `access_to`, `access_type`, `access_key` and `access_level`.