API v2.45, adds metadata support to access rules
Added access metadata for share access and also introduced the GET /share-access-rules API. The prior API to retrieve access rules will not work with API version >=2.45 Closes-Bug: #1920687 Change-Id: Iec3a3fad5e2bdf854f04ae974248d899f90bd894
This commit is contained in:
parent
eb52c16c65
commit
8b5b9cb83b
|
@ -81,6 +81,21 @@ Deny access
|
|||
|
||||
A message indicates whether the action was successful.
|
||||
|
||||
Edit share access metadata
|
||||
--------------------------
|
||||
|
||||
#. Log in to the dashboard, choose a project, and click :guilabel:`Shares`.
|
||||
|
||||
#. Go to the share that you want to deny access and choose
|
||||
:guilabel:`Manage Rules` from Actions.
|
||||
|
||||
#. Choose the rule you want to edit.
|
||||
|
||||
#. Click :guilabel:`Edit Rule Metadata`: To add share access metadata, use key=value.
|
||||
To unset metadata, use key.
|
||||
|
||||
A message indicates whether the action was successful.
|
||||
|
||||
Edit share metadata
|
||||
-------------------
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ from manilaclient import client as manila_client
|
|||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MANILA_UI_USER_AGENT_REPR = "manila_ui_plugin_for_horizon"
|
||||
MANILA_VERSION = "2.44"
|
||||
MANILA_VERSION = "2.45"
|
||||
MANILA_SERVICE_TYPE = "sharev2"
|
||||
|
||||
# API static values
|
||||
|
@ -125,7 +125,21 @@ def share_update(request, share_id, name, description, is_public=False):
|
|||
|
||||
|
||||
def share_rules_list(request, share_id):
|
||||
return manilaclient(request).shares.access_list(share_id)
|
||||
return manilaclient(request).share_access_rules.access_list(share_id)
|
||||
|
||||
|
||||
def share_rule_get(request, rule_id):
|
||||
return manilaclient(request).share_access_rules.get(rule_id)
|
||||
|
||||
|
||||
def share_rule_set_metadata(request, rule, metadata):
|
||||
return manilaclient(request).share_access_rules.set_metadata(
|
||||
rule, metadata)
|
||||
|
||||
|
||||
def share_rule_unset_metadata(request, rule, keys):
|
||||
return manilaclient(request).share_access_rules.unset_metadata(
|
||||
rule, keys)
|
||||
|
||||
|
||||
def share_export_location_list(request, share_id):
|
||||
|
@ -137,9 +151,10 @@ def share_instance_export_location_list(request, share_instance_id):
|
|||
share_instance_id)
|
||||
|
||||
|
||||
def share_allow(request, share_id, access_type, access_to, access_level):
|
||||
def share_allow(request, share_id, access_type, access_to, access_level,
|
||||
metadata=None):
|
||||
return manilaclient(request).shares.allow(
|
||||
share_id, access_type, access_to, access_level)
|
||||
share_id, access_type, access_to, access_level, metadata)
|
||||
|
||||
|
||||
def share_deny(request, share_id, rule_id):
|
||||
|
|
|
@ -334,15 +334,29 @@ class AddRule(forms.SelfHandlingForm):
|
|||
choices=(('rw', 'read-write'), ('ro', 'read-only'),))
|
||||
access_to = forms.CharField(
|
||||
label=_("Access To"), max_length="255", required=True)
|
||||
metadata = forms.CharField(
|
||||
label=_("Metadata"), required=False,
|
||||
widget=forms.Textarea(attrs={'rows': 4}))
|
||||
|
||||
def handle(self, request, data):
|
||||
share_id = self.initial['share_id']
|
||||
metadata = {}
|
||||
try:
|
||||
set_dict, unset_list = utils.parse_str_meta(data['metadata'])
|
||||
if unset_list:
|
||||
msg = _("Expected only pairs of key=value.")
|
||||
raise ValidationError(message=msg)
|
||||
metadata = set_dict
|
||||
except ValidationError as e:
|
||||
self.api_error(e.messages[0])
|
||||
return False
|
||||
try:
|
||||
manila.share_allow(
|
||||
request, share_id,
|
||||
access_to=data['access_to'],
|
||||
access_type=data['access_type'],
|
||||
access_level=data['access_level'])
|
||||
access_level=data['access_level'],
|
||||
metadata=metadata)
|
||||
message = _('Creating rule for "%s"') % data['access_to']
|
||||
messages.success(request, message)
|
||||
return True
|
||||
|
@ -353,6 +367,39 @@ class AddRule(forms.SelfHandlingForm):
|
|||
request, _('Unable to add rule.'), redirect=redirect)
|
||||
|
||||
|
||||
class UpdateRuleMetadataForm(forms.SelfHandlingForm):
|
||||
metadata = forms.CharField(widget=forms.Textarea,
|
||||
label=_("Metadata"), required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(UpdateRuleMetadataForm, self).__init__(*args, **kwargs)
|
||||
rule_metadata = utils.metadata_to_str(
|
||||
self.initial["metadata"]
|
||||
).replace('<br/>', '\r\n')
|
||||
self.initial["metadata"] = rule_metadata
|
||||
|
||||
def handle(self, request, data):
|
||||
rule_id = self.initial['rule_id']
|
||||
try:
|
||||
rule = manila.share_rule_get(self.request, rule_id)
|
||||
set_dict, unset_list = utils.parse_str_meta(data['metadata'])
|
||||
if set_dict:
|
||||
manila.share_rule_set_metadata(request, rule, set_dict)
|
||||
if unset_list:
|
||||
manila.share_rule_unset_metadata(request, rule, unset_list)
|
||||
message = _('Updating share access rule metadata ')
|
||||
messages.success(request, message)
|
||||
return True
|
||||
except ValidationError as e:
|
||||
self.api_error(e.messages[0])
|
||||
return False
|
||||
except Exception:
|
||||
redirect = reverse("horizon:project:shares:manage_rules")
|
||||
exceptions.handle(request,
|
||||
_('Unable to update rule metadata.'),
|
||||
redirect=redirect)
|
||||
|
||||
|
||||
class ResizeForm(forms.SelfHandlingForm):
|
||||
name = forms.CharField(
|
||||
max_length="255", label=_("Share Name"),
|
||||
|
|
|
@ -347,16 +347,30 @@ class DeleteRule(tables.DeleteAction):
|
|||
exceptions.handle(request, msg)
|
||||
|
||||
|
||||
class EditRuleMetadata(tables.LinkAction):
|
||||
name = "update_rule_metadata"
|
||||
verbose_name = _("Edit Rule Metadata")
|
||||
url = "horizon:project:shares:update_rule_metadata"
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
policy_rules = (("share_access_metadata", "share_access_metadata:update"),)
|
||||
|
||||
def get_policy_target(self, request, datum=None):
|
||||
project_id = None
|
||||
if datum:
|
||||
project_id = getattr(datum, "os-share-tenant-attr:tenant_id", None)
|
||||
return {"project_id": project_id}
|
||||
|
||||
def allowed(self, request, rule=None):
|
||||
return rule.state == "active"
|
||||
|
||||
|
||||
class UpdateRuleRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
def get_data(self, request, rule_id):
|
||||
rules = manila.share_rules_list(request, self.table.kwargs['share_id'])
|
||||
if rules:
|
||||
for rule in rules:
|
||||
if rule.id == rule_id:
|
||||
return rule
|
||||
raise exceptions.NotFound
|
||||
rule = manila.share_rule_get(request, rule_id)
|
||||
rule.metadata = utils.metadata_to_str(rule.metadata)
|
||||
return rule
|
||||
|
||||
|
||||
class RulesTable(tables.DataTable):
|
||||
|
@ -364,6 +378,8 @@ class RulesTable(tables.DataTable):
|
|||
access_to = tables.Column("access_to", verbose_name=_("Access to"))
|
||||
access_level = tables.Column(
|
||||
"access_level", verbose_name=_("Access Level"))
|
||||
metadata = tables.Column(
|
||||
"metadata", verbose_name=_("Metadata"))
|
||||
status = tables.Column("state", verbose_name=_("Status"))
|
||||
access_key = tables.Column("access_key", verbose_name=_("Access Key"))
|
||||
created_at = tables.Column("created_at", verbose_name=_("Created At"),
|
||||
|
@ -383,7 +399,8 @@ class RulesTable(tables.DataTable):
|
|||
AddRule,
|
||||
DeleteRule)
|
||||
row_actions = (
|
||||
DeleteRule,)
|
||||
DeleteRule,
|
||||
EditRuleMetadata,)
|
||||
|
||||
|
||||
def get_share_network(share):
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Metadata" %}:</h3>
|
||||
<p>
|
||||
{% trans "One line - one action. Empty strings will be ignored." %}<br />
|
||||
{% trans "To add metadata use:" %}
|
||||
<pre>key=value</pre>
|
||||
{% trans "To unset metadata use:" %}
|
||||
<pre>key</pre>
|
||||
{% trans "All pairs that are in field for left are set for this metadata." %}
|
||||
</p>
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Edit Rule Metadata" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/shares/_update_rule_metadata.html' %}
|
||||
{% endblock %}
|
|
@ -36,6 +36,10 @@ urlpatterns = [
|
|||
r'^(?P<share_id>[^/]+)/rule_add/$',
|
||||
shares_views.AddRuleView.as_view(),
|
||||
name='rule_add'),
|
||||
urls.url(
|
||||
r'^rules/(?P<rule_id>[^/]+)/update_rule_metadata/$',
|
||||
shares_views.UpdateRuleMetadataView.as_view(),
|
||||
name='update_rule_metadata'),
|
||||
urls.url(
|
||||
r'^(?P<share_id>[^/]+)/$',
|
||||
shares_views.DetailView.as_view(),
|
||||
|
|
|
@ -249,6 +249,53 @@ class AddRuleView(forms.ModalFormView):
|
|||
args=[self.kwargs['share_id']])
|
||||
|
||||
|
||||
class UpdateRuleMetadataView(forms.ModalFormView):
|
||||
form_class = share_form.UpdateRuleMetadataForm
|
||||
form_id = ""
|
||||
template_name = 'project/shares/update_rule_metadata.html'
|
||||
modal_header = _("Edit Rule Metadata")
|
||||
modal_id = "update_rule_metadata_modal"
|
||||
submit_label = _("Save Changes")
|
||||
submit_url = "horizon:project:shares:update_rule_metadata"
|
||||
success_url = reverse_lazy("horizon:project:shares:index")
|
||||
page_title = _('Edit Rule Metadata')
|
||||
|
||||
def get_object(self):
|
||||
if not hasattr(self, "_object"):
|
||||
rule_id = self.kwargs['rule_id']
|
||||
try:
|
||||
self._object = manila.share_rule_get(self.request, rule_id)
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve share access rule.')
|
||||
url = reverse('horizon:project:shares:manage_rules')
|
||||
exceptions.handle(self.request, msg, redirect=url)
|
||||
return self._object
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UpdateRuleMetadataView, self).\
|
||||
get_context_data(**kwargs)
|
||||
args = (self.get_object().id,)
|
||||
context['submit_url'] = reverse(self.submit_url, args=args)
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
rule = self.get_object()
|
||||
return {'rule_id': self.kwargs["rule_id"],
|
||||
'metadata': rule.metadata}
|
||||
|
||||
# To redirect to Rules Table page, after updating the rule
|
||||
# metadata. Loop is to get the share_id neccessary to display
|
||||
# Rules Table page.
|
||||
def get_success_url(self):
|
||||
shares = manila.share_list(self.request)
|
||||
for share in shares:
|
||||
rules = manila.share_rules_list(self.request, share.id)
|
||||
for rule in rules:
|
||||
if rule.id == self.kwargs["rule_id"]:
|
||||
return reverse("horizon:project:shares:manage_rules",
|
||||
args=[share.id])
|
||||
|
||||
|
||||
class ManageRulesView(tables.DataTableView):
|
||||
table_class = shares_tables.RulesTable
|
||||
template_name = 'project/shares/manage_rules.html'
|
||||
|
@ -257,7 +304,6 @@ class ManageRulesView(tables.DataTableView):
|
|||
context = super(ManageRulesView, self).get_context_data(**kwargs)
|
||||
share = manila.share_get(self.request, self.kwargs['share_id'])
|
||||
context['share_display_name'] = share.name or share.id
|
||||
context["share"] = self.get_data()
|
||||
context["page_title"] = _("Share Rules: "
|
||||
"%(share_display_name)s") % {
|
||||
'share_display_name': context['share_display_name']}
|
||||
|
@ -273,6 +319,11 @@ class ManageRulesView(tables.DataTableView):
|
|||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve share rules.'),
|
||||
redirect=redirect)
|
||||
return []
|
||||
|
||||
for rule in rules:
|
||||
rule.metadata = ui_utils.metadata_to_str(rule.metadata)
|
||||
|
||||
return rules
|
||||
|
||||
|
||||
|
|
|
@ -103,9 +103,42 @@ class ManilaApiTests(base.APITestCase):
|
|||
result = api.share_rules_list(self.request, self.id)
|
||||
|
||||
self.assertEqual(
|
||||
self.manilaclient.shares.access_list.return_value, result)
|
||||
self.manilaclient.share_access_rules.
|
||||
access_list.return_value, result)
|
||||
|
||||
self.manilaclient.shares.access_list.assert_called_once_with(self.id)
|
||||
self.manilaclient.share_access_rules.\
|
||||
access_list.assert_called_once_with(self.id)
|
||||
|
||||
def test_share_rule_get(self):
|
||||
result = api.share_rule_get(self.request, self.id)
|
||||
|
||||
self.assertEqual(
|
||||
self.manilaclient.share_access_rules.get.
|
||||
return_value, result)
|
||||
|
||||
self.manilaclient.share_access_rules.get.\
|
||||
assert_called_once_with(self.id)
|
||||
|
||||
def test_share_rule_set_metadata(self):
|
||||
fake_metadata = {
|
||||
"aim": "testing",
|
||||
"project": "test",
|
||||
}
|
||||
|
||||
api.share_rule_set_metadata(
|
||||
self.request, self.id, fake_metadata)
|
||||
|
||||
self.manilaclient.share_access_rules.\
|
||||
set_metadata.assert_called_once_with(self.id, fake_metadata)
|
||||
|
||||
def test_share_rule_unset_metadata(self):
|
||||
fake_key = "test"
|
||||
|
||||
api.share_rule_unset_metadata(
|
||||
self.request, self.id, fake_key)
|
||||
|
||||
self.manilaclient.share_access_rules.\
|
||||
unset_metadata.assert_called_once_with(self.id, fake_key)
|
||||
|
||||
def test_list_share_export_locations(self):
|
||||
api.share_export_location_list(self.request, self.id)
|
||||
|
@ -120,19 +153,22 @@ class ManilaApiTests(base.APITestCase):
|
|||
client.share_instance_export_locations.list.assert_called_once_with(
|
||||
self.id)
|
||||
|
||||
@ddt.data(("ip", "10.0.0.13", "rw"), ("ip", "10.0.0.13", None),
|
||||
("ip", "10.0.0.13", "ro"),
|
||||
("user", "demo", "rw"),
|
||||
("user", "demo", None), ("user", "demo", "ro"),
|
||||
("cephx", "alice", "rw"),
|
||||
("cephx", "alice", None), ("cephx", "alice", "ro"))
|
||||
@ddt.data(("ip", "10.0.0.13", "rw", {'testkey': "testval"}),
|
||||
("ip", "10.0.0.13", None, None),
|
||||
("ip", "10.0.0.13", "ro", {'key1': "val1"}),
|
||||
("user", "demo", "rw", {'key2': "val2"}),
|
||||
("user", "demo", None, None),
|
||||
("user", "demo", "ro", None),
|
||||
("cephx", "alice", "rw", {'test': "some"}),
|
||||
("cephx", "alice", None, None),
|
||||
("cephx", "alice", "ro", None))
|
||||
@ddt.unpack
|
||||
def test_share_allow(self, access_type, access_to, access_level):
|
||||
def test_share_allow(self, access_type, access_to, access_level, metadata):
|
||||
api.share_allow(self.request, self.id, access_type,
|
||||
access_to, access_level)
|
||||
access_to, access_level, metadata)
|
||||
|
||||
self.manilaclient.shares.allow.assert_called_once_with(
|
||||
self.id, access_type, access_to, access_level)
|
||||
self.id, access_type, access_to, access_level, metadata)
|
||||
|
||||
def test_share_deny(self):
|
||||
fake_rule_id = "fake_rule_id"
|
||||
|
|
|
@ -300,7 +300,7 @@ class ShareViewTests(test.APITestCase):
|
|||
[mock.call(mock.ANY, self.share.id) for i in (1, 2)])
|
||||
|
||||
def test_list_rules(self):
|
||||
rules = [test_data.ip_rule, test_data.user_rule, test_data.cephx_rule]
|
||||
rules = test_data.share_access_list
|
||||
self.mock_object(
|
||||
api_manila, "share_rules_list", mock.Mock(return_value=rules))
|
||||
url = reverse(
|
||||
|
@ -331,6 +331,7 @@ class ShareViewTests(test.APITestCase):
|
|||
'method': 'CreateForm',
|
||||
'access_to': 'someuser',
|
||||
'access_level': 'rw',
|
||||
'metadata': {},
|
||||
}
|
||||
|
||||
res = self.client.post(url, formData)
|
||||
|
@ -338,7 +339,8 @@ class ShareViewTests(test.APITestCase):
|
|||
api_manila.share_allow.assert_called_once_with(
|
||||
mock.ANY, self.share.id, access_type=formData['access_type'],
|
||||
access_to=formData['access_to'],
|
||||
access_level=formData['access_level'])
|
||||
access_level=formData['access_level'],
|
||||
metadata=formData['metadata'])
|
||||
self.assertRedirectsNoFollow(
|
||||
res,
|
||||
reverse('horizon:project:shares:manage_rules',
|
||||
|
@ -360,6 +362,43 @@ class ShareViewTests(test.APITestCase):
|
|||
mock.ANY, self.share.id, rule.id)
|
||||
api_manila.share_rules_list.assert_called_with(mock.ANY, self.share.id)
|
||||
|
||||
def test_update_share_rule_metadata_get(self):
|
||||
rule = test_data.user_rule
|
||||
url = reverse(
|
||||
'horizon:project:shares:update_rule_metadata', args=[rule.id])
|
||||
self.mock_object(
|
||||
api_manila, "share_rule_get", mock.Mock(return_value=rule))
|
||||
res = self.client.get(url)
|
||||
api_manila.share_rule_get.assert_called_once_with(mock.ANY, rule.id)
|
||||
self.assertNoMessages()
|
||||
self.assertTemplateUsed(
|
||||
res, 'project/shares/update_rule_metadata.html')
|
||||
|
||||
def test_update_share_rule_metadata_post(self):
|
||||
rule = test_data.user_rule
|
||||
data = {
|
||||
'metadata': 'aaa=ccc',
|
||||
}
|
||||
form_data = {
|
||||
'metadata': {'aaa': 'ccc'},
|
||||
}
|
||||
url = reverse(
|
||||
'horizon:project:shares:update_rule_metadata', args=[rule.id])
|
||||
self.mock_object(
|
||||
api_manila, "share_list", mock.Mock(
|
||||
return_value=test_data.shares_list))
|
||||
self.mock_object(
|
||||
api_manila, "share_rules_list", mock.Mock(
|
||||
return_value=test_data.share_access_list))
|
||||
self.mock_object(
|
||||
api_manila, "share_rule_get", mock.Mock(return_value=rule))
|
||||
self.mock_object(api_manila, "share_rule_set_metadata")
|
||||
|
||||
self.client.post(url, data)
|
||||
|
||||
api_manila.share_rule_set_metadata.assert_called_once_with(
|
||||
mock.ANY, rule, form_data['metadata'])
|
||||
|
||||
def test_resize_share_get(self):
|
||||
share = test_data.share
|
||||
url = reverse('horizon:project:shares:resize', args=[share.id])
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import collections
|
||||
|
||||
from manilaclient.v2 import messages
|
||||
from manilaclient.v2 import security_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_types
|
||||
|
@ -102,6 +102,8 @@ other_share = shares.Share(
|
|||
'replication_type': 'readable',
|
||||
'mount_snapshot_support': False})
|
||||
|
||||
shares_list = [share, nameless_share, other_share]
|
||||
|
||||
share_replica = share_replicas.ShareReplica(
|
||||
share_replicas.ShareReplicaManager(FakeAPIClient),
|
||||
{'id': '11023e92-8008-4c8b-8059-replica00001',
|
||||
|
@ -207,17 +209,46 @@ user_snapshot_export_locations = [
|
|||
'path': '1.1.1.2:/not/too/long/path/to/user/share_snapshot'}
|
||||
)
|
||||
]
|
||||
ip_rule = share_access_rules.ShareAccessRule(
|
||||
share_access_rules.ShareAccessRuleManager(FakeAPIClient),
|
||||
{'id': 'ca8b755c-fe13-497f-81d0-fd2f13a30a78',
|
||||
'access_level': 'rw',
|
||||
'access_to': '1.1.1.1',
|
||||
'access_type': 'ip',
|
||||
'state': 'active',
|
||||
'access_key': '',
|
||||
'created_at': '2021-03-03T14:29:41.000000',
|
||||
'updated_at': '',
|
||||
"metadata": {},
|
||||
})
|
||||
|
||||
rule = collections.namedtuple('Access', ['access_type', 'access_to', 'state',
|
||||
'id', 'access_level', 'access_key'])
|
||||
user_rule = share_access_rules.ShareAccessRule(
|
||||
share_access_rules.ShareAccessRuleManager(FakeAPIClient),
|
||||
{'id': '0837072-c49e-11e3-bd64-60a44c371189',
|
||||
'access_level': 'rw',
|
||||
'access_to': 'someuser',
|
||||
'access_type': 'user',
|
||||
'state': 'active',
|
||||
'access_key': '',
|
||||
'created_at': '2021-03-03T14:29:41.000000',
|
||||
'updated_at': '',
|
||||
'metadata': {'abc': 'ddd'},
|
||||
})
|
||||
|
||||
user_rule = rule('user', 'someuser', 'active',
|
||||
'10837072-c49e-11e3-bd64-60a44c371189', 'rw', '')
|
||||
ip_rule = rule('ip', '1.1.1.1', 'active',
|
||||
'2cc8e2f8-c49e-11e3-bd64-60a44c371189', 'rw', '')
|
||||
cephx_rule = rule('cephx', 'alice', 'active',
|
||||
'235481bc-1a84-11e6-9666-68f728a0492e', 'rw',
|
||||
'AQAdFCNYDCapMRAANuK/CiEZbog2911a+t5dcQ==')
|
||||
cephx_rule = share_access_rules.ShareAccessRule(
|
||||
share_access_rules.ShareAccessRuleManager(FakeAPIClient),
|
||||
{'id': '235481bc-1a84-11e6-9666-68f728a0492e',
|
||||
'access_level': 'rw',
|
||||
'access_to': 'alice',
|
||||
'access_type': 'cephx',
|
||||
'state': 'active',
|
||||
'access_key': 'AQAdFCNYDCapMRAANuK/CiEZbog2911a+t5dcQ==',
|
||||
'created_at': '2021-03-03T14:29:41.000000',
|
||||
'updated_at': '',
|
||||
'metadata': {'test': 'true'},
|
||||
})
|
||||
|
||||
share_access_list = [user_rule, cephx_rule, ip_rule]
|
||||
|
||||
snapshot = share_snapshots.ShareSnapshot(
|
||||
share_snapshots.ShareSnapshotManager(FakeAPIClient),
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Added access metadata for share access and
|
||||
also introduced the GET /share-access-rules API.
|
Loading…
Reference in New Issue