summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvishay Traeger <avishay@stratoscale.com>2016-08-04 18:52:44 +0300
committerxing-yang <xing.yang@emc.com>2016-07-19 18:57:09 -0400
commitd24ba31afa915e9ff54d7f2ff262f223ee326a84 (patch)
tree3ef0effb63356e4ca276c7367a148ede1da75c41
parentf7928c405824691013428177455c8257814316f5 (diff)
downloadpython-cinderclient-d24ba31afa915e9ff54d7f2ff262f223ee326a84.tar.gz
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
-rw-r--r--cinderclient/base.py7
-rw-r--r--cinderclient/tests/unit/v2/fakes.py47
-rw-r--r--cinderclient/tests/unit/v2/test_shell.py24
-rw-r--r--cinderclient/tests/unit/v2/test_volumes.py16
-rw-r--r--cinderclient/tests/unit/v3/fakes.py50
-rw-r--r--cinderclient/tests/unit/v3/test_shell.py30
-rw-r--r--cinderclient/tests/unit/v3/test_volumes.py20
-rw-r--r--cinderclient/v2/shell.py86
-rw-r--r--cinderclient/v2/volume_snapshots.py22
-rw-r--r--cinderclient/v2/volumes.py8
-rw-r--r--cinderclient/v3/shell.py89
-rw-r--r--cinderclient/v3/volume_snapshots.py15
-rw-r--r--cinderclient/v3/volumes.py14
13 files changed, 425 insertions, 3 deletions
diff --git a/cinderclient/base.py b/cinderclient/base.py
index 4e92c53..2374c5c 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 ff54c6a..a70d63c 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 bc33a2b..d7e556b 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 ab92490..0fb54ce 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 814e435..e61228e 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 85e4c64..7640b95 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 0389cc5..b3f928a 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 e58fa00..c020b7b 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='<host>',
+ help='Cinder host on which to list manageable volumes; '
+ 'takes the form: host@backend-name#pool')
+@utils.arg('--detailed',
+ metavar='<detailed>',
+ default=True,
+ help='Returned detailed information (default true).')
+@utils.arg('--marker',
+ metavar='<marker>',
+ 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='<limit>',
+ default=None,
+ help='Maximum number of volumes to return. Default=None.')
+@utils.arg('--offset',
+ metavar='<offset>',
+ default=None,
+ help='Number of volumes to skip after marker. Default=None.')
+@utils.arg('--sort',
+ metavar='<key>[:<direction>]',
+ default=None,
+ help=(('Comma-separated list of sort keys and directions in the '
+ 'form of <key>[:<asc|desc>]. '
+ '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='<host>',
+ help='Cinder host on which to list manageable snapshots; '
+ 'takes the form: host@backend-name#pool')
+@utils.arg('--detailed',
+ metavar='<detailed>',
+ default=True,
+ help='Returned detailed information (default true).')
+@utils.arg('--marker',
+ metavar='<marker>',
+ 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='<limit>',
+ default=None,
+ help='Maximum number of volumes to return. Default=None.')
+@utils.arg('--offset',
+ metavar='<offset>',
+ default=None,
+ help='Number of volumes to skip after marker. Default=None.')
+@utils.arg('--sort',
+ metavar='<key>[:<direction>]',
+ default=None,
+ help=(('Comma-separated list of sort keys and directions in the '
+ 'form of <key>[:<asc|desc>]. '
+ '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 b0fd892..a89c135 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 37c87c3..9e23e9c 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 ec66c33..1ff8c42 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='<host>',
+ help='Cinder host on which to list manageable volumes; '
+ 'takes the form: host@backend-name#pool')
+@utils.arg('--detailed',
+ metavar='<detailed>',
+ default=True,
+ help='Returned detailed information (default true).')
+@utils.arg('--marker',
+ metavar='<marker>',
+ 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='<limit>',
+ default=None,
+ help='Maximum number of volumes to return. Default=None.')
+@utils.arg('--offset',
+ metavar='<offset>',
+ default=None,
+ help='Number of volumes to skip after marker. Default=None.')
+@utils.arg('--sort',
+ metavar='<key>[:<direction>]',
+ default=None,
+ help=(('Comma-separated list of sort keys and directions in the '
+ 'form of <key>[:<asc|desc>]. '
+ '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='<volume>',
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='<host>',
+ help='Cinder host on which to list manageable snapshots; '
+ 'takes the form: host@backend-name#pool')
+@utils.arg('--detailed',
+ metavar='<detailed>',
+ default=True,
+ help='Returned detailed information (default true).')
+@utils.arg('--marker',
+ metavar='<marker>',
+ 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='<limit>',
+ default=None,
+ help='Maximum number of volumes to return. Default=None.')
+@utils.arg('--offset',
+ metavar='<offset>',
+ default=None,
+ help='Number of volumes to skip after marker. Default=None.')
+@utils.arg('--sort',
+ metavar='<key>[:<direction>]',
+ default=None,
+ help=(('Comma-separated list of sort keys and directions in the '
+ 'form of <key>[:<asc|desc>]. '
+ '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='<snapshot>',
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 c84ee73..ae117d5 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 8002490..6862a18 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)