Return-Path: X-Original-To: apmail-libcloud-notifications-archive@www.apache.org Delivered-To: apmail-libcloud-notifications-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id BD1D6173F1 for ; Fri, 27 Mar 2015 11:10:14 +0000 (UTC) Received: (qmail 3061 invoked by uid 500); 27 Mar 2015 11:10:05 -0000 Delivered-To: apmail-libcloud-notifications-archive@libcloud.apache.org Received: (qmail 3036 invoked by uid 500); 27 Mar 2015 11:10:05 -0000 Mailing-List: contact notifications-help@libcloud.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@libcloud.apache.org Delivered-To: mailing list notifications@libcloud.apache.org Received: (qmail 3027 invoked by uid 500); 27 Mar 2015 11:10:05 -0000 Delivered-To: apmail-libcloud-commits@libcloud.apache.org Received: (qmail 3024 invoked by uid 99); 27 Mar 2015 11:10:05 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 27 Mar 2015 11:10:05 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 1B8C9E17DB; Fri, 27 Mar 2015 11:10:05 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: allard@apache.org To: commits@libcloud.apache.org Message-Id: <6eb189b6263b4b92805f20122d9e3f1c@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: libcloud git commit: Add state to StorageVolume Date: Fri, 27 Mar 2015 11:10:05 +0000 (UTC) Repository: libcloud Updated Branches: refs/heads/trunk b81ede37f -> 7845e08a9 Add state to StorageVolume closes #476 Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/7845e08a Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/7845e08a Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/7845e08a Branch: refs/heads/trunk Commit: 7845e08a9d35f2c37c468c43511cf9677fb863b9 Parents: b81ede3 Author: Allard Hoeve Authored: Wed Mar 4 16:41:07 2015 +0100 Committer: Allard Hoeve Committed: Fri Mar 27 12:08:20 2015 +0100 ---------------------------------------------------------------------- CHANGES.rst | 12 ++++++++++ libcloud/compute/base.py | 12 ++++++++-- libcloud/compute/drivers/ec2.py | 20 ++++++++++++++++- libcloud/compute/drivers/ibm_sce.py | 2 +- libcloud/compute/drivers/openstack.py | 23 +++++++++++++++++++- libcloud/compute/types.py | 15 +++++++++++++ .../compute/fixtures/ec2/describe_volumes.xml | 6 ++--- .../fixtures/openstack_v1.1/_os_volumes.json | 2 +- libcloud/test/compute/test_base.py | 6 ++++- libcloud/test/compute/test_ec2.py | 15 +++++++++---- libcloud/test/compute/test_openstack.py | 7 ++++-- 11 files changed, 104 insertions(+), 16 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/7845e08a/CHANGES.rst ---------------------------------------------------------------------- diff --git a/CHANGES.rst b/CHANGES.rst index bd839d5..84e0c6b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,6 +20,18 @@ Compute (GITHUB-488, LIBCLOUD-682) [Greg Hill] +- StorageVolume objects now have an attribute `state` that holds a + state variable that is standardized state across drivers. Drivers that + currently support the `state` attribute are OpenStack and EC2. + StorageVolume objects returned by drivers that do not support the + attribute will have a `state` of `None`. When a provider returns a state + that is unknown to the driver, the state will be `UNKNOWN`. Please report + such states. A couple of drivers already put state fields in the `extra` + fields of StorageVolumes. These fields were kept for + backwards-compatibility and for reference. + (GITHUB-476) + [Allard Hoeve] + - StorageVolume objects on EC2 and OpenStack now have a key called snapshot_id in their extra dicts containing the snapshot ID the volume was based on. (GITHUB-479) http://git-wip-us.apache.org/repos/asf/libcloud/blob/7845e08a/libcloud/compute/base.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/base.py b/libcloud/compute/base.py index d0c8399..4cfcf46 100644 --- a/libcloud/compute/base.py +++ b/libcloud/compute/base.py @@ -30,7 +30,8 @@ from libcloud.utils.py3 import b import libcloud.compute.ssh from libcloud.pricing import get_size_price -from libcloud.compute.types import NodeState, DeploymentError +from libcloud.compute.types import NodeState, StorageVolumeState,\ + DeploymentError from libcloud.compute.ssh import SSHClient from libcloud.common.base import ConnectionKey from libcloud.common.base import BaseDriver @@ -68,6 +69,7 @@ __all__ = [ 'NodeDriver', 'StorageVolume', + 'StorageVolumeState', 'VolumeSnapshot', # Deprecated, moved to libcloud.utils.networking @@ -460,7 +462,8 @@ class StorageVolume(UuidMixin): A base StorageVolume class to derive from. """ - def __init__(self, id, name, size, driver, extra=None): + def __init__(self, id, name, size, driver, + state=None, extra=None): """ :param id: Storage volume ID. :type id: ``str`` @@ -474,6 +477,10 @@ class StorageVolume(UuidMixin): :param driver: Driver this image belongs to. :type driver: :class:`.NodeDriver` + :param state: Optional state of the StorageVolume. If not + provided, will default to UNKNOWN. + :type state: :class:`.StorageVolumeState` + :param extra: Optional provider specific attributes. :type extra: ``dict`` """ @@ -482,6 +489,7 @@ class StorageVolume(UuidMixin): self.size = size self.driver = driver self.extra = extra + self.state = state UuidMixin.__init__(self) def list_snapshots(self): http://git-wip-us.apache.org/repos/asf/libcloud/blob/7845e08a/libcloud/compute/drivers/ec2.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index b43aa88..67a4d52 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -41,7 +41,8 @@ from libcloud.compute.providers import Provider from libcloud.compute.base import Node, NodeDriver, NodeLocation, NodeSize from libcloud.compute.base import NodeImage, StorageVolume, VolumeSnapshot from libcloud.compute.base import KeyPair -from libcloud.compute.types import NodeState, KeyPairDoesNotExistError +from libcloud.compute.types import NodeState, KeyPairDoesNotExistError, \ + StorageVolumeState __all__ = [ 'API_VERSION', @@ -1996,6 +1997,17 @@ class BaseEC2NodeDriver(NodeDriver): 'terminated': NodeState.TERMINATED } + # http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_Volume.html + VOLUME_STATE_MAP = { + 'available': StorageVolumeState.AVAILABLE, + 'in-use': StorageVolumeState.INUSE, + 'error': StorageVolumeState.ERROR, + 'creating': StorageVolumeState.CREATING, + 'deleting': StorageVolumeState.DELETING, + 'deleted': StorageVolumeState.DELETED, + 'error_deleting': StorageVolumeState.ERROR + } + def list_nodes(self, ex_node_ids=None, ex_filters=None): """ List all nodes @@ -4699,6 +4711,11 @@ class BaseEC2NodeDriver(NodeDriver): volId = findtext(element=element, xpath='volumeId', namespace=NAMESPACE) size = findtext(element=element, xpath='size', namespace=NAMESPACE) + raw_state = findtext(element=element, xpath='status', + namespace=NAMESPACE) + + state = self.VOLUME_STATE_MAP.get(raw_state, + StorageVolumeState.UNKNOWN) # Get our tags tags = self._get_resource_tags(element) @@ -4717,6 +4734,7 @@ class BaseEC2NodeDriver(NodeDriver): name=name, size=int(size), driver=self, + state=state, extra=extra) def _to_snapshots(self, response): http://git-wip-us.apache.org/repos/asf/libcloud/blob/7845e08a/libcloud/compute/drivers/ibm_sce.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/ibm_sce.py b/libcloud/compute/drivers/ibm_sce.py index bcf73b9..66cbec6 100644 --- a/libcloud/compute/drivers/ibm_sce.py +++ b/libcloud/compute/drivers/ibm_sce.py @@ -711,7 +711,7 @@ class IBMNodeDriver(NodeDriver): object.findtext('Name'), object.findtext('Size'), self.connection.driver, - extra) + extra=extra) def _to_volume_offerings(self, object): return [self._to_volume_offering(iType) for iType in http://git-wip-us.apache.org/repos/asf/libcloud/blob/7845e08a/libcloud/compute/drivers/openstack.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py index df32d2b..8e0dc9d 100644 --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@ -45,7 +45,7 @@ from libcloud.compute.base import NodeSize, NodeImage from libcloud.compute.base import (NodeDriver, Node, NodeLocation, StorageVolume, VolumeSnapshot) from libcloud.compute.base import KeyPair -from libcloud.compute.types import NodeState, Provider +from libcloud.compute.types import NodeState, StorageVolumeState, Provider from libcloud.pricing import get_size_price from libcloud.utils.xml import findall @@ -104,6 +104,21 @@ class OpenStackNodeDriver(NodeDriver, OpenStackDriverMixin): 'UNKNOWN': NodeState.UNKNOWN } + # http://developer.openstack.org/api-ref-blockstorage-v2.html#volumes-v2 + VOLUME_STATE_MAP = { + 'creating': StorageVolumeState.CREATING, + 'available': StorageVolumeState.AVAILABLE, + 'attaching': StorageVolumeState.ATTACHING, + 'in-use': StorageVolumeState.INUSE, + 'deleting': StorageVolumeState.DELETING, + 'error': StorageVolumeState.ERROR, + 'error_deleting': StorageVolumeState.ERROR, + 'backing-up': StorageVolumeState.BACKUP, + 'restoring-backup': StorageVolumeState.BACKUP, + 'error_restoring': StorageVolumeState.ERROR, + 'error_extending': StorageVolumeState.ERROR, + } + def __new__(cls, key, secret=None, secure=True, host=None, port=None, api_version=DEFAULT_API_VERSION, **kwargs): if cls is OpenStackNodeDriver: @@ -2090,14 +2105,20 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): def _to_volume(self, api_node): if 'volume' in api_node: api_node = api_node['volume'] + + state = self.VOLUME_STATE_MAP.get(api_node['status'], + StorageVolumeState.UNKNOWN) + return StorageVolume( id=api_node['id'], name=api_node['displayName'], size=api_node['size'], + state=state, driver=self, extra={ 'description': api_node['displayDescription'], 'attachments': [att for att in api_node['attachments'] if att], + # TODO: remove in 1.18.0 'state': api_node.get('status', None), 'snapshot_id': api_node.get('snapshotId', None), 'location': api_node.get('availabilityZone', None), http://git-wip-us.apache.org/repos/asf/libcloud/blob/7845e08a/libcloud/compute/types.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py index 0aeaf4b..e59a397 100644 --- a/libcloud/compute/types.py +++ b/libcloud/compute/types.py @@ -223,6 +223,21 @@ class NodeState(object): return getattr(cls, value.upper(), None) +class StorageVolumeState(object): + """ + Standard states of a StorageVolume + """ + AVAILABLE = 0 + ERROR = 1 + INUSE = 2 + CREATING = 3 + DELETING = 4 + DELETED = 5 + BACKUP = 6 + ATTACHING = 7 + UNKNOWN = 8 + + class Architecture(object): """ Image and size architectures. http://git-wip-us.apache.org/repos/asf/libcloud/blob/7845e08a/libcloud/test/compute/fixtures/ec2/describe_volumes.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/ec2/describe_volumes.xml b/libcloud/test/compute/fixtures/ec2/describe_volumes.xml index 5dde8e4..f3ab603 100644 --- a/libcloud/test/compute/fixtures/ec2/describe_volumes.xml +++ b/libcloud/test/compute/fixtures/ec2/describe_volumes.xml @@ -15,7 +15,7 @@ 11 us-east-1c - available + in-use 2013-10-08T19:36:49.000Z @@ -24,7 +24,7 @@ 8 snap-30d37269 us-east-1d - in-use + some-unknown-status 2013-06-25T02:04:12.000Z @@ -39,4 +39,4 @@ standard - \ No newline at end of file + http://git-wip-us.apache.org/repos/asf/libcloud/blob/7845e08a/libcloud/test/compute/fixtures/openstack_v1.1/_os_volumes.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_os_volumes.json b/libcloud/test/compute/fixtures/openstack_v1.1/_os_volumes.json index b0c1a32..4218845 100644 --- a/libcloud/test/compute/fixtures/openstack_v1.1/_os_volumes.json +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_os_volumes.json @@ -32,7 +32,7 @@ "metadata": {}, "size": 50, "snapshotId": "01f48111-7866-4cd2-986a-e92683c4a363", - "status": "available", + "status": "some-unknown-state", "volumeType": "None" } ] http://git-wip-us.apache.org/repos/asf/libcloud/blob/7845e08a/libcloud/test/compute/test_base.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_base.py b/libcloud/test/compute/test_base.py index 84edf95..5173cf9 100644 --- a/libcloud/test/compute/test_base.py +++ b/libcloud/test/compute/test_base.py @@ -18,8 +18,9 @@ import unittest from libcloud.common.base import Response from libcloud.common.base import Connection, ConnectionKey, ConnectionUserAndKey from libcloud.common.types import LibcloudError -from libcloud.compute.base import Node, NodeSize, NodeImage, NodeDriver +from libcloud.compute.base import Node, NodeSize, NodeImage, NodeDriver, StorageVolume from libcloud.compute.base import NodeAuthSSHKey, NodeAuthPassword +from libcloud.compute.types import StorageVolumeState from libcloud.test import MockResponse # pylint: disable-msg=E0611 @@ -41,6 +42,9 @@ class BaseTests(unittest.TestCase): def test_base_node_image(self): NodeImage(id=0, name=0, driver=FakeDriver()) + def test_base_storage_volume(self): + StorageVolume(id="0", name="0", size=10, driver=FakeDriver(), state=StorageVolumeState.AVAILABLE) + def test_base_response(self): Response(MockResponse(status=200, body='foo'), ConnectionKey('foo')) http://git-wip-us.apache.org/repos/asf/libcloud/blob/7845e08a/libcloud/test/compute/test_ec2.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index f74fa94..9ad0a0b 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -39,7 +39,7 @@ from libcloud.compute.drivers.ec2 import ExEC2AvailabilityZone from libcloud.compute.drivers.ec2 import EC2NetworkSubnet from libcloud.compute.base import Node, NodeImage, NodeSize, NodeLocation from libcloud.compute.base import StorageVolume, VolumeSnapshot -from libcloud.compute.types import KeyPairDoesNotExistError +from libcloud.compute.types import KeyPairDoesNotExistError, StorageVolumeState from libcloud.test import MockHttpTestCase, LibcloudTestCase from libcloud.test.compute import TestCaseMixin @@ -772,18 +772,21 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual('vol-10ae5e2b', volumes[0].id) self.assertEqual(1, volumes[0].size) self.assertEqual('available', volumes[0].extra['state']) + self.assertEqual(StorageVolumeState.AVAILABLE, volumes[0].state) self.assertEqual('vol-v24bfh75', volumes[1].id) self.assertEqual(11, volumes[1].size) - self.assertEqual('available', volumes[1].extra['state']) self.assertIsNone(volumes[1].extra['snapshot_id']) + self.assertEqual('in-use', volumes[1].extra['state']) + self.assertEqual(StorageVolumeState.INUSE, volumes[1].state) self.assertEqual('vol-b6c851ec', volumes[2].id) self.assertEqual(8, volumes[2].size) - self.assertEqual('in-use', volumes[2].extra['state']) + self.assertEqual('some-unknown-status', volumes[2].extra['state']) self.assertEqual('i-d334b4b3', volumes[2].extra['instance_id']) self.assertEqual('/dev/sda1', volumes[2].extra['device']) self.assertEqual('snap-30d37269', volumes[2].extra['snapshot_id']) + self.assertEqual(StorageVolumeState.UNKNOWN, volumes[2].state) def test_create_volume(self): location = self.driver.list_locations()[0] @@ -796,6 +799,7 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): def test_destroy_volume(self): vol = StorageVolume(id='vol-4282672b', name='test', + state=StorageVolumeState.AVAILABLE, size=10, driver=self.driver) retValue = self.driver.destroy_volume(vol) @@ -803,7 +807,8 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): def test_attach(self): vol = StorageVolume(id='vol-4282672b', name='test', - size=10, driver=self.driver) + size=10, state=StorageVolumeState.AVAILABLE, + driver=self.driver) node = Node('i-4382922a', None, None, None, None, self.driver) retValue = self.driver.attach_volume(node, vol, '/dev/sdh') @@ -812,6 +817,7 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): def test_detach(self): vol = StorageVolume(id='vol-4282672b', name='test', + state=StorageVolumeState.INUSE, size=10, driver=self.driver) retValue = self.driver.detach_volume(vol) @@ -819,6 +825,7 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): def test_create_volume_snapshot(self): vol = StorageVolume(id='vol-4282672b', name='test', + state=StorageVolumeState.AVAILABLE, size=10, driver=self.driver) snap = self.driver.create_volume_snapshot( vol, 'Test snapshot') http://git-wip-us.apache.org/repos/asf/libcloud/blob/7845e08a/libcloud/test/compute/test_openstack.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index f3524ec..08b2c20 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -34,7 +34,7 @@ from libcloud.utils.py3 import u from libcloud.common.types import InvalidCredsError, MalformedResponseError, \ LibcloudError -from libcloud.compute.types import Provider, KeyPairDoesNotExistError +from libcloud.compute.types import Provider, KeyPairDoesNotExistError, StorageVolumeState from libcloud.compute.providers import get_driver from libcloud.compute.drivers.openstack import ( OpenStack_1_0_NodeDriver, OpenStack_1_0_Response, @@ -794,6 +794,7 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): self.assertEqual('cd76a3a1-c4ce-40f6-9b9f-07a61508938d', volume.id) self.assertEqual('test_volume_2', volume.name) + self.assertEqual(StorageVolumeState.AVAILABLE, volume.state) self.assertEqual(2, volume.size) self.assertEqual(volume.extra, { 'description': '', @@ -811,15 +812,17 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): 'created_at': '2013-06-24T11:20:13.000000', }) + # also test that unknown state resolves to StorageVolumeState.UNKNOWN volume = volumes[1] self.assertEqual('cfcec3bc-b736-4db5-9535-4c24112691b5', volume.id) self.assertEqual('test_volume', volume.name) self.assertEqual(50, volume.size) + self.assertEqual(StorageVolumeState.UNKNOWN, volume.state) self.assertEqual(volume.extra, { 'description': 'some description', 'attachments': [], 'snapshot_id': '01f48111-7866-4cd2-986a-e92683c4a363', - 'state': 'available', + 'state': 'some-unknown-state', 'location': 'nova', 'volume_type': 'None', 'metadata': {},