diff --git a/doc/source/users/resources/volume/index.rst b/doc/source/users/resources/volume/index.rst index ebf78763..ece8e4f9 100644 --- a/doc/source/users/resources/volume/index.rst +++ b/doc/source/users/resources/volume/index.rst @@ -5,3 +5,4 @@ Volume Resources :maxdepth: 1 v2/type + v2/volume diff --git a/doc/source/users/resources/volume/v2/volume.rst b/doc/source/users/resources/volume/v2/volume.rst new file mode 100644 index 00000000..4bfb90f3 --- /dev/null +++ b/doc/source/users/resources/volume/v2/volume.rst @@ -0,0 +1,21 @@ +openstack.volume.v2.volume +========================== + +.. automodule:: openstack.volume.v2.volume + +The Volume Class +---------------- + +The ``Volume`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.volume.v2.volume.Volume + :members: + +The VolumeDetail Class +---------------------- + +The ``VolumeDetail`` class inherits from +:class:`~openstack.volume.v2.volume.Volume`. + +.. autoclass:: openstack.volume.v2.volume.VolumeDetail + :members: diff --git a/openstack/tests/volume/v2/test_volume.py b/openstack/tests/volume/v2/test_volume.py new file mode 100644 index 00000000..2407726b --- /dev/null +++ b/openstack/tests/volume/v2/test_volume.py @@ -0,0 +1,109 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy + +import testtools + +from openstack.volume.v2 import volume + +FAKE_ID = "6685584b-1eac-4da6-b5c3-555430cf68ff" + +VOLUME = { + "status": "creating", + "name": "my_volume", + "attachments": [], + "availability_zone": "nova", + "bootable": False, + "created_at": "2014-02-21T19:52:04.949734", + "description": "something", + "volume_type": None, + "snapshot_id": "93c2e2aa-7744-4fd6-a31a-80c4726b08d7", + "source_volid": None, + "metadata": {}, + "id": FAKE_ID, + "size": 10 +} + +DETAILS = { + "os-vol-host-attr:host": "127.0.0.1", + "os-vol-tenant-attr:tenant_id": "some tenant", + "os-vol-mig-status-attr:migstat": "done", + "os-vol-mig-status-attr:name_id": "93c2e2aa-7744-4fd6-a31a-80c4726b08d7", + "replication_status": "nah", + "os-volume-replication:extended_status": "really nah", + "consistencygroup_id": "123asf-asdf123", + "os-volume-replication:driver_data": "ahasadfasdfasdfasdfsdf", + "snapshot_id": "93c2e2aa-7744-4fd6-a31a-80c4726b08d7", + "encrypted": False, +} + +VOLUME_DETAIL = copy.copy(VOLUME) +VOLUME_DETAIL.update(DETAILS) + + +class TestVolume(testtools.TestCase): + + def test_basic(self): + 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_create) + self.assertTrue(sot.allow_retrieve) + self.assertTrue(sot.allow_delete) + self.assertFalse(sot.allow_list) + + def test_create(self): + sot = volume.Volume(VOLUME) + self.assertEqual(VOLUME["id"], sot.id) + self.assertEqual(VOLUME["status"], sot.status) + self.assertEqual(VOLUME["attachments"], sot.attachments) + self.assertEqual(VOLUME["availability_zone"], sot.availability_zone) + 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.volume_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) + + +class TestVolumeDetail(testtools.TestCase): + + def test_basic(self): + sot = volume.VolumeDetail(VOLUME_DETAIL) + self.assertTrue(isinstance(sot, volume.Volume)) + self.assertEqual("/volumes/detail", sot.base_path) + + def test_create(self): + 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) + self.assertEqual(VOLUME_DETAIL["os-vol-mig-status-attr:migstat"], + sot.migration_status) + self.assertEqual(VOLUME_DETAIL["os-vol-mig-status-attr:name_id"], + sot.migration_id) + self.assertEqual(VOLUME_DETAIL["replication_status"], + sot.replication_status) + self.assertEqual( + VOLUME_DETAIL["os-volume-replication:extended_status"], + sot.extended_replication_status) + self.assertEqual(VOLUME_DETAIL["consistencygroup_id"], + sot.consistency_group) + self.assertEqual(VOLUME_DETAIL["os-volume-replication:driver_data"], + sot.replication_driver_data) + self.assertEqual(VOLUME_DETAIL["encrypted"], sot.encrypted) diff --git a/openstack/volume/v2/volume.py b/openstack/volume/v2/volume.py new file mode 100644 index 00000000..0e32088f --- /dev/null +++ b/openstack/volume/v2/volume.py @@ -0,0 +1,98 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstack import resource +from openstack.volume import volume_service + + +class Volume(resource.Resource): + resource_key = "volume" + resources_key = "volumes" + base_path = "/volumes" + service = volume_service.VolumeService() + + # capabilities + allow_retrieve = True + allow_create = True + allow_delete = True + allow_update = True + put_update = True + + # Properties + #: A UUID representing this volume. + id = resource.prop("id") + #: The name of this volume. + name = resource.prop("name") + #: A list of links associated with this volume. *Type: list* + links = resource.prop("links", type=list) + + #: The availability zone. + availability_zone = resource.prop("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 = resource.prop("source_volid") + #: The volume description. + description = resource.prop("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 = resource.prop("snapshot_id") + #: The size of the volume, in GBs. *Type: int* + 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") + #: The associated volume type. + type = resource.prop("volume_type") + #: Enables or disables the bootable attribute. You can boot an + #: instance from a bootable volume. *Type: bool* + bootable = resource.prop("bootable", type=bool) + #: One or more metadata key and value pairs to associate with the volume. + metadata = resource.prop("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") + #: TODO(briancurtin): This is currently undocumented in the API. + attachments = resource.prop("attachments") + #: The time this volume was created at. + created = resource.prop("created_at") + + +class VolumeDetail(Volume): + + base_path = "/volumes/detail" + + #: The volume's current back-end. + host = resource.prop("os-vol-host-attr:host") + #: The project ID associated with current back-end. + project_id = resource.prop("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") + #: 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") + #: Status of replication on this volume. + replication_status = resource.prop("replication_status") + #: Extended replication status on this volume. + extended_replication_status = resource.prop( + "os-volume-replication:extended_status") + #: ID of the consistency group. + consistency_group = resource.prop("consistencygroup_id") + #: Data set by the replication driver + replication_driver_data = resource.prop( + "os-volume-replication:driver_data") + #: ``True`` if this volume is encrypted, ``False`` if not. *Type: bool* + encrypted = resource.prop("encrypted", type=bool)