Support block_store types where IDs are taken
In an effort to widen support for resource properties that are IDs but can take the appropriate resource type for that ID, the block_store service throws a kink into it. The Volume type can contain a Snapshot, and a Snapshot can contain a Volume. Because of that loop, we can't do the usual `foo = resource.prop(foo, FooType)`. We can sort of emulate the behavior users will get out of it in the proxy by doing a type conversion in the create_volume and create_snapshot methods to support resource instances being passed in there. Additionally, Volume can be created from another Volume, but we can't have a prop refer to its own class, so that problem is solved the same as above. Lastly, the Volume resource has two props that can take real types: image and type. Change-Id: If8a5b24b05a341ae6bfe9dca6b93429dcfc8f408
This commit is contained in:
@@ -18,6 +18,23 @@ from openstack import proxy
|
||||
|
||||
class Proxy(proxy.BaseProxy):
|
||||
|
||||
def _convert_id(self, attrs, value, resource_type):
|
||||
"""Convert potential Resource values into IDs
|
||||
|
||||
The structure of Snapshot and Volume resources is such that their
|
||||
Resource subclasses contain properties of each other's types, because
|
||||
a Snapshot can be created of a Volume, and a Volume can be created from
|
||||
a Snapshot. Additionally, a Volume can be created from another
|
||||
Volume, yet the source_volume prop can't refer to the current class.
|
||||
We work around this by simply looking for those Resource types
|
||||
before sending them on.
|
||||
"""
|
||||
val = attrs.pop(value, None)
|
||||
if val is not None:
|
||||
if isinstance(val, resource_type):
|
||||
val = val.id
|
||||
attrs[value] = val
|
||||
|
||||
def get_snapshot(self, snapshot):
|
||||
"""Get a single snapshot
|
||||
|
||||
@@ -41,6 +58,7 @@ class Proxy(proxy.BaseProxy):
|
||||
:returns: The results of snapshot creation
|
||||
:rtype: :class:`~openstack.volume.v2.snapshot.Snapshot`
|
||||
"""
|
||||
self._convert_id(attrs, "volume", _volume.Volume)
|
||||
return self._create(_snapshot.Snapshot, **attrs)
|
||||
|
||||
def delete_snapshot(self, snapshot, ignore_missing=True):
|
||||
@@ -121,6 +139,8 @@ class Proxy(proxy.BaseProxy):
|
||||
:returns: The results of volume creation
|
||||
:rtype: :class:`~openstack.volume.v2.volume.Volume`
|
||||
"""
|
||||
self._convert_id(attrs, "source_volume", _volume.Volume)
|
||||
self._convert_id(attrs, "snapshot", _snapshot.Snapshot)
|
||||
return self._create(_volume.Volume, **attrs)
|
||||
|
||||
def delete_volume(self, volume, ignore_missing=True):
|
||||
|
@@ -11,6 +11,8 @@
|
||||
# under the License.
|
||||
|
||||
from openstack.block_store import block_store_service
|
||||
from openstack.block_store.v2 import type as _type
|
||||
from openstack.image.v2 import image as _image
|
||||
from openstack import resource
|
||||
|
||||
|
||||
@@ -50,9 +52,9 @@ class Volume(resource.Resource):
|
||||
size = resource.prop("size", type=int)
|
||||
#: The ID of the image from which you want to create the volume.
|
||||
#: Required to create a bootable volume.
|
||||
image = resource.prop("imageRef")
|
||||
image = resource.prop("imageRef", type=_image.Image)
|
||||
#: The associated volume type.
|
||||
type = resource.prop("volume_type")
|
||||
type = resource.prop("volume_type", type=_type.Type)
|
||||
#: Enables or disables the bootable attribute. You can boot an
|
||||
#: instance from a bootable volume. *Type: bool*
|
||||
bootable = resource.prop("bootable", type=bool)
|
||||
|
@@ -10,6 +10,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from openstack.block_store.v2 import _proxy
|
||||
from openstack.block_store.v2 import snapshot
|
||||
from openstack.block_store.v2 import type
|
||||
@@ -28,6 +30,16 @@ class TestVolumeProxy(test_proxy_base.TestProxyBase):
|
||||
def test_snapshot_create_attrs(self):
|
||||
self.verify_create(self.proxy.create_snapshot, snapshot.Snapshot)
|
||||
|
||||
def test_snapshot_create_volume_prop(self):
|
||||
self.proxy._create = mock.Mock()
|
||||
|
||||
id = "12345"
|
||||
vol = volume.Volume({"id": id})
|
||||
self.proxy.create_snapshot(volume=vol)
|
||||
|
||||
kwargs = self.proxy._create.call_args[1]
|
||||
self.assertEqual({"volume": id}, kwargs)
|
||||
|
||||
def test_snapshot_delete(self):
|
||||
self.verify_delete(self.proxy.delete_snapshot,
|
||||
snapshot.Snapshot, False)
|
||||
@@ -54,6 +66,18 @@ class TestVolumeProxy(test_proxy_base.TestProxyBase):
|
||||
def test_volume_create_attrs(self):
|
||||
self.verify_create(self.proxy.create_volume, volume.Volume)
|
||||
|
||||
def test_volume_create_snapshot_prop(self):
|
||||
self.proxy._create = mock.Mock()
|
||||
|
||||
id1 = "12345"
|
||||
id2 = "67890"
|
||||
snap = snapshot.Snapshot({"id": id1})
|
||||
source = volume.Volume({"id": id2})
|
||||
self.proxy.create_volume(snapshot=snap, source_volume=source)
|
||||
|
||||
kwargs = self.proxy._create.call_args[1]
|
||||
self.assertEqual({"snapshot": id1, "source_volume": id2}, kwargs)
|
||||
|
||||
def test_volume_delete(self):
|
||||
self.verify_delete(self.proxy.delete_volume, volume.Volume, False)
|
||||
|
||||
|
@@ -14,7 +14,9 @@ import copy
|
||||
|
||||
import testtools
|
||||
|
||||
from openstack.block_store.v2 import type
|
||||
from openstack.block_store.v2 import volume
|
||||
from openstack.image.v2 import image
|
||||
|
||||
FAKE_ID = "6685584b-1eac-4da6-b5c3-555430cf68ff"
|
||||
|
||||
@@ -26,9 +28,10 @@ VOLUME = {
|
||||
"bootable": False,
|
||||
"created_at": "2014-02-21T19:52:04.949734",
|
||||
"description": "something",
|
||||
"volume_type": None,
|
||||
"volume_type": "some_type",
|
||||
"snapshot_id": "93c2e2aa-7744-4fd6-a31a-80c4726b08d7",
|
||||
"source_volid": None,
|
||||
"image": "some_image",
|
||||
"metadata": {},
|
||||
"id": FAKE_ID,
|
||||
"size": 10
|
||||
@@ -74,11 +77,12 @@ class TestVolume(testtools.TestCase):
|
||||
self.assertEqual(VOLUME["bootable"], sot.bootable)
|
||||
self.assertEqual(VOLUME["created_at"], sot.created)
|
||||
self.assertEqual(VOLUME["description"], sot.description)
|
||||
self.assertEqual(VOLUME["volume_type"], sot.type)
|
||||
self.assertEqual(type.Type({"id": VOLUME["volume_type"]}), sot.type)
|
||||
self.assertEqual(VOLUME["snapshot_id"], sot.snapshot)
|
||||
self.assertEqual(VOLUME["source_volid"], sot.source_volume)
|
||||
self.assertEqual(VOLUME["metadata"], sot.metadata)
|
||||
self.assertEqual(VOLUME["size"], sot.size)
|
||||
self.assertEqual(image.Image({"id": VOLUME["image"]}), sot.image)
|
||||
|
||||
|
||||
class TestVolumeDetail(testtools.TestCase):
|
||||
|
Reference in New Issue
Block a user