~abentley/juju-ci-tools/client-from-config-4

« back to all changes in this revision

Viewing changes to azure-sdk-for-python-master/azure/__init__.py

  • Committer: Aaron Bentley
  • Date: 2014-02-24 17:18:29 UTC
  • mto: This revision was merged to the branch mainline in revision 252.
  • Revision ID: aaron.bentley@canonical.com-20140224171829-sz644yhoygu7m9dm
Use tags to identify and shut down instances.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#-------------------------------------------------------------------------
 
2
# Copyright (c) Microsoft.  All rights reserved.
 
3
#
 
4
# Licensed under the Apache License, Version 2.0 (the "License");
 
5
# you may not use this file except in compliance with the License.
 
6
# You may obtain a copy of the License at
 
7
#   http://www.apache.org/licenses/LICENSE-2.0
 
8
#
 
9
# Unless required by applicable law or agreed to in writing, software
 
10
# distributed under the License is distributed on an "AS IS" BASIS,
 
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
12
# See the License for the specific language governing permissions and
 
13
# limitations under the License.
 
14
#--------------------------------------------------------------------------
 
15
import ast
 
16
import base64
 
17
import sys
 
18
import types
 
19
import urllib2
 
20
 
 
21
from datetime import datetime
 
22
from xml.dom import minidom
 
23
from xml.sax.saxutils import escape as xml_escape
 
24
 
 
25
#--------------------------------------------------------------------------
 
26
# constants
 
27
 
 
28
__author__ = 'Microsoft Corp. <ptvshelp@microsoft.com>'
 
29
__version__ = '0.7.1'
 
30
 
 
31
#Live ServiceClient URLs
 
32
BLOB_SERVICE_HOST_BASE = '.blob.core.windows.net'
 
33
QUEUE_SERVICE_HOST_BASE = '.queue.core.windows.net'
 
34
TABLE_SERVICE_HOST_BASE = '.table.core.windows.net'
 
35
SERVICE_BUS_HOST_BASE = '.servicebus.windows.net'
 
36
MANAGEMENT_HOST = 'management.core.windows.net'
 
37
 
 
38
#Development ServiceClient URLs
 
39
DEV_BLOB_HOST = '127.0.0.1:10000'
 
40
DEV_QUEUE_HOST = '127.0.0.1:10001'
 
41
DEV_TABLE_HOST = '127.0.0.1:10002'
 
42
 
 
43
#Default credentials for Development Storage Service
 
44
DEV_ACCOUNT_NAME = 'devstoreaccount1'
 
45
DEV_ACCOUNT_KEY = 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=='
 
46
 
 
47
# All of our error messages
 
48
_ERROR_CANNOT_FIND_PARTITION_KEY = 'Cannot find partition key in request.'
 
49
_ERROR_CANNOT_FIND_ROW_KEY = 'Cannot find row key in request.'
 
50
_ERROR_INCORRECT_TABLE_IN_BATCH = 'Table should be the same in a batch operations'
 
51
_ERROR_INCORRECT_PARTITION_KEY_IN_BATCH = 'Partition Key should be the same in a batch operations'
 
52
_ERROR_DUPLICATE_ROW_KEY_IN_BATCH = 'Row Keys should not be the same in a batch operations'
 
53
_ERROR_BATCH_COMMIT_FAIL = 'Batch Commit Fail'
 
54
_ERROR_MESSAGE_NOT_PEEK_LOCKED_ON_DELETE = 'Message is not peek locked and cannot be deleted.'
 
55
_ERROR_MESSAGE_NOT_PEEK_LOCKED_ON_UNLOCK = 'Message is not peek locked and cannot be unlocked.'
 
56
_ERROR_QUEUE_NOT_FOUND = 'Queue was not found'
 
57
_ERROR_TOPIC_NOT_FOUND = 'Topic was not found'
 
58
_ERROR_CONFLICT = 'Conflict'
 
59
_ERROR_NOT_FOUND = 'Not found'
 
60
_ERROR_UNKNOWN = 'Unknown error (%s)'
 
61
_ERROR_SERVICEBUS_MISSING_INFO = 'You need to provide servicebus namespace, access key and Issuer'
 
