~soren/nova/iptables-security-groups

« back to all changes in this revision

Viewing changes to vendor/boto/boto/sdb/db/manager/xmlmanager.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-2008 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
import boto
 
22
from boto.utils import find_class, Password
 
23
from boto.sdb.db.key import Key
 
24
from boto.sdb.db.model import Model
 
25
from datetime import datetime
 
26
from xml.dom.minidom import getDOMImplementation, parse, parseString, Node
 
27
 
 
28
ISO8601 = '%Y-%m-%dT%H:%M:%SZ'
 
29
 
 
30
class XMLConverter:
 
31
    """
 
32
    Responsible for converting base Python types to format compatible with underlying
 
33
    database.  For SimpleDB, that means everything needs to be converted to a string
 
34
    when stored in SimpleDB and from a string when retrieved.
 
35
 
 
36
    To convert a value, pass it to the encode or decode method.  The encode method
 
37
    will take a Python native value and convert to DB format.  The decode method will
 
38
    take a DB format value and convert it to Python native format.  To find the appropriate
 
39
    method to call, the generic encode/decode methods will look for the type-specific
 
40
    method by searching for a method called "encode_<type name>" or "decode_<type name>".
 
41
    """
 
42
    def __init__(self, manager):
 
43
        self.manager = manager
 
44
        self.type_map = { bool : (self.encode_bool, self.decode_bool),
 
45
                          int : (self.encode_int, self.decode_int),
 
46
                          long : (self.encode_long, self.decode_long),
 
47
                          Model : (self.encode_reference, self.decode_reference),
 
48
                          Key : (self.encode_reference, self.decode_reference),
 
49
                          Password : (self.encode_password, self.decode_password),
 
50
                          datetime : (self.encode_datetime, self.decode_datetime)}
 
51
 
 
52
    def get_text_value(self, parent_node):
 
53
        value = ''
 
54
        for node in parent_node.childNodes:
 
55
            if node.nodeType == node.TEXT_NODE:
 
56
                value += node.data
 
57
        return value
 
58
 
 
59
    def encode(self, item_type, value):
 
60
        if item_type in self.type_map:
 
61
            encode = self.type_map[item_type][0]
 
62
            return encode(value)
 
63
        return value
 
64
 
 
65
    def decode(self, item_type, value):
 
66
        if item_type in self.type_map:
 
67
            decode = self.type_map[item_type][1]
 
68
            return decode(value)
 
69
        else:
 
70
            value = self.get_text_value(value)
 
71
        return value
 
72
 
 
73
    def encode_prop(self, prop, value):
 
74
        if isinstance(value, list):
 
75
            if hasattr(prop, 'item_type'):
 
76
                new_value = []
 
77
                for v in value:
 
78
                    item_type = getattr(prop, "item_type")
 
79
                    if Model in item_type.mro():
 
80
                        item_type = Model
 
81
                    new_value.append(self.encode(item_type, v))
 
82
                return new_value
 
83
            else:
 
84
                return value
 
85
        else:
 
86
            return self.encode(prop.data_type, value)
 
87
 
 
88
    def decode_prop(self, prop, value):
 
89
        if prop.data_type == list:
 
90
            if hasattr(prop, 'item_type'):
 
91
                item_type = getattr(prop, "item_type")
 
92
                if Model in item_type.mro():
 
93
                    item_type = Model
 
94
                values = []
 
95
                for item_node in value.getElementsByTagName('item'):
 
96
                    value = self.decode(item_type, item_node)
 
97
                    values.append(value)
 
98
                return values
 
99
            else:
 
100
                return self.get_text_value(value)
 
101
        else:
 
102
            return self.decode(prop.data_type, value)
 
103
 
 
104
    def encode_int(self, value):
 
105
        value = int(value)
 
106
        return '%d' % value
 
107
 
 
108
    def decode_int(self, value):
 
109
        value = self.get_text_value(value)
 
110
        if value:
 
111
            value = int(value)
 
112
        else:
 
113
            value = None
 
