From 1aa12eb2f805ee6c65ba8b9787f4859c6e27ecd7 Mon Sep 17 00:00:00 2001 From: zhongjun2 Date: Thu, 31 May 2018 11:03:54 +0800 Subject: [PATCH] Support metadata for access rule resource Now only share have metadata property. We should support it for access rule as well. Depends-On: https://review.openstack.org/#/c/570708/ Change-Id: I41b2882ecd16ed03bece05a7a77a387c9077bf5c Partly-Implements: bp metadata-for-access-rule --- manilaclient/api_versions.py | 2 +- manilaclient/tests/functional/client.py | 59 ++++++++--- .../tests/functional/test_share_access.py | 99 ++++++++++++++++++- manilaclient/tests/unit/v2/fakes.py | 39 ++++++++ manilaclient/tests/unit/v2/test_shares.py | 2 +- manilaclient/tests/unit/v2/test_shell.py | 55 ++++++++++- manilaclient/v2/client.py | 3 + manilaclient/v2/share_access_rules.py | 99 +++++++++++++++++++ manilaclient/v2/shares.py | 31 ++++-- manilaclient/v2/shell.py | 84 +++++++++++++++- ...or-share-access-rule-11a6b36c0f4c87c2.yaml | 5 + 11 files changed, 442 insertions(+), 36 deletions(-) create mode 100644 manilaclient/v2/share_access_rules.py create mode 100644 releasenotes/notes/add-metadata-for-share-access-rule-11a6b36c0f4c87c2.yaml diff --git a/manilaclient/api_versions.py b/manilaclient/api_versions.py index 60671ad8f..fe667fbe8 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.43' +MAX_VERSION = '2.45' MIN_VERSION = '2.0' DEPRECATED_VERSION = '1.0' _VERSIONED_METHOD_MAP = {} diff --git a/manilaclient/tests/functional/client.py b/manilaclient/tests/functional/client.py index 7866bcb26..01db16e23 100644 --- a/manilaclient/tests/functional/client.py +++ b/manilaclient/tests/functional/client.py @@ -982,7 +982,7 @@ class ManilaCLIClient(base.CLIClient): @not_found_wrapper def list_access(self, entity_id, columns=None, microversion=None, - is_snapshot=False): + is_snapshot=False, metadata=None): """Returns list of access rules for a share. :param entity_id: str -- Name or ID of a share or snapshot. @@ -997,6 +997,12 @@ class ManilaCLIClient(base.CLIClient): cmd = 'access-list %s ' % entity_id if columns is not None: cmd += ' --columns ' + columns + if metadata: + metadata_cli = '' + for k, v in metadata.items(): + metadata_cli += '%(k)s=%(v)s ' % {'k': k, 'v': v} + if metadata_cli: + cmd += ' --metadata %s ' % metadata_cli access_list_raw = self.manila(cmd, microversion=microversion) return output_parser.listing(access_list_raw) @@ -1009,6 +1015,33 @@ class ManilaCLIClient(base.CLIClient): return access raise tempest_lib_exc.NotFound() + @not_found_wrapper + def access_show(self, access_id, microversion=None): + raw_access = self.manila("access-show %s" % access_id, + microversion=microversion) + return output_parser.details(raw_access) + + @not_found_wrapper + def access_set_metadata(self, access_id, metadata, microversion=None): + if not (isinstance(metadata, dict) and metadata): + msg = ('Provided invalid metadata for setting of access rule' + ' metadata - %s' % metadata) + raise exceptions.InvalidData(message=msg) + cmd = "access-metadata %s set " % access_id + for k, v in metadata.items(): + cmd += '%(k)s=%(v)s ' % {'k': k, 'v': v} + return self.manila(cmd, microversion=microversion) + + @not_found_wrapper + def access_unset_metadata(self, access_id, keys, microversion=None): + if not (isinstance(keys, (list, tuple, set)) and keys): + raise exceptions.InvalidData( + message='Provided invalid keys - %s' % keys) + cmd = 'access-metadata %s unset ' % access_id + for key in keys: + cmd += '%s ' % key + return self.manila(cmd, microversion=microversion) + @not_found_wrapper def snapshot_access_allow(self, snapshot_id, access_type, access_to, microversion=None): @@ -1032,16 +1065,20 @@ class ManilaCLIClient(base.CLIClient): @not_found_wrapper def access_allow(self, share_id, access_type, access_to, access_level, - microversion=None): - raw_access = self.manila( - 'access-allow --access-level %(level)s %(id)s %(type)s ' - '%(access_to)s' % { - 'level': access_level, - 'id': share_id, - 'type': access_type, - 'access_to': access_to, - }, - microversion=microversion) + metadata=None, microversion=None): + cmd = ('access-allow --access-level %(level)s %(id)s %(type)s ' + '%(access_to)s' % { + 'level': access_level, + 'id': share_id, + 'type': access_type, + 'access_to': access_to}) + if metadata: + metadata_cli = '' + for k, v in metadata.items(): + metadata_cli += '%(k)s=%(v)s ' % {'k': k, 'v': v} + if metadata_cli: + cmd += ' --metadata %s ' % metadata_cli + raw_access = self.manila(cmd, microversion=microversion) return output_parser.details(raw_access) @not_found_wrapper diff --git a/manilaclient/tests/functional/test_share_access.py b/manilaclient/tests/functional/test_share_access.py index 012edcdb3..861e8999a 100644 --- a/manilaclient/tests/functional/test_share_access.py +++ b/manilaclient/tests/functional/test_share_access.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import ast + import ddt from tempest.lib import exceptions as tempest_lib_exc @@ -68,16 +70,19 @@ class ShareAccessReadWriteBase(base.BaseTestCase): 'ipv6': ['2001:db8::%d' % i for i in int_range], } - def _test_create_list_access_rule_for_share(self, microversion): + def _test_create_list_access_rule_for_share( + self, microversion, metadata=None): access_type = self.access_types[0] access = self.user_client.access_allow( self.share['id'], access_type, self.access_to[access_type].pop(), - self.access_level, microversion=microversion) + self.access_level, metadata=metadata, microversion=microversion) return access - @ddt.data("1.0", "2.0", "2.6", "2.7", "2.21", "2.33") + @ddt.data(*set([ + "1.0", "2.0", "2.6", "2.7", "2.21", "2.33", "2.44", "2.45", + api_versions.MAX_VERSION])) def test_create_list_access_rule_for_share(self, microversion): self.skip_if_microversion_not_supported(microversion) access = self._test_create_list_access_rule_for_share( @@ -95,8 +100,9 @@ class ShareAccessReadWriteBase(base.BaseTestCase): if (api_versions.APIVersion(microversion) >= api_versions.APIVersion("2.33")): self.assertTrue( - all(('access_key' and 'created_at' and 'updated_at') - in a for a in access_list)) + all(all(key in access for key in ( + 'access_key', 'created_at', 'updated_at')) + for access in access_list)) elif (api_versions.APIVersion(microversion) >= api_versions.APIVersion("2.21")): self.assertTrue(all('access_key' in a for a in access_list)) @@ -156,6 +162,89 @@ class ShareAccessReadWriteBase(base.BaseTestCase): self.assertRaises(tempest_lib_exc.NotFound, self.user_client.get_access, share_id, access['id']) + @ddt.data(*set(["2.45", api_versions.MAX_VERSION])) + def test_create_list_access_rule_with_metadata(self, microversion): + self.skip_if_microversion_not_supported(microversion) + + md1 = {"key1": "value1", "key2": "value2"} + md2 = {"key3": "value3", "key4": "value4"} + self._test_create_list_access_rule_for_share( + metadata=md1, microversion=microversion) + access = self._test_create_list_access_rule_for_share( + metadata=md2, microversion=microversion) + access_list = self.user_client.list_access( + self.share['id'], metadata={"key4": "value4"}, + microversion=microversion) + self.assertEqual(1, len(access_list)) + # Verify share metadata + get_access = self.user_client.access_show( + access_list[0]['id'], microversion=microversion) + metadata = ast.literal_eval(get_access['metadata']) + self.assertEqual(2, len(metadata)) + self.assertIn('key3', metadata) + self.assertIn('key4', metadata) + self.assertEqual(md2['key3'], metadata['key3']) + self.assertEqual(md2['key4'], metadata['key4']) + self.assertEqual(access['id'], access_list[0]['id']) + + self.user_client.access_deny(access['share_id'], access['id']) + self.user_client.wait_for_access_rule_deletion(access['share_id'], + access['id']) + + @ddt.data(*set(["2.45", api_versions.MAX_VERSION])) + def test_create_update_show_access_rule_with_metadata(self, microversion): + self.skip_if_microversion_not_supported(microversion) + + md1 = {"key1": "value1", "key2": "value2"} + md2 = {"key3": "value3", "key2": "value4"} + # create a access rule with metadata + access = self._test_create_list_access_rule_for_share( + metadata=md1, microversion=microversion) + # get the access rule + get_access = self.user_client.access_show( + access['id'], microversion=microversion) + # verify access rule + self.assertEqual(access['id'], get_access['id']) + self.assertEqual(md1, ast.literal_eval(get_access['metadata'])) + + # update access rule metadata + self.user_client.access_set_metadata( + access['id'], metadata=md2, microversion=microversion) + get_access = self.user_client.access_show( + access['id'], microversion=microversion) + + # verify access rule after update access rule metadata + self.assertEqual( + {"key1": "value1", "key2": "value4", "key3": "value3"}, + ast.literal_eval(get_access['metadata'])) + self.assertEqual(access['id'], get_access['id']) + + @ddt.data(*set(["2.45", api_versions.MAX_VERSION])) + def test_delete_access_rule_metadata(self, microversion): + self.skip_if_microversion_not_supported(microversion) + + md = {"key1": "value1", "key2": "value2"} + # create a access rule with metadata + access = self._test_create_list_access_rule_for_share( + metadata=md, microversion=microversion) + # get the access rule + get_access = self.user_client.access_show( + access['id'], microversion=microversion) + + # verify access rule + self.assertEqual(access['id'], get_access['id']) + self.assertEqual(md, ast.literal_eval(get_access['metadata'])) + + # delete access rule metadata + self.user_client.access_unset_metadata( + access['id'], keys=["key1", "key2"], microversion=microversion) + get_access = self.user_client.access_show( + access['id'], microversion=microversion) + + # verify access rule after delete access rule metadata + self.assertEqual({}, ast.literal_eval(get_access['metadata'])) + self.assertEqual(access['id'], get_access['id']) + @ddt.data("1.0", "2.0", "2.6", "2.7", "2.21", "2.33") def test_create_delete_ip_access_rule(self, microversion): self._create_delete_access_rule( diff --git a/manilaclient/tests/unit/v2/fakes.py b/manilaclient/tests/unit/v2/fakes.py index 2d583088e..554c4eb6f 100644 --- a/manilaclient/tests/unit/v2/fakes.py +++ b/manilaclient/tests/unit/v2/fakes.py @@ -505,6 +505,45 @@ class FakeHTTPClient(fakes.FakeHTTPClient): raise AssertionError("Unexpected share action: %s" % action) return (resp, {}, _body) + def get_share_access_rules(self, **kw): + access = { + 'access_list': [{ + 'access_level': 'rw', + 'state': 'active', + 'id': '1122', + 'access_type': 'ip', + 'access_to': '10.0.0.7', + 'metadata': {'key1': 'v1'} + }] + } + return (200, {}, access) + + def get_share_access_rules_9999(self, **kw): + access = { + 'access': { + 'access_level': 'rw', + 'state': 'active', + 'id': '9999', + 'access_type': 'ip', + 'access_to': '10.0.0.7', + 'metadata': {'key1': 'v1'} + } + } + return (200, {}, access) + + def put_share_access_rules_9999_metadata(self, **kw): + return (200, {}, {'metadata': {'key1': 'v1', 'key2': 'v2'}}) + + def delete_share_access_rules_9999_metadata_key1(self, **kw): + return (200, {}, None) + + def get_shares_2222(self, **kw): + share = {'share': {'id': 2222, 'name': 'sharename'}} + return (200, {}, share) + + def post_shares_2222_action(self, body, **kw): + return (202, {}, {'access': {}}) + def post_share_networks(self, **kwargs): return (202, {}, {'share_network': {}}) diff --git a/manilaclient/tests/unit/v2/test_shares.py b/manilaclient/tests/unit/v2/test_shares.py index e0d9ca7f7..16d253c48 100644 --- a/manilaclient/tests/unit/v2/test_shares.py +++ b/manilaclient/tests/unit/v2/test_shares.py @@ -49,7 +49,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) + self.share, access_type, access_to, access_level, None) # Testcases for class ShareManager diff --git a/manilaclient/tests/unit/v2/test_shell.py b/manilaclient/tests/unit/v2/test_shell.py index f3d4a1456..15ec74525 100644 --- a/manilaclient/tests/unit/v2/test_shell.py +++ b/manilaclient/tests/unit/v2/test_shell.py @@ -23,6 +23,7 @@ from oslo_utils import strutils import six from six.moves.urllib import parse +from manilaclient import api_versions from manilaclient import client from manilaclient.common.apiclient import utils as apiclient_utils from manilaclient.common import cliutils @@ -1705,17 +1706,63 @@ class ShellTest(test_utils.TestCase): self.assertRaises(SystemExit, self.run_command, "access-allow --access-level fake 1111 ip 10.0.0.6") + def test_allow_access_with_metadata(self): + expected = { + "allow_access": { + "access_type": "ip", + "access_to": "10.0.0.6", + "metadata": {"key1": "v1", "key2": "v2"}, + } + } + + self.run_command( + "access-allow 2222 ip 10.0.0.6 --metadata key1=v1 key2=v2", + version="2.45") + self.assert_called("POST", "/shares/2222/action", body=expected) + + def test_set_access_metadata(self): + expected = { + "metadata": { + "key1": "v1", + "key2": "v2", + } + } + self.run_command( + "access-metadata 9999 set key1=v1 key2=v2", + version="2.45") + self.assert_called("PUT", "/share-access-rules/9999/metadata", + body=expected) + + def test_unset_access_metadata(self): + self.run_command( + "access-metadata 9999 unset key1", + version="2.45") + self.assert_called("DELETE", "/share-access-rules/9999/metadata/key1") + + @ddt.data("1.0", "2.0", "2.44") + def test_allow_access_with_metadata_not_support_version(self, version): + self.assertRaises( + exceptions.CommandError, + self.run_command, + "access-allow 2222 ip 10.0.0.6 --metadata key1=v1", + version=version, + ) + @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_access_list(self): - self.run_command("access-list 1111") + @ddt.data(*set(["2.44", "2.45", api_versions.MAX_VERSION])) + def test_access_list(self, version): + self.run_command("access-list 1111", version=version) + version = api_versions.APIVersion(version) cliutils.print_list.assert_called_with( mock.ANY, ['id', 'access_type', 'access_to', 'access_level', 'state', 'access_key', 'created_at', 'updated_at']) @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_access_list_select_column(self): - self.run_command("access-list 1111 --columns id,access_type") + @ddt.data(*set(["2.44", "2.45", api_versions.MAX_VERSION])) + def test_access_list_select_column(self, version): + self.run_command("access-list 1111 --columns id,access_type", + version=version) cliutils.print_list.assert_called_with( mock.ANY, ['Id', 'Access_Type']) diff --git a/manilaclient/v2/client.py b/manilaclient/v2/client.py index 2cb0cf90f..c1482781f 100644 --- a/manilaclient/v2/client.py +++ b/manilaclient/v2/client.py @@ -28,6 +28,7 @@ from manilaclient.v2 import quotas from manilaclient.v2 import scheduler_stats from manilaclient.v2 import security_services from manilaclient.v2 import services +from manilaclient.v2 import share_access_rules from manilaclient.v2 import share_export_locations from manilaclient.v2 import share_group_snapshots from manilaclient.v2 import share_group_type_access @@ -237,6 +238,8 @@ class Client(object): self.share_servers = share_servers.ShareServerManager(self) self.share_replicas = share_replicas.ShareReplicaManager(self) self.pools = scheduler_stats.PoolManager(self) + self.share_access_rules = ( + share_access_rules.ShareAccessRuleManager(self)) self._load_extensions(extensions) diff --git a/manilaclient/v2/share_access_rules.py b/manilaclient/v2/share_access_rules.py new file mode 100644 index 000000000..e69382697 --- /dev/null +++ b/manilaclient/v2/share_access_rules.py @@ -0,0 +1,99 @@ +# Copyright 2018 Huawei Corporation. +# All Rights Reserved. +# +# Copyright 2018 +# +# 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. +"""Interface for share access rules extension.""" + +from six.moves.urllib import parse + +from manilaclient import api_versions +from manilaclient import base +from manilaclient.common.apiclient import base as common_base +from manilaclient import utils + + +RESOURCE_PATH = '/share-access-rules/%s' +RESOURCE_NAME = 'access' + +RESOURCES_METADATA_PATH = '/share-access-rules/%s/metadata' +RESOURCE_METADATA_PATH = '/share-access-rules/%s/metadata/%s' +RESOURCE_LIST_PATH = '/share-access-rules?share_id=%s' + + +class ShareAccessRule(common_base.Resource): + """A Share Access Rule.""" + + def __repr__(self): + return "" % self.id + + def delete(self): + """"Delete this share access rule.""" + self.manager.delete(self) + + +class ShareAccessRuleManager(base.ManagerWithFind): + """Manage :class:`ShareAccessRule` resources.""" + + resource_class = ShareAccessRule + + @api_versions.wraps("2.45") + def get(self, share_access_rule): + """Get a specific share access rule. + + :param share_access_rule: either instance of ShareAccessRule, or text + with UUID + :rtype: :class:`ShareAccessRule` + """ + share_access_rule_id = common_base.getid(share_access_rule) + url = RESOURCE_PATH % share_access_rule_id + return self._get(url, RESOURCE_NAME) + + @api_versions.wraps("2.45") + def set_metadata(self, access, metadata): + """Set or update metadata for share access rule. + + :param share_access_rule: either share access rule object or + text with its ID. + :param metadata: A list of keys to be set. + """ + body = {'metadata': metadata} + access_id = common_base.getid(access) + url = RESOURCES_METADATA_PATH % access_id + return self._update(url, body, "metadata") + + @api_versions.wraps("2.45") + def unset_metadata(self, access, keys): + """Unset metadata on a share access rule. + + :param keys: A list of keys on this object to be unset + :return: None if successful, else API response on failure + """ + for k in keys: + url = RESOURCE_METADATA_PATH % (common_base.getid(access), k) + self._delete(url) + + @api_versions.wraps("2.45") + def access_list(self, share, search_opts): + query_string = "" + if search_opts: + search_opts = utils.unicode_key_value_to_string(search_opts) + params = sorted( + [(k, v) for (k, v) in list(search_opts.items()) if v]) + if params: + query_string = "&%s" % parse.urlencode(params) + url = RESOURCE_LIST_PATH % common_base.getid(share) + query_string + + return self._list(url, 'access_list') diff --git a/manilaclient/v2/shares.py b/manilaclient/v2/shares.py index 2642b2dfd..c2a04854b 100644 --- a/manilaclient/v2/shares.py +++ b/manilaclient/v2/shares.py @@ -78,9 +78,10 @@ class Share(common_base.Resource): """Delete the specified share ignoring its current state.""" self.manager.force_delete(self) - def allow(self, access_type, access, access_level): + def allow(self, access_type, access, access_level, metadata=None): """Allow access to a share.""" - return self.manager.allow(self, access_type, access, access_level) + return self.manager.allow( + self, access_type, access, access_level, metadata) def deny(self, id): """Deny access from IP to a share.""" @@ -505,13 +506,15 @@ class ShareManager(base.ManagerWithFind): ', '.join(valid_access_types)) raise exceptions.CommandError(msg) - def _do_allow(self, share, access_type, access, access_level, action_name): + def _do_allow(self, share, access_type, access, access_level, action_name, + metadata=None): """Allow access to a share. :param share: either share object or text with its ID. :param access_type: string that represents access type ('ip','domain') :param access: string that represents access ('127.0.0.1') :param access_level: string that represents access level ('rw', 'ro') + :param metadata: A dict of key/value pairs to be set """ access_params = { 'access_type': access_type, @@ -519,37 +522,47 @@ class ShareManager(base.ManagerWithFind): } if access_level: access_params['access_level'] = access_level + if metadata: + access_params['metadata'] = metadata access = self._action(action_name, share, access_params)[1]["access"] return access @api_versions.wraps("1.0", "2.6") - def allow(self, share, access_type, access, access_level): + def allow(self, share, access_type, access, access_level, metadata=None): self._validate_access(access_type, access) return self._do_allow( share, access_type, access, access_level, "os-allow_access") @api_versions.wraps("2.7", "2.12") # noqa - def allow(self, share, access_type, access, access_level): + def allow(self, share, access_type, access, access_level, metadata=None): self._validate_access(access_type, access) return self._do_allow( share, access_type, access, access_level, "allow_access") @api_versions.wraps("2.13", "2.37") # noqa - def allow(self, share, access_type, access, access_level): + def allow(self, share, access_type, access, access_level, metadata=None): valid_access_types = ('ip', 'user', 'cert', 'cephx') self._validate_access(access_type, access, valid_access_types) return self._do_allow( share, access_type, access, access_level, "allow_access") - @api_versions.wraps("2.38") # noqa - def allow(self, share, access_type, access, access_level): + @api_versions.wraps("2.38", "2.44") # noqa + def allow(self, share, access_type, access, access_level, metadata=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") + @api_versions.wraps("2.45") # noqa + def allow(self, share, access_type, access, access_level, metadata=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) + def _do_deny(self, share, access_id, action_name): """Deny access to a share. @@ -582,7 +595,7 @@ class ShareManager(base.ManagerWithFind): def access_list(self, share): return self._do_access_list(share, "os-access_list") - @api_versions.wraps("2.7") # noqa + @api_versions.wraps("2.7", "2.44") # noqa def access_list(self, share): return self._do_access_list(share, "access_list") diff --git a/manilaclient/v2/shell.py b/manilaclient/v2/shell.py index e34258d17..f318d80e4 100644 --- a/manilaclient/v2/shell.py +++ b/manilaclient/v2/shell.py @@ -1302,13 +1302,72 @@ def do_show(cs, args): action='single_alias', help='Share access level ("rw" and "ro" access levels are supported). ' 'Defaults to rw.') +@cliutils.arg( + '--metadata', + type=str, + nargs='*', + metavar='', + help='Space Separated list of key=value pairs of metadata items. ' + 'OPTIONAL: Default=None. Available only for microversion >= 2.45.', + default=None) def do_access_allow(cs, args): - """Allow access to the share.""" + """Allow access to a given share.""" + access_metadata = None + if cs.api_version.matches(api_versions.APIVersion("2.45"), + api_versions.APIVersion()): + access_metadata = _extract_metadata(args) + elif getattr(args, 'metadata'): + raise exceptions.CommandError( + "Adding metadata to access rules is supported only beyond " + "API version 2.45") + share = _find_share(cs, args.share) - access = share.allow(args.access_type, args.access_to, args.access_level) + access = share.allow(args.access_type, args.access_to, args.access_level, + access_metadata) cliutils.print_dict(access) +@api_versions.wraps("2.45") +@cliutils.arg( + 'access_id', + metavar='', + help='ID of the NAS share access rule.') +def do_access_show(cs, args): + """Show details about a NAS share access rule.""" + access = cs.share_access_rules.get(args.access_id) + view_data = access._info.copy() + cliutils.print_dict(view_data) + + +@api_versions.wraps("2.45") +@cliutils.arg( + 'access_id', + metavar='', + help='ID of the NAS share access rule.') +@cliutils.arg( + 'action', + metavar='', + choices=['set', 'unset'], + help="Actions: 'set' or 'unset'.") +@cliutils.arg( + 'metadata', + metavar='', + nargs='+', + default=[], + help='Space separated key=value pairs of metadata items to set. ' + 'To unset only keys are required. ') +def do_access_metadata(cs, args): + """Set or delete metadata on a share access rule.""" + share_access = cs.share_access_rules.get(args.access_id) + metadata = _extract_metadata(args) + + if args.action == 'set': + cs.share_access_rules.set_metadata(share_access, metadata) + elif args.action == 'unset': + cs.share_access_rules.unset_metadata( + share_access, sorted(list(metadata), reverse=True)) + + @api_versions.wraps("2.32") @cliutils.arg( 'snapshot', @@ -1437,6 +1496,14 @@ def do_access_list(cs, args): default=None, help='Comma separated list of columns to be displayed ' 'example --columns "access_type,access_to".') +@cliutils.arg( + '--metadata', + type=str, + nargs='*', + metavar='', + help='Filters results by a metadata key and value. OPTIONAL: ' + 'Default=None. Available only for microversion >= 2.45', + default=None) def do_access_list(cs, args): """Show access list for share.""" list_of_keys = [ @@ -1444,11 +1511,18 @@ def do_access_list(cs, args): 'access_key', 'created_at', 'updated_at', ] + share = _find_share(cs, args.share) + if cs.api_version < api_versions.APIVersion("2.45"): + if getattr(args, 'metadata'): + raise exceptions.CommandError( + "Filtering access rules by metadata is supported only beyond " + "API version 2.45") + access_list = share.access_list() + else: + access_list = cs.share_access_rules.access_list( + share, {'metadata': _extract_metadata(args)}) if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) - - share = _find_share(cs, args.share) - access_list = share.access_list() cliutils.print_list(access_list, list_of_keys) diff --git a/releasenotes/notes/add-metadata-for-share-access-rule-11a6b36c0f4c87c2.yaml b/releasenotes/notes/add-metadata-for-share-access-rule-11a6b36c0f4c87c2.yaml new file mode 100644 index 000000000..10dc1994e --- /dev/null +++ b/releasenotes/notes/add-metadata-for-share-access-rule-11a6b36c0f4c87c2.yaml @@ -0,0 +1,5 @@ +--- +features: + - Added support to create share access rule metadata, update existing share + access rule metadata and delete share access rule metadata. Also added a + new shell command to show details of a share access rule.