summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-09-05 03:32:33 +0000
committerGerrit Code Review <review@openstack.org>2014-09-05 03:32:33 +0000
commit4162c9ff5c68ff3078137f47cceb070e66f8ebf6 (patch)
tree3c0ca6c8f88e3cfa10928c86274bc5dea446e2ac
parent8e87c0b600d0742fbdd69ab770dbeb9dca7cf58c (diff)
parent953ac3ec6d98a5a57a35b776fdaf3cc20d69fc44 (diff)
downloadpython-cinderclient-4162c9ff5c68ff3078137f47cceb070e66f8ebf6.tar.gz
Merge "Add client support in Cinder for volume replication"
-rw-r--r--cinderclient/tests/v2/fakes.py10
-rw-r--r--cinderclient/tests/v2/test_shell.py18
-rw-r--r--cinderclient/tests/v2/test_volumes.py3
-rw-r--r--cinderclient/v2/shell.py26
-rw-r--r--cinderclient/v2/volumes.py21
5 files changed, 74 insertions, 4 deletions
diff --git a/cinderclient/tests/v2/fakes.py b/cinderclient/tests/v2/fakes.py
index f271653..5439efb 100644
--- a/cinderclient/tests/v2/fakes.py
+++ b/cinderclient/tests/v2/fakes.py
@@ -372,6 +372,10 @@ class FakeHTTPClient(base_client.HTTPClient):
assert list(body[action]) == ['bootable']
elif action == 'os-unmanage':
assert body[action] is None
+ elif action == 'os-promote-replica':
+ assert body[action] is None
+ elif action == 'os-reenable-replica':
+ assert body[action] is None
else:
raise AssertionError("Unexpected action: %s" % action)
return (resp, {}, _body)
@@ -835,3 +839,9 @@ class FakeHTTPClient(base_client.HTTPClient):
volume = _stub_volume(id='1234')
volume.update(kw['body']['volume'])
return (202, {}, {'volume': volume})
+
+ def post_os_promote_replica_1234(self, **kw):
+ return (202, {}, {})
+
+ def post_os_reenable_replica_1234(self, **kw):
+ return (202, {}, {})
diff --git a/cinderclient/tests/v2/test_shell.py b/cinderclient/tests/v2/test_shell.py
index d473a17..696d627 100644
--- a/cinderclient/tests/v2/test_shell.py
+++ b/cinderclient/tests/v2/test_shell.py
@@ -137,6 +137,14 @@ class ShellTest(utils.TestCase):
self.assert_called_anytime('POST', '/volumes', partial_body=expected)
self.assert_called('GET', '/volumes/1234')
+ def test_create_volume_from_replica(self):
+ expected = {'volume': {'size': None}}
+
+ expected['volume']['source_replica'] = '1234'
+ self.run_command('create --source-replica=1234')
+ self.assert_called_anytime('POST', '/volumes', partial_body=expected)
+ self.assert_called('GET', '/volumes/1234')
+
def test_create_size_required_if_not_snapshot_or_clone(self):
self.assertRaises(SystemExit, self.run_command, 'create')
@@ -522,3 +530,13 @@ class ShellTest(utils.TestCase):
self.run_command('unmanage 1234')
self.assert_called('POST', '/volumes/1234/action',
body={'os-unmanage': None})
+
+ def test_replication_promote(self):
+ self.run_command('replication-promote 1234')
+ self.assert_called('POST', '/volumes/1234/action',
+ body={'os-promote-replica': None})
+
+ def test_replication_reenable(self):
+ self.run_command('replication-reenable 1234')
+ self.assert_called('POST', '/volumes/1234/action',
+ body={'os-reenable-replica': None})
diff --git a/cinderclient/tests/v2/test_volumes.py b/cinderclient/tests/v2/test_volumes.py
index fa42b87..3b3bbcb 100644
--- a/cinderclient/tests/v2/test_volumes.py
+++ b/cinderclient/tests/v2/test_volumes.py
@@ -66,7 +66,8 @@ class VolumesTest(utils.TestCase):
'attach_status': 'detached',
'volume_type': None,
'project_id': None,
- 'metadata': {}},
+ 'metadata': {},
+ 'source_replica': None},
'OS-SCH-HNT:scheduler_hints': 'uuid'}
cs.assert_called('POST', '/volumes', body=expected)
diff --git a/cinderclient/v2/shell.py b/cinderclient/v2/shell.py
index 6fed443..2405dd5 100644
--- a/cinderclient/v2/shell.py
+++ b/cinderclient/v2/shell.py
@@ -226,7 +226,8 @@ def do_show(cs, args):
class CheckSizeArgForCreate(argparse.Action):
def __call__(self, parser, args, values, option_string=None):
- if (values or args.snapshot_id or args.source_volid) is None:
+ if (values or args.snapshot_id or args.source_volid
+ or args.source_replica) is None:
parser.error('Size is a required parameter if snapshot '
'or source volume is not specified.')
setattr(args, self.dest, values)
@@ -251,6 +252,10 @@ class CheckSizeArgForCreate(argparse.Action):
help='Creates volume from volume ID. Default=None.')
@utils.arg('--source_volid',
help=argparse.SUPPRESS)
+@utils.arg('--source-replica',
+ metavar='<source-replica>',
+ default=None,
+ help='Creates volume from replicated volume ID. Default=None.')
@utils.arg('--image-id',
metavar='<image-id>',
default=None,
@@ -335,7 +340,8 @@ def do_create(cs, args):
availability_zone=args.availability_zone,
imageRef=args.image_id,
metadata=volume_metadata,
- scheduler_hints=hints)
+ scheduler_hints=hints,
+ source_replica=args.source_replica)
info = dict()
volume = cs.volumes.get(volume.id)
@@ -1672,3 +1678,19 @@ def do_manage(cs, args):
@utils.service_type('volumev2')
def do_unmanage(cs, args):
utils.find_volume(cs, args.volume).unmanage(args.volume)
+
+
+@utils.arg('volume', metavar='<volume>',
+ help='Name or ID of the volume to promote.')
+@utils.service_type('volumev2')
+def do_replication_promote(cs, args):
+ """Promote a secondary volume to primary for a relationship."""
+ utils.find_volume(cs, args.volume).promote(args.volume)
+
+
+@utils.arg('volume', metavar='<volume>',
+ help='Name or ID of the volume to reenable replication.')
+@utils.service_type('volumev2')
+def do_replication_reenable(cs, args):
+ """Sync the secondary volume with primary for a relationship."""
+ utils.find_volume(cs, args.volume).reenable(args.volume)
diff --git a/cinderclient/v2/volumes.py b/cinderclient/v2/volumes.py
index 9a687d0..98afd93 100644
--- a/cinderclient/v2/volumes.py
+++ b/cinderclient/v2/volumes.py
@@ -152,6 +152,14 @@ class Volume(base.Resource):
"""Unmanage a volume."""
self.manager.unmanage(volume)
+ def promote(self, volume):
+ """Promote secondary to be primary in relationship."""
+ self.manager.promote(volume)
+
+ def reenable(self, volume):
+ """Sync the secondary volume with primary for a relationship."""
+ self.manager.reenable(volume)
+
class VolumeManager(base.ManagerWithFind):
"""Manage :class:`Volume` resources."""
@@ -161,7 +169,8 @@ class VolumeManager(base.ManagerWithFind):
name=None, description=None,
volume_type=None, user_id=None,
project_id=None, availability_zone=None,
- metadata=None, imageRef=None, scheduler_hints=None):
+ metadata=None, imageRef=None, scheduler_hints=None,
+ source_replica=None):
"""Creates a volume.
:param size: Size of volume in GB
@@ -175,6 +184,7 @@ class VolumeManager(base.ManagerWithFind):
:param metadata: Optional metadata to set on volume creation
:param imageRef: reference to an image stored in glance
:param source_volid: ID of source volume to clone from
+ :param source_replica: ID of source volume to clone replica
:param scheduler_hints: (optional extension) arbitrary key-value pairs
specified by the client to help boot an instance
:rtype: :class:`Volume`
@@ -198,6 +208,7 @@ class VolumeManager(base.ManagerWithFind):
'metadata': volume_metadata,
'imageRef': imageRef,
'source_volid': source_volid,
+ 'source_replica': source_replica,
}}
if scheduler_hints:
@@ -498,3 +509,11 @@ class VolumeManager(base.ManagerWithFind):
def unmanage(self, volume):
"""Unmanage a volume."""
return self._action('os-unmanage', volume, None)
+
+ def promote(self, volume):
+ """Promote secondary to be primary in relationship."""
+ return self._action('os-promote-replica', volume, None)
+
+ def reenable(self, volume):
+ """Sync the secondary volume with primary for a relationship."""
+ return self._action('os-reenable-replica', volume, None)