114
        return value
 
115
 
 
116
    def encode_long(self, value):
 
117
        value = long(value)
 
118
        return '%d' % value
 
119
 
 
120
    def decode_long(self, value):
 
121
        value = self.get_text_value(value)
 
122
        return long(value)
 
123
 
 
124
    def encode_bool(self, value):
 
125
        if value == True:
 
126
            return 'true'
 
127
        else:
 
128
            return 'false'
 
129
 
 
130
    def decode_bool(self, value):
 
131
        value = self.get_text_value(value)
 
132
        if value.lower() == 'true':
 
133
            return True
 
134
        else:
 
135
            return False
 
136
 
 
137
    def encode_datetime(self, value):
 
138
        return value.strftime(ISO8601)
 
139
 
 
140
    def decode_datetime(self, value):
 
141
        value = self.get_text_value(value)
 
142
        try:
 
143
            return datetime.strptime(value, ISO8601)
 
144
        except:
 
145
            return None
 
146
 
 
147
    def encode_reference(self, value):
 
148
        if isinstance(value, str) or isinstance(value, unicode):
 
149
            return value
 
150
        if value == None:
 
151
            return ''
 
152
        else:
 
153
            val_node = self.manager.doc.createElement("object")
 
154
            val_node.setAttribute('id', value.id)
 
155
            val_node.setAttribute('class', '%s.%s' % (value.__class__.__module__, value.__class__.__name__))
 
156
            return val_node
 
157
 
 
158
    def decode_reference(self, value):
 
159
        if not value:
 
160
            return None
 
161
        try:
 
162
            value = value.childNodes[0]
 
163
            class_name = value.getAttribute("class")
 
164
            id = value.getAttribute("id")
 
165
            cls = find_class(class_name)
 
166
            return cls.get_by_ids(id)
 
167
        except:
 
168
            return None
 
169
 
 
170
    def encode_password(self, value):
 
171
        if value and len(value) > 0:
 
172
            return str(value)
 
173
        else:
 
174
            return None
 
175
 
 
176
    def decode_password(self, value):
 
177
        value = self.get_text_value(value)
 
178
        return Password(value)
 
179
 
 
180
 
 
181
class XMLManager(object):
 
182
    
 
183
    def __init__(self, cls, db_name, db_user, db_passwd,
 
184
                 db_host, db_port, db_table, ddl_dir, enable_ssl):
 
185
        self.cls = cls
 
186
        if not db_name:
 
187
            db_name = cls.__name__.lower()
 
188
        self.db_name = db_name
 
189
        self.db_user = db_user
 
190
        self.db_passwd = db_passwd
 
191
        self.db_host = db_host
 
192
        self.db_port = db_port
 
193
        self.db_table = db_table
 
194
        self.ddl_dir = ddl_dir
 
195
        self.s3 = None
 
196
        self.converter = XMLConverter(self)
 
197
        self.impl = getDOMImplementation()
 
198
        self.doc = self.impl.createDocument(None, 'objects', None)
 
199
 
 
200
        self.connection = None
 
201
        self.enable_ssl = enable_ssl
 
202
        self.auth_header = None
 
203
        if self.db_user:
 
204
            import base64
 
205
            base64string = base64.encodestring('%s:%s' % (self.db_user, self.db_passwd))[:-1]
 
206
            authheader =  "Basic %s" % base64string
 
207
            self.auth_header = authheader
 
208
 
 
209
    def _connect(self):
 
210
        if self.db_host:
 
211
            if self.enable_ssl:
 
212
                from httplib import HTTPSConnection as Connection
 
213
            else:
 
214
                from httplib import HTTPConnection as Connection
 
215
 
 
216
            self.connection = Connection(self.db_host, self.db_port)
 
217
 
 
218
    def _make_request(self, method, url, post_data=None, body=None):
 
219
        """
 
220
        Make a request on this connection
 
221
        """
 
222
        if not self.connection:
 
223
            self._connect()
 
224
        try:
 
225
            self.connection.close()
 
226
        except:
 
