Merge "Add share instances to shared file systems"

This commit is contained in:
Zuul 2023-04-03 13:24:08 +00:00 committed by Gerrit Code Review
commit 79d1743d2a
11 changed files with 454 additions and 1 deletions

View File

@ -20,3 +20,40 @@ of the share in other availability zones.
.. literalinclude:: ../examples/shared_file_system/availability_zones.py
:pyobject: list_availability_zones
Share Instances
---------------
Administrators can list, show information for, explicitly set the state of,
and force-delete share instances.
.. literalinclude:: ../examples/shared_file_system/share_instances.py
:pyobject: share_instances
Get Share Instance
------------------
Shows details for a single share instance.
.. literalinclude:: ../examples/shared_file_system/share_instances.py
:pyobject: get_share_instance
Reset Share Instance Status
---------------------------
Explicitly updates the state of a share instance.
.. literalinclude:: ../examples/shared_file_system/share_instances.py
:pyobject: reset_share_instance_status
Delete Share Instance
---------------------
Force-deletes a share instance.
.. literalinclude:: ../examples/shared_file_system/share_instances.py
:pyobject: delete_share_instance

View File

@ -100,3 +100,16 @@ Create and manipulate Share Networks with the Shared File Systems service.
:noindex:
:members: share_networks, get_share_network, delete_share_network,
update_share_network, create_share_network
Shared File System Share Instances
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Administrators can list, show information for, explicitly set the
state of, and force-delete share instances within the Shared File
Systems Service.
.. autoclass:: openstack.shared_file_system.v2._proxy.Proxy
:noindex:
:members: share_instances, get_share_instance,
reset_share_instance_status,
delete_share_instance

View File

@ -8,7 +8,8 @@ Shared File System service resources
v2/storage_pool
v2/limit
v2/share
v2/user_message
v2/share_instance
v2/share_snapshot
v2/share_snapshot_instance
v2/share_network
v2/user_message

View File

@ -0,0 +1,13 @@
openstack.shared_file_system.v2.share_instance
==============================================
.. automodule:: openstack.shared_file_system.v2.share_instance
The ShareInstance Class
-----------------------
The ``ShareInstance`` class inherits from
:class:`~openstack.resource.Resource`.
.. autoclass:: openstack.shared_file_system.v2.share_instance.ShareInstance
:members:

View File

@ -0,0 +1,41 @@
# 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.
"""
List resources from the Shared File System service.
For a full guide see
https://docs.openstack.org/openstacksdk/latest/user/guides/shared_file_system.html
"""
def share_instances(conn, **query):
print('List all share instances:')
for si in conn.share.share_instances(**query):
print(si)
def get_share_instance(conn, share_instance_id):
print('Get share instance with given Id:')
share_instance = conn.share.get_share_instance(share_instance_id)
print(share_instance)
def reset_share_instance_status(conn, share_instance_id, status):
print('Reset the status of the share instance with the given '
'share_instance_id to the given status')
conn.share.reset_share_instance_status(share_instance_id, status)
def delete_share_instance(conn, share_instance_id):
print('Force-delete the share instance with the given share_instance_id')
conn.share.delete_share_instance(share_instance_id)

View File

