~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/boto/boto/sdb/connection.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/
 
2
#
 
3
# Permission is hereby granted, free of charge, to any person obtaining a
 
4
# copy of this software and associated documentation files (the
 
5
# "Software"), to deal in the Software without restriction, including
 
6
# without limitation the rights to use, copy, modify, merge, publish, dis-
 
7
# tribute, sublicense, and/or sell copies of the Software, and to permit
 
8
# persons to whom the Software is furnished to do so, subject to the fol-
 
9
# lowing conditions:
 
10
#
 
11
# The above copyright notice and this permission notice shall be included
 
12
# in all copies or substantial portions of the Software.
 
13
#
 
14
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 
15
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
 
16
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
 
17
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 
18
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
19
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 
20
# IN THE SOFTWARE.
 
21
 
 
22
import xml.sax
 
23
import threading
 
24
from boto import handler
 
25
from boto.connection import AWSQueryConnection
 
26
from boto.sdb.domain import Domain, DomainMetaData
 
27
from boto.sdb.item import Item
 
28
from boto.sdb.regioninfo import SDBRegionInfo
 
29
from boto.exception import SDBResponseError
 
30
from boto.resultset import ResultSet
 
31
import warnings
 
32
 
 
33
 
 
34
class ItemThread(threading.Thread):
 
35
    
 
36
    def __init__(self, name, domain_name, item_names):
 
37
        threading.Thread.__init__(self, name=name)
 
38
        print 'starting %s with %d items' % (name, len(item_names))
 
39
        self.domain_name = domain_name
 
40
        self.conn = SDBConnection()
 
41
        self.item_names = item_names
 
42
        self.items = []
 
43
        
 
44
    def run(self):
 
45
        for item_name in self.item_names:
 
46
            item = self.conn.get_attributes(self.domain_name, item_name)
 
47
            self.items.append(item)
 
48
 
 
49
#boto.set_stream_logger('sdb')
 
50
 
 
51
class SDBConnection(AWSQueryConnection):
 
52
 
 
53
    DefaultRegionName = 'us-east-1'
 
54
    DefaultRegionEndpoint = 'sdb.amazonaws.com'
 
55
    APIVersion = '2009-04-15'
 
56
    SignatureVersion = '2'
 
57
    ResponseError = SDBResponseError
 
58
 
 
59
    def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
 
60
                 is_secure=True, port=None, proxy=None, proxy_port=None,
 
61
                 proxy_user=None, proxy_pass=None, debug=0,
 
62
                 https_connection_factory=None, region=None, path='/', converter=None):
 
63
        if not region:
 
64
            region = SDBRegionInfo(self, self.DefaultRegionName, self.DefaultRegionEndpoint)
 
65
        self.region = region
 
66
        AWSQueryConnection.__init__(self, aws_access_key_id, aws_secret_access_key,
 
67
                                    is_secure, port, proxy, proxy_port, proxy_user, proxy_pass,
 
68
                                    self.region.endpoint, debug, https_connection_factory, path)
 
69
        self.box_usage = 0.0
 
70
        self.converter = converter
 
71
        self.item_cls = Item
 
72
 
 
73
    def set_item_cls(self, cls):
 
74
        self.item_cls = cls
 
75
 
 
76
    def build_name_value_list(self, params, attributes, replace=False,
 
77
                              label='Attribute'):
 
78
        keys = attributes.keys()
 
79
        keys.sort()
 
80
        i = 1
 
81
        for key in keys:
 
82
            value = attributes[key]
 
83
            if isinstance(value, list):
 
84
                for v in value:
 
85
                    params['%s.%d.Name'%(label,i)] = key
 
86
                    if self.converter:
 
87
                        v = self.converter.encode(v)
 
88
                    params['%s.%d.Value'%(label,i)] = v
 
89
                    if replace:
 
90
                        params['%s.%d.Replace'%(label,i)] = 'true'
 
91
                    i += 1
 
92
            else:
 
93
                params['%s.%d.Name'%(label,i)] = key
 
94
                if self.converter:
 
95
                    value = self.converter.encode(value)
 
96
                params['%s.%d.Value'%(label,i)] = value
 
97
                if replace:
 
98
                    params['%s.%d.Replace'%(label,i)] = 'true'
 
99
            i += 1
 
100
 
 
101
    def build_expected_value(self, params, expected_value):
 
102
        params['Expected.1.Name'] = expected_value[0]
 
103
        if expected_value[1] == True:
 
104
            params['Expected.1.Exists'] = 'true'
 
105
        elif expected_value[1] == False:
 
106
            params['Expected.1.Exists'] = 'false'
 
107
        else:
 
108
            params['Expected.1.Value'] = expected_value[1]
 