227
            pass
 
228
        self.connection.connect()
 
229
        headers = {}
 
230
        if self.auth_header:
 
231
            headers["Authorization"] = self.auth_header
 
232
        self.connection.request(method, url, body, headers)
 
233
        resp = self.connection.getresponse()
 
234
        return resp
 
235
 
 
236
    def new_doc(self):
 
237
        return self.impl.createDocument(None, 'objects', None)
 
238
 
 
239
    def _object_lister(self, cls, doc):
 
240
        for obj_node in doc.getElementsByTagName('object'):
 
241
            if not cls:
 
242
                class_name = obj_node.getAttribute('class')
 
243
                cls = find_class(class_name)
 
244
            id = obj_node.getAttribute('id')
 
245
            obj = cls(id)
 
246
            for prop_node in obj_node.getElementsByTagName('property'):
 
247
                prop_name = prop_node.getAttribute('name')
 
248
                prop = obj.find_property(prop_name)
 
249
                if prop:
 
250
                    if hasattr(prop, 'item_type'):
 
251
                        value = self.get_list(prop_node, prop.item_type)
 
252
                    else:
 
253
                        value = self.decode_value(prop, prop_node)
 
254
                        value = prop.make_value_from_datastore(value)
 
255
                    setattr(obj, prop.name, value)
 
256
            yield obj
 
257
 
 
258
    def reset(self):
 
259
        self._connect()
 
260
 
 
261
    def get_doc(self):
 
262
        return self.doc
 
263
            
 
264
    def encode_value(self, prop, value):
 
265
        return self.converter.encode_prop(prop, value)
 
266
 
 
267
    def decode_value(self, prop, value):
 
268
        return self.converter.decode_prop(prop, value)
 
269
 
 
270
    def get_s3_connection(self):
 
271
        if not self.s3:
 
272
            self.s3 = boto.connect_s3(self.aws_access_key_id, self.aws_secret_access_key)
 
273
        return self.s3
 
274
 
 
275
    def get_list(self, prop_node, item_type):
 
276
        values = []
 
277
        try:
 
278
            items_node = prop_node.getElementsByTagName('items')[0]
 
279
        except:
 
280
            return []
 
281
        for item_node in items_node.getElementsByTagName('item'):
 
282
            value = self.converter.decode(item_type, item_node)
 
283
            values.append(value)
 
284
        return values
 
285
 
 
286
    def get_object_from_doc(self, cls, id, doc):
 
287
        obj_node = doc.getElementsByTagName('object')[0]
 
288
        if not cls:
 
289
            class_name = obj_node.getAttribute('class')
 
290
            cls = find_class(class_name)
 
291
        if not id:
 
292
            id = obj_node.getAttribute('id')
 
293
        obj = cls(id)
 
294
        for prop_node in obj_node.getElementsByTagName('property'):
 
295
            prop_name = prop_node.getAttribute('name')
 
296
            prop = obj.find_property(prop_name)
 
297
            value = self.decode_value(prop, prop_node)
 
298
            value = prop.make_value_from_datastore(value)
 
299
            if value != None:
 
300
                try:
 
301
                    setattr(obj, prop.name, value)
 
302
                except:
 
303
                    pass
 
304
        return obj
 
305
 
 
306
    def get_props_from_doc(self, cls, id, doc):
 
307
        """
 
308
        Pull out the properties from this document
 
309
        Returns the class, the properties in a hash, and the id if provided as a tuple
 
310
        :return: (cls, props, id)
 
311
        """
 
312
        obj_node = doc.getElementsByTagName('object')[0]
 
313
        if not cls:
 
314
            class_name = obj_node.getAttribute('class')
 
315
            cls = find_class(class_name)
 
316
        if not id:
 
317
            id = obj_node.getAttribute('id')
 
318
        props = {}
 
319
        for prop_node in obj_node.getElementsByTagName('property'):
 
320
            prop_name = prop_node.getAttribute('name')
 
321
            prop = cls.find_property(prop_name)
 
