You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by an...@apache.org on 2016/01/05 11:19:29 UTC
[11/26] libcloud git commit: Prototype of an EBS driver for backup
and doing snapshots
Prototype of an EBS driver for backup and doing snapshots
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/de6452ad
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/de6452ad
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/de6452ad
Branch: refs/heads/trunk
Commit: de6452ad8ef520094c69e495e7e6ae07d7d686b2
Parents: 039d524
Author: Anthony Shaw <an...@gmail.com>
Authored: Tue Nov 10 12:19:08 2015 +1100
Committer: Anthony Shaw <an...@gmail.com>
Committed: Tue Nov 10 12:19:08 2015 +1100
----------------------------------------------------------------------
docs/backup/examples.rst | 2 +-
libcloud/backup/base.py | 8 +-
libcloud/backup/drivers/ebs.py | 386 ++++++++++++++++++++++++++++++++++++
libcloud/backup/types.py | 2 +
4 files changed, 393 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/de6452ad/docs/backup/examples.rst
----------------------------------------------------------------------
diff --git a/docs/backup/examples.rst b/docs/backup/examples.rst
index 2e71462..3bc5a9b 100644
--- a/docs/backup/examples.rst
+++ b/docs/backup/examples.rst
@@ -1,7 +1,7 @@
:orphan:
Backup Examples
-============
+===============
Getting a node from a compute driver and enabling backup
---------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/de6452ad/libcloud/backup/base.py
----------------------------------------------------------------------
diff --git a/libcloud/backup/base.py b/libcloud/backup/base.py
index b36dd6a..f57f5d8 100644
--- a/libcloud/backup/base.py
+++ b/libcloud/backup/base.py
@@ -355,10 +355,10 @@ class BackupDriver(BaseDriver):
:param end_date: The end date to show jobs between (optional)
:type end_date: :class:`datetime.datetime``
- :rtype: ``list`` of :class:`BackupTargetJob`
+ :rtype: ``list`` of :class:`BackupTargetRecoveryPoint`
"""
raise NotImplementedError(
- 'delete_target not implemented for this driver')
+ 'list_recovery_points not implemented for this driver')
def recover_target(self, target, recovery_point, path=None):
"""
@@ -376,7 +376,7 @@ class BackupDriver(BaseDriver):
:rtype: Instance of :class:`BackupTargetJob`
"""
raise NotImplementedError(
- 'delete_target not implemented for this driver')
+ 'recover_target not implemented for this driver')
def recover_target_out_of_place(self, target, recovery_point,
recovery_target, path=None):
@@ -398,7 +398,7 @@ class BackupDriver(BaseDriver):
:rtype: Instance of :class:`BackupTargetJob`
"""
raise NotImplementedError(
- 'delete_target not implemented for this driver')
+ 'recover_target_out_of_place not implemented for this driver')
def get_target_job(self, target, id):
"""
http://git-wip-us.apache.org/repos/asf/libcloud/blob/de6452ad/libcloud/backup/drivers/ebs.py
----------------------------------------------------------------------
diff --git a/libcloud/backup/drivers/ebs.py b/libcloud/backup/drivers/ebs.py
new file mode 100644
index 0000000..dbd2d4c
--- /dev/null
+++ b/libcloud/backup/drivers/ebs.py
@@ -0,0 +1,386 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+__all__ = [
+ 'EBSBackupDriver'
+]
+
+from datetime import datetime
+
+from libcloud.utils.xml import findtext, findall
+
+from libcloud.backup.base import BackupDriver, BackupTargetRecoveryPoint,\
+ BackupTargetJob, BackupTarget
+from libcloud.backup.types import BackupTargetType, BackupTargetJobStatusType
+from libcloud.common.aws import AWSGenericResponse, SignedAWSConnection
+
+
+VERSION = '2015-10-01'
+HOST = 'ec2.amazonaws.com'
+ROOT = '/%s/' % (VERSION)
+NS = 'http://ec2.amazonaws.com/doc/%s/' % (VERSION, )
+
+
+class EBSResponse(AWSGenericResponse):
+ """
+ Amazon EBS response class.
+ """
+ namespace = NS
+ exceptions = {}
+ xpath = 'Error'
+
+
+class EBSConnection(SignedAWSConnection):
+ version = VERSION
+ host = HOST
+ responseCls = EBSResponse
+ service_name = 'backup'
+
+
+class EBSBackupDriver(BackupDriver):
+ name = 'Amazon EBS Backup Driver'
+ website = 'http://aws.amazon.com/ebs/'
+ connectionCls = EBSConnection
+
+ def __init__(self, access_id, secret, region):
+ super(EBSBackupDriver, self).__init__(access_id, secret)
+ self.region = region
+ self.connection.host = HOST % (region)
+
+ def get_supported_target_types(self):
+ """
+ Get a list of backup target types this driver supports
+
+ :return: ``list`` of :class:``BackupTargetType``
+ """
+ return [BackupTargetType.VOLUME]
+
+ def list_targets(self):
+ """
+ List all backuptargets
+
+ :rtype: ``list`` of :class:`BackupTarget`
+ """
+ raise NotImplementedError(
+ 'list_targets not implemented for this driver')
+
+ def create_target(self, name, address,
+ type=BackupTargetType.VOLUME, extra=None):
+ """
+ Creates a new backup target
+
+ :param name: Name of the target
+ :type name: ``str``
+
+ :param address: The volume ID.
+ :type address: ``str``
+
+ :param type: Backup target type (Physical, Virtual, ...).
+ :type type: :class:`BackupTargetType`
+
+ :param extra: (optional) Extra attributes (driver specific).
+ :type extra: ``dict``
+
+ :rtype: Instance of :class:`BackupTarget`
+ """
+ # Does nothing since any volume can be snapped at anytime.
+ return self.ex_get_target_by_volume_id(address)
+
+ def create_target_from_node(self, node, type=BackupTargetType.VIRTUAL,
+ extra=None):
+ """
+ Creates a new backup target from an existing node
+
+ :param node: The Node to backup
+ :type node: ``Node``
+
+ :param type: Backup target type (Physical, Virtual, ...).
+ :type type: :class:`BackupTargetType`
+
+ :param extra: (optional) Extra attributes (driver specific).
+ :type extra: ``dict``
+
+ :rtype: Instance of :class:`BackupTarget`
+ """
+ return self.create_target(name=node.name,
+ address=node.public_ips[0],
+ type=BackupTargetType.VOLUME,
+ extra=None)
+
+ def create_target_from_container(self, container,
+ type=BackupTargetType.OBJECT,
+ extra=None):
+ """
+ Creates a new backup target from an existing storage container
+
+ :param node: The Container to backup
+ :type node: ``Container``
+
+ :param type: Backup target type (Physical, Virtual, ...).
+ :type type: :class:`BackupTargetType`
+
+ :param extra: (optional) Extra attributes (driver specific).
+ :type extra: ``dict``
+
+ :rtype: Instance of :class:`BackupTarget`
+ """
+ raise NotImplementedError(
+ 'create_target_from_container not implemented for this driver')
+
+ def update_target(self, target, name, address, extra):
+ """
+ Update the properties of a backup target
+
+ :param target: Backup target to update
+ :type target: Instance of :class:`BackupTarget`
+
+ :param name: Name of the target
+ :type name: ``str``
+
+ :param address: Hostname, FQDN, IP, file path etc.
+ :type address: ``str``
+
+ :param extra: (optional) Extra attributes (driver specific).
+ :type extra: ``dict``
+
+ :rtype: Instance of :class:`BackupTarget`
+ """
+ # Does nothing since any volume can be snapped at anytime.
+ return self.ex_get_target_by_volume_id(address)
+
+ def delete_target(self, target):
+ """
+ Delete a backup target
+
+ :param target: Backup target to delete
+ :type target: Instance of :class:`BackupTarget`
+ """
+ raise NotImplementedError(
+ 'delete_target not implemented for this driver')
+
+ def list_recovery_points(self, target, start_date=None, end_date=None):
+ """
+ List the recovery points available for a target
+
+ :param target: Backup target to delete
+ :type target: Instance of :class:`BackupTarget`
+
+ :param start_date: The start date to show jobs between (optional)
+ :type start_date: :class:`datetime.datetime`
+
+ :param end_date: The end date to show jobs between (optional)
+ :type end_date: :class:`datetime.datetime``
+
+ :rtype: ``list`` of :class:`BackupTargetRecoveryPoint`
+ """
+ params = {
+ 'Action': 'DescribeSnapshots',
+ 'Filter.1.Name': 'volume-id',
+ 'Filter.1.Value': target.extra['volume-id']
+ }
+ data = self.connection.request(ROOT, params=params).object
+ return self._to_recovery_points(data, target)
+
+ def recover_target(self, target, recovery_point, path=None):
+ """
+ Recover a backup target to a recovery point
+
+ :param target: Backup target to delete
+ :type target: Instance of :class:`BackupTarget`
+
+ :param recovery_point: Backup target with the backup data
+ :type recovery_point: Instance of :class:`BackupTarget`
+
+ :param path: The part of the recovery point to recover (optional)
+ :type path: ``str``
+
+ :rtype: Instance of :class:`BackupTargetJob`
+ """
+ raise NotImplementedError(
+ 'delete_target not implemented for this driver')
+
+ def recover_target_out_of_place(self, target, recovery_point,
+ recovery_target, path=None):
+ """
+ Recover a backup target to a recovery point out-of-place
+
+ :param target: Backup target with the backup data
+ :type target: Instance of :class:`BackupTarget`
+
+ :param recovery_point: Backup target with the backup data
+ :type recovery_point: Instance of :class:`BackupTarget`
+
+ :param recovery_target: Backup target with to recover the data to
+ :type recovery_target: Instance of :class:`BackupTarget`
+
+ :param path: The part of the recovery point to recover (optional)
+ :type path: ``str``
+
+ :rtype: Instance of :class:`BackupTargetJob`
+ """
+ raise NotImplementedError(
+ 'delete_target not implemented for this driver')
+
+ def get_target_job(self, target, id):
+ """
+ Get a specific backup job by ID
+
+ :param target: Backup target with the backup data
+ :type target: Instance of :class:`BackupTarget`
+
+ :param id: Backup target with the backup data
+ :type id: Instance of :class:`BackupTarget`
+
+ :rtype: :class:`BackupTargetJob`
+ """
+ jobs = self.list_target_jobs(target)
+ return list(filter(lambda x: x.id == id, jobs))[0]
+
+ def list_target_jobs(self, target):
+ """
+ List the backup jobs on a target
+
+ :param target: Backup target with the backup data
+ :type target: Instance of :class:`BackupTarget`
+
+ :rtype: ``list`` of :class:`BackupTargetJob`
+ """
+ params = {
+ 'Action': 'DescribeSnapshots',
+ 'Filter.1.Name': 'volume-id',
+ 'Filter.1.Value': target.extra['volume-id'],
+ 'Filter.2.Name': 'status',
+ 'Filter.2.Value': 'pending'
+ }
+ data = self.connection.request(ROOT, params=params).object
+ return self._to_jobs(data)
+
+ def create_target_job(self, target, extra=None):
+ """
+ Create a new backup job on a target
+
+ :param target: Backup target with the backup data
+ :type target: Instance of :class:`BackupTarget`
+
+ :param extra: (optional) Extra attributes (driver specific).
+ :type extra: ``dict``
+
+ :rtype: Instance of :class:`BackupTargetJob`
+ """
+ params = {
+ 'Action': 'DescribeSnapshots',
+ 'VolumeId': target.extra['volume-id']
+ }
+ data = self.connection.request(ROOT, params=params).object
+ return self._to_jobs(data)[0]
+
+ def resume_target_job(self, target, job):
+ """
+ Resume a suspended backup job on a target
+
+ :param target: Backup target with the backup data
+ :type target: Instance of :class:`BackupTarget`
+
+ :param job: Backup target job to resume
+ :type job: Instance of :class:`BackupTargetJob`
+
+ :rtype: ``bool``
+ """
+ raise NotImplementedError(
+ 'resume_target_job not implemented for this driver')
+
+ def suspend_target_job(self, target, job):
+ """
+ Suspend a running backup job on a target
+
+ :param target: Backup target with the backup data
+ :type target: Instance of :class:`BackupTarget`
+
+ :param job: Backup target job to suspend
+ :type job: Instance of :class:`BackupTargetJob`
+
+ :rtype: ``bool``
+ """
+ raise NotImplementedError(
+ 'suspend_target_job not implemented for this driver')
+
+ def cancel_target_job(self, target, job):
+ """
+ Cancel a backup job on a target
+
+ :param target: Backup target with the backup data
+ :type target: Instance of :class:`BackupTarget`
+
+ :param job: Backup target job to cancel
+ :type job: Instance of :class:`BackupTargetJob`
+
+ :rtype: ``bool``
+ """
+ raise NotImplementedError(
+ 'cancel_target_job not implemented for this driver')
+
+ def _to_recovery_points(self, data, target):
+ xpath = 'DescribeSnapshotsResponse/snapshotSet/item'
+ return [self._to_recovery_point(el, target)
+ for el in findall(element=data, xpath=xpath, namespace=NS)]
+
+ def _to_recovery_point(self, el, target):
+ id = findtext(element=el, xpath='snapshotId', namespace=NS)
+ date = datetime.strptime(
+ findtext(element=el, xpath='startTime', namespace=NS),
+ 'YYYY-MM-DDTHH:MM:SS.SSSZ')
+ point = BackupTargetRecoveryPoint(
+ id=id,
+ date=date,
+ target=target,
+ driver=self.connection.driver,
+ extra={
+ },
+ )
+ return point
+
+ def _to_jobs(self, data):
+ xpath = 'DescribeSnapshotsResponse/snapshotSet/item'
+ return [self._to_job(el)
+ for el in findall(element=data, xpath=xpath, namespace=NS)]
+
+ def _to_job(self, el):
+ id = findtext(element=el, xpath='snapshotId', namespace=NS)
+ progress = findtext(element=el, xpath='progress', namespace=NS)\
+ .replace('%', '')
+ volume_id = findtext(element=el, xpath='volumeId', namespace=NS)
+ target = self.ex_get_target_by_volume_id(volume_id)
+ job = BackupTargetJob(
+ id=id,
+ status=BackupTargetJobStatusType.PENDING,
+ progress=int(progress),
+ target=target,
+ driver=self.connection.driver,
+ extra={
+ },
+ )
+ return job
+
+ def ex_get_target_by_volume_id(self, volume_id):
+ return BackupTarget(
+ id=volume_id,
+ name=volume_id,
+ address=volume_id,
+ type=BackupTargetType.VOLUME,
+ driver=self.connection.driver,
+ extra={
+ "volume-id": volume_id
+ }
+ )
http://git-wip-us.apache.org/repos/asf/libcloud/blob/de6452ad/libcloud/backup/types.py
----------------------------------------------------------------------
diff --git a/libcloud/backup/types.py b/libcloud/backup/types.py
index 8f77d4c..96da228 100644
--- a/libcloud/backup/types.py
+++ b/libcloud/backup/types.py
@@ -44,6 +44,8 @@ class BackupTargetType(object):
OBJECT = 'Object'
""" Denotes an object based file system """
+ VOLUME = 'Volume'
+ """ Denotes a block storage volume """
class BackupTargetJobStatusType(object):
"""