diff options
author | Ronen Kat <ronenkat@il.ibm.com> | 2014-07-28 22:50:41 +0300 |
---|---|---|
committer | Ronen Kat <ronenkat@il.ibm.com> | 2014-08-27 06:38:50 +0300 |
commit | 953ac3ec6d98a5a57a35b776fdaf3cc20d69fc44 (patch) | |
tree | 150d215b3adcb3bdd7957fb48aa84c4754693458 | |
parent | 44f842b1b99b6454075ee1cdc391650221a29bad (diff) | |
download | python-cinderclient-953ac3ec6d98a5a57a35b776fdaf3cc20d69fc44.tar.gz |
Add client support in Cinder for volume replication
Provide Cinder client support to manage volume replication.
Cinder-specs available at https://review.openstack.org/#/c/98308/
Change-Id: Id60bacbcc113d42730e822b29b2fa78be94d3276
Implements: blueprint volume-replication
-rw-r--r-- | cinderclient/tests/v2/fakes.py | 10 | ||||
-rw-r--r-- | cinderclient/tests/v2/test_shell.py | 18 | ||||
-rw-r--r-- | cinderclient/tests/v2/test_volumes.py | 3 | ||||
-rw-r--r-- | cinderclient/v2/shell.py | 26 | ||||
-rw-r--r-- | cinderclient/v2/volumes.py | 21 |
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) |