Merge "Add share instances to shared file systems"
This commit is contained in:
commit
79d1743d2a
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
41
examples/shared_file_system/share_instances.py
Normal file
41
examples/shared_file_system/share_instances.py
Normal 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)
|
@ -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)
|
||||
|
83
openstack/shared_file_system/v2/share_instance.py
Normal file
83
openstack/shared_file_system/v2/share_instance.py
Normal 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
|
||||
#: service’s 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')
|
@ -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)
|
@ -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()
|
||||
|
@ -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)
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added support to list, get, reset status of,
|
||||
and force delete share instances
|
||||
(from shared file system service).
|
Loading…
Reference in New Issue
Block a user