Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,34 @@ 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)
[Allard Hoeve]

- OpenStack driver: deprecated ex_create_snapshot and ex_delete_snapshot in
favor of create_volume_snapshot and destroy_volume_snapshot. Updated base
driver method create_storage_volume argument name to be optional.
(GITHUB-478)
[Allard Hoeve]

- Add support for creating volumes based on snapshots to EC2 and OS drivers.
Also modify signature of base NodeDriver.create_volume to reflect the fact
that all drivers expect a StorageSnapshot object as the snapshot argument.
(GITHUB-467, LIBCLOUD-672)
[Allard Hoeve]

- VolumeSnapshots now have a `created` attribute that is a `datetime`
field showing the creation datetime of the snapshot. The field in
Expand Down
10 changes: 10 additions & 0 deletions docs/upgrade_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ Development
VolumeSnapshot.extra containing the original string is maintained, so
this is a backwards-compatible change.

* The OpenStack compute driver methods ex_create_snapshot and
ex_delete_snapshot are now deprecated by the standard methods
create_volume_snapshot and destroy_volume_snapshot. You should update your
code.

* The compute base driver now considers the name argument to
create_volume_snapshot to be optional. All official implementations of this
methods already considered it optional. You should update any custom
drivers if they rely on the name being mandatory.

Libcloud 0.16.0
---------------

Expand Down
23 changes: 20 additions & 3 deletions libcloud/compute/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@

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
Expand Down Expand Up @@ -68,6 +69,7 @@
'NodeDriver',

'StorageVolume',
'StorageVolumeState',
'VolumeSnapshot',

# Deprecated, moved to libcloud.utils.networking
Expand Down Expand Up @@ -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``
Expand All @@ -474,6 +477,10 @@ def __init__(self, id, name, size, driver, extra=None):
: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``
"""
Expand All @@ -482,6 +489,7 @@ def __init__(self, id, name, size, driver, extra=None):
self.size = size
self.driver = driver
self.extra = extra
self.state = state
UuidMixin.__init__(self)

def list_snapshots(self):
Expand Down Expand Up @@ -1016,10 +1024,16 @@ def create_volume(self, size, name, location=None, snapshot=None):
raise NotImplementedError(
'create_volume not implemented for this driver')

def create_volume_snapshot(self, volume, name):
def create_volume_snapshot(self, volume, name=None):
"""
Creates a snapshot of the storage volume.

:param volume: The StorageVolume to create a VolumeSnapshot from
:type volume: :class:`.VolumeSnapshot`

:param name: Name of created snapshot (optional)
:type name: `str`

:rtype: :class:`VolumeSnapshot`
"""
raise NotImplementedError(
Expand Down Expand Up @@ -1071,6 +1085,9 @@ def destroy_volume_snapshot(self, snapshot):
"""
Destroys a snapshot.

:param snapshot: The snapshot to delete
:type snapshot: :class:`VolumeSnapshot`

:rtype: :class:`bool`
"""
raise NotImplementedError(
Expand Down
14 changes: 5 additions & 9 deletions libcloud/compute/drivers/cloudstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -3577,13 +3577,17 @@ def list_snapshots(self):
list_snapshots.append(self._to_snapshot(snap))
return list_snapshots

def create_volume_snapshot(self, volume):
def create_volume_snapshot(self, volume, name=None):
"""
Create snapshot from volume

:param volume: Instance of ``StorageVolume``
:type volume: ``StorageVolume``

:param name: The name of the snapshot is disregarded
by CloudStack drivers
:type name: `str`

:rtype: :class:`VolumeSnapshot`
"""
snapshot = self._async_request(command='createSnapshot',
Expand All @@ -3592,14 +3596,6 @@ def create_volume_snapshot(self, volume):
return self._to_snapshot(snapshot['snapshot'])

def destroy_volume_snapshot(self, snapshot):
"""
Destroy snapshot

:param snapshot: Instance of ``VolumeSnapshot``
:type volume: ``VolumeSnapshot``