109
            
 
110
 
 
111
    def build_batch_list(self, params, items, replace=False):
 
112
        item_names = items.keys()
 
113
        i = 0
 
114
        for item_name in item_names:
 
115
            j = 0
 
116
            item = items[item_name]
 
117
            attr_names = item.keys()
 
118
            params['Item.%d.ItemName' % i] = item_name
 
119
            for attr_name in attr_names:
 
120
                value = item[attr_name]
 
121
                if isinstance(value, list):
 
122
                    for v in value:
 
123
                        if self.converter:
 
124
                            v = self.converter.encode(v)
 
125
                        params['Item.%d.Attribute.%d.Name' % (i,j)] = attr_name
 
126
                        params['Item.%d.Attribute.%d.Value' % (i,j)] = v
 
127
                        if replace:
 
128
                            params['Item.%d.Attribute.%d.Replace' % (i,j)] = 'true'
 
129
                        j += 1
 
130
                else:
 
131
                    params['Item.%d.Attribute.%d.Name' % (i,j)] = attr_name
 
132
                    if self.converter:
 
133
                        value = self.converter.encode(value)
 
134
                    params['Item.%d.Attribute.%d.Value' % (i,j)] = value
 
135
                    if replace:
 
136
                        params['Item.%d.Attribute.%d.Replace' % (i,j)] = 'true'
 
137
                    j += 1
 
138
            i += 1
 
139
 
 
140
    def build_name_list(self, params, attribute_names):
 
141
        i = 1
 
142
        attribute_names.sort()
 
143
        for name in attribute_names:
 
144
            params['Attribute.%d.Name'%i] = name
 
145
            i += 1
 
146
 
 
147
    def get_usage(self):
 
148
        """
 
149
        Returns the BoxUsage accumulated on this SDBConnection object.
 
150
 
 
151
        :rtype: float
 
152
        :return: The accumulated BoxUsage of all requests made on the connection.
 
153
        """
 
154
        return self.box_usage
 
155
 
 
156
    def print_usage(self):
 
157
        """
 
158
        Print the BoxUsage and approximate costs of all requests made on this connection.
 
159
        """
 
160
        print 'Total Usage: %f compute seconds' % self.box_usage
 
161
        cost = self.box_usage * 0.14
 
162
        print 'Approximate Cost: $%f' % cost
 
163
 
 
164
    def get_domain(self, domain_name, validate=True):
 
165
        domain = Domain(self, domain_name)
 
166
        if validate:
 
167
            self.select(domain, """select * from `%s` limit 1""" % domain_name)
 
168
        return domain
 
169
 
 
170
    def lookup(self, domain_name, validate=True):
 
171
        """
 
172
        Lookup an existing SimpleDB domain
 
173
 
 
174
        :type domain_name: string
 
175
        :param domain_name: The name of the new domain
 
176
 
 
177
        :rtype: :class:`boto.sdb.domain.Domain` object or None
 
178
        :return: The Domain object or None if the domain does not exist.
 
179
        """
 
180
        try:
 
181
            domain = self.get_domain(domain_name, validate)
 
182
        except:
 
183
            domain = None
 
184
        return domain
 
185
 
 
186
    def get_all_domains(self, max_domains=None, next_token=None):
 
187
        params = {}
 
188
        if max_domains:
 
189
            params['MaxNumberOfDomains'] = max_domains
 
190
        if next_token:
 
191
            params['NextToken'] = next_token
 
192
        return self.get_list('ListDomains', params, [('DomainName', Domain)])
 
193
        
 
194
    def create_domain(self, domain_name):
 
195
        """
 
196
        Create a SimpleDB domain.
 
197
 
 
198
        :type domain_name: string
 
199
        :param domain_name: The name of the new domain
 
200
 
 
201
        :rtype: :class:`boto.sdb.domain.Domain` object
 
202
        :return: The newly created domain
 
203
        """
 
204
        params = {'DomainName':domain_name}
 
205
        d = self.get_object('CreateDomain', params, Domain)
 
206
        d.name = domain_name
 
207
        return d
 
208
 
 
209
    def get_domain_and_name(self, domain_or_name):
 
210
        if (isinstance(domain_or_name, Domain)):
 
211
            return (domain_or_name, domain_or_name.name)
 
212
        else:
 
213
            return (self.get_domain(domain_or_name), domain_or_name)
 
214
        
 
215
    def delete_domain(self, domain_or_name):
 
216
        """
 
217
        Delete a SimpleDB domain.
 
218
 
 
219
        :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
 
220
        :param domain_or_name: Either the name of a domain or a Domain object
 
221
 
 
222
        :rtype: bool
 
223
        :return: True if successful
 
224
        
 
225
        B{Note:} This will delete the domain and all items within the domain.
 
226
        """
 
