From d24ba31afa915e9ff54d7f2ff262f223ee326a84 Mon Sep 17 00:00:00 2001 From: Avishay Traeger Date: Thu, 4 Aug 2016 18:52:44 +0300 Subject: [PATCH] List manageable volumes and snapshots Cinder currently has the ability to take over the management of existing volumes and snapshots ("manage existing") and to relinquish management of volumes and snapshots ("unmanage"). The API to manage an existing volume takes a reference, which is a driver-specific string that is used to identify the volume on the storage backend. This patch adds the client code for APIs for listing volumes and snapshots available for management to make this flow more user-friendly. Change-Id: Icd81a77294d9190ac6dbaa7e7d35e4dedf45e49f Implements: blueprint list-manage-existing --- cinderclient/base.py | 7 +- cinderclient/tests/unit/v2/fakes.py | 47 ++++++++++++ cinderclient/tests/unit/v2/test_shell.py | 24 ++++++ cinderclient/tests/unit/v2/test_volumes.py | 16 ++++ cinderclient/tests/unit/v3/fakes.py | 50 ++++++++++++ cinderclient/tests/unit/v3/test_shell.py | 30 ++++++++ cinderclient/tests/unit/v3/test_volumes.py | 20 +++++ cinderclient/v2/shell.py | 86 +++++++++++++++++++++ cinderclient/v2/volume_snapshots.py | 22 +++++- cinderclient/v2/volumes.py | 8 ++ cinderclient/v3/shell.py | 89 ++++++++++++++++++++++ cinderclient/v3/volume_snapshots.py | 15 ++++ cinderclient/v3/volumes.py | 14 ++++ 13 files changed, 425 insertions(+), 3 deletions(-) diff --git a/cinderclient/base.py b/cinderclient/base.py index 4e92c534e..2374c5cb4 100644 --- a/cinderclient/base.py +++ b/cinderclient/base.py @@ -34,7 +34,7 @@ from cinderclient import utils # Valid sort directions and client sort keys SORT_DIR_VALUES = ('asc', 'desc') SORT_KEY_VALUES = ('id', 'status', 'size', 'availability_zone', 'name', - 'bootable', 'created_at') + 'bootable', 'created_at', 'reference') # Mapping of client keys to actual sort keys SORT_KEY_MAPPINGS = {'name': 'display_name'} # Additional sort keys for resources @@ -126,7 +126,7 @@ class Manager(common_base.HookableMixin): def _build_list_url(self, resource_type, detailed=True, search_opts=None, marker=None, limit=None, sort_key=None, sort_dir=None, - sort=None): + sort=None, offset=None): if search_opts is None: search_opts = {} @@ -156,6 +156,9 @@ class Manager(common_base.HookableMixin): query_params['sort_dir'] = self._format_sort_dir_param( sort_dir) + if offset: + query_params['offset'] = offset + # Transform the dict to a sequence of two-element tuples in fixed # order, then the encoded string will be consistent in Python 2&3. query_string = "" diff --git a/cinderclient/tests/unit/v2/fakes.py b/cinderclient/tests/unit/v2/fakes.py index ff54c6ab7..a70d63cdc 100644 --- a/cinderclient/tests/unit/v2/fakes.py +++ b/cinderclient/tests/unit/v2/fakes.py @@ -1156,11 +1156,58 @@ class FakeHTTPClient(base_client.HTTPClient): def put_snapshots_1234_metadata(self, **kw): return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) + def get_os_volume_manage(self, **kw): + vol_id = "volume-ffffffff-0000-ffff-0000-ffffffffffff" + vols = [{"size": 4, "safe_to_manage": False, "actual_size": 4.0, + "reference": {"source-name": vol_id}}, + {"size": 5, "safe_to_manage": True, "actual_size": 4.3, + "reference": {"source-name": "myvol"}}] + return (200, {}, {"manageable-volumes": vols}) + + def get_os_volume_manage_detail(self, **kw): + vol_id = "volume-ffffffff-0000-ffff-0000-ffffffffffff" + vols = [{"size": 4, "reason_not_safe": "volume in use", + "safe_to_manage": False, "extra_info": "qos_setting:high", + "reference": {"source-name": vol_id}, + "actual_size": 4.0}, + {"size": 5, "reason_not_safe": None, "safe_to_manage": True, + "extra_info": "qos_setting:low", "actual_size": 4.3, + "reference": {"source-name": "myvol"}}] + return (200, {}, {"manageable-volumes": vols}) + def post_os_volume_manage(self, **kw): volume = _stub_volume(id='1234') volume.update(kw['body']['volume']) return (202, {}, {'volume': volume}) + def get_os_snapshot_manage(self, **kw): + snap_id = "snapshot-ffffffff-0000-ffff-0000-ffffffffffff" + snaps = [{"actual_size": 4.0, "size": 4, + "safe_to_manage": False, "source_id_type": "source-name", + "source_cinder_id": "00000000-ffff-0000-ffff-00000000", + "reference": {"source-name": snap_id}, + "source_identifier": "volume-00000000-ffff-0000-ffff-000000"}, + {"actual_size": 4.3, "reference": {"source-name": "mysnap"}, + "source_id_type": "source-name", "source_identifier": "myvol", + "safe_to_manage": True, "source_cinder_id": None, "size": 5}] + return (200, {}, {"manageable-snapshots": snaps}) + + def get_os_snapshot_manage_detail(self, **kw): + snap_id = "snapshot-ffffffff-0000-ffff-0000-ffffffffffff" + snaps = [{"actual_size": 4.0, "size": 4, + "safe_to_manage": False, "source_id_type": "source-name", + "source_cinder_id": "00000000-ffff-0000-ffff-00000000", + "reference": {"source-name": snap_id}, + "source_identifier": "volume-00000000-ffff-0000-ffff-000000", + "extra_info": "qos_setting:high", + "reason_not_safe": "snapshot in use"}, + {"actual_size": 4.3, "reference": {"source-name": "mysnap"}, + "safe_to_manage": True, "source_cinder_id": None, + "source_id_type": "source-name", "identifier": "mysnap", + "source_identifier": "myvol", "size": 5, + "extra_info": "qos_setting:low", "reason_not_safe": None}] + return (200, {}, {"manageable-snapshots": snaps}) + def post_os_snapshot_manage(self, **kw): snapshot = _stub_snapshot(id='1234', volume_id='volume_id1') snapshot.update(kw['body']['snapshot']) diff --git a/cinderclient/tests/unit/v2/test_shell.py b/cinderclient/tests/unit/v2/test_shell.py index bc33a2b43..d7e556bac 100644 --- a/cinderclient/tests/unit/v2/test_shell.py +++ b/cinderclient/tests/unit/v2/test_shell.py @@ -1115,6 +1115,18 @@ class ShellTest(utils.TestCase): 'bootable': False}} self.assert_called_anytime('POST', '/os-volume-manage', body=expected) + def test_volume_manageable_list(self): + self.run_command('manageable-list fakehost') + self.assert_called('GET', '/os-volume-manage/detail?host=fakehost') + + def test_volume_manageable_list_details(self): + self.run_command('manageable-list fakehost --detailed True') + self.assert_called('GET', '/os-volume-manage/detail?host=fakehost') + + def test_volume_manageable_list_no_details(self): + self.run_command('manageable-list fakehost --detailed False') + self.assert_called('GET', '/os-volume-manage?host=fakehost') + def test_volume_unmanage(self): self.run_command('unmanage 1234') self.assert_called('POST', '/volumes/1234/action', @@ -1327,6 +1339,18 @@ class ShellTest(utils.TestCase): self.assert_called_anytime('POST', '/os-snapshot-manage', body=expected) + def test_snapshot_manageable_list(self): + self.run_command('snapshot-manageable-list fakehost') + self.assert_called('GET', '/os-snapshot-manage/detail?host=fakehost') + + def test_snapshot_manageable_list_details(self): + self.run_command('snapshot-manageable-list fakehost --detailed True') + self.assert_called('GET', '/os-snapshot-manage/detail?host=fakehost') + + def test_snapshot_manageable_list_no_details(self): + self.run_command('snapshot-manageable-list fakehost --detailed False') + self.assert_called('GET', '/os-snapshot-manage?host=fakehost') + def test_snapshot_unmanage(self): self.run_command('snapshot-unmanage 1234') self.assert_called('POST', '/snapshots/1234/action', diff --git a/cinderclient/tests/unit/v2/test_volumes.py b/cinderclient/tests/unit/v2/test_volumes.py index ab92490ea..0fb54cee9 100644 --- a/cinderclient/tests/unit/v2/test_volumes.py +++ b/cinderclient/tests/unit/v2/test_volumes.py @@ -274,6 +274,14 @@ class VolumesTest(utils.TestCase): cs.assert_called('POST', '/os-volume-manage', {'volume': expected}) self._assert_request_id(vol) + def test_volume_list_manageable(self): + cs.volumes.list_manageable('host1', detailed=False) + cs.assert_called('GET', '/os-volume-manage?host=host1') + + def test_volume_list_manageable_detailed(self): + cs.volumes.list_manageable('host1', detailed=True) + cs.assert_called('GET', '/os-volume-manage/detail?host=host1') + def test_volume_unmanage(self): v = cs.volumes.get('1234') self._assert_request_id(v) @@ -288,6 +296,14 @@ class VolumesTest(utils.TestCase): cs.assert_called('POST', '/os-snapshot-manage', {'snapshot': expected}) self._assert_request_id(vol) + def test_snapshot_list_manageable(self): + cs.volume_snapshots.list_manageable('host1', detailed=False) + cs.assert_called('GET', '/os-snapshot-manage?host=host1') + + def test_snapshot_list_manageable_detailed(self): + cs.volume_snapshots.list_manageable('host1', detailed=True) + cs.assert_called('GET', '/os-snapshot-manage/detail?host=host1') + def test_replication_promote(self): v = cs.volumes.get('1234') self._assert_request_id(v) diff --git a/cinderclient/tests/unit/v3/fakes.py b/cinderclient/tests/unit/v3/fakes.py index 814e435f8..e61228e90 100644 --- a/cinderclient/tests/unit/v3/fakes.py +++ b/cinderclient/tests/unit/v3/fakes.py @@ -364,3 +364,53 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient): def delete_group_snapshots_1234(self, **kw): return (202, {}, {}) + + # + # Manageable volumes/snapshots + # + def get_manageable_volumes(self, **kw): + vol_id = "volume-ffffffff-0000-ffff-0000-ffffffffffff" + vols = [{"size": 4, "safe_to_manage": False, "actual_size": 4.0, + "reference": {"source-name": vol_id}}, + {"size": 5, "safe_to_manage": True, "actual_size": 4.3, + "reference": {"source-name": "myvol"}}] + return (200, {}, {"manageable-volumes": vols}) + + def get_manageable_volumes_detail(self, **kw): + vol_id = "volume-ffffffff-0000-ffff-0000-ffffffffffff" + vols = [{"size": 4, "reason_not_safe": "volume in use", + "safe_to_manage": False, "extra_info": "qos_setting:high", + "reference": {"source-name": vol_id}, + "actual_size": 4.0}, + {"size": 5, "reason_not_safe": None, "safe_to_manage": True, + "extra_info": "qos_setting:low", "actual_size": 4.3, + "reference": {"source-name": "myvol"}}] + return (200, {}, {"manageable-volumes": vols}) + + def get_manageable_snapshots(self, **kw): + snap_id = "snapshot-ffffffff-0000-ffff-0000-ffffffffffff" + snaps = [{"actual_size": 4.0, "size": 4, + "safe_to_manage": False, "source_id_type": "source-name", + "source_cinder_id": "00000000-ffff-0000-ffff-00000000", + "reference": {"source-name": snap_id}, + "source_identifier": "volume-00000000-ffff-0000-ffff-000000"}, + {"actual_size": 4.3, "reference": {"source-name": "mysnap"}, + "source_id_type": "source-name", "source_identifier": "myvol", + "safe_to_manage": True, "source_cinder_id": None, "size": 5}] + return (200, {}, {"manageable-snapshots": snaps}) + + def get_manageable_snapshots_detail(self, **kw): + snap_id = "snapshot-ffffffff-0000-ffff-0000-ffffffffffff" + snaps = [{"actual_size": 4.0, "size": 4, + "safe_to_manage": False, "source_id_type": "source-name", + "source_cinder_id": "00000000-ffff-0000-ffff-00000000", + "reference": {"source-name": snap_id}, + "source_identifier": "volume-00000000-ffff-0000-ffff-000000", + "extra_info": "qos_setting:high", + "reason_not_safe": "snapshot in use"}, + {"actual_size": 4.3, "reference": {"source-name": "mysnap"}, + "safe_to_manage": True, "source_cinder_id": None, + "source_id_type": "source-name", "identifier": "mysnap", + "source_identifier": "myvol", "size": 5, + "extra_info": "qos_setting:low", "reason_not_safe": None}] + return (200, {}, {"manageable-snapshots": snaps}) diff --git a/cinderclient/tests/unit/v3/test_shell.py b/cinderclient/tests/unit/v3/test_shell.py index 85e4c64f8..7640b952e 100644 --- a/cinderclient/tests/unit/v3/test_shell.py +++ b/cinderclient/tests/unit/v3/test_shell.py @@ -309,3 +309,33 @@ class ShellTest(utils.TestCase): cmd += src self.run_command(cmd) self.assert_called_anytime('POST', '/groups/action', body=expected) + + def test_volume_manageable_list(self): + self.run_command('--os-volume-api-version 3.8 ' + 'manageable-list fakehost') + self.assert_called('GET', '/manageable_volumes/detail?host=fakehost') + + def test_volume_manageable_list_details(self): + self.run_command('--os-volume-api-version 3.8 ' + 'manageable-list fakehost --detailed True') + self.assert_called('GET', '/manageable_volumes/detail?host=fakehost') + + def test_volume_manageable_list_no_details(self): + self.run_command('--os-volume-api-version 3.8 ' + 'manageable-list fakehost --detailed False') + self.assert_called('GET', '/manageable_volumes?host=fakehost') + + def test_snapshot_manageable_list(self): + self.run_command('--os-volume-api-version 3.8 ' + 'snapshot-manageable-list fakehost') + self.assert_called('GET', '/manageable_snapshots/detail?host=fakehost') + + def test_snapshot_manageable_list_details(self): + self.run_command('--os-volume-api-version 3.8 ' + 'snapshot-manageable-list fakehost --detailed True') + self.assert_called('GET', '/manageable_snapshots/detail?host=fakehost') + + def test_snapshot_manageable_list_no_details(self): + self.run_command('--os-volume-api-version 3.8 ' + 'snapshot-manageable-list fakehost --detailed False') + self.assert_called('GET', '/manageable_snapshots?host=fakehost') diff --git a/cinderclient/tests/unit/v3/test_volumes.py b/cinderclient/tests/unit/v3/test_volumes.py index 0389cc52e..b3f928a37 100644 --- a/cinderclient/tests/unit/v3/test_volumes.py +++ b/cinderclient/tests/unit/v3/test_volumes.py @@ -65,3 +65,23 @@ class VolumesTest(utils.TestCase): 'group_id': '1234'}} cs.assert_called('POST', '/volumes', body=expected) self._assert_request_id(vol) + + def test_volume_list_manageable(self): + cs = fakes.FakeClient(api_versions.APIVersion('3.8')) + cs.volumes.list_manageable('host1', detailed=False) + cs.assert_called('GET', '/manageable_volumes?host=host1') + + def test_volume_list_manageable_detailed(self): + cs = fakes.FakeClient(api_versions.APIVersion('3.8')) + cs.volumes.list_manageable('host1', detailed=True) + cs.assert_called('GET', '/manageable_volumes/detail?host=host1') + + def test_snapshot_list_manageable(self): + cs = fakes.FakeClient(api_versions.APIVersion('3.8')) + cs.volume_snapshots.list_manageable('host1', detailed=False) + cs.assert_called('GET', '/manageable_snapshots?host=host1') + + def test_snapshot_list_manageable_detailed(self): + cs = fakes.FakeClient(api_versions.APIVersion('3.8')) + cs.volume_snapshots.list_manageable('host1', detailed=True) + cs.assert_called('GET', '/manageable_snapshots/detail?host=host1') diff --git a/cinderclient/v2/shell.py b/cinderclient/v2/shell.py index e58fa0047..c020b7bb9 100644 --- a/cinderclient/v2/shell.py +++ b/cinderclient/v2/shell.py @@ -108,3 +108,89 @@ def do_upload_to_image(cs, args): args.image_name, args.container_format, args.disk_format)) + +@utils.arg('host', + metavar='', + help='Cinder host on which to list manageable volumes; ' + 'takes the form: host@backend-name#pool') +@utils.arg('--detailed', + metavar='', + default=True, + help='Returned detailed information (default true).') +@utils.arg('--marker', + metavar='', + default=None, + help='Begin returning volumes that appear later in the volume ' + 'list than that represented by this volume id. ' + 'Default=None.') +@utils.arg('--limit', + metavar='', + default=None, + help='Maximum number of volumes to return. Default=None.') +@utils.arg('--offset', + metavar='', + default=None, + help='Number of volumes to skip after marker. Default=None.') +@utils.arg('--sort', + metavar='[:]', + default=None, + help=(('Comma-separated list of sort keys and directions in the ' + 'form of [:]. ' + 'Valid keys: %s. ' + 'Default=None.') % ', '.join(base.SORT_KEY_VALUES))) +@utils.service_type('volumev2') +def do_manageable_list(cs, args): + """Lists all manageable volumes.""" + detailed = strutils.bool_from_string(args.detailed) + volumes = cs.volumes.list_manageable(host=args.host, detailed=detailed, + marker=args.marker, limit=args.limit, + offset=args.offset, sort=args.sort) + columns = ['reference', 'size', 'safe_to_manage'] + if detailed: + columns.extend(['reason_not_safe', 'cinder_id', 'extra_info']) + utils.print_list(volumes, columns, sortby_index=None) + + +@utils.arg('host', + metavar='', + help='Cinder host on which to list manageable snapshots; ' + 'takes the form: host@backend-name#pool') +@utils.arg('--detailed', + metavar='', + default=True, + help='Returned detailed information (default true).') +@utils.arg('--marker', + metavar='', + default=None, + help='Begin returning volumes that appear later in the volume ' + 'list than that represented by this volume id. ' + 'Default=None.') +@utils.arg('--limit', + metavar='', + default=None, + help='Maximum number of volumes to return. Default=None.') +@utils.arg('--offset', + metavar='', + default=None, + help='Number of volumes to skip after marker. Default=None.') +@utils.arg('--sort', + metavar='[:]', + default=None, + help=(('Comma-separated list of sort keys and directions in the ' + 'form of [:]. ' + 'Valid keys: %s. ' + 'Default=None.') % ', '.join(base.SORT_KEY_VALUES))) +@utils.service_type('volumev2') +def do_snapshot_manageable_list(cs, args): + """Lists all manageable snapshots.""" + detailed = strutils.bool_from_string(args.detailed) + snapshots = cs.volume_snapshots.list_manageable(host=args.host, + detailed=detailed, + marker=args.marker, + limit=args.limit, + offset=args.offset, + sort=args.sort) + columns = ['reference', 'size', 'safe_to_manage', 'source_reference'] + if detailed: + columns.extend(['reason_not_safe', 'cinder_id', 'extra_info']) + utils.print_list(snapshots, columns, sortby_index=None) diff --git a/cinderclient/v2/volume_snapshots.py b/cinderclient/v2/volume_snapshots.py index b0fd89291..a89c135f4 100644 --- a/cinderclient/v2/volume_snapshots.py +++ b/cinderclient/v2/volume_snapshots.py @@ -15,5 +15,25 @@ """Volume snapshot interface (v2 extension).""" -from cinderclient.v3.volume_snapshots import * # flake8: noqa +from cinderclient import api_versions +from cinderclient.v3 import volume_snapshots + +class Snapshot(volume_snapshots.Snapshot): + def list_manageable(self, host, detailed=True, marker=None, limit=None, + offset=None, sort=None): + return self.manager.list_manageable(host, detailed=detailed, + marker=marker, limit=limit, + offset=offset, sort=sort) + + +class SnapshotManager(volume_snapshots.SnapshotManager): + resource_class = Snapshot + + @api_versions.wraps("2.0") + def list_manageable(self, host, detailed=True, marker=None, limit=None, + offset=None, sort=None): + url = self._build_list_url("os-snapshot-manage", detailed=detailed, + search_opts={'host': host}, marker=marker, + limit=limit, offset=offset, sort=sort) + return self._list(url, "manageable-snapshots") diff --git a/cinderclient/v2/volumes.py b/cinderclient/v2/volumes.py index 37c87c3f2..9e23e9ce3 100644 --- a/cinderclient/v2/volumes.py +++ b/cinderclient/v2/volumes.py @@ -43,3 +43,11 @@ class VolumeManager(volumes.VolumeManager): 'image_name': image_name, 'container_format': container_format, 'disk_format': disk_format}) + + @api_versions.wraps("2.0") + def list_manageable(self, host, detailed=True, marker=None, limit=None, + offset=None, sort=None): + url = self._build_list_url("os-volume-manage", detailed=detailed, + search_opts={'host': host}, marker=marker, + limit=limit, offset=offset, sort=sort) + return self._list(url, "manageable-volumes") diff --git a/cinderclient/v3/shell.py b/cinderclient/v3/shell.py index ec66c333d..1ff8c425b 100644 --- a/cinderclient/v3/shell.py +++ b/cinderclient/v3/shell.py @@ -2577,6 +2577,49 @@ def do_manage(cs, args): utils.print_dict(info) +@utils.service_type('volumev3') +@api_versions.wraps('3.8') +@utils.arg('host', + metavar='', + help='Cinder host on which to list manageable volumes; ' + 'takes the form: host@backend-name#pool') +@utils.arg('--detailed', + metavar='', + default=True, + help='Returned detailed information (default true).') +@utils.arg('--marker', + metavar='', + default=None, + help='Begin returning volumes that appear later in the volume ' + 'list than that represented by this volume id. ' + 'Default=None.') +@utils.arg('--limit', + metavar='', + default=None, + help='Maximum number of volumes to return. Default=None.') +@utils.arg('--offset', + metavar='', + default=None, + help='Number of volumes to skip after marker. Default=None.') +@utils.arg('--sort', + metavar='[:]', + default=None, + help=(('Comma-separated list of sort keys and directions in the ' + 'form of [:]. ' + 'Valid keys: %s. ' + 'Default=None.') % ', '.join(base.SORT_KEY_VALUES))) +def do_manageable_list(cs, args): + """Lists all manageable volumes.""" + detailed = strutils.bool_from_string(args.detailed) + volumes = cs.volumes.list_manageable(host=args.host, detailed=detailed, + marker=args.marker, limit=args.limit, + offset=args.offset, sort=args.sort) + columns = ['reference', 'size', 'safe_to_manage'] + if detailed: + columns.extend(['reason_not_safe', 'cinder_id', 'extra_info']) + utils.print_list(volumes, columns, sortby_index=None) + + @utils.arg('volume', metavar='', help='Name or ID of the volume to unmanage.') @utils.service_type('volumev3') @@ -3240,6 +3283,52 @@ def do_snapshot_manage(cs, args): utils.print_dict(info) +@utils.service_type('volumev3') +@api_versions.wraps('3.8') +@utils.arg('host', + metavar='', + help='Cinder host on which to list manageable snapshots; ' + 'takes the form: host@backend-name#pool') +@utils.arg('--detailed', + metavar='', + default=True, + help='Returned detailed information (default true).') +@utils.arg('--marker', + metavar='', + default=None, + help='Begin returning volumes that appear later in the volume ' + 'list than that represented by this volume id. ' + 'Default=None.') +@utils.arg('--limit', + metavar='', + default=None, + help='Maximum number of volumes to return. Default=None.') +@utils.arg('--offset', + metavar='', + default=None, + help='Number of volumes to skip after marker. Default=None.') +@utils.arg('--sort', + metavar='[:]', + default=None, + help=(('Comma-separated list of sort keys and directions in the ' + 'form of [:]. ' + 'Valid keys: %s. ' + 'Default=None.') % ', '.join(base.SORT_KEY_VALUES))) +def do_snapshot_manageable_list(cs, args): + """Lists all manageable snapshots.""" + detailed = strutils.bool_from_string(args.detailed) + snapshots = cs.volume_snapshots.list_manageable(host=args.host, + detailed=detailed, + marker=args.marker, + limit=args.limit, + offset=args.offset, + sort=args.sort) + columns = ['reference', 'size', 'safe_to_manage', 'source_reference'] + if detailed: + columns.extend(['reason_not_safe', 'cinder_id', 'extra_info']) + utils.print_list(snapshots, columns, sortby_index=None) + + @utils.arg('snapshot', metavar='', help='Name or ID of the snapshot to unmanage.') @utils.service_type('volumev3') diff --git a/cinderclient/v3/volume_snapshots.py b/cinderclient/v3/volume_snapshots.py index c84ee732b..ae117d53e 100644 --- a/cinderclient/v3/volume_snapshots.py +++ b/cinderclient/v3/volume_snapshots.py @@ -15,6 +15,7 @@ """Volume snapshot interface (v3 extension).""" +from cinderclient import api_versions from cinderclient import base from cinderclient.openstack.common.apiclient import base as common_base @@ -63,6 +64,12 @@ class Snapshot(base.Resource): self.manager.manage(volume_id=volume_id, ref=ref, name=name, description=description, metadata=metadata) + def list_manageable(self, host, detailed=True, marker=None, limit=None, + offset=None, sort=None): + return self.manager.list_manageable(host, detailed=detailed, + marker=marker, limit=limit, + offset=offset, sort=sort) + def unmanage(self, snapshot): """Unmanage a snapshot.""" self.manager.unmanage(snapshot) @@ -204,6 +211,14 @@ class SnapshotManager(base.ManagerWithFind): } return self._create('/os-snapshot-manage', body, 'snapshot') + @api_versions.wraps("3.8") + def list_manageable(self, host, detailed=True, marker=None, limit=None, + offset=None, sort=None): + url = self._build_list_url("manageable_snapshots", detailed=detailed, + search_opts={'host': host}, marker=marker, + limit=limit, offset=offset, sort=sort) + return self._list(url, "manageable-snapshots") + def unmanage(self, snapshot): """Unmanage a snapshot.""" return self._action('os-unmanage', snapshot, None) diff --git a/cinderclient/v3/volumes.py b/cinderclient/v3/volumes.py index 800249051..6862a183e 100644 --- a/cinderclient/v3/volumes.py +++ b/cinderclient/v3/volumes.py @@ -193,6 +193,12 @@ class Volume(base.Resource): availability_zone=availability_zone, metadata=metadata, bootable=bootable) + def list_manageable(self, host, detailed=True, marker=None, limit=None, + offset=None, sort=None): + return self.manager.list_manageable(host, detailed=detailed, + marker=marker, limit=limit, + offset=offset, sort=sort) + def unmanage(self, volume): """Unmanage a volume.""" return self.manager.unmanage(volume) @@ -624,6 +630,14 @@ class VolumeManager(base.ManagerWithFind): }} return self._create('/os-volume-manage', body, 'volume') + @api_versions.wraps("3.8") + def list_manageable(self, host, detailed=True, marker=None, limit=None, + offset=None, sort=None): + url = self._build_list_url("manageable_volumes", detailed=detailed, + search_opts={'host': host}, marker=marker, + limit=limit, offset=offset, sort=sort) + return self._list(url, "manageable-volumes") + def unmanage(self, volume): """Unmanage a volume.""" return self._action('os-unmanage', volume, None)