Browse code

2008-04-29 Michal Ludvig <michal@logix.cz>

* s3db, S3/SimpleDB.py: Initial support for Amazon SimpleDB.
For now implements ListDomains() call and most of the
infrastructure required for request creation.



git-svn-id: https://s3tools.svn.sourceforge.net/svnroot/s3tools/s3cmd/trunk@178 830e0280-6d2a-0410-9c65-932aecc39d9d

Michal Ludvig authored on 2008/04/29 18:43:51
Showing 4 changed files
... ...
@@ -1,5 +1,11 @@
1 1
 2008-04-29  Michal Ludvig  <michal@logix.cz>
2 2
 
3
+	* s3db, S3/SimpleDB.py: Initial support for Amazon SimpleDB. 
4
+	  For now implements ListDomains() call and most of the 
5
+	  infrastructure required for request creation.
6
+
7
+2008-04-29  Michal Ludvig  <michal@logix.cz>
8
+
3 9
 	* S3/Exceptions.py: Exceptions moved out of S3.S3
4 10
 	* S3/SortedDict.py: rewritten from scratch to preserve
5 11
 	  case of keys while still sorting in case-ignore mode.
... ...
@@ -15,6 +15,7 @@ class Config(object):
15 15
 	secret_key = ""
16 16
 	host_base = "s3.amazonaws.com"
17 17
 	host_bucket = "%(bucket)s.s3.amazonaws.com"
18
+	simpledb_host = "sdb.amazonaws.com"
18 19
 	verbosity = logging.WARNING
19 20
 	send_chunk = 4096
20 21
 	recv_chunk = 4096
21 22
new file mode 100644
... ...
@@ -0,0 +1,102 @@
0
+## Amazon SimpleDB library
1
+## Author: Michal Ludvig <michal@logix.cz>
2
+##         http://www.logix.cz/michal
3
+## License: GPL Version 2
4
+
5
+"""
6
+Low-level class for working with Amazon SimpleDB
7
+"""
8
+
9
+import time
10
+import urllib
11
+import base64
12
+import hmac
13
+import sha
14
+import httplib
15
+from logging import debug, info, warning, error
16
+
17
+from Utils import convertTupleListToDict
18
+from SortedDict import SortedDict
19
+from Exceptions import *
20
+
21
+class SimpleDB(object):
22
+	# API Version
23
+	# See http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/
24
+	Version = "2007-11-07"
25
+	SignatureVersion = 1
26
+
27
+	def __init__(self, config):
28
+		self.config = config
29
+
30
+	def ListDomains(self, MaxNumberOfDomains = 100):
31
+		'''
32
+		Lists all domains associated with our Access Key. Returns 
33
+		domain names up to the limit set by MaxNumberOfDomains.
34
+		'''
35
+		parameters = SortedDict()
36
+		parameters['MaxNumberOfDomains'] = MaxNumberOfDomains
37
+		response = self.send_request("ListDomains", domain = None, parameters = parameters)
38
+		return response
39
+	
40
+	def send_request(self, *args, **kwargs):
41
+		request = self.create_request(*args, **kwargs)
42
+		debug("Request: %s" % repr(request))
43
+		conn = self.get_connection()
44
+		conn.request("GET", self.format_uri(request['uri_params']))
45
+		http_response = conn.getresponse()
46
+		response = {}
47
+		response["status"] = http_response.status
48
+		response["reason"] = http_response.reason
49
+		response["headers"] = convertTupleListToDict(http_response.getheaders())
50
+		response["data"] =  http_response.read()
51
+		debug("Response: " + str(response))
52
+		conn.close()
53
+
54
+		if response["status"] < 200 or response["status"] > 299:
55
+			raise S3Error(response)
56
+
57
+		return response
58
+
59
+	def create_request(self, action, domain, parameters = None):
60
+		if not parameters:
61
+			parameters = SortedDict()
62
+		parameters['AWSAccessKeyId'] = self.config.access_key
63
+		parameters['Version'] = self.Version
64
+		parameters['SignatureVersion'] = self.SignatureVersion
65
+		parameters['Action'] = action
66
+		parameters['Timestamp'] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
67
+		if domain:
68
+			parameters['DomainName'] = domain
69
+		parameters['Signature'] = self.sign_request(parameters)
70
+		parameters.keys_return_lowercase = False
71
+		uri_params = urllib.urlencode(parameters)
72
+		request = {}
73
+		request['uri_params'] = uri_params
74
+		request['parameters'] = parameters
75
+		return request
76
+
77
+	def sign_request(self, parameters):
78
+		h = ""
79
+		parameters.keys_sort_lowercase = True
80
+		parameters.keys_return_lowercase = False
81
+		for key in parameters:
82
+			h += "%s%s" % (key, parameters[key])
83
+		debug("SignRequest: %s" % h)
84
+		return base64.encodestring(hmac.new(self.config.secret_key, h, sha).digest()).strip()
85
+
86
+	def get_connection(self):
87
+		if self.config.proxy_host != "":
88
+			return httplib.HTTPConnection(self.config.proxy_host, self.config.proxy_port)
89
+		else:
90
+			if self.config.use_https:
91
+				return httplib.HTTPSConnection(self.config.simpledb_host)
92
+			else:
93
+				return httplib.HTTPConnection(self.config.simpledb_host)
94
+
95
+	def format_uri(self, uri_params):
96
+		if self.config.proxy_host != "":
97
+			uri = "http://%s/?%s" % (self.config.simpledb_host, uri_params)
98
+		else:
99
+			uri = "/?%s" % uri_params
100
+		debug('format_uri(): ' + uri)
101
+		return uri
0 102
new file mode 100755
... ...
@@ -0,0 +1,29 @@
0
+#!/usr/bin/env python
1
+
2
+## Amazon S3 manager
3
+## Author: Michal Ludvig <michal@logix.cz>
4
+##         http://www.logix.cz/michal
5
+## License: GPL Version 2
6
+
7
+import sys
8
+import os
9
+import logging
10
+
11
+from optparse import OptionParser, Option, OptionValueError, IndentedHelpFormatter
12
+from logging import debug, info, warning, error
13
+
14
+## Our modules
15
+from S3 import PkgInfo
16
+from S3.SimpleDB import SimpleDB
17
+from S3.Config import Config
18
+from S3.Exceptions import *
19
+
20
+
21
+if __name__ == '__main__':
22
+	if float("%d.%d" %(sys.version_info[0], sys.version_info[1])) < 2.4:
23
+		sys.stderr.write("ERROR: Python 2.4 or higher required, sorry.\n")
24
+		sys.exit(1)
25
+	logging.root.setLevel(logging.DEBUG)
26
+	cfg = Config(os.getenv("HOME")+"/.s3cfg")
27
+	sdb = SimpleDB(cfg)
28
+	print sdb.ListDomains()