62
_ERROR_STORAGE_MISSING_INFO = 'You need to provide both account name and access key'
 
63
_ERROR_ACCESS_POLICY = 'share_access_policy must be either SignedIdentifier or AccessPolicy instance'
 
64
_ERROR_VALUE_SHOULD_NOT_BE_NULL  = '%s should not be None.'
 
65
_ERROR_CANNOT_SERIALIZE_VALUE_TO_ENTITY = 'Cannot serialize the specified value (%s) to an entity.  Please use an EntityProperty (which can specify custom types), int, str, bool, or datetime'
 
66
 
 
67
_USER_AGENT_STRING = 'pyazure/' + __version__
 
68
 
 
69
METADATA_NS = 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata'
 
70
 
 
71
class WindowsAzureData(object):
 
72
    ''' This is the base of data class.  It is only used to check whether it is instance or not. '''
 
73
    pass
 
74
 
 
75
class WindowsAzureError(Exception):
 
76
    ''' WindowsAzure Excpetion base class. '''
 
77
    def __init__(self, message):
 
78
        Exception.__init__(self, message)
 
79
 
 
80
class WindowsAzureConflictError(WindowsAzureError):
 
81
    '''Indicates that the resource could not be created because it already
 
82
    exists'''
 
83
    def __init__(self, message):
 
84
        self.message = message
 
85
 
 
86
class WindowsAzureMissingResourceError(WindowsAzureError):
 
87
    '''Indicates that a request for a request for a resource (queue, table, 
 
88
    container, etc...) failed because the specified resource does not exist'''
 
89
    def __init__(self, message):
 
90
        self.message = message
 
91
 
 
92
class Feed:
 
93
    pass
 
94
 
 
95
class _Base64String(str):
 
96
    pass
 
97
 
 
98
class HeaderDict(dict):
 
99
    def __getitem__(self, index):
 
100
        return super(HeaderDict, self).__getitem__(index.lower())
 
101
 
 
102
def _get_readable_id(id_name, id_prefix_to_skip):
 
103
    """simplified an id to be more friendly for us people"""
 
104
    # id_name is in the form 'https://namespace.host.suffix/name'
 
105
    # where name may contain a forward slash!
 
106
    pos = id_name.find('//')
 
107
    if pos != -1:
 
108
        pos += 2
 
109
        if id_prefix_to_skip:
 
110
            pos = id_name.find(id_prefix_to_skip, pos)
 
111
            if pos != -1:
 
112
                pos += len(id_prefix_to_skip)
 
113
        pos = id_name.find('/', pos)
 
114
        if pos != -1:
 
115
            return id_name[pos+1:]
 
116
    return id_name
 
117
 
 
118
def _get_entry_properties(xmlstr, include_id, id_prefix_to_skip=None):
 
119
    ''' get properties from entry xml '''
 
120
    xmldoc = minidom.parseString(xmlstr)
 
121
    properties = {}
 
122
    
 
123
    for entry in _get_child_nodes(xmldoc, 'entry'):
 
124
        etag = entry.getAttributeNS(METADATA_NS, 'etag')
 
125
        if etag:
 
126
            properties['etag'] = etag
 
127
        for updated in _get_child_nodes(entry, 'updated'):
 
128
            properties['updated'] = updated.firstChild.nodeValue
 
129
        for name in _get_children_from_path(entry, 'author', 'name'):
 
130
            if name.firstChild is not None:
 
131
                properties['author'] = name.firstChild.nodeValue
 
132
            
 
133
        if include_id:
 
134
            for id in _get_child_nodes(entry, 'id'):
 
135
                properties['name'] = _get_readable_id(id.firstChild.nodeValue, id_prefix_to_skip)
 
136
 
 
137
    return properties
 
138
 
 
139
def _get_first_child_node_value(parent_node, node_name):
 
140
    xml_attrs = _get_child_nodes(parent_node, node_name)
 
141
    if xml_attrs:
 
142
        xml_attr = xml_attrs[0]
 
143
        if xml_attr.firstChild:
 
144
            value = xml_attr.firstChild.nodeValue
 
145
            return value
 
146
 
 
147
def _get_child_nodes(node, tagName):
 
