Add list method and query support for cinder volume and snapshot
This patch do those things: 1. Add list method for volume and snapshot in _proxy.py 2. Change the Volume(block_store/v2/volume.Volume)'s base class to resource2 to support QueryParameters. 3. Change the Snapshot(block_store/v2/snapshot.Snapshot)'s base class to resource2 to support QueryParameters. 4. Change the type(block_store/v2/type/type.Type)'s base class to resource2. 5. Add query support for volume and snapshot with parameters: all_tenants, name, status, volume_id(snapshot only). Change-Id: I60f537ea8fd67283fd84addb334bdd932f537635
This commit is contained in:
parent
b2a0d5c6a5
commit
cb69d09f4a
@ -13,10 +13,10 @@
|
||||
from openstack.block_store.v2 import snapshot as _snapshot
|
||||
from openstack.block_store.v2 import type as _type
|
||||
from openstack.block_store.v2 import volume as _volume
|
||||
from openstack import proxy
|
||||
from openstack import proxy2
|
||||
|
||||
|
||||
class Proxy(proxy.BaseProxy):
|
||||
class Proxy(proxy2.BaseProxy):
|
||||
|
||||
def get_snapshot(self, snapshot):
|
||||
"""Get a single snapshot
|
||||
@ -31,6 +31,28 @@ class Proxy(proxy.BaseProxy):
|
||||
"""
|
||||
return self._get(_snapshot.Snapshot, snapshot)
|
||||
|
||||
def snapshots(self, details=True, **query):
|
||||
"""Retrieve a generator of snapshots
|
||||
|
||||
:param bool details: When set to ``False``
|
||||
:class:`~openstack.block_store.v2.snapshot.Snapshot`
|
||||
objects will be returned. The default, ``True``, will cause
|
||||
:class:`~openstack.block_store.v2.snapshot.SnapshotDetail`
|
||||
objects to be returned.
|
||||
:param kwargs \*\*query: Optional query parameters to be sent to limit
|
||||
the snapshots being returned. Available parameters include:
|
||||
|
||||
* name: Name of the snapshot as a string.
|
||||
* all_tenants: Whether return the snapshots of all tenants.
|
||||
* volume_id: volume id of a snapshot.
|
||||
* status: Value of the status of the snapshot so that you can
|
||||
filter on "available" for example.
|
||||
|
||||
:returns: A generator of snapshot objects.
|
||||
"""
|
||||
snapshot = _snapshot.SnapshotDetail if details else _snapshot.Snapshot
|
||||
return self._list(snapshot, paginated=True, **query)
|
||||
|
||||
def create_snapshot(self, **attrs):
|
||||
"""Create a new snapshot from attributes
|
||||
|
||||
@ -72,6 +94,13 @@ class Proxy(proxy.BaseProxy):
|
||||
"""
|
||||
return self._get(_type.Type, type)
|
||||
|
||||
def types(self):
|
||||
"""Retrieve a generator of volume types
|
||||
|
||||
:returns: A generator of volume type objects.
|
||||
"""
|
||||
return self._list(_type.Type, paginated=False)
|
||||
|
||||
def create_type(self, **attrs):
|
||||
"""Create a new type from attributes
|
||||
|
||||
@ -111,6 +140,27 @@ class Proxy(proxy.BaseProxy):
|
||||
"""
|
||||
return self._get(_volume.Volume, volume)
|
||||
|
||||
def volumes(self, details=True, **query):
|
||||
"""Retrieve a generator of volumes
|
||||
|
||||
:param bool details: When set to ``False``
|
||||
:class:`~openstack.block_store.v2.volume.Volume` objects
|
||||
will be returned. The default, ``True``, will cause
|
||||
:class:`~openstack.block_store.v2.volume.VolumeDetail`
|
||||
objects to be returned.
|
||||
:param kwargs \*\*query: Optional query parameters to be sent to limit
|
||||
the volumes being returned. Available parameters include:
|
||||
|
||||
* name: Name of the volume as a string.
|
||||
* all_tenants: Whether return the volumes of all tenants
|
||||
* status: Value of the status of the volume so that you can filter
|
||||
on "available" for example.
|
||||
|
||||
:returns: A generator of volume objects.
|
||||
"""
|
||||
volume = _volume.VolumeDetail if details else _volume.Volume
|
||||
return self._list(volume, paginated=True, **query)
|
||||
|
||||
def create_volume(self, **attrs):
|
||||
"""Create a new volume from attributes
|
||||
|
||||
|
@ -12,43 +12,47 @@
|
||||
|
||||
from openstack.block_store import block_store_service
|
||||
from openstack import format
|
||||
from openstack import resource
|
||||
from openstack import resource2
|
||||
|
||||
|
||||
class Snapshot(resource.Resource):
|
||||
class Snapshot(resource2.Resource):
|
||||
resource_key = "snapshot"
|
||||
resources_key = "snapshots"
|
||||
base_path = "/snapshots"
|
||||
service = block_store_service.BlockStoreService()
|
||||
|
||||
_query_mapping = resource2.QueryParameters('all_tenants', 'name', 'status',
|
||||
'volume_id')
|
||||
|
||||
# capabilities
|
||||
allow_retrieve = True
|
||||
allow_get = True
|
||||
allow_create = True
|
||||
allow_delete = True
|
||||
allow_update = True
|
||||
allow_list = True
|
||||
|
||||
# Properties
|
||||
#: A ID representing this snapshot.
|
||||
id = resource.prop("id")
|
||||
id = resource2.Body("id")
|
||||
#: Name of the snapshot. Default is None.
|
||||
name = resource.prop("name")
|
||||
name = resource2.Body("name")
|
||||
|
||||
#: The current status of this snapshot. Potential values are creating,
|
||||
#: available, deleting, error, and error_deleting.
|
||||
status = resource.prop("status")
|
||||
status = resource2.Body("status")
|
||||
#: Description of snapshot. Default is None.
|
||||
description = resource.prop("description")
|
||||
description = resource2.Body("description")
|
||||
#: The timestamp of this snapshot creation.
|
||||
created_at = resource.prop("created_at")
|
||||
created_at = resource2.Body("created_at")
|
||||
#: Metadata associated with this snapshot.
|
||||
metadata = resource.prop("metadata", type=dict)
|
||||
metadata = resource2.Body("metadata", type=dict)
|
||||
#: The ID of the volume this snapshot was taken of.
|
||||
volume_id = resource.prop("volume_id")
|
||||
volume_id = resource2.Body("volume_id")
|
||||
#: The size of the volume, in GBs.
|
||||
size = resource.prop("size", type=int)
|
||||
size = resource2.Body("size", type=int)
|
||||
#: Indicate whether to create snapshot, even if the volume is attached.
|
||||
#: Default is ``False``. *Type: bool*
|
||||
is_forced = resource.prop("force", type=format.BoolStr)
|
||||
is_forced = resource2.Body("force", type=format.BoolStr)
|
||||
|
||||
|
||||
class SnapshotDetail(Snapshot):
|
||||
@ -56,6 +60,6 @@ class SnapshotDetail(Snapshot):
|
||||
base_path = "/snapshots/detail"
|
||||
|
||||
#: The percentage of completeness the snapshot is currently at.
|
||||
progress = resource.prop("os-extended-snapshot-attributes:progress")
|
||||
progress = resource2.Body("os-extended-snapshot-attributes:progress")
|
||||
#: The project ID this snapshot is associated with.
|
||||
project_id = resource.prop("os-extended-snapshot-attributes:project_id")
|
||||
project_id = resource2.Body("os-extended-snapshot-attributes:project_id")
|
||||
|
@ -11,24 +11,25 @@
|
||||
# under the License.
|
||||
|
||||
from openstack.block_store import block_store_service
|
||||
from openstack import resource
|
||||
from openstack import resource2
|
||||
|
||||
|
||||
class Type(resource.Resource):
|
||||
class Type(resource2.Resource):
|
||||
resource_key = "volume_type"
|
||||
resources_key = "volume_types"
|
||||
base_path = "/types"
|
||||
service = block_store_service.BlockStoreService()
|
||||
|
||||
# capabilities
|
||||
allow_retrieve = True
|
||||
allow_get = True
|
||||
allow_create = True
|
||||
allow_delete = True
|
||||
allow_list = True
|
||||
|
||||
# Properties
|
||||
#: A ID representing this type.
|
||||
id = resource.prop("id")
|
||||
id = resource2.Body("id")
|
||||
#: Name of the type.
|
||||
name = resource.prop("name")
|
||||
name = resource2.Body("name")
|
||||
#: A dict of extra specifications. "capabilities" is a usual key.
|
||||
extra_specs = resource.prop("extra_specs", type=dict)
|
||||
extra_specs = resource2.Body("extra_specs", type=dict)
|
||||
|
@ -12,63 +12,66 @@
|
||||
|
||||
from openstack.block_store import block_store_service
|
||||
from openstack import format
|
||||
from openstack import resource
|
||||
from openstack import resource2
|
||||
|
||||
|
||||
class Volume(resource.Resource):
|
||||
class Volume(resource2.Resource):
|
||||
resource_key = "volume"
|
||||
resources_key = "volumes"
|
||||
base_path = "/volumes"
|
||||
service = block_store_service.BlockStoreService()
|
||||
|
||||
_query_mapping = resource2.QueryParameters('all_tenants', 'name', 'status')
|
||||
|
||||
# capabilities
|
||||
allow_retrieve = True
|
||||
allow_get = True
|
||||
allow_create = True
|
||||
allow_delete = True
|
||||
allow_update = True
|
||||
allow_list = True
|
||||
|
||||
# Properties
|
||||
#: A ID representing this volume.
|
||||
id = resource.prop("id")
|
||||
id = resource2.Body("id")
|
||||
#: The name of this volume.
|
||||
name = resource.prop("name")
|
||||
name = resource2.Body("name")
|
||||
#: A list of links associated with this volume. *Type: list*
|
||||
links = resource.prop("links", type=list)
|
||||
links = resource2.Body("links", type=list)
|
||||
|
||||
#: The availability zone.
|
||||
availability_zone = resource.prop("availability_zone")
|
||||
availability_zone = resource2.Body("availability_zone")
|
||||
#: To create a volume from an existing volume, specify the ID of
|
||||
#: the existing volume. If specified, the volume is created with
|
||||
#: same size of the source volume.
|
||||
source_volume_id = resource.prop("source_volid")
|
||||
source_volume_id = resource2.Body("source_volid")
|
||||
#: The volume description.
|
||||
description = resource.prop("description")
|
||||
description = resource2.Body("description")
|
||||
#: To create a volume from an existing snapshot, specify the ID of
|
||||
#: the existing volume snapshot. If specified, the volume is created
|
||||
#: in same availability zone and with same size of the snapshot.
|
||||
snapshot_id = resource.prop("snapshot_id")
|
||||
snapshot_id = resource2.Body("snapshot_id")
|
||||
#: The size of the volume, in GBs. *Type: int*
|
||||
size = resource.prop("size", type=int)
|
||||
size = resource2.Body("size", type=int)
|
||||
#: The ID of the image from which you want to create the volume.
|
||||
#: Required to create a bootable volume.
|
||||
image_id = resource.prop("imageRef")
|
||||
image_id = resource2.Body("imageRef")
|
||||
#: The name of the associated volume type.
|
||||
volume_type = resource.prop("volume_type")
|
||||
volume_type = resource2.Body("volume_type")
|
||||
#: Enables or disables the bootable attribute. You can boot an
|
||||
#: instance from a bootable volume. *Type: bool*
|
||||
is_bootable = resource.prop("bootable", type=format.BoolStr)
|
||||
is_bootable = resource2.Body("bootable", type=format.BoolStr)
|
||||
#: One or more metadata key and value pairs to associate with the volume.
|
||||
metadata = resource.prop("metadata")
|
||||
metadata = resource2.Body("metadata")
|
||||
|
||||
#: One of the following values: creating, available, attaching, in-use
|
||||
#: deleting, error, error_deleting, backing-up, restoring-backup,
|
||||
#: error_restoring. For details on these statuses, see the
|
||||
#: Block Storage API documentation.
|
||||
status = resource.prop("status")
|
||||
status = resource2.Body("status")
|
||||
#: TODO(briancurtin): This is currently undocumented in the API.
|
||||
attachments = resource.prop("attachments")
|
||||
attachments = resource2.Body("attachments")
|
||||
#: The timestamp of this volume creation.
|
||||
created_at = resource.prop("created_at")
|
||||
created_at = resource2.Body("created_at")
|
||||
|
||||
|
||||
class VolumeDetail(Volume):
|
||||
@ -76,24 +79,24 @@ class VolumeDetail(Volume):
|
||||
base_path = "/volumes/detail"
|
||||
|
||||
#: The volume's current back-end.
|
||||
host = resource.prop("os-vol-host-attr:host")
|
||||
host = resource2.Body("os-vol-host-attr:host")
|
||||
#: The project ID associated with current back-end.
|
||||
project_id = resource.prop("os-vol-tenant-attr:tenant_id")
|
||||
project_id = resource2.Body("os-vol-tenant-attr:tenant_id")
|
||||
#: The status of this volume's migration (None means that a migration
|
||||
#: is not currently in progress).
|
||||
migration_status = resource.prop("os-vol-mig-status-attr:migstat")
|
||||
migration_status = resource2.Body("os-vol-mig-status-attr:migstat")
|
||||
#: The volume ID that this volume's name on the back-end is based on.
|
||||
migration_id = resource.prop("os-vol-mig-status-attr:name_id")
|
||||
migration_id = resource2.Body("os-vol-mig-status-attr:name_id")
|
||||
#: Status of replication on this volume.
|
||||
replication_status = resource.prop("replication_status")
|
||||
replication_status = resource2.Body("replication_status")
|
||||
#: Extended replication status on this volume.
|
||||
extended_replication_status = resource.prop(
|
||||
extended_replication_status = resource2.Body(
|
||||
"os-volume-replication:extended_status")
|
||||
#: ID of the consistency group.
|
||||
consistency_group_id = resource.prop("consistencygroup_id")
|
||||
consistency_group_id = resource2.Body("consistencygroup_id")
|
||||
#: Data set by the replication driver
|
||||
replication_driver_data = resource.prop(
|
||||
replication_driver_data = resource2.Body(
|
||||
"os-volume-replication:driver_data")
|
||||
#: ``True`` if this volume is encrypted, ``False`` if not.
|
||||
#: *Type: bool*
|
||||
is_encrypted = resource.prop("encrypted", type=format.BoolStr)
|
||||
is_encrypted = resource2.Body("encrypted", type=format.BoolStr)
|
||||
|
@ -14,10 +14,10 @@ from openstack.block_store.v2 import _proxy
|
||||
from openstack.block_store.v2 import snapshot
|
||||
from openstack.block_store.v2 import type
|
||||
from openstack.block_store.v2 import volume
|
||||
from openstack.tests.unit import test_proxy_base
|
||||
from openstack.tests.unit import test_proxy_base2
|
||||
|
||||
|
||||
class TestVolumeProxy(test_proxy_base.TestProxyBase):
|
||||
class TestVolumeProxy(test_proxy_base2.TestProxyBase):
|
||||
def setUp(self):
|
||||
super(TestVolumeProxy, self).setUp()
|
||||
self.proxy = _proxy.Proxy(self.session)
|
||||
@ -25,6 +25,18 @@ class TestVolumeProxy(test_proxy_base.TestProxyBase):
|
||||
def test_snapshot_get(self):
|
||||
self.verify_get(self.proxy.get_snapshot, snapshot.Snapshot)
|
||||
|
||||
def test_snapshots_detailed(self):
|
||||
self.verify_list(self.proxy.snapshots, snapshot.SnapshotDetail,
|
||||
paginated=True,
|
||||
method_kwargs={"details": True, "query": 1},
|
||||
expected_kwargs={"query": 1})
|
||||
|
||||
def test_snapshots_not_detailed(self):
|
||||
self.verify_list(self.proxy.snapshots, snapshot.Snapshot,
|
||||
paginated=True,
|
||||
method_kwargs={"details": False, "query": 1},
|
||||
expected_kwargs={"query": 1})
|
||||
|
||||
def test_snapshot_create_attrs(self):
|
||||
self.verify_create(self.proxy.create_snapshot, snapshot.Snapshot)
|
||||
|
||||
@ -39,6 +51,9 @@ class TestVolumeProxy(test_proxy_base.TestProxyBase):
|
||||
def test_type_get(self):
|
||||
self.verify_get(self.proxy.get_type, type.Type)
|
||||
|
||||
def test_types(self):
|
||||
self.verify_list(self.proxy.types, type.Type, paginated=False)
|
||||
|
||||
def test_type_create_attrs(self):
|
||||
self.verify_create(self.proxy.create_type, type.Type)
|
||||
|
||||
@ -51,6 +66,18 @@ class TestVolumeProxy(test_proxy_base.TestProxyBase):
|
||||
def test_volume_get(self):
|
||||
self.verify_get(self.proxy.get_volume, volume.Volume)
|
||||
|
||||
def test_volumes_detailed(self):
|
||||
self.verify_list(self.proxy.volumes, volume.VolumeDetail,
|
||||
paginated=True,
|
||||
method_kwargs={"details": True, "query": 1},
|
||||
expected_kwargs={"query": 1})
|
||||
|
||||
def test_volumes_not_detailed(self):
|
||||
self.verify_list(self.proxy.volumes, volume.Volume,
|
||||
paginated=True,
|
||||
method_kwargs={"details": False, "query": 1},
|
||||
expected_kwargs={"query": 1})
|
||||
|
||||
def test_volume_create_attrs(self):
|
||||
self.verify_create(self.proxy.create_volume, volume.Volume)
|
||||
|
||||
|
@ -44,17 +44,24 @@ class TestSnapshot(testtools.TestCase):
|
||||
sot = snapshot.Snapshot(SNAPSHOT)
|
||||
self.assertEqual("snapshot", sot.resource_key)
|
||||
self.assertEqual("snapshots", sot.resources_key)
|
||||
self.assertEqual("id", sot.id_attribute)
|
||||
self.assertEqual("/snapshots", sot.base_path)
|
||||
self.assertEqual("volume", sot.service.service_type)
|
||||
self.assertTrue(sot.allow_get)
|
||||
self.assertTrue(sot.allow_update)
|
||||
self.assertTrue(sot.allow_create)
|
||||
self.assertTrue(sot.allow_retrieve)
|
||||
self.assertTrue(sot.allow_delete)
|
||||
self.assertFalse(sot.allow_list)
|
||||
self.assertTrue(sot.allow_list)
|
||||
|
||||
self.assertDictEqual({"name": "name",
|
||||
"status": "status",
|
||||
"all_tenants": "all_tenants",
|
||||
"volume_id": "volume_id",
|
||||
"limit": "limit",
|
||||
"marker": "marker"},
|
||||
sot._query_mapping._mapping)
|
||||
|
||||
def test_create_basic(self):
|
||||
sot = snapshot.Snapshot(SNAPSHOT)
|
||||
sot = snapshot.Snapshot(**SNAPSHOT)
|
||||
self.assertEqual(SNAPSHOT["id"], sot.id)
|
||||
self.assertEqual(SNAPSHOT["status"], sot.status)
|
||||
self.assertEqual(SNAPSHOT["created_at"], sot.created_at)
|
||||
@ -73,7 +80,7 @@ class TestSnapshotDetail(testtools.TestCase):
|
||||
self.assertEqual("/snapshots/detail", sot.base_path)
|
||||
|
||||
def test_create_detailed(self):
|
||||
sot = snapshot.SnapshotDetail(DETAILED_SNAPSHOT)
|
||||
sot = snapshot.SnapshotDetail(**DETAILED_SNAPSHOT)
|
||||
|
||||
self.assertEqual(
|
||||
DETAILED_SNAPSHOT["os-extended-snapshot-attributes:progress"],
|
||||
|
@ -27,23 +27,23 @@ TYPE = {
|
||||
class TestType(testtools.TestCase):
|
||||
|
||||
def test_basic(self):
|
||||
sot = type.Type(TYPE)
|
||||
sot = type.Type(**TYPE)
|
||||
self.assertEqual("volume_type", sot.resource_key)
|
||||
self.assertEqual("volume_types", sot.resources_key)
|
||||
self.assertEqual("id", sot.id_attribute)
|
||||
self.assertEqual("/types", sot.base_path)
|
||||
self.assertEqual("volume", sot.service.service_type)
|
||||
self.assertTrue(sot.allow_create)
|
||||
self.assertTrue(sot.allow_retrieve)
|
||||
self.assertTrue(sot.allow_get)
|
||||
self.assertTrue(sot.allow_delete)
|
||||
self.assertFalse(sot.allow_list)
|
||||
self.assertTrue(sot.allow_list)
|
||||
self.assertFalse(sot.allow_update)
|
||||
|
||||
def test_new(self):
|
||||
sot = type.Type.new(id=FAKE_ID)
|
||||
self.assertEqual(FAKE_ID, sot.id)
|
||||
|
||||
def test_create(self):
|
||||
sot = type.Type(TYPE)
|
||||
sot = type.Type(**TYPE)
|
||||
self.assertEqual(TYPE["id"], sot.id)
|
||||
self.assertEqual(TYPE["extra_specs"], sot.extra_specs)
|
||||
self.assertEqual(TYPE["name"], sot.name)
|
||||
|
@ -58,16 +58,23 @@ class TestVolume(testtools.TestCase):
|
||||
sot = volume.Volume(VOLUME)
|
||||
self.assertEqual("volume", sot.resource_key)
|
||||
self.assertEqual("volumes", sot.resources_key)
|
||||
self.assertEqual("id", sot.id_attribute)
|
||||
self.assertEqual("/volumes", sot.base_path)
|
||||
self.assertEqual("volume", sot.service.service_type)
|
||||
self.assertTrue(sot.allow_get)
|
||||
self.assertTrue(sot.allow_create)
|
||||
self.assertTrue(sot.allow_retrieve)
|
||||
self.assertTrue(sot.allow_update)
|
||||
self.assertTrue(sot.allow_delete)
|
||||
self.assertFalse(sot.allow_list)
|
||||
self.assertTrue(sot.allow_list)
|
||||
|
||||
self.assertDictEqual({"name": "name",
|
||||
"status": "status",
|
||||
"all_tenants": "all_tenants",
|
||||
"limit": "limit",
|
||||
"marker": "marker"},
|
||||
sot._query_mapping._mapping)
|
||||
|
||||
def test_create(self):
|
||||
sot = volume.Volume(VOLUME)
|
||||
sot = volume.Volume(**VOLUME)
|
||||
self.assertEqual(VOLUME["id"], sot.id)
|
||||
self.assertEqual(VOLUME["status"], sot.status)
|
||||
self.assertEqual(VOLUME["attachments"], sot.attachments)
|
||||
@ -91,7 +98,7 @@ class TestVolumeDetail(testtools.TestCase):
|
||||
self.assertEqual("/volumes/detail", sot.base_path)
|
||||
|
||||
def test_create(self):
|
||||
sot = volume.VolumeDetail(VOLUME_DETAIL)
|
||||
sot = volume.VolumeDetail(**VOLUME_DETAIL)
|
||||
self.assertEqual(VOLUME_DETAIL["os-vol-host-attr:host"], sot.host)
|
||||
self.assertEqual(VOLUME_DETAIL["os-vol-tenant-attr:tenant_id"],
|
||||
sot.project_id)
|
||||
|
Loading…
Reference in New Issue
Block a user