Add share access rules to shared file system
Introduce Share Access Rules class with basic methods including create, view, list, get and delete to shared file system storage service Change-Id: I120212e6f1a01644479dc9bc4547b022049c49a1 Co-Authored-By: Samuel Loegering <samloegering@icloud.com> (cherry picked from commit 012b5a128a9318e116ca77f29c7f4dc8b88637db)
This commit is contained in:
parent
d416746e5c
commit
56e3660736
doc/source/user
openstack
releasenotes/notes
@ -126,3 +126,15 @@ service.
|
|||||||
:noindex:
|
:noindex:
|
||||||
:members: share_network_subnets, get_share_network_subnet,
|
:members: share_network_subnets, get_share_network_subnet,
|
||||||
create_share_network_subnet, delete_share_network_subnet
|
create_share_network_subnet, delete_share_network_subnet
|
||||||
|
|
||||||
|
|
||||||
|
Shared File System Share Access Rules
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Create, View, and Delete access rules for shares from the
|
||||||
|
Shared File Systems service.
|
||||||
|
|
||||||
|
.. autoclass:: openstack.shared_file_system.v2._proxy.Proxy
|
||||||
|
:noindex:
|
||||||
|
:members: access_rules, get_access_rule, create_access_rule,
|
||||||
|
delete_access_rule
|
||||||
|
@ -14,3 +14,4 @@ Shared File System service resources
|
|||||||
v2/share_snapshot_instance
|
v2/share_snapshot_instance
|
||||||
v2/share_network
|
v2/share_network
|
||||||
v2/user_message
|
v2/user_message
|
||||||
|
v2/share_access_rule
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
openstack.shared_file_system.v2.share_access_rule
|
||||||
|
=================================================
|
||||||
|
|
||||||
|
.. automodule:: openstack.shared_file_system.v2.share_access_rule
|
||||||
|
|
||||||
|
The ShareAccessRule Class
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
The ``ShareAccessRule`` class inherits from
|
||||||
|
:class:`~openstack.resource.Resource`.
|
||||||
|
|
||||||
|
.. autoclass:: openstack.shared_file_system.v2.share_access_rule.ShareAccessRule
|
||||||
|
:members:
|
@ -689,7 +689,6 @@ class Proxy(adapter.Adapter, Generic[T]):
|
|||||||
:class:`~openstack.resource.Resource` that doesn't match
|
:class:`~openstack.resource.Resource` that doesn't match
|
||||||
the ``resource_type``.
|
the ``resource_type``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data = resource_type.list(
|
data = resource_type.list(
|
||||||
self, paginated=paginated, base_path=base_path,
|
self, paginated=paginated, base_path=base_path,
|
||||||
**attrs
|
**attrs
|
||||||
|
@ -14,6 +14,9 @@ from openstack import proxy
|
|||||||
from openstack import resource
|
from openstack import resource
|
||||||
from openstack.shared_file_system.v2 import (
|
from openstack.shared_file_system.v2 import (
|
||||||
availability_zone as _availability_zone)
|
availability_zone as _availability_zone)
|
||||||
|
from openstack.shared_file_system.v2 import (
|
||||||
|
share_access_rule as _share_access_rule
|
||||||
|
)
|
||||||
from openstack.shared_file_system.v2 import (
|
from openstack.shared_file_system.v2 import (
|
||||||
share_export_locations as _share_export_locations
|
share_export_locations as _share_export_locations
|
||||||
)
|
)
|
||||||
@ -55,6 +58,7 @@ class Proxy(proxy.Proxy):
|
|||||||
"share_instance": _share_instance.ShareInstance,
|
"share_instance": _share_instance.ShareInstance,
|
||||||
"share_export_locations":
|
"share_export_locations":
|
||||||
_share_export_locations.ShareExportLocation,
|
_share_export_locations.ShareExportLocation,
|
||||||
|
"share_access_rule": _share_access_rule.ShareAccessRule,
|
||||||
}
|
}
|
||||||
|
|
||||||
def availability_zones(self):
|
def availability_zones(self):
|
||||||
@ -628,3 +632,54 @@ class Proxy(proxy.Proxy):
|
|||||||
return self._get(
|
return self._get(
|
||||||
_share_export_locations.ShareExportLocation,
|
_share_export_locations.ShareExportLocation,
|
||||||
export_location_id, share_id=share_id)
|
export_location_id, share_id=share_id)
|
||||||
|
|
||||||
|
def access_rules(self, share, **query):
|
||||||
|
"""Lists the access rules on a share.
|
||||||
|
|
||||||
|
:returns: A generator of the share access rules.
|
||||||
|
:rtype: :class:`~openstack.shared_file_system.v2.
|
||||||
|
share_access_rules.ShareAccessRules`
|
||||||
|
"""
|
||||||
|
share = self._get_resource(_share.Share, share)
|
||||||
|
return self._list(
|
||||||
|
_share_access_rule.ShareAccessRule,
|
||||||
|
share_id=share.id, **query)
|
||||||
|
|
||||||
|
def get_access_rule(self, access_id):
|
||||||
|
"""List details of an access rule.
|
||||||
|
|
||||||
|
:param access_id: The id of the access rule to get
|
||||||
|
:returns: Details of the identified access rule.
|
||||||
|
:rtype: :class:`~openstack.shared_file_system.v2.
|
||||||
|
share_access_rules.ShareAccessRules`
|
||||||
|
"""
|
||||||
|
return self._get(
|
||||||
|
_share_access_rule.ShareAccessRule, access_id)
|
||||||
|
|
||||||
|
def create_access_rule(self, share_id, **attrs):
|
||||||
|
"""Creates an access rule from attributes
|
||||||
|
|
||||||
|
:returns: Details of the new access rule
|
||||||
|
:param share_id: The ID of the share
|
||||||
|
:param dict attrs: Attributes which will be used to create
|
||||||
|
a :class:`~openstack.shared_file_system.v2.
|
||||||
|
share_access_rules.ShareAccessRules`, comprised of the
|
||||||
|
properties on the ShareAccessRules class.
|
||||||
|
:rtype: :class:`~openstack.shared_file_system.v2.
|
||||||
|
share_access_rules.ShareAccessRules`
|
||||||
|
"""
|
||||||
|
base_path = "/shares/%s/action" % (share_id,)
|
||||||
|
return self._create(
|
||||||
|
_share_access_rule.ShareAccessRule, base_path=base_path, **attrs)
|
||||||
|
|
||||||
|
def delete_access_rule(self, access_id, share_id, ignore_missing=True):
|
||||||
|
"""Deletes an access rule
|
||||||
|
|
||||||
|
:param access_id: The id of the access rule to get
|
||||||
|
:param share_id: The ID of the share
|
||||||
|
|
||||||
|
:rtype: ``None``
|
||||||
|
"""
|
||||||
|
res = self._get_resource(
|
||||||
|
_share_access_rule.ShareAccessRule, access_id)
|
||||||
|
res.delete(self, share_id, ignore_missing=ignore_missing)
|
||||||
|
86
openstack/shared_file_system/v2/share_access_rule.py
Normal file
86
openstack/shared_file_system/v2/share_access_rule.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from openstack import exceptions
|
||||||
|
from openstack import resource
|
||||||
|
from openstack import utils
|
||||||
|
|
||||||
|
|
||||||
|
class ShareAccessRule(resource.Resource):
|
||||||
|
resource_key = "share_access_rule"
|
||||||
|
resources_key = "access_list"
|
||||||
|
base_path = "/share-access-rules"
|
||||||
|
|
||||||
|
# capabilities
|
||||||
|
allow_create = True
|
||||||
|
allow_fetch = True
|
||||||
|
allow_commit = False
|
||||||
|
allow_delete = True
|
||||||
|
allow_list = True
|
||||||
|
allow_head = False
|
||||||
|
|
||||||
|
_query_mapping = resource.QueryParameters("share_id")
|
||||||
|
|
||||||
|
#: Properties
|
||||||
|
#: The access credential of the entity granted share access.
|
||||||
|
access_key = resource.Body("access_key", type=str)
|
||||||
|
#: The access level to the share.
|
||||||
|
access_level = resource.Body("access_level", type=str)
|
||||||
|
#: The object of the access rule.
|
||||||
|
access_list = resource.Body("access_list", type=str)
|
||||||
|
#: The value that defines the access.
|
||||||
|
access_to = resource.Body("access_to", type=str)
|
||||||
|
#: The access rule type.
|
||||||
|
access_type = resource.Body("access_type", type=str)
|
||||||
|
#: The date and time stamp when the resource was created within the
|
||||||
|
#: service’s database.
|
||||||
|
created_at = resource.Body("created_at", type=str)
|
||||||
|
#: One or more access rule metadata key and value pairs as a dictionary
|
||||||
|
#: of strings.
|
||||||
|
metadata = resource.Body("metadata", type=dict)
|
||||||
|
#: The UUID of the share to which you are granted or denied access.
|
||||||
|
share_id = resource.Body("share_id", type=str)
|
||||||
|
#: The state of the access rule.
|
||||||
|
state = resource.Body("state", type=str)
|
||||||
|
#: The date and time stamp when the resource was last updated within
|
||||||
|
#: the service’s database.
|
||||||
|
updated_at = resource.Body("updated_at", type=str)
|
||||||
|
|
||||||
|
def _action(self, session, body, url,
|
||||||
|
action='patch', microversion=None):
|
||||||
|
headers = {'Accept': ''}
|
||||||
|
|
||||||
|
if microversion is None:
|
||||||
|
microversion = \
|
||||||
|
self._get_microversion(session, action=action)
|
||||||
|
|
||||||
|
session.post(
|
||||||
|
url,
|
||||||
|
json=body,
|
||||||
|
headers=headers,
|
||||||
|
microversion=microversion)
|
||||||
|
|
||||||
|
def create(self, session, **kwargs):
|
||||||
|
return super().create(session,
|
||||||
|
resource_request_key='allow_access',
|
||||||
|
resource_response_key='access',
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
def delete(self, session, share_id, ignore_missing=True):
|
||||||
|
body = {'deny_access': {'access_id' : self.id}}
|
||||||
|
url = utils.urljoin('/shares', share_id, 'action')
|
||||||
|
try:
|
||||||
|
response = self._action(session, body, url)
|
||||||
|
except exceptions.ResourceNotFound:
|
||||||
|
if not ignore_missing:
|
||||||
|
raise
|
||||||
|
return response
|
@ -0,0 +1,70 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from openstack.tests.functional.shared_file_system import base
|
||||||
|
|
||||||
|
|
||||||
|
class ShareAccessRuleTest(base.BaseSharedFileSystemTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ShareAccessRuleTest, self).setUp()
|
||||||
|
|
||||||
|
self.SHARE_NAME = self.getUniqueString()
|
||||||
|
mys = self.create_share(
|
||||||
|
name=self.SHARE_NAME, size=2, share_type="dhss_false",
|
||||||
|
share_protocol='NFS', description=None)
|
||||||
|
self.user_cloud.shared_file_system.wait_for_status(
|
||||||
|
mys,
|
||||||
|
status='available',
|
||||||
|
failures=['error'],
|
||||||
|
interval=5,
|
||||||
|
wait=self._wait_for_timeout)
|
||||||
|
self.assertIsNotNone(mys)
|
||||||
|
self.assertIsNotNone(mys.id)
|
||||||
|
self.SHARE_ID = mys.id
|
||||||
|
self.SHARE = mys
|
||||||
|
access_rule = self.user_cloud.share.create_access_rule(
|
||||||
|
self.SHARE_ID,
|
||||||
|
access_level="rw",
|
||||||
|
access_type="ip",
|
||||||
|
access_to="0.0.0.0/0"
|
||||||
|
)
|
||||||
|
self.ACCESS_ID = access_rule.id
|
||||||
|
self.RESOURCE_KEY = access_rule.resource_key
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
acr = self.user_cloud.share.delete_access_rule(
|
||||||
|
self.ACCESS_ID,
|
||||||
|
self.SHARE_ID,
|
||||||
|
ignore_missing=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIsNone(acr)
|
||||||
|
super(ShareAccessRuleTest, self).tearDown()
|
||||||
|
|
||||||
|
def test_get_access_rule(self):
|
||||||
|
sot = self.user_cloud.shared_file_system.get_access_rule(
|
||||||
|
self.ACCESS_ID
|
||||||
|
)
|
||||||
|
self.assertEqual(self.ACCESS_ID, sot.id)
|
||||||
|
|
||||||
|
def test_list_access_rules(self):
|
||||||
|
rules = self.user_cloud.shared_file_system.access_rules(
|
||||||
|
self.SHARE,
|
||||||
|
details=True
|
||||||
|
)
|
||||||
|
self.assertGreater(len(list(rules)), 0)
|
||||||
|
for rule in rules:
|
||||||
|
for attribute in ('id', 'created_at', 'updated_at',
|
||||||
|
'access_level', 'access_type', 'access_to',
|
||||||
|
'share_id', 'access_key', 'metadata'):
|
||||||
|
self.assertTrue(hasattr(rule, attribute))
|
@ -12,6 +12,9 @@
|
|||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
from openstack.shared_file_system.v2 import (
|
||||||
|
share_access_rule
|
||||||
|
)
|
||||||
from openstack.shared_file_system.v2 import _proxy
|
from openstack.shared_file_system.v2 import _proxy
|
||||||
from openstack.shared_file_system.v2 import limit
|
from openstack.shared_file_system.v2 import limit
|
||||||
from openstack.shared_file_system.v2 import share
|
from openstack.shared_file_system.v2 import share
|
||||||
@ -343,3 +346,37 @@ class TestShareNetworkSubnetResource(test_proxy_base.TestProxyBase):
|
|||||||
method_args=["fake_network_id", "fake_sub_network_id"],
|
method_args=["fake_network_id", "fake_sub_network_id"],
|
||||||
expected_args=["fake_sub_network_id"],
|
expected_args=["fake_sub_network_id"],
|
||||||
expected_kwargs={'share_network_id': 'fake_network_id'})
|
expected_kwargs={'share_network_id': 'fake_network_id'})
|
||||||
|
|
||||||
|
|
||||||
|
class TestAccessRuleProxy(test_proxy_base.TestProxyBase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestAccessRuleProxy, self).setUp()
|
||||||
|
self.proxy = _proxy.Proxy(self.session)
|
||||||
|
|
||||||
|
def test_access_ruless(self):
|
||||||
|
self.verify_list(
|
||||||
|
self.proxy.access_rules,
|
||||||
|
share_access_rule.ShareAccessRule,
|
||||||
|
method_args=["test_share"],
|
||||||
|
expected_args=[],
|
||||||
|
expected_kwargs={"share_id": "test_share"})
|
||||||
|
|
||||||
|
def test_access_rules_get(self):
|
||||||
|
self.verify_get(
|
||||||
|
self.proxy.get_access_rule, share_access_rule.ShareAccessRule)
|
||||||
|
|
||||||
|
def test_access_rules_create(self):
|
||||||
|
self.verify_create(
|
||||||
|
self.proxy.create_access_rule,
|
||||||
|
share_access_rule.ShareAccessRule,
|
||||||
|
method_args=["share_id"],
|
||||||
|
expected_args=[])
|
||||||
|
|
||||||
|
def test_access_rules_delete(self):
|
||||||
|
self._verify(
|
||||||
|
"openstack.shared_file_system.v2.share_access_rule."
|
||||||
|
+ "ShareAccessRule.delete",
|
||||||
|
self.proxy.delete_access_rule,
|
||||||
|
method_args=['access_id', 'share_id', 'ignore_missing'],
|
||||||
|
expected_args=[self.proxy , 'share_id'])
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from openstack.shared_file_system.v2 import share_access_rule
|
||||||
|
from openstack.tests.unit import base
|
||||||
|
|
||||||
|
EXAMPLE = {
|
||||||
|
"access_level": "rw",
|
||||||
|
"state": "error",
|
||||||
|
"id": "507bf114-36f2-4f56-8cf4-857985ca87c1",
|
||||||
|
"share_id": "fb213952-2352-41b4-ad7b-2c4c69d13eef",
|
||||||
|
"access_type": "cert",
|
||||||
|
"access_to": "example.com",
|
||||||
|
"access_key": None,
|
||||||
|
"created_at": "2021-09-12T02:01:04.000000",
|
||||||
|
"updated_at": "2021-09-12T02:01:04.000000",
|
||||||
|
"metadata": {
|
||||||
|
"key1": "value1",
|
||||||
|
"key2": "value2"}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestShareAccessRule(base.TestCase):
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
rules_resource = share_access_rule.ShareAccessRule()
|
||||||
|
self.assertEqual('access_list', rules_resource.resources_key)
|
||||||
|
self.assertEqual('/share-access-rules', rules_resource.base_path)
|
||||||
|
self.assertTrue(rules_resource.allow_list)
|
||||||
|
|
||||||
|
self.assertDictEqual({
|
||||||
|
"limit": "limit",
|
||||||
|
"marker": "marker",
|
||||||
|
"share_id": "share_id"
|
||||||
|
},
|
||||||
|
rules_resource._query_mapping._mapping)
|
||||||
|
|
||||||
|
def test_make_share_access_rules(self):
|
||||||
|
rules_resource = share_access_rule.ShareAccessRule(**EXAMPLE)
|
||||||
|
self.assertEqual(EXAMPLE['id'], rules_resource.id)
|
||||||
|
self.assertEqual(EXAMPLE['access_level'], rules_resource.access_level)
|
||||||
|
self.assertEqual(EXAMPLE['state'], rules_resource.state)
|
||||||
|
self.assertEqual(EXAMPLE['id'], rules_resource.id)
|
||||||
|
self.assertEqual(EXAMPLE['access_type'], rules_resource.access_type)
|
||||||
|
self.assertEqual(EXAMPLE['access_to'], rules_resource.access_to)
|
||||||
|
self.assertEqual(EXAMPLE['access_key'], rules_resource.access_key)
|
||||||
|
self.assertEqual(EXAMPLE['created_at'], rules_resource.created_at)
|
||||||
|
self.assertEqual(EXAMPLE['updated_at'], rules_resource.updated_at)
|
||||||
|
self.assertEqual(EXAMPLE['metadata'], rules_resource.metadata)
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Added support to create, list, get and delete share access rules with the
|
||||||
|
shared file system service.
|
Loading…
x
Reference in New Issue
Block a user