148
    return [childNode for childNode in node.getElementsByTagName(tagName)
 
149
            if childNode.parentNode == node]
 
150
 
 
151
def _get_children_from_path(node, *path):
 
152
    '''descends through a hierarchy of nodes returning the list of children
 
153
    at the inner most level.  Only returns children who share a common parent,
 
154
    not cousins.'''
 
155
    cur = node
 
156
    for index, child in enumerate(path):    
 
157
        if isinstance(child, basestring):
 
158
            next = _get_child_nodes(cur, child)
 
159
        else:
 
160
            next = _get_child_nodesNS(cur, *child)
 
161
        if index == len(path) - 1:
 
162
            return next
 
163
        elif not next:
 
164
            break
 
165
 
 
166
        cur = next[0]
 
167
    return []
 
168
 
 
169
def _get_child_nodesNS(node, ns, tagName):
 
170
    return [childNode for childNode in node.getElementsByTagNameNS(ns, tagName)
 
171
            if childNode.parentNode == node]
 
172
 
 
173
def _create_entry(entry_body):
 
174
    ''' Adds common part of entry to a given entry body and return the whole xml. '''
 
175
    updated_str = datetime.utcnow().isoformat()    
 
176
    if datetime.utcnow().utcoffset() is None:
 
177
        updated_str += '+00:00'
 
178
    
 
179
    entry_start = '''<?xml version="1.0" encoding="utf-8" standalone="yes"?>   
 
180
<entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom" >
 
181
<title /><updated>{updated}</updated><author><name /></author><id />
 
182
<content type="application/xml">
 
183
    {body}</content></entry>'''
 
184
    return entry_start.format(updated=updated_str, body=entry_body)
 
185
 
 
186
def _to_datetime(strtime):
 
187
    return datetime.strptime(strtime, "%Y-%m-%dT%H:%M:%S.%f")
 
188
 
 
189
_KNOWN_SERIALIZATION_XFORMS = {'include_apis':'IncludeAPIs',
 
190
                               'message_id': 'MessageId',
 
191
                               'content_md5':'Content-MD5',
 
192
                               'last_modified': 'Last-Modified',
 
193
                               'cache_control': 'Cache-Control',
 
194
                               'account_admin_live_email_id': 'AccountAdminLiveEmailId',
 
195
                               'service_admin_live_email_id': 'ServiceAdminLiveEmailId',
 
196
                               'subscription_id': 'SubscriptionID',
 
197
                               'fqdn': 'FQDN',
 
198
                               'private_id': 'PrivateID',
 
199
                               'os_virtual_hard_disk': 'OSVirtualHardDisk',
 
200
                               'logical_disk_size_in_gb':'LogicalDiskSizeInGB',
 
201
                               'logical_size_in_gb':'LogicalSizeInGB',
 
202
                               'os':'OS',
 
203
                               'persistent_vm_downtime_info':'PersistentVMDowntimeInfo',
 
204
                               }
 
205
 
 
206
def _get_serialization_name(element_name):
 
207
    """converts a Python name into a serializable name"""
 
208
    known = _KNOWN_SERIALIZATION_XFORMS.get(element_name)
 
209
    if known is not None:
 
210
        return known
 
211
 
 
212
    if element_name.startswith('x_ms_'):
 
213
        return element_name.replace('_', '-')
 
214
    if element_name.endswith('_id'):
 
215
        element_name = element_name.replace('_id', 'ID')
 
216
    for name in ['content_', 'last_modified', 'if_', 'cache_control']:
 
217
        if element_name.startswith(name): 
 
218
            element_name = element_name.replace('_', '-_')    
 
219
 
 
220
    return ''.join(name.capitalize() for name in element_name.split('_'))
 
221
 
 
222
def _str(value):
 
223
    if isinstance(value, unicode):
 
224
        return value.encode('utf-8')
 
225
 
 
226
    return str(value)
 
227
 
 
228
def _str_or_none(value):
 
229
    if isinstance(value, unicode):
 
230
        return value.encode('utf-8')
 
231
 
 
232
    if value is None:
 
233
        return None
 
234
 
 
235
    return str(value)
 
236
 
 
237
def _int_or_none(value):
 
238
    if value is None:
 
