Merge "volume: Add volume group replication actions"

This commit is contained in:
Zuul 2025-05-20 17:45:27 +00:00 committed by Gerrit Code Review
commit d246545a9d
5 changed files with 236 additions and 22 deletions

View File

@ -1625,26 +1625,6 @@ class Proxy(proxy.Proxy):
"""
return _group.Group.create_from_source(self, **attrs)
def reset_group_status(self, group, status):
"""Reset group status
:param group: The :class:`~openstack.block_storage.v3.group.Group`
to set the state.
:param status: The status for a group.
:returns: ``None``
"""
res = self._get_resource(_group.Group, group)
return res.reset_status(self, status)
def reset_group_state(self, group, status):
warnings.warn(
"reset_group_state is a deprecated alias for reset_group_status "
"and will be removed in a future release.",
os_warnings.RemovedInSDK60Warning,
)
return self.reset_group_status(group, status)
def delete_group(self, group, delete_volumes=False):
"""Delete a group
@ -1670,6 +1650,72 @@ class Proxy(proxy.Proxy):
"""
return self._update(_group.Group, group, **attrs)
def reset_group_status(self, group, status):
"""Reset group status
:param group: The :class:`~openstack.block_storage.v3.group.Group`
to set the state.
:param status: The status for a group.
:returns: ``None``
"""
res = self._get_resource(_group.Group, group)
return res.reset_status(self, status)
def reset_group_state(self, group, status):
warnings.warn(
"reset_group_state is a deprecated alias for reset_group_status "
"and will be removed in a future release.",
os_warnings.RemovedInSDK60Warning,
)
return self.reset_group_status(group, status)
def enable_group_replication(self, group):
"""Enable replication for a group
:param group: The :class:`~openstack.block_storage.v3.group.Group`
to enable replication for.
:returns: ``None``
"""
res = self._get_resource(_group.Group, group)
return res.enable_replication(self)
def disable_group_replication(self, group):
"""Disable replication for a group
:param group: The :class:`~openstack.block_storage.v3.group.Group`
to disable replication for.
:returns: ``None``
"""
res = self._get_resource(_group.Group, group)
return res.disable_replication(self)
def failover_group_replication(
self,
group,
*,
allowed_attached_volume=False,
secondary_backend_id=None,
):
"""Failover replication for a group
:param group: The :class:`~openstack.block_storage.v3.group.Group`
to failover replication for.
:param allowed_attached_volume: Whether to allow attached volumes in
the group.
:param secondary_backend_id: The secondary backend ID.
:returns: ``None``
"""
res = self._get_resource(_group.Group, group)
return res.failover_replication(
self,
allowed_attached_volume=allowed_attached_volume,
secondary_backend_id=secondary_backend_id,
)
# ====== AVAILABILITY ZONES ======
def availability_zones(self):
"""Return a generator of availability zones

View File

@ -46,6 +46,7 @@ class Group(resource.Resource):
group_snapshot_id = resource.Body("group_snapshot_id")
group_type = resource.Body("group_type")
project_id = resource.Body("project_id")
replication_targets = resource.Body("replication_targets", type=list)
replication_status = resource.Body("replication_status")
source_group_id = resource.Body("source_group_id")
status = resource.Body("status")
@ -68,8 +69,64 @@ class Group(resource.Resource):
body = {'delete': {'delete-volumes': delete_volumes}}
self._action(session, body)
def fetch_replication_targets(self, session):
"""Fetch replication targets for the group.
:param session: The session to use for making this request.
:return: This group with the ``replication_targets`` field populated.
"""
body = {'list_replication_targets': None}
response = self._action(session, body)
self._body.attributes.update(
{'replication_targets': response.json()['replication_targets']}
)
return self
def enable_replication(self, session):
"""Enable replication for the group.
:param session: The session to use for making this request.
"""
body = {'enable_replication': None}
self._action(session, body)
def disable_replication(self, session):
"""Disable replication for the group.
:param session: The session to use for making this request.
"""
body = {'disable_replication': None}
self._action(session, body)
def failover_replication(
self,
session,
*,
allowed_attached_volume=False,
secondary_backend_id=None,
):
"""Failover replication for the group.
:param session: The session to use for making this request.
:param allowed_attached_volume: Whether to allow attached volumes in
the group.
:param secondary_backend_id: The secondary backend ID.
:returns: None
"""
body = {
'modify_body_for_action': {
'allow_attached_volume': allowed_attached_volume,
'secondary_backend_id': secondary_backend_id,
},
}
self._action(session, body)
def reset_status(self, session, status):
"""Resets the status for a group."""
"""Resets the status for a group.
:param session: The session to use for making this request.
:param status: The status for the group.
"""
body = {'reset_status': {'status': status}}
self._action(session, body)

