From a5aa8d3cbda01617366a3f78a86c3f2050792d81 Mon Sep 17 00:00:00 2001 From: Thurloat Date: Thu, 30 Oct 2014 22:01:41 -0300 Subject: [PATCH] Add backend that supports container tokens To the moon! initial commit --- README => README.md | 50 ++++++++++---- duplicity/backends/swiftkeysbackend.py | 96 ++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 13 deletions(-) rename README => README.md (65%) create mode 100644 duplicity/backends/swiftkeysbackend.py diff --git a/README b/README.md similarity index 65% rename from README rename to README.md index 59dc290..32e249e 100644 --- a/README +++ b/README.md @@ -1,23 +1,42 @@ -INSTALLATION: +# Notes -Thank you for trying duplicity. To install, run: +This is a fork of duplicity, which adds a `swiftkeys://` storage backend +that supports Swift Container Keys +(https://www.clouda.ca/blog/tech/dash/bulk-storage-container-api-keys/). For +more the original project information, check the announcement blog post +(https://www.clouda.ca/blog/general/using-bulk-storage-container-keys-with-duplicity/) + +# Installation - python setup.py install +If you have not downloaded it yet, you can grab the latest stable version from +the repo archive page, and continue using the regular install steps below. -The build process can be also be run separately: +``` + wget https://bitbucket.org/clouda/duplicity/get/321.tar.gz + tar -zxvf 321.tar.gz + cd duplicity-swiftkeys-xxx/ +``` + +Thank you for trying duplicity. To install, run: - python setup.py build +``` + pip install python-swiftclient lockfile + python setup.py install +``` -The default prefix is /usr, so files are put in /usr/bin, -/usr/share/man/, etc. An alternate prefix can be specified using the ---prefix= option. For example: +# Usage - python setup.py install --prefix=/usr/local - export PYTHONPATH='/usr/local/lib/python2.x/site-packages/' - /usr/local/bin/duplicity -V +``` + export SWIFT_STORAGE_URL="https:///v1/AUTH_" + export SWIFT_CONTAINER_FULL_TOKEN="" + duplicity swiftkeys:// +``` +You can get your tenant\_id filled bulk storage URL from the Dashboard under +API Access and listed as Object Store. -REQUIREMENTS: + +# Requirements * Python v2.6 or later * librsync v0.9.6 or later @@ -62,7 +81,7 @@ the class to gpginterface in the hope that package maintainers will stumble over it and stop this problematic behaviour for good. -HELP: +# Help For more information see the duplicity home page at: @@ -71,3 +90,8 @@ For more information see the duplicity home page at: or post to the mailing list at http://mail.nongnu.org/mailman/listinfo/duplicity-talk/ + +If there is an issue with the storage backend, you can log a bug on the +project at + + https://github.com/cloudbrewery/duplicity-swiftkeys/issues diff --git a/duplicity/backends/swiftkeysbackend.py b/duplicity/backends/swiftkeysbackend.py new file mode 100644 index 0000000..b022691 --- /dev/null +++ b/duplicity/backends/swiftkeysbackend.py @@ -0,0 +1,96 @@ +# Copyright 2015 (c) Cloud Brewery Inc. +# +# This file is part of duplicity. +# +# Duplicity is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# Duplicity is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with duplicity; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import os + +import duplicity.backend +from duplicity import log +from duplicity.errors import BackendException + +import swiftclient.client +from swiftclient.client import HTTPConnection as _HTTPConnection + + +class ContainerKeyHTTPConnection(_HTTPConnection): + + def _request(self, *args, **kwargs): + kwargs['headers']['X-Container-Meta-Full-Key'] = \ + os.environ['SWIFT_CONTAINER_FULL_TOKEN'] + kwargs['headers'].pop('X-Auth-Token', None) + kwargs['headers'].pop('x-auth-token', None) + return _HTTPConnection._request(self, *args, **kwargs) + + +swiftclient.client.HTTPConnection = ContainerKeyHTTPConnection + + +class SwiftKeysBackend(duplicity.backend.Backend): + """ + A Backend for Swift using Container Keys + """ + def __init__(self, parsed_url): + + from swiftclient import Connection, ClientException + + self.resp_exc = ClientException + conn_kwargs = {} + if 'SWIFT_CONTAINER_FULL_TOKEN' not in os.environ: + raise BackendException('SWIFT_CONTAINER_FULL_TOKEN environment ' + 'variable not set.') + if 'SWIFT_STORAGE_URL' not in os.environ: + raise BackendException('SWIFT_STORAGE_URL environment variable ' + 'not set.') + + self.container_full_token = os.environ['SWIFT_CONTAINER_FULL_TOKEN'] + self.swift_storage_url = os.environ['SWIFT_STORAGE_URL'] + + conn_kwargs['preauthurl'] = self.swift_storage_url + conn_kwargs['preauthtoken'] = 'discarded' + + self.container = parsed_url.path.lstrip('/') + + self.conn = Connection(**conn_kwargs) + + def _error_code(self, operation, e): + if isinstance(e, self.resp_exc): + if e.http_status == 404: + return log.ErrorCode.backend_not_found + + def _put(self, source_path, remote_filename): + self.conn.put_object(self.container, remote_filename, + file(source_path.name)) + + def _get(self, remote_filename, local_path): + headers, body = self.conn.get_object(self.container, remote_filename) + with open(local_path.name, 'wb') as f: + for chunk in body: + f.write(chunk) + + def _list(self): + headers, objs = self.conn.get_container(self.container) + return [o['name'] for o in objs] + + def _delete(self, filename): + self.conn.delete_object(self.container, filename) + + def _query(self, filename): + sobject = self.conn.head_object(self.container, filename) + return {'size': int(sobject['content-length'])} + + +duplicity.backend.register_backend("swiftkeys", SwiftKeysBackend)