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
This commit is contained in:
@@ -27,7 +27,7 @@ from manilaclient import utils
|
|||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
MAX_VERSION = '2.43'
|
MAX_VERSION = '2.45'
|
||||||
MIN_VERSION = '2.0'
|
MIN_VERSION = '2.0'
|
||||||
DEPRECATED_VERSION = '1.0'
|
DEPRECATED_VERSION = '1.0'
|
||||||
_VERSIONED_METHOD_MAP = {}
|
_VERSIONED_METHOD_MAP = {}
|
||||||
|
@@ -982,7 +982,7 @@ class ManilaCLIClient(base.CLIClient):
|
|||||||
|
|
||||||
@not_found_wrapper
|
@not_found_wrapper
|
||||||
def list_access(self, entity_id, columns=None, microversion=None,
|
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.
|
"""Returns list of access rules for a share.
|
||||||
|
|
||||||
:param entity_id: str -- Name or ID of a share or snapshot.
|
: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
|
cmd = 'access-list %s ' % entity_id
|
||||||
if columns is not None:
|
if columns is not None:
|
||||||
cmd += ' --columns ' + columns
|
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)
|
access_list_raw = self.manila(cmd, microversion=microversion)
|
||||||
return output_parser.listing(access_list_raw)
|
return output_parser.listing(access_list_raw)
|
||||||
|
|
||||||
@@ -1009,6 +1015,33 @@ class ManilaCLIClient(base.CLIClient):
|
|||||||
return access
|
return access
|
||||||
raise tempest_lib_exc.NotFound()
|
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
|
@not_found_wrapper
|
||||||
def snapshot_access_allow(self, snapshot_id, access_type, access_to,
|
def snapshot_access_allow(self, snapshot_id, access_type, access_to,
|
||||||
microversion=None):
|
microversion=None):
|
||||||
@@ -1032,16 +1065,20 @@ class ManilaCLIClient(base.CLIClient):
|
|||||||
|
|
||||||
@not_found_wrapper
|
@not_found_wrapper
|
||||||
def access_allow(self, share_id, access_type, access_to, access_level,
|
def access_allow(self, share_id, access_type, access_to, access_level,
|
||||||
microversion=None):
|
metadata=None, microversion=None):
|
||||||
raw_access = self.manila(
|
cmd = ('access-allow --access-level %(level)s %(id)s %(type)s '
|
||||||
'access-allow --access-level %(level)s %(id)s %(type)s '
|
'%(access_to)s' % {
|
||||||
'%(access_to)s' % {
|
'level': access_level,
|
||||||
'level': access_level,
|
'id': share_id,
|
||||||
'id': share_id,
|
'type': access_type,
|
||||||
'type': access_type,
|
'access_to': access_to})
|
||||||
'access_to': access_to,
|
if metadata:
|
||||||
},
|
metadata_cli = ''
|
||||||
microversion=microversion)
|
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)
|
return output_parser.details(raw_access)
|
||||||
|
|
||||||
@not_found_wrapper
|
@not_found_wrapper
|
||||||
|
@@ -13,6 +13,8 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import ast
|
||||||
|
|
||||||
import ddt
|
import ddt
|
||||||
from tempest.lib import exceptions as tempest_lib_exc
|
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],
|
'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_type = self.access_types[0]
|
||||||
|
|
||||||
access = self.user_client.access_allow(
|
access = self.user_client.access_allow(
|
||||||
self.share['id'], access_type, self.access_to[access_type].pop(),
|
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
|
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):
|
def test_create_list_access_rule_for_share(self, microversion):
|
||||||
self.skip_if_microversion_not_supported(microversion)
|
self.skip_if_microversion_not_supported(microversion)
|
||||||
access = self._test_create_list_access_rule_for_share(
|
access = self._test_create_list_access_rule_for_share(
|
||||||
@@ -95,8 +100,9 @@ class ShareAccessReadWriteBase(base.BaseTestCase):
|
|||||||
if (api_versions.APIVersion(microversion) >=
|
if (api_versions.APIVersion(microversion) >=
|
||||||
api_versions.APIVersion("2.33")):
|
api_versions.APIVersion("2.33")):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
all(('access_key' and 'created_at' and 'updated_at')
|
all(all(key in access for key in (
|
||||||
in a for a in access_list))
|
'access_key', 'created_at', 'updated_at'))
|
||||||
|
for access in access_list))
|
||||||
elif (api_versions.APIVersion(microversion) >=
|
elif (api_versions.APIVersion(microversion) >=
|
||||||
api_versions.APIVersion("2.21")):
|
api_versions.APIVersion("2.21")):
|
||||||
self.assertTrue(all('access_key' in a for a in access_list))
|
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.assertRaises(tempest_lib_exc.NotFound,
|
||||||
self.user_client.get_access, share_id, access['id'])
|
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")
|
@ddt.data("1.0", "2.0", "2.6", "2.7", "2.21", "2.33")
|
||||||
def test_create_delete_ip_access_rule(self, microversion):
|
def test_create_delete_ip_access_rule(self, microversion):
|
||||||
self._create_delete_access_rule(
|
self._create_delete_access_rule(
|
||||||
|
@@ -505,6 +505,45 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
|
|||||||
raise AssertionError("Unexpected share action: %s" % action)
|
raise AssertionError("Unexpected share action: %s" % action)
|
||||||
return (resp, {}, _body)
|
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):
|
def post_share_networks(self, **kwargs):
|
||||||
return (202, {}, {'share_network': {}})
|
return (202, {}, {'share_network': {}})
|
||||||
|
|
||||||
|
@@ -49,7 +49,7 @@ class SharesTest(utils.TestCase):
|
|||||||
self.share.allow(access_type, access_to, access_level)
|
self.share.allow(access_type, access_to, access_level)
|
||||||
|
|
||||||
self.share.manager.allow.assert_called_once_with(
|
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
|
# Testcases for class ShareManager
|
||||||
|
|
||||||
|
@@ -23,6 +23,7 @@ from oslo_utils import strutils
|
|||||||
import six
|
import six
|
||||||
from six.moves.urllib import parse
|
from six.moves.urllib import parse
|
||||||
|
|
||||||
|
from manilaclient import api_versions
|
||||||
from manilaclient import client
|
from manilaclient import client
|
||||||
from manilaclient.common.apiclient import utils as apiclient_utils
|
from manilaclient.common.apiclient import utils as apiclient_utils
|
||||||
from manilaclient.common import cliutils
|
from manilaclient.common import cliutils
|
||||||
@@ -1705,17 +1706,63 @@ class ShellTest(test_utils.TestCase):
|
|||||||
self.assertRaises(SystemExit, self.run_command,
|
self.assertRaises(SystemExit, self.run_command,
|
||||||
"access-allow --access-level fake 1111 ip 10.0.0.6")
|
"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())
|
@mock.patch.object(cliutils, 'print_list', mock.Mock())
|
||||||
def test_access_list(self):
|
@ddt.data(*set(["2.44", "2.45", api_versions.MAX_VERSION]))
|
||||||
self.run_command("access-list 1111")
|
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(
|
cliutils.print_list.assert_called_with(
|
||||||
mock.ANY,
|
mock.ANY,
|
||||||
['id', 'access_type', 'access_to', 'access_level', 'state',
|
['id', 'access_type', 'access_to', 'access_level', 'state',
|
||||||
'access_key', 'created_at', 'updated_at'])
|
'access_key', 'created_at', 'updated_at'])
|
||||||
|
|
||||||
@mock.patch.object(cliutils, 'print_list', mock.Mock())
|
@mock.patch.object(cliutils, 'print_list', mock.Mock())
|
||||||
def test_access_list_select_column(self):
|
@ddt.data(*set(["2.44", "2.45", api_versions.MAX_VERSION]))
|
||||||
self.run_command("access-list 1111 --columns id,access_type")
|
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(
|
cliutils.print_list.assert_called_with(
|
||||||
mock.ANY,
|
mock.ANY,
|
||||||
['Id', 'Access_Type'])
|
['Id', 'Access_Type'])
|
||||||
|
@@ -28,6 +28,7 @@ from manilaclient.v2 import quotas
|
|||||||
from manilaclient.v2 import scheduler_stats
|
from manilaclient.v2 import scheduler_stats
|
||||||
from manilaclient.v2 import security_services
|
from manilaclient.v2 import security_services
|
||||||
from manilaclient.v2 import 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_export_locations
|
||||||
from manilaclient.v2 import share_group_snapshots
|
from manilaclient.v2 import share_group_snapshots
|
||||||
from manilaclient.v2 import share_group_type_access
|
from manilaclient.v2 import share_group_type_access
|
||||||
@@ -237,6 +238,8 @@ class Client(object):
|
|||||||
self.share_servers = share_servers.ShareServerManager(self)
|
self.share_servers = share_servers.ShareServerManager(self)
|
||||||
self.share_replicas = share_replicas.ShareReplicaManager(self)
|
self.share_replicas = share_replicas.ShareReplicaManager(self)
|
||||||
self.pools = scheduler_stats.PoolManager(self)
|
self.pools = scheduler_stats.PoolManager(self)
|
||||||
|
self.share_access_rules = (
|
||||||
|
share_access_rules.ShareAccessRuleManager(self))
|
||||||
|
|
||||||
self._load_extensions(extensions)
|
self._load_extensions(extensions)
|
||||||
|
|
||||||
|
99
manilaclient/v2/share_access_rules.py
Normal file
99
manilaclient/v2/share_access_rules.py
Normal file
@@ -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 "<Share Access Rule: %s>" % 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')
|
@@ -78,9 +78,10 @@ class Share(common_base.Resource):
|
|||||||
"""Delete the specified share ignoring its current state."""
|
"""Delete the specified share ignoring its current state."""
|
||||||
self.manager.force_delete(self)
|
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."""
|
"""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):
|
def deny(self, id):
|
||||||
"""Deny access from IP to a share."""
|
"""Deny access from IP to a share."""
|
||||||
@@ -505,13 +506,15 @@ class ShareManager(base.ManagerWithFind):
|
|||||||
', '.join(valid_access_types))
|
', '.join(valid_access_types))
|
||||||
raise exceptions.CommandError(msg)
|
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.
|
"""Allow access to a share.
|
||||||
|
|
||||||
:param share: either share object or text with its ID.
|
:param share: either share object or text with its ID.
|
||||||
:param access_type: string that represents access type ('ip','domain')
|
:param access_type: string that represents access type ('ip','domain')
|
||||||
:param access: string that represents access ('127.0.0.1')
|
:param access: string that represents access ('127.0.0.1')
|
||||||
:param access_level: string that represents access level ('rw', 'ro')
|
:param access_level: string that represents access level ('rw', 'ro')
|
||||||
|
:param metadata: A dict of key/value pairs to be set
|
||||||
"""
|
"""
|
||||||
access_params = {
|
access_params = {
|
||||||
'access_type': access_type,
|
'access_type': access_type,
|
||||||
@@ -519,37 +522,47 @@ class ShareManager(base.ManagerWithFind):
|
|||||||
}
|
}
|
||||||
if access_level:
|
if access_level:
|
||||||
access_params['access_level'] = access_level
|
access_params['access_level'] = access_level
|
||||||
|
if metadata:
|
||||||
|
access_params['metadata'] = metadata
|
||||||
access = self._action(action_name, share,
|
access = self._action(action_name, share,
|
||||||
access_params)[1]["access"]
|
access_params)[1]["access"]
|
||||||
return access
|
return access
|
||||||
|
|
||||||
@api_versions.wraps("1.0", "2.6")
|
@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)
|
self._validate_access(access_type, access)
|
||||||
return self._do_allow(
|
return self._do_allow(
|
||||||
share, access_type, access, access_level, "os-allow_access")
|
share, access_type, access, access_level, "os-allow_access")
|
||||||
|
|
||||||
@api_versions.wraps("2.7", "2.12") # noqa
|
@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)
|
self._validate_access(access_type, access)
|
||||||
return self._do_allow(
|
return self._do_allow(
|
||||||
share, access_type, access, access_level, "allow_access")
|
share, access_type, access, access_level, "allow_access")
|
||||||
|
|
||||||
@api_versions.wraps("2.13", "2.37") # noqa
|
@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')
|
valid_access_types = ('ip', 'user', 'cert', 'cephx')
|
||||||
self._validate_access(access_type, access, valid_access_types)
|
self._validate_access(access_type, access, valid_access_types)
|
||||||
return self._do_allow(
|
return self._do_allow(
|
||||||
share, access_type, access, access_level, "allow_access")
|
share, access_type, access, access_level, "allow_access")
|
||||||
|
|
||||||
@api_versions.wraps("2.38") # noqa
|
@api_versions.wraps("2.38", "2.44") # 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')
|
valid_access_types = ('ip', 'user', 'cert', 'cephx')
|
||||||
self._validate_access(access_type, access, valid_access_types,
|
self._validate_access(access_type, access, valid_access_types,
|
||||||
enable_ipv6=True)
|
enable_ipv6=True)
|
||||||
return self._do_allow(
|
return self._do_allow(
|
||||||
share, access_type, access, access_level, "allow_access")
|
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):
|
def _do_deny(self, share, access_id, action_name):
|
||||||
"""Deny access to a share.
|
"""Deny access to a share.
|
||||||
|
|
||||||
@@ -582,7 +595,7 @@ class ShareManager(base.ManagerWithFind):
|
|||||||
def access_list(self, share):
|
def access_list(self, share):
|
||||||
return self._do_access_list(share, "os-access_list")
|
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):
|
def access_list(self, share):
|
||||||
return self._do_access_list(share, "access_list")
|
return self._do_access_list(share, "access_list")
|
||||||
|
|
||||||
|
@@ -1302,13 +1302,72 @@ def do_show(cs, args):
|
|||||||
action='single_alias',
|
action='single_alias',
|
||||||
help='Share access level ("rw" and "ro" access levels are supported). '
|
help='Share access level ("rw" and "ro" access levels are supported). '
|
||||||
'Defaults to rw.')
|
'Defaults to rw.')
|
||||||
|
@cliutils.arg(
|
||||||
|
'--metadata',
|
||||||
|
type=str,
|
||||||
|
nargs='*',
|
||||||
|
metavar='<key=value>',
|
||||||
|
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):
|
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)
|
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)
|
cliutils.print_dict(access)
|
||||||
|
|
||||||
|
|
||||||
|
@api_versions.wraps("2.45")
|
||||||
|
@cliutils.arg(
|
||||||
|
'access_id',
|
||||||
|
metavar='<access_id>',
|
||||||
|
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='<access_id>',
|
||||||
|
help='ID of the NAS share access rule.')
|
||||||
|
@cliutils.arg(
|
||||||
|
'action',
|
||||||
|
metavar='<action>',
|
||||||
|
choices=['set', 'unset'],
|
||||||
|
help="Actions: 'set' or 'unset'.")
|
||||||
|
@cliutils.arg(
|
||||||
|
'metadata',
|
||||||
|
metavar='<key=value>',
|
||||||
|
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")
|
@api_versions.wraps("2.32")
|
||||||
@cliutils.arg(
|
@cliutils.arg(
|
||||||
'snapshot',
|
'snapshot',
|
||||||
@@ -1437,6 +1496,14 @@ def do_access_list(cs, args):
|
|||||||
default=None,
|
default=None,
|
||||||
help='Comma separated list of columns to be displayed '
|
help='Comma separated list of columns to be displayed '
|
||||||
'example --columns "access_type,access_to".')
|
'example --columns "access_type,access_to".')
|
||||||
|
@cliutils.arg(
|
||||||
|
'--metadata',
|
||||||
|
type=str,
|
||||||
|
nargs='*',
|
||||||
|
metavar='<key=value>',
|
||||||
|
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):
|
def do_access_list(cs, args):
|
||||||
"""Show access list for share."""
|
"""Show access list for share."""
|
||||||
list_of_keys = [
|
list_of_keys = [
|
||||||
@@ -1444,11 +1511,18 @@ def do_access_list(cs, args):
|
|||||||
'access_key', 'created_at', 'updated_at',
|
'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:
|
if args.columns is not None:
|
||||||
list_of_keys = _split_columns(columns=args.columns)
|
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)
|
cliutils.print_list(access_list, list_of_keys)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -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.
|
Reference in New Issue
Block a user