239
        return None
 
240
 
 
241
    return str(int(value))
 
242
 
 
243
def _bool_or_none(value):
 
244
    if value is None:
 
245
        return None
 
246
 
 
247
    if isinstance(value, bool):
 
248
        if value:
 
249
            return 'true'
 
250
        else:
 
251
            return 'false'
 
252
 
 
253
    return str(value)
 
254
 
 
255
def _convert_class_to_xml(source, xml_prefix = True):
 
256
    if source is None:
 
257
        return ''
 
258
 
 
259
    xmlstr = ''
 
260
    if xml_prefix:
 
261
        xmlstr = '<?xml version="1.0" encoding="utf-8"?>'
 
262
    
 
263
    if isinstance(source, list):
 
264
        for value in source:
 
265
            xmlstr += _convert_class_to_xml(value, False)
 
266
    elif isinstance(source, WindowsAzureData):
 
267
        class_name = source.__class__.__name__
 
268
        xmlstr += '<' + class_name + '>'
 
269
        for name, value in vars(source).iteritems():
 
270
            if value is not None:
 
271
                if isinstance(value, list) or isinstance(value, WindowsAzureData):
 
272
                    xmlstr += _convert_class_to_xml(value, False)
 
273
                else:
 
274
                    xmlstr += ('<' + _get_serialization_name(name) + '>' + 
 
275
                               xml_escape(str(value)) + '</' + 
 
276
                               _get_serialization_name(name) + '>')
 
277
        xmlstr += '</' + class_name + '>'
 
278
    return xmlstr
 
279
 
 
280
def _find_namespaces_from_child(parent, child, namespaces):
 
281
    """Recursively searches from the parent to the child,
 
282
    gathering all the applicable namespaces along the way"""
 
283
    for cur_child in parent.childNodes:
 
284
        if cur_child is child:
 
285
            return True
 
286
        if _find_namespaces_from_child(cur_child, child, namespaces):
 
287
            # we are the parent node
 
288
            for key in cur_child.attributes.keys():
 
289
                if key.startswith('xmlns:') or key == 'xmlns': 
 
290
                    namespaces[key] = cur_child.attributes[key]
 
291
            break
 
292
    return False
 
293
 
 
294
def _find_namespaces(parent, child):
 
295
    res = {}
 
296
    for key in parent.documentElement.attributes.keys():
 
297
        if key.startswith('xmlns:') or key == 'xmlns': 
 
298
            res[key] = parent.documentElement.attributes[key]
 
299
    _find_namespaces_from_child(parent, child, res)
 
300
    return res
 
301
 
 
302
def _clone_node_with_namespaces(node_to_clone, original_doc):
 
303
    clone = node_to_clone.cloneNode(True)
 
304
    
 
305
    for key, value in _find_namespaces(original_doc, node_to_clone).iteritems():
 
306
        clone.attributes[key] = value
 
307
 
 
308
    return clone
 
309
 
 
310
def _convert_response_to_feeds(response, convert_func):
 
311
    if response is None:
 
312
        return None
 
313
 
 
314
    feeds = _list_of(Feed)
 
315
    
 
316
    x_ms_continuation = HeaderDict()
 
317
    for name, value in response.headers:
 
318
        if 'x-ms-continuation' in name:
 
319
            x_ms_continuation[name[len('x-ms-continuation')+1:]] = value
 
320
    if x_ms_continuation:
 
321
        setattr(feeds, 'x_ms_continuation', x_ms_continuation)
 
322
 
 
323
    xmldoc = minidom.parseString(response.body)
 
324
    xml_entries = _get_children_from_path(xmldoc, 'feed', 'entry')
 
325
    if not xml_entries:
 
326
        xml_entries = _get_children_from_path(xmldoc, 'entry') #in some cases, response contains only entry but no feed
 
327
    for xml_entry in xml_entries:
 
328
        new_node = _clone_node_with_namespaces(xml_entry, xmldoc)
 
329
        feeds.append(convert_func(new_node.toxml('utf-8')))
 
330
 
 
331
    return feeds
 
332
 
 
333
def _validate_not_none(param_name, param):
 
334
    if param is None:
 
