Repository: libcloud Updated Branches: refs/heads/trunk e62eb2bcc -> 50cba636e
LIBCLOUD-861: Implement Volume Snapshot Operations for Digital Ocean * Add methods for creating, listing and deleting volume snapshots to the Digital Ocean driver. * Add the corresponding tests and their fixtures. Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/8c5a4aff Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/8c5a4aff Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/8c5a4aff Branch: refs/heads/trunk Commit: 8c5a4aff28c1f0eb7b7229558f2e88b83691df73 Parents: e4e56e9 Author: Fahri Cihan Demirci <[email protected]> Authored: Sat Oct 15 05:17:41 2016 -0400 Committer: Fahri Cihan Demirci <[email protected]> Committed: Sun Oct 16 00:08:15 2016 -0400 ---------------------------------------------------------------------- libcloud/compute/drivers/digitalocean.py | 54 +++++++++++++++++++- .../digitalocean_v2/create_volume_snapshot.json | 14 +++++ .../digitalocean_v2/list_volume_snapshots.json | 44 ++++++++++++++++ libcloud/test/compute/test_digitalocean_v2.py | 40 +++++++++++++++ 4 files changed, 151 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/8c5a4aff/libcloud/compute/drivers/digitalocean.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/digitalocean.py b/libcloud/compute/drivers/digitalocean.py index b195bae..7a676b6 100644 --- a/libcloud/compute/drivers/digitalocean.py +++ b/libcloud/compute/drivers/digitalocean.py @@ -27,7 +27,7 @@ from libcloud.common.types import InvalidCredsError from libcloud.compute.types import Provider, NodeState from libcloud.compute.base import NodeImage, NodeSize, NodeLocation, KeyPair from libcloud.compute.base import Node, NodeDriver -from libcloud.compute.base import StorageVolume +from libcloud.compute.base import StorageVolume, VolumeSnapshot __all__ = [ 'DigitalOceanNodeDriver', @@ -398,6 +398,49 @@ class DigitalOcean_v2_NodeDriver(DigitalOcean_v2_BaseDriver, return all([r.status == httplib.ACCEPTED for r in responses]) + def create_volume_snapshot(self, volume, name): + """ + Create a new volume snapshot. + + :param volume: Volume to create a snapshot for + :type volume: class:`StorageVolume` + + :return: The newly created volume snapshot. + :rtype: :class:`VolumeSnapshot` + """ + attr = {'name': name} + res = self.connection.request('/v2/volumes/%s/snapshots' % ( + volume.id), data=json.dumps(attr), method='POST') + data = res.object['snapshot'] + return self._to_volume_snapshot(data=data) + + def list_volume_snapshots(self, volume): + """ + List snapshots for a volume. + + :param volume: Volume to list snapshots for + :type volume: class:`StorageVolume` + + :return: List of volume snapshots. + :rtype: ``list`` of :class: `StorageVolume` + """ + data = self._paginated_request('/v2/volumes/%s/snapshots' % + (volume.id), 'snapshots') + return list(map(self._to_volume_snapshot, data)) + + def delete_volume_snapshot(self, snapshot): + """ + Delete a volume snapshot + + :param snapshot: volume snapshot to delete + :type snapshot: class:`VolumeSnapshot` + + :rtype: ``bool`` + """ + res = self.connection.request('v2/snapshots/%s' % (snapshot.id), + method='DELETE') + return res.status == httplib.NO_CONTENT + def _to_node(self, data): extra_keys = ['memory', 'vcpus', 'disk', 'region', 'image', 'size_slug', 'locked', 'created_at', 'networks', @@ -467,3 +510,12 @@ class DigitalOcean_v2_NodeDriver(DigitalOcean_v2_BaseDriver, private_key=None, driver=self, extra=extra) + + def _to_volume_snapshot(self, data): + extra = {'created_at': data['created_at'], + 'resource_id': data['resource_id'], + 'regions': data['regions'], + 'min_disk_size': data['min_disk_size']} + return VolumeSnapshot(id=data['id'], name=data['name'], + size=data['size_gigabytes'], + driver=self, extra=extra) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8c5a4aff/libcloud/test/compute/fixtures/digitalocean_v2/create_volume_snapshot.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/digitalocean_v2/create_volume_snapshot.json b/libcloud/test/compute/fixtures/digitalocean_v2/create_volume_snapshot.json new file mode 100644 index 0000000..ce4a691 --- /dev/null +++ b/libcloud/test/compute/fixtures/digitalocean_v2/create_volume_snapshot.json @@ -0,0 +1,14 @@ +{ + "snapshot": { + "id": "c0def940-9324-11e6-9a56-000f533176b1", + "name": "test-snapshot", + "regions": [ + "nyc1" + ], + "created_at": "2016-10-15T22:14:34Z", + "resource_id": "8371d4fe-92b0-11e6-9a56-000f533176b1", + "resource_type": "volume", + "min_disk_size": 1, + "size_gigabytes": 0 + } +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/8c5a4aff/libcloud/test/compute/fixtures/digitalocean_v2/list_volume_snapshots.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/digitalocean_v2/list_volume_snapshots.json b/libcloud/test/compute/fixtures/digitalocean_v2/list_volume_snapshots.json new file mode 100644 index 0000000..21ede3a --- /dev/null +++ b/libcloud/test/compute/fixtures/digitalocean_v2/list_volume_snapshots.json @@ -0,0 +1,44 @@ +{ + "snapshots": [ + { + "id": "c0def940-9324-11e6-9a56-000f533176b1", + "name": "test-snapshot", + "regions": [ + "nyc1" + ], + "created_at": "2016-10-15T22:14:34Z", + "resource_id": "8371d4fe-92b0-11e6-9a56-000f533176b1", + "resource_type": "volume", + "min_disk_size": 1, + "size_gigabytes": 0 + }, + { + "id": "c2036724-9343-11e6-aef4-000f53315a41", + "name": "test-snapshot-2", + "regions": [ + "nyc1" + ], + "created_at": "2016-10-16T01:56:30Z", + "resource_id": "8371d4fe-92b0-11e6-9a56-000f533176b1", + "resource_type": "volume", + "min_disk_size": 1, + "size_gigabytes": 0 + }, + { + "id": "d347e033-9343-11e6-9a56-000f533176b1", + "name": "test-snapshot-3", + "regions": [ + "nyc1" + ], + "created_at": "2016-10-16T01:56:59Z", + "resource_id": "8371d4fe-92b0-11e6-9a56-000f533176b1", + "resource_type": "volume", + "min_disk_size": 1, + "size_gigabytes": 0 + } + ], + "links": {}, + "meta": { + "total": 3 + } +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/8c5a4aff/libcloud/test/compute/test_digitalocean_v2.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_digitalocean_v2.py b/libcloud/test/compute/test_digitalocean_v2.py index a215e81..c7a916d 100644 --- a/libcloud/test/compute/test_digitalocean_v2.py +++ b/libcloud/test/compute/test_digitalocean_v2.py @@ -236,6 +236,30 @@ class DigitalOcean_v2_Tests(LibcloudTestCase): resp = self.driver.destroy_volume(volume) self.assertTrue(resp) + def test_list_volume_snapshots(self): + volume = self.driver.list_volumes()[0] + snapshots = self.driver.list_volume_snapshots(volume) + self.assertEqual(len(snapshots), 3) + snapshot1, snapshot2, snapshot3 = snapshots + self.assertEqual(snapshot1.id, "c0def940-9324-11e6-9a56-000f533176b1") + self.assertEqual(snapshot2.id, "c2036724-9343-11e6-aef4-000f53315a41") + self.assertEqual(snapshot3.id, "d347e033-9343-11e6-9a56-000f533176b1") + + def test_create_volume_snapshot(self): + volume = self.driver.list_volumes()[0] + DigitalOceanMockHttp.type = 'CREATE' + snapshot = self.driver.create_volume_snapshot(volume, 'test-snapshot') + self.assertEqual(snapshot.id, "c0def940-9324-11e6-9a56-000f533176b1") + self.assertEqual(snapshot.name, 'test-snapshot') + self.assertEqual(volume.driver, self.driver) + + def test_delete_volume_snapshot(self): + volume = self.driver.list_volumes()[0] + snapshot = self.driver.list_volume_snapshots(volume)[0] + DigitalOceanMockHttp.type = 'DELETE' + result = self.driver.delete_volume_snapshot(snapshot) + self.assertTrue(result) + class DigitalOceanMockHttp(MockHttpTestCase): fixtures = ComputeFileFixtures('digitalocean_v2') @@ -353,5 +377,21 @@ class DigitalOceanMockHttp(MockHttpTestCase): return (httplib.NO_CONTENT, None, {}, httplib.responses[httplib.NO_CONTENT]) + def _v2_volumes_62766883_2c28_11e6_b8e6_000f53306ae1_snapshots_CREATE( + self, method, url, body, headers): + body = self.fixtures.load('create_volume_snapshot.json') + return (httplib.CREATED, body, {}, httplib.responses[httplib.CREATED]) + + def _v2_volumes_62766883_2c28_11e6_b8e6_000f53306ae1_snapshots( + self, method, url, body, headers): + body = self.fixtures.load('list_volume_snapshots.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _v2_snapshots_c0def940_9324_11e6_9a56_000f533176b1_DELETE( + self, method, url, body, headers): + return (httplib.NO_CONTENT, None, {}, + httplib.responses[httplib.NO_CONTENT]) + + if __name__ == '__main__': sys.exit(unittest.main())