227
        domain, domain_name = self.get_domain_and_name(domain_or_name)
 
228
        params = {'DomainName':domain_name}
 
229
        return self.get_status('DeleteDomain', params)
 
230
        
 
231
    def domain_metadata(self, domain_or_name):
 
232
        """
 
233
        Get the Metadata for a SimpleDB domain.
 
234
 
 
235
        :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
 
236
        :param domain_or_name: Either the name of a domain or a Domain object
 
237
 
 
238
        :rtype: :class:`boto.sdb.domain.DomainMetaData` object
 
239
        :return: The newly created domain metadata object
 
240
        """
 
241
        domain, domain_name = self.get_domain_and_name(domain_or_name)
 
242
        params = {'DomainName':domain_name}
 
243
        d = self.get_object('DomainMetadata', params, DomainMetaData)
 
244
        d.domain = domain
 
245
        return d
 
246
        
 
247
    def put_attributes(self, domain_or_name, item_name, attributes,
 
248
                       replace=True, expected_value=None):
 
249
        """
 
250
        Store attributes for a given item in a domain.
 
251
 
 
252
        :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
 
253
        :param domain_or_name: Either the name of a domain or a Domain object
 
254
 
 
255
        :type item_name: string
 
256
        :param item_name: The name of the item whose attributes are being stored.
 
257
 
 
258
        :type attribute_names: dict or dict-like object
 
259
        :param attribute_names: The name/value pairs to store as attributes
 
260
 
 
261
        :type expected_value: list
 
262
        :param expected_value: If supplied, this is a list or tuple consisting
 
263
                               of a single attribute name and expected value.
 
264
                               The list can be of the form:
 
265
                                * ['name', 'value']
 
266
                               In which case the call will first verify
 
267
                               that the attribute "name" of this item has
 
268
                               a value of "value".  If it does, the delete
 
269
                               will proceed, otherwise a ConditionalCheckFailed
 
270
                               error will be returned.
 
271
                               The list can also be of the form:
 
272
                                * ['name', True|False]
 
273
                               which will simply check for the existence (True)
 
274
                               or non-existencve (False) of the attribute.
 
275
 
 
276
        :type replace: bool
 
277
        :param replace: Whether the attribute values passed in will replace
 
278
                        existing values or will be added as addition values.
 
279
                        Defaults to True.
 
280
 
 
281
        :rtype: bool
 
282
        :return: True if successful
 
283
        """
 
284
        domain, domain_name = self.get_domain_and_name(domain_or_name)
 
285
        params = {'DomainName' : domain_name,
 
286
                  'ItemName' : item_name}
 
287
        self.build_name_value_list(params, attributes, replace)
 
288
        if expected_value:
 
289
            self.build_expected_value(params, expected_value)
 
290
        return self.get_status('PutAttributes', params)
 
291
 
 
292
    def batch_put_attributes(self, domain_or_name, items, replace=True):
 
293
        """
 
294
        Store attributes for multiple items in a domain.
 
295
 
 
296
        :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
 
297
        :param domain_or_name: Either the name of a domain or a Domain object
 
298
 
 
299
        :type items: dict or dict-like object
 
300
        :param items: A dictionary-like object.  The keys of the dictionary are
 
301
                      the item names and the values are themselves dictionaries
 
302
                      of attribute names/values, exactly the same as the
 
303
                      attribute_names parameter of the scalar put_attributes
 
304
                      call.
 
305
 
 
306
        :type replace: bool
 
307
        :param replace: Whether the attribute values passed in will replace
 
308
                        existing values or will be added as addition values.
 
309
                        Defaults to True.
 
310
 
 
311
        :rtype: bool
 
312
        :return: True if successful
 
313
        """
 
314
        domain, domain_name = self.get_domain_and_name(domain_or_name)
 
315
        params = {'DomainName' : domain_name}
 
316
        self.build_batch_list(params, items, replace)
 
317
        return self.get_status('BatchPutAttributes', params, verb='POST')
 
318
 
 
319
    def get_attributes(self, domain_or_name, item_name, attribute_names=None,
 
320
                       consistent_read=False, item=None):
 
321
        """
 
322
        Retrieve attributes for a given item in a domain.
 
323
 
 
324
        :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
 
325
        :param domain_or_name: Either the name of a domain or a Domain object
 
326
 
 
327
        :type item_name: string
 
328
        :param item_name: The name of the item whose attributes are being retrieved.
 
329
 
 
330
        :type attribute_names: string or list of strings
 
331
        :param attribute_names: An attribute name or list of attribute names.  This
 
332
                                parameter is optional.  If not supplied, all attributes
 
333
                                will be retrieved for the item.
 
334
 
 
335
        :type consistent_read: bool
 
336
        :param consistent_read: When set to true, ensures that the most recent
 
337
                                data is returned.
 
338
 
 
339
        :rtype: :class:`boto.sdb.item.Item`
 
340
        :return: An Item mapping type containing the requested attribute name/values
 
341
        """
 