335
        raise TypeError(_ERROR_VALUE_SHOULD_NOT_BE_NULL % (param_name))
 
336
 
 
337
def _fill_list_of(xmldoc, element_type, xml_element_name):
 
338
    xmlelements = _get_child_nodes(xmldoc, xml_element_name)
 
339
    return [_parse_response_body_from_xml_node(xmlelement, element_type) for xmlelement in xmlelements]
 
340
 
 
341
def _fill_scalar_list_of(xmldoc, element_type, parent_xml_element_name, xml_element_name):
 
342
    '''Converts an xml fragment into a list of scalar types.  The parent xml element contains a 
 
343
    flat list of xml elements which are converted into the specified scalar type and added to the list.
 
344
    Example:
 
345
    xmldoc=
 
346
     <Endpoints>
 
347
        <Endpoint>http://{storage-service-name}.blob.core.windows.net/</Endpoint>
 
348
        <Endpoint>http://{storage-service-name}.queue.core.windows.net/</Endpoint>
 
349
        <Endpoint>http://{storage-service-name}.table.core.windows.net/</Endpoint>
 
350
      </Endpoints>
 
351
    element_type=str
 
352
    parent_xml_element_name='Endpoints'
 
353
    xml_element_name='Endpoint'
 
354
    '''
 
355
    xmlelements = _get_child_nodes(xmldoc, parent_xml_element_name)
 
356
    if xmlelements:
 
357
        xmlelements = _get_child_nodes(xmlelements[0], xml_element_name)
 
358
        return [_get_node_value(xmlelement, element_type) for xmlelement in xmlelements]
 
359
 
 
360
def _fill_dict(xmldoc, element_name):    
 
361
    xmlelements = _get_child_nodes(xmldoc, element_name)
 
362
    if xmlelements:
 
363
        return_obj = {}
 
364
        for child in xmlelements[0].childNodes:
 
365
            if child.firstChild:
 
366
                return_obj[child.nodeName] = child.firstChild.nodeValue
 
367
        return return_obj
 
368
 
 
369
def _fill_dict_of(xmldoc, parent_xml_element_name, pair_xml_element_name, key_xml_element_name, value_xml_element_name):
 
370
    '''Converts an xml fragment into a dictionary. The parent xml element contains a 
 
371
    list of xml elements where each element has a child element for the key, and another for the value.
 
372
    Example:
 
373
    xmldoc=
 
374
      <ExtendedProperties>
 
375
        <ExtendedProperty>
 
376
          <Name>Ext1</Name>
 
377
          <Value>Val1</Value>
 
378
        </ExtendedProperty>
 
379
        <ExtendedProperty>
 
380
          <Name>Ext2</Name>
 
381
          <Value>Val2</Value>
 
382
        </ExtendedProperty>
 
383
      </ExtendedProperties>
 
384
    element_type=str
 
385
    parent_xml_element_name='ExtendedProperties'
 
386
    pair_xml_element_name='ExtendedProperty'
 
387
    key_xml_element_name='Name'
 
388
    value_xml_element_name='Value'
 
389
    '''
 
390
    return_obj = { }
 
391
 
 
392
    xmlelements = _get_child_nodes(xmldoc, parent_xml_element_name)
 
393
    if xmlelements:
 
394
        xmlelements = _get_child_nodes(xmlelements[0], pair_xml_element_name)
 
395
        for pair in xmlelements:
 
396
            keys = _get_child_nodes(pair, key_xml_element_name)
 
397
            values = _get_child_nodes(pair, value_xml_element_name)
 
398
            if keys and values:
 
399
                key = keys[0].firstChild.nodeValue
 
400
                value = values[0].firstChild.nodeValue
 
401
                return_obj[key] = value
 
402
 
 
403
    return return_obj
 
404
 
 
405
def _fill_instance_child(xmldoc, element_name, return_type):
 
406
    '''Converts a child of the current dom element to the specified type.  The child name
 
407
    '''
 
408
    xmlelements = _get_child_nodes(xmldoc, _get_serialization_name(element_name))
 
409
 
 
410
    if not xmlelements:
 
411
        return None
 
412
 
 
413
    return_obj = return_type()
 
414
    _fill_data_to_return_object(xmlelements[0], return_obj)
 
