block storage: Add support for the GroupSnapshot resource
Introduce the GroupSnapshot resource, fill in its resources, and implement API calls to support the Cinder v3 API Change-Id: I1189285892d64935912830d22fd2f7e59a797e87 Task: 41855 Story: 2008619 Task: 42074 Story: 2008621
This commit is contained in:
parent
71a8466f0f
commit
2b7469cc85
doc/source/user/proxies
openstack
block_storage/v3
tests
releasenotes/notes
@ -71,6 +71,14 @@ Group Operations
|
|||||||
:members: create_group, create_group_from_source, delete_group, update_group,
|
:members: create_group, create_group_from_source, delete_group, update_group,
|
||||||
get_group, find_group, groups, reset_group_state
|
get_group, find_group, groups, reset_group_state
|
||||||
|
|
||||||
|
Group Snapshot Operations
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: openstack.block_storage.v3._proxy.Proxy
|
||||||
|
:noindex:
|
||||||
|
:members: create_group_snapshot, delete_group_snapshot, get_group_snapshot,
|
||||||
|
find_group_snapshot, group_snapshots, reset_group_snapshot_state
|
||||||
|
|
||||||
Group Type Operations
|
Group Type Operations
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ from openstack.block_storage.v3 import backup as _backup
|
|||||||
from openstack.block_storage.v3 import capabilities as _capabilities
|
from openstack.block_storage.v3 import capabilities as _capabilities
|
||||||
from openstack.block_storage.v3 import extension as _extension
|
from openstack.block_storage.v3 import extension as _extension
|
||||||
from openstack.block_storage.v3 import group as _group
|
from openstack.block_storage.v3 import group as _group
|
||||||
|
from openstack.block_storage.v3 import group_snapshot as _group_snapshot
|
||||||
from openstack.block_storage.v3 import group_type as _group_type
|
from openstack.block_storage.v3 import group_type as _group_type
|
||||||
from openstack.block_storage.v3 import limits as _limits
|
from openstack.block_storage.v3 import limits as _limits
|
||||||
from openstack.block_storage.v3 import quota_set as _quota_set
|
from openstack.block_storage.v3 import quota_set as _quota_set
|
||||||
@ -1103,6 +1104,91 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
|
|||||||
|
|
||||||
return self._list(availability_zone.AvailabilityZone)
|
return self._list(availability_zone.AvailabilityZone)
|
||||||
|
|
||||||
|
# ====== GROUP SNAPSHOT ======
|
||||||
|
def get_group_snapshot(self, group_snapshot_id):
|
||||||
|
"""Get a group snapshot
|
||||||
|
|
||||||
|
:param group_snapshot_id: The ID of the group snapshot to get.
|
||||||
|
|
||||||
|
:returns: A GroupSnapshot instance.
|
||||||
|
:rtype: :class:`~openstack.block_storage.v3.group_snapshot`
|
||||||
|
"""
|
||||||
|
return self._get(_group_snapshot.GroupSnapshot, group_snapshot_id)
|
||||||
|
|
||||||
|
def find_group_snapshot(self, name_or_id, ignore_missing=True):
|
||||||
|
"""Find a single group snapshot
|
||||||
|
|
||||||
|
:param name_or_id: The name or ID of a group snapshot.
|
||||||
|
:param bool ignore_missing: When set to ``False``
|
||||||
|
:class:`~openstack.exceptions.ResourceNotFound` will be raised
|
||||||
|
when the group snapshot does not exist.
|
||||||
|
|
||||||
|
:returns: One :class:`~openstack.block_storage.v3.group_snapshot`
|
||||||
|
:raises: :class:`~openstack.exceptions.ResourceNotFound`
|
||||||
|
when no resource can be found.
|
||||||
|
"""
|
||||||
|
return self._find(
|
||||||
|
_group_snapshot.GroupSnapshot, name_or_id,
|
||||||
|
ignore_missing=ignore_missing)
|
||||||
|
|
||||||
|
def group_snapshots(self, details=True, **query):
|
||||||
|
"""Retrieve a generator of group snapshots
|
||||||
|
|
||||||
|
:param bool details: When ``True``, returns
|
||||||
|
:class:`~openstack.block_storage.v3.group_snapshot.GroupSnapshot`
|
||||||
|
objects with additional attributes filled.
|
||||||
|
:param kwargs query: Optional query parameters to be sent to limit
|
||||||
|
the group snapshots being returned.
|
||||||
|
:returns: A generator of group snapshtos.
|
||||||
|
"""
|
||||||
|
base_path = '/group_snapshots'
|
||||||
|
if details:
|
||||||
|
base_path = '/group_snapshots/detail'
|
||||||
|
|
||||||
|
return self._list(
|
||||||
|
_group_snapshot.GroupSnapshot,
|
||||||
|
base_path=base_path,
|
||||||
|
**query,
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_group_snapshot(self, **attrs):
|
||||||
|
"""Create a group snapshot
|
||||||
|
|
||||||
|
:param dict attrs: Keyword arguments which will be used to create a
|
||||||
|
:class:`~openstack.block_storage.v3.group_snapshot.GroupSnapshot`
|
||||||
|
comprised of the properties on the GroupSnapshot class.
|
||||||
|
|
||||||
|
:returns: The results of group snapshot creation.
|
||||||
|
:rtype: :class:`~openstack.block_storage.v3.group_snapshot`.
|
||||||
|
"""
|
||||||
|
return self._create(_group_snapshot.GroupSnapshot, **attrs)
|
||||||
|
|
||||||
|
def reset_group_snapshot_state(self, group_snapshot, state):
|
||||||
|
"""Reset group snapshot status
|
||||||
|
|
||||||
|
:param group_snapshot: The
|
||||||
|
:class:`~openstack.block_storage.v3.group_snapshot.GroupSnapshot`
|
||||||
|
to set the state.
|
||||||
|
:param state: The state of the group snapshot to be set.
|
||||||
|
|
||||||
|
:returns: None
|
||||||
|
"""
|
||||||
|
resource = self._get_resource(
|
||||||
|
_group_snapshot.GroupSnapshot, group_snapshot)
|
||||||
|
resource.reset_state(self, state)
|
||||||
|
|
||||||
|
def delete_group_snapshot(self, group_snapshot, ignore_missing=True):
|
||||||
|
"""Delete a group snapshot
|
||||||
|
|
||||||
|
:param group_snapshot: The :class:`~openstack.block_storage.v3.
|
||||||
|
group_snapshot.GroupSnapshot` to delete.
|
||||||
|
|
||||||
|
:returns: None
|
||||||
|
"""
|
||||||
|
self._delete(
|
||||||
|
_group_snapshot.GroupSnapshot, group_snapshot,
|
||||||
|
ignore_missing=ignore_missing)
|
||||||
|
|
||||||
# ====== GROUP TYPE ======
|
# ====== GROUP TYPE ======
|
||||||
def get_group_type(self, group_type):
|
def get_group_type(self, group_type):
|
||||||
"""Get a specific group type
|
"""Get a specific group type
|
||||||
@ -1180,7 +1266,7 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
|
|||||||
When set to ``True``, no exception will be set when attempting to
|
When set to ``True``, no exception will be set when attempting to
|
||||||
delete a nonexistent zone.
|
delete a nonexistent zone.
|
||||||
|
|
||||||
:returns: ''None''
|
:returns: None
|
||||||
"""
|
"""
|
||||||
self._delete(
|
self._delete(
|
||||||
_group_type.GroupType, group_type, ignore_missing=ignore_missing)
|
_group_type.GroupType, group_type, ignore_missing=ignore_missing)
|
||||||
|
72
openstack/block_storage/v3/group_snapshot.py
Normal file
72
openstack/block_storage/v3/group_snapshot.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# 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 GroupSnapshot(resource.Resource):
|
||||||
|
resource_key = "group_snapshot"
|
||||||
|
resources_key = "group_snapshots"
|
||||||
|
base_path = "/group_snapshots"
|
||||||
|
|
||||||
|
# capabilities
|
||||||
|
allow_fetch = True
|
||||||
|
allow_create = True
|
||||||
|
allow_delete = True
|
||||||
|
allow_commit = False
|
||||||
|
allow_list = True
|
||||||
|
|
||||||
|
#: Properties
|
||||||
|
#: The date and time when the resource was created.
|
||||||
|
created_at = resource.Body("created_at")
|
||||||
|
#: The group snapshot description.
|
||||||
|
description = resource.Body("description")
|
||||||
|
#: The UUID of the source group.
|
||||||
|
group_id = resource.Body("group_id")
|
||||||
|
#: The group type ID.
|
||||||
|
group_type_id = resource.Body("group_type_id")
|
||||||
|
#: The ID of the group snapshot.
|
||||||
|
id = resource.Body("id")
|
||||||
|
#: The group snapshot name.
|
||||||
|
name = resource.Body("name")
|
||||||
|
#: The UUID of the volume group snapshot project.
|
||||||
|
project_id = resource.Body("project_id")
|
||||||
|
#: The status of the generic group snapshot.
|
||||||
|
status = resource.Body("status")
|
||||||
|
|
||||||
|
# Pagination support was added in microversion 3.29
|
||||||
|
_max_microversion = '3.29'
|
||||||
|
|
||||||
|
def _action(self, session, body, microversion=None):
|
||||||
|
"""Preform aggregate actions given the message body."""
|
||||||
|
url = utils.urljoin(self.base_path, self.id, 'action')
|
||||||
|
headers = {'Accept': ''}
|
||||||
|
# TODO(stephenfin): This logic belongs in openstack.resource I suspect
|
||||||
|
if microversion is None:
|
||||||
|
if session.default_microversion:
|
||||||
|
microversion = session.default_microversion
|
||||||
|
else:
|
||||||
|
microversion = utils.maximum_supported_microversion(
|
||||||
|
session, self._max_microversion,
|
||||||
|
)
|
||||||
|
response = session.post(
|
||||||
|
url, json=body, headers=headers, microversion=microversion,
|
||||||
|
)
|
||||||
|
exceptions.raise_from_response(response)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def reset_state(self, session, state):
|
||||||
|
"""Resets the status for a group snapshot."""
|
||||||
|
body = {'reset_status': {'status': state}}
|
||||||
|
return self._action(session, body)
|
@ -11,7 +11,9 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from openstack.block_storage.v3 import group as _group
|
from openstack.block_storage.v3 import group as _group
|
||||||
|
from openstack.block_storage.v3 import group_snapshot as _group_snapshot
|
||||||
from openstack.block_storage.v3 import group_type as _group_type
|
from openstack.block_storage.v3 import group_type as _group_type
|
||||||
|
from openstack.block_storage.v3 import volume as _volume
|
||||||
from openstack.tests.functional.block_storage.v3 import base
|
from openstack.tests.functional.block_storage.v3 import base
|
||||||
|
|
||||||
|
|
||||||
@ -151,3 +153,65 @@ class TestGroup(base.BaseBlockStorageTest):
|
|||||||
group = self.conn.block_storage.get_group(self.group.id)
|
group = self.conn.block_storage.get_group(self.group.id)
|
||||||
self.assertEqual(group_name, group.name)
|
self.assertEqual(group_name, group.name)
|
||||||
self.assertEqual(group_description, group.description)
|
self.assertEqual(group_description, group.description)
|
||||||
|
|
||||||
|
def test_group_snapshot(self):
|
||||||
|
# group snapshots require a volume
|
||||||
|
# no need for a teardown as the deletion of the group (with the
|
||||||
|
# 'delete_volumes' flag) will handle this but we do need to wait for
|
||||||
|
# the thing to be created
|
||||||
|
volume_name = self.getUniqueString()
|
||||||
|
self.volume = self.conn.block_storage.create_volume(
|
||||||
|
name=volume_name,
|
||||||
|
volume_type=self.volume_type.id,
|
||||||
|
group_id=self.group.id,
|
||||||
|
size=1,
|
||||||
|
)
|
||||||
|
self.conn.block_storage.wait_for_status(
|
||||||
|
self.volume,
|
||||||
|
status='available',
|
||||||
|
failures=['error'],
|
||||||
|
interval=2,
|
||||||
|
wait=self._wait_for_timeout,
|
||||||
|
)
|
||||||
|
self.assertIsInstance(self.volume, _volume.Volume)
|
||||||
|
|
||||||
|
group_snapshot_name = self.getUniqueString()
|
||||||
|
self.group_snapshot = self.conn.block_storage.create_group_snapshot(
|
||||||
|
name=group_snapshot_name,
|
||||||
|
group_id=self.group.id,
|
||||||
|
)
|
||||||
|
self.conn.block_storage.wait_for_status(
|
||||||
|
self.group_snapshot,
|
||||||
|
status='available',
|
||||||
|
failures=['error'],
|
||||||
|
interval=2,
|
||||||
|
wait=self._wait_for_timeout,
|
||||||
|
)
|
||||||
|
self.assertIsInstance(
|
||||||
|
self.group_snapshot,
|
||||||
|
_group_snapshot.GroupSnapshot,
|
||||||
|
)
|
||||||
|
|
||||||
|
# get
|
||||||
|
group_snapshot = self.conn.block_storage.get_group_snapshot(
|
||||||
|
self.group_snapshot.id,
|
||||||
|
)
|
||||||
|
self.assertEqual(self.group_snapshot.name, group_snapshot.name)
|
||||||
|
|
||||||
|
# find
|
||||||
|
group_snapshot = self.conn.block_storage.find_group_snapshot(
|
||||||
|
self.group_snapshot.name,
|
||||||
|
)
|
||||||
|
self.assertEqual(self.group_snapshot.id, group_snapshot.id)
|
||||||
|
|
||||||
|
# list
|
||||||
|
group_snapshots = self.conn.block_storage.group_snapshots()
|
||||||
|
# other tests may have created group snapshot and there can be defaults
|
||||||
|
# so we don't assert that this is the *only* group snapshot present
|
||||||
|
self.assertIn(self.group_snapshot.id, {g.id for g in group_snapshots})
|
||||||
|
|
||||||
|
# update (not supported)
|
||||||
|
|
||||||
|
# delete
|
||||||
|
self.conn.block_storage.delete_group_snapshot(self.group_snapshot)
|
||||||
|
self.conn.block_storage.wait_for_delete(self.group_snapshot)
|
||||||
|
51
openstack/tests/unit/block_storage/v3/test_group_snapshot.py
Normal file
51
openstack/tests/unit/block_storage/v3/test_group_snapshot.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# 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.block_storage.v3 import group_snapshot
|
||||||
|
from openstack.tests.unit import base
|
||||||
|
|
||||||
|
GROUP_SNAPSHOT = {
|
||||||
|
"id": "6f519a48-3183-46cf-a32f-41815f813986",
|
||||||
|
"group_id": "6f519a48-3183-46cf-a32f-41815f814444",
|
||||||
|
"status": "available",
|
||||||
|
"created_at": "2015-09-16T09:28:52.000000",
|
||||||
|
"name": "my_group_snapshot1",
|
||||||
|
"description": "my first group snapshot",
|
||||||
|
"group_type_id": "7270c56e-6354-4528-8e8b-f54dee2232c8",
|
||||||
|
"project_id": "7ccf4863071f44aeb8f141f65780c51b",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestGroupSnapshot(base.TestCase):
|
||||||
|
def test_basic(self):
|
||||||
|
resource = group_snapshot.GroupSnapshot()
|
||||||
|
self.assertEqual("group_snapshot", resource.resource_key)
|
||||||
|
self.assertEqual("group_snapshots", resource.resources_key)
|
||||||
|
self.assertEqual("/group_snapshots", resource.base_path)
|
||||||
|
self.assertTrue(resource.allow_create)
|
||||||
|
self.assertTrue(resource.allow_fetch)
|
||||||
|
self.assertTrue(resource.allow_delete)
|
||||||
|
self.assertTrue(resource.allow_list)
|
||||||
|
self.assertFalse(resource.allow_commit)
|
||||||
|
|
||||||
|
def test_make_resource(self):
|
||||||
|
resource = group_snapshot.GroupSnapshot(**GROUP_SNAPSHOT)
|
||||||
|
self.assertEqual(GROUP_SNAPSHOT["created_at"], resource.created_at)
|
||||||
|
self.assertEqual(GROUP_SNAPSHOT["description"], resource.description)
|
||||||
|
self.assertEqual(GROUP_SNAPSHOT["group_id"], resource.group_id)
|
||||||
|
self.assertEqual(
|
||||||
|
GROUP_SNAPSHOT["group_type_id"], resource.group_type_id
|
||||||
|
)
|
||||||
|
self.assertEqual(GROUP_SNAPSHOT["id"], resource.id)
|
||||||
|
self.assertEqual(GROUP_SNAPSHOT["name"], resource.name)
|
||||||
|
self.assertEqual(GROUP_SNAPSHOT["project_id"], resource.project_id)
|
||||||
|
self.assertEqual(GROUP_SNAPSHOT["status"], resource.status)
|
@ -17,6 +17,7 @@ from openstack.block_storage.v3 import backup
|
|||||||
from openstack.block_storage.v3 import capabilities
|
from openstack.block_storage.v3 import capabilities
|
||||||
from openstack.block_storage.v3 import extension
|
from openstack.block_storage.v3 import extension
|
||||||
from openstack.block_storage.v3 import group
|
from openstack.block_storage.v3 import group
|
||||||
|
from openstack.block_storage.v3 import group_snapshot
|
||||||
from openstack.block_storage.v3 import group_type
|
from openstack.block_storage.v3 import group_type
|
||||||
from openstack.block_storage.v3 import limits
|
from openstack.block_storage.v3 import limits
|
||||||
from openstack.block_storage.v3 import quota_set
|
from openstack.block_storage.v3 import quota_set
|
||||||
@ -181,6 +182,55 @@ class TestGroup(TestVolumeProxy):
|
|||||||
self._verify(self.proxy.reset_group_state, group.Group)
|
self._verify(self.proxy.reset_group_state, group.Group)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGroupSnapshot(TestVolumeProxy):
|
||||||
|
def test_group_snapshot_get(self):
|
||||||
|
self.verify_get(
|
||||||
|
self.proxy.get_group_snapshot, group_snapshot.GroupSnapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_group_snapshot_find(self):
|
||||||
|
self.verify_find(
|
||||||
|
self.proxy.find_group_snapshot, group_snapshot.GroupSnapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_group_snapshots(self):
|
||||||
|
self.verify_list(
|
||||||
|
self.proxy.group_snapshots,
|
||||||
|
group_snapshot.GroupSnapshot,
|
||||||
|
expected_kwargs={},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_group_snapshots__detailed(self):
|
||||||
|
self.verify_list(
|
||||||
|
self.proxy.group_snapshots,
|
||||||
|
group_snapshot.GroupSnapshot,
|
||||||
|
method_kwargs={'details': True, 'query': 1},
|
||||||
|
expected_kwargs={
|
||||||
|
'query': 1,
|
||||||
|
'base_path': '/group_snapshots/detail',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_group_snapshot_create(self):
|
||||||
|
self.verify_create(
|
||||||
|
self.proxy.create_group_snapshot, group_snapshot.GroupSnapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_group_snapshot_delete(self):
|
||||||
|
self.verify_delete(
|
||||||
|
self.proxy.delete_group_snapshot,
|
||||||
|
group_snapshot.GroupSnapshot,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_group_snapshot_delete_ignore(self):
|
||||||
|
self.verify_delete(
|
||||||
|
self.proxy.delete_group_snapshot,
|
||||||
|
group_snapshot.GroupSnapshot,
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestGroupType(TestVolumeProxy):
|
class TestGroupType(TestVolumeProxy):
|
||||||
def test_group_type_get(self):
|
def test_group_type_get(self):
|
||||||
self.verify_get(self.proxy.get_group_type, group_type.GroupType)
|
self.verify_get(self.proxy.get_group_type, group_type.GroupType)
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add support for group snapshots to the block storage service.
|
Loading…
x
Reference in New Issue
Block a user