Tiramisu: replication group support
This patch adds CLI support for replication group. It is built upon the generic volume groups. Server side patch is here: https://review.openstack.org/#/c/352228/ Depends-On: I4d488252bd670b3ebabbcc9f5e29e0e4e913765a Change-Id: I462c3ab8c9c3a6a1b434748f81d208359ffd2431 Implements: blueprint replication-cg
This commit is contained in:
@@ -369,6 +369,9 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient):
|
||||
action = list(body)[0]
|
||||
if action == 'delete':
|
||||
assert 'delete-volumes' in body[action]
|
||||
elif action in ('enable_replication', 'disable_replication',
|
||||
'failover_replication', 'list_replication_targets'):
|
||||
assert action in body
|
||||
else:
|
||||
raise AssertionError("Unexpected action: %s" % action)
|
||||
return (resp, {}, {})
|
||||
|
@@ -158,3 +158,57 @@ class GroupsTest(utils.TestCase):
|
||||
cs.assert_called('POST', '/groups/action',
|
||||
body=expected)
|
||||
self._assert_request_id(grp)
|
||||
|
||||
def test_enable_replication_group(self):
|
||||
expected = {'enable_replication': {}}
|
||||
g0 = cs.groups.list()[0]
|
||||
grp = g0.enable_replication()
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
grp = cs.groups.enable_replication('1234')
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
grp = cs.groups.enable_replication(g0)
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
|
||||
def test_disable_replication_group(self):
|
||||
expected = {'disable_replication': {}}
|
||||
g0 = cs.groups.list()[0]
|
||||
grp = g0.disable_replication()
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
grp = cs.groups.disable_replication('1234')
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
grp = cs.groups.disable_replication(g0)
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
|
||||
def test_failover_replication_group(self):
|
||||
expected = {'failover_replication':
|
||||
{'allow_attached_volume': False,
|
||||
'secondary_backend_id': None}}
|
||||
g0 = cs.groups.list()[0]
|
||||
grp = g0.failover_replication()
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
grp = cs.groups.failover_replication('1234')
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
grp = cs.groups.failover_replication(g0)
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
|
||||
def test_list_replication_targets(self):
|
||||
expected = {'list_replication_targets': {}}
|
||||
g0 = cs.groups.list()[0]
|
||||
grp = g0.list_replication_targets()
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
grp = cs.groups.list_replication_targets('1234')
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
grp = cs.groups.list_replication_targets(g0)
|
||||
self._assert_request_id(grp)
|
||||
cs.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
|
@@ -754,3 +754,37 @@ class ShellTest(utils.TestCase):
|
||||
command += ' --withreplication %s' % replication
|
||||
self.run_command(command)
|
||||
self.assert_called('GET', '/os-services')
|
||||
|
||||
def test_group_enable_replication(self):
|
||||
cmd = '--os-volume-api-version 3.38 group-enable-replication 1234'
|
||||
self.run_command(cmd)
|
||||
expected = {'enable_replication': {}}
|
||||
self.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
|
||||
def test_group_disable_replication(self):
|
||||
cmd = '--os-volume-api-version 3.38 group-disable-replication 1234'
|
||||
self.run_command(cmd)
|
||||
expected = {'disable_replication': {}}
|
||||
self.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
|
||||
@ddt.data((False, None), (True, None),
|
||||
(False, "backend1"), (True, "backend1"),
|
||||
(False, "default"), (True, "default"))
|
||||
@ddt.unpack
|
||||
def test_group_failover_replication(self, attach_vol, backend):
|
||||
attach = '--allow-attached-volume ' if attach_vol else ''
|
||||
backend_id = ('--secondary-backend-id ' + backend) if backend else ''
|
||||
cmd = ('--os-volume-api-version 3.38 group-failover-replication 1234 '
|
||||
+ attach + backend_id)
|
||||
self.run_command(cmd)
|
||||
expected = {'failover_replication':
|
||||
{'allow_attached_volume': attach_vol,
|
||||
'secondary_backend_id': backend if backend else None}}
|
||||
self.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
|
||||
def test_group_list_replication_targets(self):
|
||||
cmd = ('--os-volume-api-version 3.38 group-list-replication-targets'
|
||||
' 1234')
|
||||
self.run_command(cmd)
|
||||
expected = {'list_replication_targets': {}}
|
||||
self.assert_called('POST', '/groups/1234/action', body=expected)
|
||||
|
@@ -39,6 +39,25 @@ class Group(base.Resource):
|
||||
"""Reset the group's state with specified one"""
|
||||
return self.manager.reset_state(self, state)
|
||||
|
||||
def enable_replication(self):
|
||||
"""Enables replication for this group."""
|
||||
return self.manager.enable_replication(self)
|
||||
|
||||
def disable_replication(self):
|
||||
"""Disables replication for this group."""
|
||||
return self.manager.disable_replication(self)
|
||||
|
||||
def failover_replication(self, allow_attached_volume=False,
|
||||
secondary_backend_id=None):
|
||||
"""Fails over replication for this group."""
|
||||
return self.manager.failover_replication(self,
|
||||
allow_attached_volume,
|
||||
secondary_backend_id)
|
||||
|
||||
def list_replication_targets(self):
|
||||
"""Lists replication targets for this group."""
|
||||
return self.manager.list_replication_targets(self)
|
||||
|
||||
|
||||
class GroupManager(base.ManagerWithFind):
|
||||
"""Manage :class:`Group` resources."""
|
||||
@@ -180,3 +199,55 @@ class GroupManager(base.ManagerWithFind):
|
||||
url = '/groups/%s/action' % base.getid(group)
|
||||
resp, body = self.api.client.post(url, body=body)
|
||||
return common_base.TupleWithMeta((resp, body), resp)
|
||||
|
||||
def enable_replication(self, group):
|
||||
"""Enables replication for a group.
|
||||
|
||||
:param group: the :class:`Group` to enable replication.
|
||||
"""
|
||||
body = {'enable_replication': {}}
|
||||
self.run_hooks('modify_body_for_action', body, 'group')
|
||||
url = '/groups/%s/action' % base.getid(group)
|
||||
resp, body = self.api.client.post(url, body=body)
|
||||
return common_base.TupleWithMeta((resp, body), resp)
|
||||
|
||||
def disable_replication(self, group):
|
||||
"""disables replication for a group.
|
||||
|
||||
:param group: the :class:`Group` to disable replication.
|
||||
"""
|
||||
body = {'disable_replication': {}}
|
||||
self.run_hooks('modify_body_for_action', body, 'group')
|
||||
url = '/groups/%s/action' % base.getid(group)
|
||||
resp, body = self.api.client.post(url, body=body)
|
||||
return common_base.TupleWithMeta((resp, body), resp)
|
||||
|
||||
def failover_replication(self, group, allow_attached_volume=False,
|
||||
secondary_backend_id=None):
|
||||
"""fails over replication for a group.
|
||||
|
||||
:param group: the :class:`Group` to failover.
|
||||
:param allow attached volumes: allow attached volumes in the group.
|
||||
:param secondary_backend_id: secondary backend id.
|
||||
"""
|
||||
body = {
|
||||
'failover_replication': {
|
||||
'allow_attached_volume': allow_attached_volume,
|
||||
'secondary_backend_id': secondary_backend_id
|
||||
}
|
||||
}
|
||||
self.run_hooks('modify_body_for_action', body, 'group')
|
||||
url = '/groups/%s/action' % base.getid(group)
|
||||
resp, body = self.api.client.post(url, body=body)
|
||||
return common_base.TupleWithMeta((resp, body), resp)
|
||||
|
||||
def list_replication_targets(self, group):
|
||||
"""List replication targets for a group.
|
||||
|
||||
:param group: the :class:`Group` to list replication targets.
|
||||
"""
|
||||
body = {'list_replication_targets': {}}
|
||||
self.run_hooks('modify_body_for_action', body, 'group')
|
||||
url = '/groups/%s/action' % base.getid(group)
|
||||
resp, body = self.api.client.post(url, body=body)
|
||||
return common_base.TupleWithMeta((resp, body), resp)
|
||||
|
@@ -1174,7 +1174,74 @@ def do_group_update(cs, args):
|
||||
print("Request to update group '%s' has been accepted." % args.group)
|
||||
|
||||
|
||||
@api_versions.wraps('3.14')
|
||||
@api_versions.wraps('3.38')
|
||||
@utils.arg('group',
|
||||
metavar='<group>',
|
||||
help='Name or ID of the group.')
|
||||
def do_group_enable_replication(cs, args):
|
||||
"""Enables replication for group."""
|
||||
|
||||
shell_utils.find_group(cs, args.group).enable_replication()
|
||||
|
||||
|
||||
@api_versions.wraps('3.38')
|
||||
@utils.arg('group',
|
||||
metavar='<group>',
|
||||
help='Name or ID of the group.')
|
||||
def do_group_disable_replication(cs, args):
|
||||
"""Disables replication for group."""
|
||||
|
||||
shell_utils.find_group(cs, args.group).disable_replication()
|
||||
|
||||
|
||||
@api_versions.wraps('3.38')
|
||||
@utils.arg('group',
|
||||
metavar='<group>',
|
||||
help='Name or ID of the group.')
|
||||
@utils.arg('--allow-attached-volume',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Allows or disallows group with '
|
||||
'attached volumes to be failed over.')
|
||||
@utils.arg('--secondary-backend-id',
|
||||
metavar='<secondary_backend_id>',
|
||||
help='Secondary backend id. Default=None.')
|
||||
def do_group_failover_replication(cs, args):
|
||||
"""Fails over replication for group."""
|
||||
|
||||
shell_utils.find_group(cs, args.group).failover_replication(
|
||||
allow_attached_volume=args.allow_attached_volume,
|
||||
secondary_backend_id=args.secondary_backend_id)
|
||||
|
||||
|
||||
@api_versions.wraps('3.38')
|
||||
@utils.arg('group',
|
||||
metavar='<group>',
|
||||
help='Name or ID of the group.')
|
||||
def do_group_list_replication_targets(cs, args):
|
||||
"""Lists replication targets for group.
|
||||
|
||||
Example value for replication_targets:
|
||||
|
||||
.. code-block: json
|
||||
|
||||
{
|
||||
'replication_targets': [{'backend_id': 'vendor-id-1',
|
||||
'unique_key': 'val1',
|
||||
......},
|
||||
{'backend_id': 'vendor-id-2',
|
||||
'unique_key': 'val2',
|
||||
......}]
|
||||
}
|
||||
"""
|
||||
|
||||
rc, replication_targets = shell_utils.find_group(
|
||||
cs, args.group).list_replication_targets()
|
||||
rep_targets = replication_targets.get('replication_targets')
|
||||
if rep_targets and len(rep_targets) > 0:
|
||||
utils.print_list(rep_targets, [key for key in rep_targets[0].keys()])
|
||||
|
||||
|
||||
@utils.arg('--all-tenants',
|
||||
dest='all_tenants',
|
||||
metavar='<0|1>',
|
||||
|
@@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added support for replication group APIs ``enable_replication``,
|
||||
``disable_replication``, ``failover_replication`` and
|
||||
``list_replication_targets``.
|
Reference in New Issue
Block a user