415
 
 
416
    return return_obj
 
417
 
 
418
def _fill_instance_element(element, return_type):
 
419
    """Converts a DOM element into the specified object""" 
 
420
    return _parse_response_body_from_xml_node(element, return_type)
 
421
 
 
422
 
 
423
def _fill_data_minidom(xmldoc, element_name, data_member):
 
424
    xmlelements = _get_child_nodes(xmldoc, _get_serialization_name(element_name))
 
425
 
 
426
    if not xmlelements or not xmlelements[0].childNodes:
 
427
        return None
 
428
 
 
429
    value = xmlelements[0].firstChild.nodeValue
 
430
 
 
431
    if data_member is None:
 
432
        return value
 
433
    elif isinstance(data_member, datetime):
 
434
        return _to_datetime(value)
 
435
    elif type(data_member) is types.BooleanType:
 
436
        return value.lower() != 'false'
 
437
    else:
 
438
        return type(data_member)(value)
 
439
 
 
440
def _get_node_value(xmlelement, data_type):
 
441
    value = xmlelement.firstChild.nodeValue
 
442
    if data_type is datetime:
 
443
        return _to_datetime(value)
 
444
    elif data_type is types.BooleanType:
 
445
        return value.lower() != 'false'
 
446
    else:
 
447
        return data_type(value)
 
448
 
 
449
def _get_request_body(request_body):
 
450
    '''Converts an object into a request body.  If it's None
 
451
    we'll return an empty string, if it's one of our objects it'll
 
452
    convert it to XML and return it.  Otherwise we just use the object
 
453
    directly'''
 
454
    if request_body is None:
 
455
        return ''
 
456
    elif isinstance(request_body, WindowsAzureData):
 
457
        return _convert_class_to_xml(request_body)
 
458
 
 
459
    return _str(request_body)
 
460
 
 
461
def _parse_enum_results_list(response, return_type, resp_type, item_type):
 
462
    """resp_body is the XML we received
 
463
resp_type is a string, such as Containers,
 
464
return_type is the type we're constructing, such as ContainerEnumResults
 
465
item_type is the type object of the item to be created, such as Container
 
466
 
 
467
This function then returns a ContainerEnumResults object with the
 
468
containers member populated with the results.
 
469
"""
 
470
    
 
471
    # parsing something like:
 
472
    # <EnumerationResults ... >
 
473
    #   <Queues>
 
474
    #       <Queue>
 
475
    #           <Something />
 
476
    #           <SomethingElse />
 
477
    #       </Queue>
 
478
    #   </Queues>
 
479
    # </EnumerationResults>
 
480
    respbody = response.body
 
481
    return_obj = return_type()
 
482
    doc = minidom.parseString(respbody)
 
483
 
 
484
    items = []
 
485
    for enum_results in _get_child_nodes(doc, 'EnumerationResults'):
 
486
        # path is something like Queues, Queue
 
487
        for child in _get_children_from_path(enum_results, resp_type, resp_type[:-1]):
 
488
            items.append(_fill_instance_element(child, item_type))
 
489
 
 
490
        for name, value in vars(return_obj).iteritems():
 
491
            if name == resp_type.lower():   # queues, Queues, this is the list its self which we populated above
 
492
                # the list its self.
 
493
                continue
 
494
            value = _fill_data_minidom(enum_results, name, value)
 
495
            if value is not None:
 
496
                setattr(return_obj, name, value)
 
497
 
 
498
    setattr(return_obj, resp_type.lower(), items)
 
499
    return return_obj
 
500
 
 
501
def _parse_simple_list(response, type, item_type, list_name):
 
502
    respbody = response.body
 
503
    res = type()
 
504
    res_items = []
 
505
    doc = minidom.parseString(respbody)
 
506
    type_name = type.__name__
 
507
    item_name = item_type.__name__
 
508
    for item in _get_children_from_path(doc, type_name, item_name):
 
509
        res_items.append(_fill_instance_element(item, item_type))
 
510
 
 
511
    setattr(res, list_name, res_items)
 
512
    return res
 
513
 
 
514
def _parse_response(response, return_type):  
 
515
    '''
 
516
    parse the HTTPResponse's body and fill all the data into a class of return_type
 
517
    '''
 