:rtype: ``bool``
"""
self._async_request(command='deleteSnapshot',
params={'id': snapshot.id},
method='GET')
Expand Down
25 changes: 22 additions & 3 deletions libcloud/compute/drivers/ec2.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
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',
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -2422,7 +2434,7 @@ def create_volume_snapshot(self, volume, name=None):
:param volume: Instance of ``StorageVolume``
:type volume: ``StorageVolume``

:param name: Name of snapshot
:param name: Name of snapshot (optional)
:type name: ``str``

:rtype: :class:`VolumeSnapshot`
Expand Down Expand Up @@ -3445,7 +3457,8 @@ def ex_create_tags(self, resource, tags):
Create tags for a resource (Node or StorageVolume).

:param resource: Resource to be tagged
:type resource: :class:`Node` or :class:`StorageVolume`
:type resource: :class:`Node` or :class:`StorageVolume` or
:class:`VolumeSnapshot`

:param tags: A dictionary or other mapping of strings to strings,
associating tag names with tag values.
Expand Down Expand Up @@ -4698,6 +4711,11 @@ def _to_volume(self, element, name=None):
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)
Expand All @@ -4716,6 +4734,7 @@ def _to_volume(self, element, name=None):
name=name,
size=int(size),
driver=self,
state=state,
extra=extra)

def _to_snapshots(self, response):
Expand Down
2 changes: 1 addition & 1 deletion libcloud/compute/drivers/ibm_sce.py
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ def _to_volume(self, object):
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
Expand Down
77 changes: 65 additions & 12 deletions libcloud/compute/drivers/openstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
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

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -1615,6 +1630,41 @@ def list_volume_snapshots(self, volume):
return [snapshot for snapshot in self.ex_list_snapshots()
if snapshot.extra['volume_id'] == volume.id]

def create_volume_snapshot(self, volume, name=None, ex_description=None,
ex_force=True):
"""
Create snapshot from volume

:param volume: Instance of `StorageVolume`
:type volume: `StorageVolume`

:param name: Name of snapshot (optional)
:type name: `str`

:param ex_description: Description of the snapshot (optional)
:type ex_description: `str`

:param ex_force: Specifies if we create a snapshot that is not in
state `available`. For example `in-use`. Defaults
to True. (optional)
:type ex_force: `bool`

:rtype: :class:`VolumeSnapshot`
"""
data = {'snapshot': {'display_name': name,
'display_description': ex_description,
'volume_id': volume.id,
'force': ex_force}}

return self._to_snapshot(self.connection.request('/os-snapshots',
method='POST',
data=data).object)

def destroy_volume_snapshot(self, snapshot):
resp = self.connection.request('/os-snapshots/%s' % snapshot.id,
method='DELETE')
return resp.status == httplib.NO_CONTENT

def ex_create_snapshot(self, volume, name, description=None, force=False):
"""
Create a snapshot based off of a volume.
Expand All @@ -1633,14 +1683,11 @@ def ex_create_snapshot(self, volume, name, description=None, force=False):

:rtype: :class:`VolumeSnapshot`
"""
data = {'snapshot': {'display_name': name,
'display_description': description,
'volume_id': volume.id,
'force': force}}

return self._to_snapshot(self.connection.request('/os-snapshots',
method='POST',
data=data).object)
warnings.warn('This method has been deprecated in favor of the '
'create_volume_snapshot method')
return self.create_volume_snapshot(volume, name,
ex_description=description,
ex_force=force)

def ex_delete_snapshot(self, snapshot):
"""
Expand All @@ -1651,9 +1698,9 @@ def ex_delete_snapshot(self, snapshot):

:rtype: ``bool``
"""
resp = self.connection.request('/os-snapshots/%s' % snapshot.id,
method='DELETE')
return resp.status == httplib.NO_CONTENT
warnings.warn('This method has been deprecated in favor of the '
'destroy_volume_snapshot method')
return self.destroy_volume_snapshot(snapshot)

def _to_security_group_rules(self, obj):
return [self._to_security_group_rule(security_group_rule) for
Expand Down Expand Up @@ -2058,14 +2105,20 @@ def _to_node(self, api_node):
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),
Expand Down
15 changes: 15 additions & 0 deletions libcloud/compute/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,21 @@ def fromstring(cls, value):
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.
Expand Down
Loading