322
            value = self.decode_value(prop, prop_node)
 
323
            value = prop.make_value_from_datastore(value)
 
324
            if value != None:
 
325
                props[prop.name] = value
 
326
        return (cls, props, id)
 
327
        
 
328
        
 
329
    def get_object(self, cls, id):
 
330
        if not self.connection:
 
331
            self._connect()
 
332
 
 
333
        if not self.connection:
 
334
            raise NotImplementedError("Can't query without a database connection")
 
335
        url = "/%s/%s" % (self.db_name, id)
 
336
        resp = self._make_request('GET', url)
 
337
        if resp.status == 200:
 
338
            doc = parse(resp)
 
339
        else:
 
340
            raise Exception("Error: %s" % resp.status)
 
341
        return self.get_object_from_doc(cls, id, doc)
 
342
 
 
343
    def query(self, cls, filters, limit=None, order_by=None):
 
344
        if not self.connection:
 
345
            self._connect()
 
346
 
 
347
        if not self.connection:
 
348
            raise NotImplementedError("Can't query without a database connection")
 
349
 
 
350
        from urllib import urlencode
 
351
 
 
352
        query = str(self._build_query(cls, filters, limit, order_by))
 
353
        if query:
 
354
            url = "/%s?%s" % (self.db_name, urlencode({"query": query}))
 
355
        else: 
 
356
            url = "/%s" % self.db_name
 
357
        resp = self._make_request('GET', url)
 
358
        if resp.status == 200:
 
359
            doc = parse(resp)
 
360
        else:
 
361
            raise Exception("Error: %s" % resp.status)
 
362
        return self._object_lister(cls, doc)
 
363
 
 
364
    def _build_query(self, cls, filters, limit, order_by):
 
365
        import types
 
366
        if len(filters) > 4:
 
367
            raise Exception('Too many filters, max is 4')
 
368
        parts = []
 
369
        properties = cls.properties(hidden=False)
 
370
        for filter, value in filters:
 
371
            name, op = filter.strip().split()
 
372
            found = False
 
373
            for property in properties:
 
374
                if property.name == name:
 
375
                    found = True
 
376
                    if types.TypeType(value) == types.ListType:
 
377
                        filter_parts = []
 
378
                        for val in value:
 
379
                            val = self.encode_value(property, val)
 
380
                            filter_parts.append("'%s' %s '%s'" % (name, op, val))
 
381
                        parts.append("[%s]" % " OR ".join(filter_parts))
 
382
                    else:
 
383
                        value = self.encode_value(property, value)
 
384
                        parts.append("['%s' %s '%s']" % (name, op, value))
 
385
            if not found:
 
386
                raise Exception('%s is not a valid field' % name)
 
387
        if order_by:
 
388
            if order_by.startswith("-"):
 
389
                key = order_by[1:]
 
390
                type = "desc"
 
391
            else:
 
392
                key = order_by
 
393
                type = "asc"
 
394
            parts.append("['%s' starts-with ''] sort '%s' %s" % (key, key, type))
 
395
        return ' intersection '.join(parts)
 
396
 
 
397
    def query_gql(self, query_string, *args, **kwds):
 
398
        raise NotImplementedError, "GQL queries not supported in XML"
 
399
 
 
400
    def save_list(self, doc, items, prop_node):
 
401
        items_node = doc.createElement('items')
 
402
        prop_node.appendChild(items_node)
 
403
        for item in items:
 
404
            item_node = doc.createElement('item')
 
405
            items_node.appendChild(item_node)
 
406
            if isinstance(item, Node):
 
407
                item_node.appendChild(item)
 
408
            else:
 
409
                text_node = doc.createTextNode(item)
 
410
                item_node.appendChild(text_node)
 
411
 
 
412
    def save_object(self, obj):
 
413
        """
 
414
        Marshal the object and do a PUT
 
415
        """
 
416
        doc = self.marshal_object(obj)
 
417
        if obj.id:
 
418
            url = "/%s/%s" % (self.db_name, obj.id)
 