342
        domain, domain_name = self.get_domain_and_name(domain_or_name)
 
343
        params = {'DomainName' : domain_name,
 
344
                  'ItemName' : item_name}
 
345
        if consistent_read:
 
346
            params['ConsistentRead'] = 'true'
 
347
        if attribute_names:
 
348
            if not isinstance(attribute_names, list):
 
349
                attribute_names = [attribute_names]
 
350
            self.build_list_params(params, attribute_names, 'AttributeName')
 
351
        response = self.make_request('GetAttributes', params)
 
352
        body = response.read()
 
353
        if response.status == 200:
 
354
            if item == None:
 
355
                item = self.item_cls(domain, item_name)
 
356
            h = handler.XmlHandler(item, self)
 
357
            xml.sax.parseString(body, h)
 
358
            return item
 
359
        else:
 
360
            raise SDBResponseError(response.status, response.reason, body)
 
361
        
 
362
    def delete_attributes(self, domain_or_name, item_name, attr_names=None,
 
363
                          expected_value=None):
 
364
        """
 
365
        Delete attributes from a given item in a domain.
 
366
 
 
367
        :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
 
368
        :param domain_or_name: Either the name of a domain or a Domain object
 
369
 
 
370
        :type item_name: string
 
371
        :param item_name: The name of the item whose attributes are being deleted.
 
372
 
 
373
        :type attributes: dict, list or :class:`boto.sdb.item.Item`
 
374
        :param attributes: Either a list containing attribute names which will cause
 
375
                           all values associated with that attribute name to be deleted or
 
376
                           a dict or Item containing the attribute names and keys and list
 
377
                           of values to delete as the value.  If no value is supplied,
 
378
                           all attribute name/values for the item will be deleted.
 
379
                           
 
380
        :type expected_value: list
 
381
        :param expected_value: If supplied, this is a list or tuple consisting
 
382
                               of a single attribute name and expected value.
 
383
                               The list can be of the form:
 
384
                                * ['name', 'value']
 
385
                               In which case the call will first verify
 
386
                               that the attribute "name" of this item has
 
387
                               a value of "value".  If it does, the delete
 
388
                               will proceed, otherwise a ConditionalCheckFailed
 
389
                               error will be returned.
 
390
                               The list can also be of the form:
 
391
                                * ['name', True|False]
 
392
                               which will simply check for the existence (True)
 
393
                               or non-existencve (False) of the attribute.
 
394
 
 
395
        :rtype: bool
 
396
        :return: True if successful
 
397
        """
 
398
        domain, domain_name = self.get_domain_and_name(domain_or_name)
 
399
        params = {'DomainName':domain_name,
 
400
                  'ItemName' : item_name}
 
401
        if attr_names:
 
402
            if isinstance(attr_names, list):
 
403
                self.build_name_list(params, attr_names)
 
404
            elif isinstance(attr_names, dict) or isinstance(attr_names, self.item_cls):
 
405
                self.build_name_value_list(params, attr_names)
 
406
        if expected_value:
 
407
            self.build_expected_value(params, expected_value)
 
408
        return self.get_status('DeleteAttributes', params)
 
409
        
 
410
    def select(self, domain_or_name, query='', next_token=None,
 
411
               consistent_read=False):
 
412
        """
 
413
        Returns a set of Attributes for item names within domain_name that match the query.
 
414
        The query must be expressed in using the SELECT style syntax rather than the
 
415
        original SimpleDB query language.
 
416
        Even though the select request does not require a domain object, a domain
 
417
        object must be passed into this method so the Item objects returned can
 
418
        point to the appropriate domain.
 
419
        
 
420
        :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
 
421
        :param domain_or_name: Either the name of a domain or a Domain object
 
422
 
 
423
        :type query: string
 
424
        :param query: The SimpleDB query to be performed.
 
425
 
 
426
        :type consistent_read: bool
 
427
        :param consistent_read: When set to true, ensures that the most recent
 
428
                                data is returned.
 
429
 
 
430
        :rtype: ResultSet
 
431
        :return: An iterator containing the results.
 
432
        """
 
433
        domain, domain_name = self.get_domain_and_name(domain_or_name)
 
434
        params = {'SelectExpression' : query}
 
435
        if consistent_read:
 
436
            params['ConsistentRead'] = 'true'
 
437
        if next_token:
 
438
            params['NextToken'] = next_token
 
439
        return self.get_list('Select', params, [('Item', self.item_cls)],
 
440
                             parent=domain)
 
441