518
    return _parse_response_body_from_xml_text(response.body, return_type)
 
519
 
 
520
def _fill_data_to_return_object(node, return_obj):
 
521
    members = dict(vars(return_obj))
 
522
    for name, value in members.iteritems():
 
523
        if isinstance(value, _list_of):
 
524
            setattr(return_obj, name, _fill_list_of(node, value.list_type, value.xml_element_name))
 
525
        elif isinstance(value, _scalar_list_of):
 
526
            setattr(return_obj, name, _fill_scalar_list_of(node, value.list_type, _get_serialization_name(name), value.xml_element_name))
 
527
        elif isinstance(value, _dict_of):
 
528
            setattr(return_obj, name, _fill_dict_of(node, _get_serialization_name(name), value.pair_xml_element_name, value.key_xml_element_name, value.value_xml_element_name))
 
529
        elif isinstance(value, WindowsAzureData):
 
530
            setattr(return_obj, name, _fill_instance_child(node, name, value.__class__))
 
531
        elif isinstance(value, dict):
 
532
            setattr(return_obj, name, _fill_dict(node, _get_serialization_name(name)))
 
533
        elif isinstance(value, _Base64String):
 
534
            value = _fill_data_minidom(node, name, '')
 
535
            if value is not None:
 
536
                value = base64.b64decode(value)
 
537
                try:
 
538
                    value = value.decode('utf-8')
 
539
                except: pass
 
540
            #always set the attribute, so we don't end up returning an object with type _Base64String
 
541
            setattr(return_obj, name, value)
 
542
        else:
 
543
            value = _fill_data_minidom(node, name, value)
 
544
            if value is not None:
 
545
                setattr(return_obj, name, value)
 
546
 
 
547
def _parse_response_body_from_xml_node(node, return_type):
 
548
    '''
 
549
    parse the xml and fill all the data into a class of return_type
 
550
    '''
 
551
    return_obj = return_type()
 
552
    _fill_data_to_return_object(node, return_obj)
 
553
 
 
554
    return return_obj
 
555
 
 
556
def _parse_response_body_from_xml_text(respbody, return_type):
 
557
    '''
 
558
    parse the xml and fill all the data into a class of return_type
 
559
    '''
 
560
    doc = minidom.parseString(respbody)
 
561
    return_obj = return_type()
 
562
    for node in _get_child_nodes(doc, return_type.__name__):
 
563
        _fill_data_to_return_object(node, return_obj)
 
564
 
 
565
    return return_obj
 
566
 
 
567
class _dict_of(dict):
 
568
    """a dict which carries with it the xml element names for key,val.
 
569
    Used for deserializaion and construction of the lists"""
 
570
    def __init__(self, pair_xml_element_name, key_xml_element_name, value_xml_element_name):
 
571
        self.pair_xml_element_name = pair_xml_element_name
 
572
        self.key_xml_element_name = key_xml_element_name
 
573
        self.value_xml_element_name = value_xml_element_name
 
574
 
 
575
class _list_of(list):
 
576
    """a list which carries with it the type that's expected to go in it.
 
577
    Used for deserializaion and construction of the lists"""
 
578
    def __init__(self, list_type, xml_element_name=None):
 
579
        self.list_type = list_type
 
580
        if xml_element_name is None:
 
581
            self.xml_element_name = list_type.__name__
 
582
        else:
 
583
            self.xml_element_name = xml_element_name
 
584
 
 
585
class _scalar_list_of(list):
 
586
    """a list of scalar types which carries with it the type that's 
 
587
    expected to go in it along with its xml element name.
 
588
    Used for deserializaion and construction of the lists"""
 
589
    def __init__(self, list_type, xml_element_name):
 
590
        self.list_type = list_type
 
591
        self.xml_element_name = xml_element_name
 
592
 
 
593
def _update_request_uri_query_local_storage(request, use_local_storage):
 
594
    ''' create correct uri and query for the request '''
 
595
    uri, query = _update_request_uri_query(request)
 
596
    if use_local_storage:
 
597
        return '/' + DEV_ACCOUNT_NAME + uri, query
 
598
    return uri, query
 