@ -31,6 +31,7 @@ from openstack.shared_file_system.v2 import (
)
from openstack.shared_file_system.v2 import limit as _limit
from openstack.shared_file_system.v2 import share as _share
from openstack.shared_file_system.v2 import share_instance as _share_instance
class Proxy(proxy.Proxy):
@ -44,6 +45,7 @@ class Proxy(proxy.Proxy):
"share_network": _share_network.ShareNetwork,
"share_snapshot_instance":
_share_snapshot_instance.ShareSnapshotInstance,
"share_instance": _share_instance.ShareInstance,
}
def availability_zones(self):
@ -435,3 +437,55 @@ class Proxy(proxy.Proxy):
share_network.ShareNetwork`
"""
return self._create(_share_network.ShareNetwork, **attrs)
def share_instances(self, **query):
"""Lists all share instances.
:param kwargs query: Optional query parameters to be sent to limit
the share instances being returned. Available parameters include:
* export_location_id: The export location UUID that can be used
to filter share instances.
* export_location_path: The export location path that can be used
to filter share instances.
:returns: Details of share instances resources
:rtype: :class:`~openstack.shared_file_system.v2.
share_instance.ShareInstance`
"""
return self._list(
_share_instance.ShareInstance, **query)
def get_share_instance(self, share_instance_id):
"""Shows details for a single share instance
:param share_instance_id: The UUID of the share instance to get
:returns: Details of the identified share instance
:rtype: :class:`~openstack.shared_file_system.v2.
share_instance.ShareInstance`
"""
return self._get(_share_instance.ShareInstance, share_instance_id)
def reset_share_instance_status(self, share_instance_id, status):
"""Explicitly updates the state of a share instance.
:param share_instance_id: The UUID of the share instance to reset.
:param status: The share or share instance status to be set.
:returns: ``None``
"""
res = self._get_resource(_share_instance.ShareInstance,
share_instance_id)
res.reset_status(self, status)
def delete_share_instance(self, share_instance_id):
"""Force-deletes a share instance
:param share_instance: The ID of the share instance to delete
:returns: ``None``
"""
res = self._get_resource(_share_instance.ShareInstance,
share_instance_id)
res.force_delete(self)

View File

@ -0,0 +1,83 @@
# 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 ShareInstance(resource.Resource):
resource_key = "share_instance"
resources_key = "share_instances"
base_path = "/share_instances"
# capabilities
allow_create = False
allow_fetch = True
allow_commit = False
allow_delete = False
allow_list = True
allow_head = False
#: Properties
#: The share instance access rules status. A valid value is active,
#: error, or syncing.
access_rules_status = resource.Body("access_rules_status", type=str)
#: The name of the availability zone the share exists within.
availability_zone = resource.Body("availability_zone", type=str)
#: If the share instance has its cast_rules_to_readonly attribute
#: set to True, all existing access rules be cast to read/only.
cast_rules_to_readonly = resource.Body("cast_rules_to_readonly", type=bool)
#: The date and time stamp when the resource was created within the
#: services database.
created_at = resource.Body("created_at", type=str)
#: The host name of the service back end that the resource is
#: contained within.
host = resource.Body("host", type=str)
#: The progress of the share creation.
progress = resource.Body("progress", type=str)
#: The share replica state. Has set value only when replication is used.
#: List of possible values: active, in_sync, out_of_sync, error
replica_state = resource.Body("replica_state", type=str)
#: The UUID of the share to which the share instance belongs to.
share_id = resource.Body("share_id", type=str)
#: The share network ID where the resource is exported to.
share_network_id = resource.Body("share_network_id", type=str)
#: The UUID of the share server.
share_server_id = resource.Body("share_server_id", type=str)
#: The share or share instance status.
status = resource.Body("status", type=str)
def _action(self, session, body, action='patch', microversion=None):
"""Perform share instance actions given the message body"""
url = utils.urljoin(self.base_path, self.id, 'action')
headers = {'Accept': ''}
extra_attrs = {}
if microversion:
# Set microversion override
extra_attrs['microversion'] = microversion
else:
extra_attrs['microversion'] = \
self._get_microversion(session, action=action)
response = session.post(url, json=body, headers=headers, **extra_attrs)
exceptions.raise_from_response(response)
return response
def reset_status(self, session, reset_status):
"""Reset share instance to given status"""
body = {"reset_status": {"status": reset_status}}
self._action(session, body)
def force_delete(self, session):
"""Force delete share instance"""
body = {"force_delete": None}
self._action(session, body, action='delete')

View File

@ -0,0 +1,68 @@
# 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 resource
from openstack.shared_file_system.v2 import share_instance as _share_instance
from openstack.tests.functional.shared_file_system import base
class ShareInstanceTest(base.BaseSharedFileSystemTest):
min_microversion = '2.7'
def setUp(self):
super(ShareInstanceTest, self).setUp()
self.SHARE_NAME = self.getUniqueString()
my_share = self.create_share(
name=self.SHARE_NAME, size=2, share_type="dhss_false",
share_protocol='NFS', description=None)
self.SHARE_ID = my_share.id
instances_list = self.operator_cloud.share.share_instances()
self.SHARE_INSTANCE_ID = None
for i in instances_list:
if i.share_id == self.SHARE_ID:
self.SHARE_INSTANCE_ID = i.id
def test_get(self):
sot = self.operator_cloud.share.get_share_instance(
self.SHARE_INSTANCE_ID)
assert isinstance(sot, _share_instance.ShareInstance)
self.assertEqual(self.SHARE_INSTANCE_ID, sot.id)
def test_list_share_instances(self):
share_instances = self.operator_cloud.share.share_instances()
self.assertGreater(len(list(share_instances)), 0)
for share_instance in share_instances:
for attribute in ('id', 'name', 'created_at',
'access_rules_status',
'availability_zone'):
self.assertTrue(hasattr(share_instance, attribute))
def test_reset(self):
res = self.operator_cloud.share.reset_share_instance_status(
self.SHARE_INSTANCE_ID, 'error')
self.assertIsNone(res)
sot = self.operator_cloud.share.get_share_instance(
self.SHARE_INSTANCE_ID)
self.assertEqual('error', sot.status)
def test_delete(self):
sot = self.operator_cloud.share.get_share_instance(
self.SHARE_INSTANCE_ID)
fdel = self.operator_cloud.share.delete_share_instance(
self.SHARE_INSTANCE_ID)
resource.wait_for_delete(self.operator_cloud.share, sot,
wait=self._wait_for_timeout,
interval=2)
self.assertIsNone(fdel)

View File

@ -15,6 +15,7 @@ from unittest import mock
from openstack.shared_file_system.v2 import _proxy
from openstack.shared_file_system.v2 import limit
from openstack.shared_file_system.v2 import share
from openstack.shared_file_system.v2 import share_instance
from openstack.shared_file_system.v2 import share_network
from openstack.shared_file_system.v2 import share_snapshot
from openstack.shared_file_system.v2 import share_snapshot_instance
@ -61,6 +62,31 @@ class TestSharedFileSystemShare(TestSharedFileSystemProxy):
def test_share_update(self):
self.verify_update(self.proxy.update_share, share.Share)
def test_share_instances(self):
self.verify_list(self.proxy.share_instances,
share_instance.ShareInstance)
def test_share_instance_get(self):
self.verify_get(self.proxy.get_share_instance,
share_instance.ShareInstance)
def test_share_instance_reset(self):
self._verify(
"openstack.shared_file_system.v2.share_instance."
+ "ShareInstance.reset_status",
self.proxy.reset_share_instance_status,
method_args=['id', 'available'],
expected_args=[self.proxy, 'available'],
)
def test_share_instance_delete(self):
self._verify(
"openstack.shared_file_system.v2.share_instance."
+ "ShareInstance.force_delete",
self.proxy.delete_share_instance,
method_args=['id'],
expected_args=[self.proxy])
@mock.patch("openstack.resource.wait_for_status")
def test_wait_for(self, mock_wait):
mock_resource = mock.Mock()

View File

@ -0,0 +1,111 @@
# 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 unittest import mock
from keystoneauth1 import adapter
from openstack.shared_file_system.v2 import share_instance
from openstack.tests.unit import base
IDENTIFIER = "75559a8b-c90c-42a7-bda2-edbe86acfb7b"
EXAMPLE = {
"status": "available",
"progress": "100%",
"share_id": "d94a8548-2079-4be0-b21c-0a887acd31ca",
"availability_zone": "nova",
"replica_state": None,
"created_at": "2015-09-07T08:51:34.000000",
"cast_rules_to_readonly": False,
"share_network_id": "713df749-aac0-4a54-af52-10f6c991e80c",
"share_server_id": "ba11930a-bf1a-4aa7-bae4-a8dfbaa3cc73",
"host": "manila2@generic1#GENERIC1",
"access_rules_status": "active",
"id": IDENTIFIER
}
class TestShareInstances(base.TestCase):
def test_basic(self):
share_instance_resource = share_instance.ShareInstance()
self.assertEqual('share_instances',
share_instance_resource.resources_key)
self.assertEqual('/share_instances', share_instance_resource.base_path)
self.assertTrue(share_instance_resource.allow_list)
self.assertFalse(share_instance_resource.allow_create)
self.assertTrue(share_instance_resource.allow_fetch)
self.assertFalse(share_instance_resource.allow_commit)
self.assertFalse(share_instance_resource.allow_delete)
def test_make_share_instances(self):
share_instance_resource = share_instance.ShareInstance(**EXAMPLE)
self.assertEqual(EXAMPLE['status'], share_instance_resource.status)
self.assertEqual(EXAMPLE['progress'], share_instance_resource.progress)
self.assertEqual(EXAMPLE['share_id'], share_instance_resource.share_id)
self.assertEqual(EXAMPLE['availability_zone'],
share_instance_resource.availability_zone)
self.assertEqual(EXAMPLE['replica_state'],
share_instance_resource.replica_state)
self.assertEqual(EXAMPLE['created_at'],
share_instance_resource.created_at)
self.assertEqual(EXAMPLE['cast_rules_to_readonly'],
share_instance_resource.cast_rules_to_readonly)
self.assertEqual(EXAMPLE['share_network_id'],
share_instance_resource.share_network_id)
self.assertEqual(EXAMPLE['share_server_id'],
share_instance_resource.share_server_id)
self.assertEqual(EXAMPLE['host'], share_instance_resource.host)
self.assertEqual(EXAMPLE['access_rules_status'],
share_instance_resource.access_rules_status)
self.assertEqual(EXAMPLE['id'], share_instance_resource.id)
class TestShareInstanceActions(TestShareInstances):
def setUp(self):
super(TestShareInstanceActions, self).setUp()
self.resp = mock.Mock()
self.resp.body = None
self.resp.status_code = 200
self.resp.json = mock.Mock(return_value=self.resp.body)
self.sess = mock.Mock(spec=adapter.Adapter)
self.sess.default_microversion = '3.0'
self.sess.post = mock.Mock(return_value=self.resp)
self.sess._get_connection = mock.Mock(return_value=self.cloud)
def test_reset_status(self):
sot = share_instance.ShareInstance(**EXAMPLE)
microversion = sot._get_microversion(self.sess, action='patch')
self.assertIsNone(sot.reset_status(self.sess, 'active'))
url = f'share_instances/{IDENTIFIER}/action'
body = {"reset_status": {"status": 'active'}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, json=body, headers=headers,
microversion=microversion)
def test_force_delete(self):
sot = share_instance.ShareInstance(**EXAMPLE)
microversion = sot._get_microversion(self.sess, action='delete')
self.assertIsNone(sot.force_delete(self.sess))
url = f'share_instances/{IDENTIFIER}/action'
body = {'force_delete': None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, json=body, headers=headers,
microversion=microversion)

View File

@ -0,0 +1,6 @@
---
features:
- |
Added support to list, get, reset status of,
and force delete share instances
(from shared file system service).