Author: tomaz
Date: Thu Oct 6 09:28:31 2011
New Revision: 1179549
URL: http://svn.apache.org/viewvc?rev=1179549&view=rev
Log:
Add Google Storage storage driver.
Added:
libcloud/trunk/libcloud/storage/drivers/google_storage.py
Modified:
libcloud/trunk/libcloud/storage/providers.py
libcloud/trunk/libcloud/storage/types.py
Added: libcloud/trunk/libcloud/storage/drivers/google_storage.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/storage/drivers/google_storage.py?rev=1179549&view=auto
==============================================================================
--- libcloud/trunk/libcloud/storage/drivers/google_storage.py (added)
+++ libcloud/trunk/libcloud/storage/drivers/google_storage.py Thu Oct 6
09:28:31 2011
@@ -0,0 +1,134 @@
+# 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.
+
+import time
+import httplib
+import urllib
+import copy
+import base64
+import hmac
+
+from hashlib import sha1
+from email.utils import formatdate
+
+from libcloud.common.base import ConnectionUserAndKey
+
+from libcloud.storage.drivers.s3 import S3StorageDriver, S3Response
+from libcloud.storage.drivers.s3 import S3RawResponse
+
+SIGNATURE_IDENTIFIER = 'GOOG1'
+
+# Docs are a lie. Actual namespace returned is different that the one listed in
+# the docs.
+AUTH_HOST = 'commondatastorage.googleapis.com'
+API_VERSION = '2006-03-01'
+NAMESPACE = 'http://doc.s3.amazonaws.com/%s' % (API_VERSION)
+
+
+class GoogleStorageConnection(ConnectionUserAndKey):
+ """
+ Repersents a single connection to the Google storage API endpoint.
+ """
+
+ host = AUTH_HOST
+ responseCls = S3Response
+ rawResponseCls = S3RawResponse
+
+ def add_default_headers(self, headers):
+ date = formatdate(usegmt=True)
+ headers['Date'] = date
+ return headers
+
+ def pre_connect_hook(self, params, headers):
+ signature = self._get_aws_auth_param(method=self.method,
+ headers=headers,
+ params=params,
+ expires=None,
+ secret_key=self.key,
+ path=self.action)
+ headers['Authorization'] = '%s %s:%s' % (SIGNATURE_IDENTIFIER,
+ self.user_id, signature)
+ return params, headers
+
+ def _get_aws_auth_param(self, method, headers, params, expires,
+ secret_key, path='/'):
+ # TODO: Refactor and re-use in S3 driver
+ """
+ Signature = URL-Encode( Base64( HMAC-SHA1( YourSecretAccessKeyID,
+ UTF-8-Encoding-Of( StringToSign ) ) ) );
+
+ StringToSign = HTTP-VERB + "\n" +
+ Content-MD5 + "\n" +
+ Content-Type + "\n" +
+ Date + "\n" +
+ CanonicalizedHeaders +
+ CanonicalizedResource;
+ """
+ special_header_keys = ['content-md5', 'content-type', 'date']
+ special_header_values = {}
+ extension_header_values = {}
+
+ headers_copy = copy.deepcopy(headers)
+ for key, value in headers_copy.iteritems():
+ if key.lower() in special_header_keys:
+ if key.lower() == 'date':
+ value = value.strip()
+ else:
+ value = value.lower().strip()
+ special_header_values[key.lower()] = value
+ elif key.lower().startswith('x-goog-'):
+ extension_header_values[key.lower()] = value.strip()
+
+ if not 'content-md5' in special_header_values:
+ special_header_values['content-md5'] = ''
+
+ if not 'content-type' in special_header_values:
+ special_header_values['content-type'] = ''
+
+ keys_sorted = special_header_values.keys()
+ keys_sorted.sort()
+
+ buf = [method]
+ for key in keys_sorted:
+ value = special_header_values[key]
+ buf.append(value)
+ string_to_sign = '\n'.join(buf)
+
+ keys_sorted = extension_header_values.keys()
+ keys_sorted.sort()
+
+ extension_header_string = []
+ for key in keys_sorted:
+ value = extension_header_values[key]
+ extension_header_string.append('%s:%s' % (key, value))
+ extension_header_string = '\n'.join(extension_header_string)
+
+ values_to_sign = []
+ for value in [string_to_sign, extension_header_string, path]:
+ if value:
+ values_to_sign.append(value)
+
+ string_to_sign = '\n'.join(values_to_sign)
+ b64_hmac = base64.b64encode(
+ hmac.new(secret_key, string_to_sign, digestmod=sha1).digest()
+ )
+ return b64_hmac
+
+
+class GoogleStorageDriver(S3StorageDriver):
+ name = 'Google Storage'
+ connectionCls = GoogleStorageConnection
+ hash_type = 'md5'
+ namespace = NAMESPACE
Modified: libcloud/trunk/libcloud/storage/providers.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/storage/providers.py?rev=1179549&r1=1179548&r2=1179549&view=diff
==============================================================================
--- libcloud/trunk/libcloud/storage/providers.py (original)
+++ libcloud/trunk/libcloud/storage/providers.py Thu Oct 6 09:28:31 2011
@@ -35,6 +35,8 @@ DRIVERS = {
('libcloud.storage.drivers.s3', 'S3APNEStorageDriver'),
Provider.NINEFOLD:
('libcloud.storage.drivers.ninefold', 'NinefoldStorageDriver'),
+ Provider.GOOGLE_STORAGE:
+ ('libcloud.storage.drivers.google_storage', 'GoogleStorageDriver')
}
def get_driver(provider):
Modified: libcloud/trunk/libcloud/storage/types.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/storage/types.py?rev=1179549&r1=1179548&r2=1179549&view=diff
==============================================================================
--- libcloud/trunk/libcloud/storage/types.py (original)
+++ libcloud/trunk/libcloud/storage/types.py Thu Oct 6 09:28:31 2011
@@ -38,6 +38,7 @@ class Provider(object):
@cvar S3_AP_SOUTHEAST_HOST: Amazon S3 Asia South East (Singapore)
@cvar S3_AP_NORTHEAST_HOST: Amazon S3 Asia South East (Tokyo)
@cvar NINEFOLD: Ninefold
+ @cvar GOOGLE_STORAGE Google Storage
"""
DUMMY = 0
CLOUDFILES_US = 1
@@ -48,6 +49,7 @@ class Provider(object):
S3_AP_SOUTHEAST = 6
S3_AP_NORTHEAST = 7
NINEFOLD = 8
+ GOOGLE_STORAGE = 9
class ContainerError(LibcloudError):
error_type = 'ContainerError'