* s3db, S3/SimpleDB.py: Implemented almost full SimpleDB API.
git-svn-id: https://s3tools.svn.sourceforge.net/svnroot/s3tools/s3cmd/trunk@179 830e0280-6d2a-0410-9c65-932aecc39d9d
... | ... |
@@ -28,6 +28,10 @@ class SimpleDB(object): |
28 | 28 |
def __init__(self, config): |
29 | 29 |
self.config = config |
30 | 30 |
|
31 |
+ ## ------------------------------------------------ |
|
32 |
+ ## Methods implementing SimpleDB API |
|
33 |
+ ## ------------------------------------------------ |
|
34 |
+ |
|
31 | 35 |
def ListDomains(self, MaxNumberOfDomains = 100): |
32 | 36 |
''' |
33 | 37 |
Lists all domains associated with our Access Key. Returns |
... | ... |
@@ -35,12 +39,79 @@ class SimpleDB(object): |
35 | 35 |
''' |
36 | 36 |
parameters = SortedDict() |
37 | 37 |
parameters['MaxNumberOfDomains'] = MaxNumberOfDomains |
38 |
- response = self.send_request("ListDomains", domain = None, parameters = parameters) |
|
39 |
- return response |
|
40 |
- |
|
38 |
+ return self.send_request("ListDomains", DomainName = None, parameters = parameters) |
|
39 |
+ |
|
40 |
+ def CreateDomain(self, DomainName): |
|
41 |
+ return self.send_request("CreateDomain", DomainName = DomainName) |
|
42 |
+ |
|
43 |
+ def DeleteDomain(self, DomainName): |
|
44 |
+ return self.send_request("DeleteDomain", DomainName = DomainName) |
|
45 |
+ |
|
46 |
+ def PutAttributes(self, DomainName, ItemName, Attributes): |
|
47 |
+ parameters = SortedDict() |
|
48 |
+ parameters['ItemName'] = ItemName |
|
49 |
+ seq = 0 |
|
50 |
+ for attrib in Attributes: |
|
51 |
+ if type(Attributes[attrib]) == type(list()): |
|
52 |
+ for value in Attributes[attrib]: |
|
53 |
+ parameters['Attribute.%d.Name' % seq] = attrib |
|
54 |
+ parameters['Attribute.%d.Value' % seq] = unicode(value) |
|
55 |
+ seq += 1 |
|
56 |
+ else: |
|
57 |
+ parameters['Attribute.%d.Name' % seq] = attrib |
|
58 |
+ parameters['Attribute.%d.Value' % seq] = unicode(Attributes[attrib]) |
|
59 |
+ seq += 1 |
|
60 |
+ ## TODO: |
|
61 |
+ ## - support for Attribute.N.Replace |
|
62 |
+ ## - support for multiple values for one attribute |
|
63 |
+ return self.send_request("PutAttributes", DomainName = DomainName, parameters = parameters) |
|
64 |
+ |
|
65 |
+ def GetAttributes(self, DomainName, ItemName, Attributes = []): |
|
66 |
+ parameters = SortedDict() |
|
67 |
+ parameters['ItemName'] = ItemName |
|
68 |
+ seq = 0 |
|
69 |
+ for attrib in Attributes: |
|
70 |
+ parameters['AttributeName.%d' % seq] = attrib |
|
71 |
+ seq += 1 |
|
72 |
+ return self.send_request("GetAttributes", DomainName = DomainName, parameters = parameters) |
|
73 |
+ |
|
74 |
+ def DeleteAttributes(self, DomainName, ItemName, Attributes = {}): |
|
75 |
+ """ |
|
76 |
+ Remove specified Attributes from ItemName. |
|
77 |
+ Attributes parameter can be either: |
|
78 |
+ - not specified, in which case the whole Item is removed |
|
79 |
+ - list, e.g. ['Attr1', 'Attr2'] in which case these parameters are removed |
|
80 |
+ - dict, e.g. {'Attr' : 'One', 'Attr' : 'Two'} in which case the |
|
81 |
+ specified values are removed from multi-value attributes. |
|
82 |
+ """ |
|
83 |
+ parameters = SortedDict() |
|
84 |
+ parameters['ItemName'] = ItemName |
|
85 |
+ seq = 0 |
|
86 |
+ for attrib in Attributes: |
|
87 |
+ parameters['Attribute.%d.Name' % seq] = attrib |
|
88 |
+ if type(Attributes) == type(dict()): |
|
89 |
+ parameters['Attribute.%d.Value' % seq] = unicode(Attributes[attrib]) |
|
90 |
+ seq += 1 |
|
91 |
+ return self.send_request("DeleteAttributes", DomainName = DomainName, parameters = parameters) |
|
92 |
+ |
|
93 |
+ def Query(self, DomainName, QueryExpression = None, MaxNumberOfItems = None, NextToken = None): |
|
94 |
+ parameters = SortedDict() |
|
95 |
+ if QueryExpression: |
|
96 |
+ parameters['QueryExpression'] = QueryExpression |
|
97 |
+ if MaxNumberOfItems: |
|
98 |
+ parameters['MaxNumberOfItems'] = MaxNumberOfItems |
|
99 |
+ if NextToken: |
|
100 |
+ parameters['NextToken'] = NextToken |
|
101 |
+ return self.send_request("Query", DomainName = DomainName, parameters = parameters) |
|
102 |
+ ## Handle NextToken? Or maybe not - let the upper level do it |
|
103 |
+ |
|
104 |
+ ## ------------------------------------------------ |
|
105 |
+ ## Low-level methods for handling SimpleDB requests |
|
106 |
+ ## ------------------------------------------------ |
|
107 |
+ |
|
41 | 108 |
def send_request(self, *args, **kwargs): |
42 | 109 |
request = self.create_request(*args, **kwargs) |
43 |
- debug("Request: %s" % repr(request)) |
|
110 |
+ #debug("Request: %s" % repr(request)) |
|
44 | 111 |
conn = self.get_connection() |
45 | 112 |
conn.request("GET", self.format_uri(request['uri_params'])) |
46 | 113 |
http_response = conn.getresponse() |
... | ... |
@@ -49,24 +120,24 @@ class SimpleDB(object): |
49 | 49 |
response["reason"] = http_response.reason |
50 | 50 |
response["headers"] = convertTupleListToDict(http_response.getheaders()) |
51 | 51 |
response["data"] = http_response.read() |
52 |
- debug("Response: " + str(response)) |
|
53 | 52 |
conn.close() |
54 | 53 |
|
55 | 54 |
if response["status"] < 200 or response["status"] > 299: |
55 |
+ debug("Response: " + str(response)) |
|
56 | 56 |
raise S3Error(response) |
57 | 57 |
|
58 | 58 |
return response |
59 | 59 |
|
60 |
- def create_request(self, action, domain, parameters = None): |
|
60 |
+ def create_request(self, Action, DomainName, parameters = None): |
|
61 | 61 |
if not parameters: |
62 | 62 |
parameters = SortedDict() |
63 | 63 |
parameters['AWSAccessKeyId'] = self.config.access_key |
64 | 64 |
parameters['Version'] = self.Version |
65 | 65 |
parameters['SignatureVersion'] = self.SignatureVersion |
66 |
- parameters['Action'] = action |
|
66 |
+ parameters['Action'] = Action |
|
67 | 67 |
parameters['Timestamp'] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) |
68 |
- if domain: |
|
69 |
- parameters['DomainName'] = domain |
|
68 |
+ if DomainName: |
|
69 |
+ parameters['DomainName'] = DomainName |
|
70 | 70 |
parameters['Signature'] = self.sign_request(parameters) |
71 | 71 |
parameters.keys_return_lowercase = False |
72 | 72 |
uri_params = urllib.urlencode(parameters) |
... | ... |
@@ -81,7 +152,7 @@ class SimpleDB(object): |
81 | 81 |
parameters.keys_return_lowercase = False |
82 | 82 |
for key in parameters: |
83 | 83 |
h += "%s%s" % (key, parameters[key]) |
84 |
- debug("SignRequest: %s" % h) |
|
84 |
+ #debug("SignRequest: %s" % h) |
|
85 | 85 |
return base64.encodestring(hmac.new(self.config.secret_key, h, sha).digest()).strip() |
86 | 86 |
|
87 | 87 |
def get_connection(self): |
... | ... |
@@ -98,5 +169,5 @@ class SimpleDB(object): |
98 | 98 |
uri = "http://%s/?%s" % (self.config.simpledb_host, uri_params) |
99 | 99 |
else: |
100 | 100 |
uri = "/?%s" % uri_params |
101 |
- debug('format_uri(): ' + uri) |
|
101 |
+ #debug('format_uri(): ' + uri) |
|
102 | 102 |
return uri |
... | ... |
@@ -1,5 +1,5 @@ |
1 | 1 |
#!/usr/bin/env python |
2 |
- |
|
2 |
+# vim: set fileencoding=utf-8 : |
|
3 | 3 |
## Amazon S3 manager |
4 | 4 |
## Author: Michal Ludvig <michal@logix.cz> |
5 | 5 |
## http://www.logix.cz/michal |
... | ... |
@@ -18,12 +18,36 @@ from S3.SimpleDB import SimpleDB |
18 | 18 |
from S3.Config import Config |
19 | 19 |
from S3.Exceptions import * |
20 | 20 |
|
21 |
- |
|
21 |
+def display_response(response): |
|
22 |
+ print "%s\n%s\n%s" % ('-'*40, response['data'], '-'*40) |
|
23 |
+ |
|
22 | 24 |
if __name__ == '__main__': |
23 | 25 |
if float("%d.%d" %(sys.version_info[0], sys.version_info[1])) < 2.4: |
24 | 26 |
sys.stderr.write("ERROR: Python 2.4 or higher required, sorry.\n") |
25 | 27 |
sys.exit(1) |
26 |
- logging.root.setLevel(logging.DEBUG) |
|
27 | 28 |
cfg = Config(os.getenv("HOME")+"/.s3cfg") |
29 |
+ |
|
30 |
+ logging.root.setLevel(logging.DEBUG) |
|
28 | 31 |
sdb = SimpleDB(cfg) |
29 |
- print sdb.ListDomains() |
|
32 |
+ |
|
33 |
+ try: |
|
34 |
+ display_response(sdb.ListDomains()) |
|
35 |
+ |
|
36 |
+ display_response(sdb.CreateDomain("logix.cz-test")) |
|
37 |
+ |
|
38 |
+ display_response(sdb.ListDomains()) |
|
39 |
+ |
|
40 |
+ display_response(sdb.PutAttributes("logix.cz-test", "AbCd", {'First': "One", "Second" : 2, "Third" : u"drei"})) |
|
41 |
+ display_response(sdb.PutAttributes("logix.cz-test", "XyZ", {'xyz' : ['x', 'y', 'z'], 'Third' : u'traja'})) |
|
42 |
+ |
|
43 |
+ display_response(sdb.GetAttributes("logix.cz-test", "AbCd", ['Second', 'Third'])) |
|
44 |
+ display_response(sdb.GetAttributes("logix.cz-test", "XyZ")) |
|
45 |
+ |
|
46 |
+ display_response(sdb.Query("logix.cz-test", "['xyz' = 'z']")) |
|
47 |
+ |
|
48 |
+ display_response(sdb.DeleteDomain("logix.cz-test")) |
|
49 |
+ |
|
50 |
+ display_response(sdb.ListDomains()) |
|
51 |
+ except S3Error, e: |
|
52 |
+ error(e) |
|
53 |
+ error(e.info) |