View File

@ -93,6 +93,85 @@ class TestGroupAction(base.TestCase):
url, json=body, microversion=sot._max_microversion
)
def test_enable_replication(self):
sot = group.Group(**GROUP)
ret = sot.enable_replication(self.sess)
self.assertIsNone(ret)
url = f'groups/{GROUP_ID}/action'
body = {'enable_replication': None}
self.sess.post.assert_called_with(
url,
json=body,
microversion=sot._max_microversion,
)
def test_disable_replication(self):
sot = group.Group(**GROUP)
ret = sot.disable_replication(self.sess)
self.assertIsNone(ret)
url = f'groups/{GROUP_ID}/action'
body = {'disable_replication': None}
self.sess.post.assert_called_with(
url,
json=body,
microversion=sot._max_microversion,
)
def test_failover_replication(self):
sot = group.Group(**GROUP)
ret = sot.failover_replication(
self.sess, allowed_attached_volume=True, secondary_backend_id=None
)
self.assertIsNone(ret)
url = f'groups/{GROUP_ID}/action'
body = {
'modify_body_for_action': {
'allow_attached_volume': True,
'secondary_backend_id': None,
}
}
self.sess.post.assert_called_with(
url,
json=body,
microversion=sot._max_microversion,
)
def test_fetch_replication_targets(self):
resp = mock.Mock()
resp.links = {}
resp.json = mock.Mock(
return_value={
'replication_targets': [
{
'backend_id': 'vendor-id-1',
'unique_key': 'val1',
},
],
}
)
resp.status_code = 200
self.sess.post = mock.Mock(return_value=resp)
sot = group.Group(**GROUP)
result = sot.fetch_replication_targets(self.sess)
self.assertEqual(
[
{
'backend_id': 'vendor-id-1',
'unique_key': 'val1',
},
],
sot.replication_targets,
)
self.assertEqual(sot, result)
def test_reset_status(self):
sot = group.Group(**GROUP)

View File

@ -237,7 +237,7 @@ class TestGroup(TestVolumeProxy):
def test_group_update(self):
self.verify_update(self.proxy.update_group, group.Group)
def test_reset_group_status(self):
def test_group_reset_status(self):
self._verify(
"openstack.block_storage.v3.group.Group.reset_status",
self.proxy.reset_group_status,
@ -245,6 +245,34 @@ class TestGroup(TestVolumeProxy):
expected_args=[self.proxy, "new_status"],
)
def test_group_enable_replication(self):
self._verify(
"openstack.block_storage.v3.group.Group.enable_replication",
self.proxy.enable_group_replication,
method_args=["value"],
expected_args=[self.proxy],
)
def test_group_disable_replication(self):
self._verify(
"openstack.block_storage.v3.group.Group.disable_replication",
self.proxy.disable_group_replication,
method_args=["value"],
expected_args=[self.proxy],
)
def test_group_failover_replication(self):
self._verify(
"openstack.block_storage.v3.group.Group.failover_replication",
self.proxy.failover_group_replication,
method_args=["value"],
expected_args=[self.proxy],
expected_kwargs={
'allowed_attached_volume': False,
'secondary_backend_id': None,
},
)
class TestGroupSnapshot(TestVolumeProxy):
def test_group_snapshot_get(self):

View File

@ -0,0 +1,4 @@
---
features:
- |
Added support for the volume group replication actions.