599
 
 
600
def _update_request_uri_query(request):
 
601
    '''pulls the query string out of the URI and moves it into 
 
602
    the query portion of the request object.  If there are already
 
603
    query parameters on the request the parameters in the URI will
 
604
    appear after the existing parameters'''
 
605
 
 
606
    if '?' in request.path:
 
607
        request.path, _, query_string = request.path.partition('?')
 
608
        if query_string:
 
609
            query_params = query_string.split('&')
 
610
            for query in query_params:
 
611
                if '=' in query:
 
612
                    name, _, value = query.partition('=')
 
613
                    request.query.append((name, value))
 
614
 
 
615
    request.path = urllib2.quote(request.path, '/()$=\',')
 
616
 
 
617
    #add encoded queries to request.path. 
 
618
    if request.query:
 
619
        request.path += '?' 
 
620
        for name, value in request.query:
 
621
            if value is not None:
 
622
                request.path += name + '=' + urllib2.quote(value, '/()$=\',') + '&'
 
623
        request.path = request.path[:-1]
 
624
 
 
625
    return request.path, request.query
 
626
 
 
627
def _dont_fail_on_exist(error):
 
628
    ''' don't throw exception if the resource exists. This is called by create_* APIs with fail_on_exist=False'''
 
629
    if isinstance(error, WindowsAzureConflictError):
 
630
        return False
 
631
    else:
 
632
        raise error
 
633
 
 
634
def _dont_fail_not_exist(error):
 
635
    ''' don't throw exception if the resource doesn't exist. This is called by create_* APIs with fail_on_exist=False'''
 
636
    if isinstance(error, WindowsAzureMissingResourceError):
 
637
        return False
 
638
    else:
 
639
        raise error
 
640
 
 
641
def _general_error_handler(http_error):
 
642
    ''' Simple error handler for azure.'''
 
643
    if http_error.status == 409:
 
644
        raise WindowsAzureConflictError(_ERROR_CONFLICT)
 
645
    elif http_error.status == 404:
 
646
        raise WindowsAzureMissingResourceError(_ERROR_NOT_FOUND)
 
647
    else:
 
648
        if http_error.respbody is not None:
 
649
            raise WindowsAzureError(_ERROR_UNKNOWN % http_error.message + '\n' + http_error.respbody)
 
650
        else:
 
651
            raise WindowsAzureError(_ERROR_UNKNOWN % http_error.message)
 
652
    
 
653
def _parse_response_for_dict(response):
 
654
    ''' Extracts name-values from response header. Filter out the standard http headers.'''
 
655
    
 
656
    if response is None:
 
657
        return None
 
658
    http_headers = ['server', 'date', 'location', 'host', 
 
659
                    'via', 'proxy-connection', 'connection']
 
660
    return_dict = HeaderDict()
 
661
    if response.headers:
 
662
        for name, value in response.headers:
 
663
            if not name.lower() in http_headers:
 
664
                return_dict[name] = value
 
665
 
 
666
    return return_dict
 
667
 
 
668
def _parse_response_for_dict_prefix(response, prefixes):
 
669
    ''' Extracts name-values for names starting with prefix from response header. Filter out the standard http headers.'''
 
670
 
 
671
    if response is None:
 
672
        return None
 
673
    return_dict = {}    
 
674
    orig_dict = _parse_response_for_dict(response)
 
675
    if orig_dict:
 
676
        for name, value in orig_dict.iteritems():
 
677
            for prefix_value in prefixes:
 
678
                if name.lower().startswith(prefix_value.lower()):
 
679
                    return_dict[name] = value
 
680
                    break
 
681
        return return_dict
 
682
    else:
 
683
        return None
 
684
 
 
685
def _parse_response_for_dict_filter(response, filter):
 
686
    ''' Extracts name-values for names in filter from response header. Filter out the standard http headers.'''
 
687
    if response is None:
 
688
        return None
 
689
    return_dict = {}    
 
690
    orig_dict = _parse_response_for_dict(response)
 
691
    if orig_dict:
 
692
        for name, value in orig_dict.iteritems():
 
693
            if name.lower() in filter:
 
694
                return_dict[name] = value
 
695
        return return_dict
 
696
    else:
 
697
        return None