419
        else:
 
420
            url = "/%s" % (self.db_name)
 
421
        resp = self._make_request("PUT", url, body=doc.toxml())
 
422
        new_obj = self.get_object_from_doc(obj.__class__, None, parse(resp))
 
423
        obj.id = new_obj.id
 
424
        for prop in obj.properties():
 
425
            try:
 
426
                propname = prop.name
 
427
            except AttributeError:
 
428
                propname = None
 
429
            if propname:
 
430
                value = getattr(new_obj, prop.name)
 
431
                if value:
 
432
                    setattr(obj, prop.name, value)
 
433
        return obj
 
434
 
 
435
 
 
436
    def marshal_object(self, obj, doc=None):
 
437
        if not doc:
 
438
            doc = self.new_doc()
 
439
        if not doc:
 
440
            doc = self.doc
 
441
        obj_node = doc.createElement('object')
 
442
 
 
443
        if obj.id:
 
444
            obj_node.setAttribute('id', obj.id)
 
445
 
 
446
        obj_node.setAttribute('class', '%s.%s' % (obj.__class__.__module__,
 
447
                                                  obj.__class__.__name__))
 
448
        root = doc.documentElement
 
449
        root.appendChild(obj_node)
 
450
        for property in obj.properties(hidden=False):
 
451
            prop_node = doc.createElement('property')
 
452
            prop_node.setAttribute('name', property.name)
 
453
            prop_node.setAttribute('type', property.type_name)
 
454
            value = property.get_value_for_datastore(obj)
 
455
            if value is not None:
 
456
                value = self.encode_value(property, value)
 
457
                if isinstance(value, list):
 
458
                    self.save_list(doc, value, prop_node)
 
459
                elif isinstance(value, Node):
 
460
                    prop_node.appendChild(value)
 
461
                else:
 
462
                    text_node = doc.createTextNode(str(value))
 
463
                    prop_node.appendChild(text_node)
 
464
            obj_node.appendChild(prop_node)
 
465
 
 
466
        return doc
 
467
 
 
468
    def unmarshal_object(self, fp, cls=None, id=None):
 
469
        if isinstance(fp, str) or isinstance(fp, unicode):
 
470
            doc = parseString(fp)
 
471
        else:
 
472
            doc = parse(fp)
 
473
        return self.get_object_from_doc(cls, id, doc)
 
474
    
 
475
    def unmarshal_props(self, fp, cls=None, id=None):
 
476
        """
 
477
        Same as unmarshalling an object, except it returns
 
478
        from "get_props_from_doc"
 
479
        """
 
480
        if isinstance(fp, str) or isinstance(fp, unicode):
 
481
            doc = parseString(fp)
 
482
        else:
 
483
            doc = parse(fp)
 
484
        return self.get_props_from_doc(cls, id, doc)
 
485
 
 
486
    def delete_object(self, obj):
 
487
        url = "/%s/%s" % (self.db_name, obj.id)
 
488
        return self._make_request("DELETE", url)
 
489
 
 
490
    def set_key_value(self, obj, name, value):
 
491
        self.domain.put_attributes(obj.id, {name : value}, replace=True)
 
492
 
 
493
    def delete_key_value(self, obj, name):
 
494
        self.domain.delete_attributes(obj.id, name)
 
495
 
 
496
    def get_key_value(self, obj, name):
 
497
        a = self.domain.get_attributes(obj.id, name)
 
498
        if a.has_key(name):
 
499
            return a[name]
 
500
        else:
 
501
            return None
 
502
    
 
503
    def get_raw_item(self, obj):
 
504
        return self.domain.get_item(obj.id)
 
505
 
 
506
    def set_property(self, prop, obj, name, value):
 
507
        pass
 
508
 
 
509
    def get_property(self, prop, obj, name):
 
510
        pass
 
511
 
 
512
    def load_object(self, obj):
 
513
        if not obj._loaded:
 
514
            obj = obj.get_by_id(obj.id)
 
515
            obj._loaded = True
 
516
        return obj
 
517