@@ -76,11 +76,12 @@ def _set_logging(self):
76
76
77
77
return logger
78
78
79
- def _get_request_format (self , api = None , call = None ):
79
+ def _get_request_format (self , api = None , call = None , use_cookie_auth = False ):
80
80
if not api : api = self .API_TYPE_SOAP
81
81
return {
82
82
'api' : api ,
83
83
'call' : call ,
84
+ 'use_cookie_auth' : use_cookie_auth ,
84
85
'query' : None ,
85
86
'data' : None ,
86
87
}
@@ -108,6 +109,9 @@ def _request(self, request, auth_required=True):
108
109
REST API calls this will be a dict converted to JSON automatically
109
110
by this method
110
111
112
+ use_cookie_auth
113
+ Whether or not to use an HTTP Cookie in lieu of a querystring for authorization
114
+
111
115
## Output
112
116
113
117
Returns a dict:
@@ -139,9 +143,9 @@ def _request(self, request, auth_required=True):
139
143
# add the authentication parameters
140
144
if auth_required :
141
145
if request ['api' ] == self .API_TYPE_REST :
142
- # sID is a query string
143
- if not request ['query' ]: request ['query' ] = {}
144
- request ['query' ]['sID' ] = self ._sessions [self .API_TYPE_REST ]
146
+ if not request [ 'use_cookie_auth' ]: # sID is a query string
147
+ if not request ['query' ]: request ['query' ] = {}
148
+ request ['query' ]['sID' ] = self ._sessions [self .API_TYPE_REST ]
145
149
elif request ['api' ] == self .API_TYPE_SOAP :
146
150
# sID is part of the data
147
151
if not request ['data' ]: request ['data' ] = {}
@@ -182,6 +186,11 @@ def _request(self, request, auth_required=True):
182
186
183
187
# authentication calls don't accept the Accept header
184
188
if request ['call' ].startswith ('authentication' ): del (headers ['Accept' ])
189
+
190
+ # some rest calls use a cookie to pass the sID
191
+ if request ['api' ] == self .API_TYPE_REST and request ['use_cookie_auth' ]:
192
+ headers ['Cookie' ] = 'sID="{}"' .format (self ._sessions [self .API_TYPE_REST ])
193
+
185
194
if request ['api' ] == self .API_TYPE_REST and request ['call' ] in [
186
195
'apiVersion' ,
187
196
'status/manager/ping'
@@ -229,6 +238,7 @@ def _request(self, request, auth_required=True):
229
238
result = {
230
239
'status' : response .getcode () if response else None ,
231
240
'raw' : response .read () if response else None ,
241
+ 'headers' : dict (response .headers ) if response else dict (),
232
242
'data' : None
233
243
}
234
244
bytes_of_data = len (result ['raw' ]) if result ['raw' ] else 0
@@ -254,12 +264,14 @@ def _request(self, request, auth_required=True):
254
264
else :
255
265
# JSON response
256
266
try :
257
- if result ['raw' ]:
258
- result ['data' ] = json .loads (result ['raw' ])
267
+ if result ['raw' ] and result ['status' ] != 204 :
268
+ result ['type' ] = result ['headers' ]['content-type' ]
269
+ result ['data' ] = json .loads (result ['raw' ]) if 'json' in result ['type' ] else None
259
270
except Exception , json_err :
260
271
# report the exception as 'info' because it's not fatal and the data is
261
272
# still captured in result['raw']
262
273
self .log ("Could not convert response from call {} to JSON. Threw exception:\n \t {}" .format (request ['call' ], json_err ), level = 'info' )
274
+
263
275
return result
264
276
265
277
def _prefix_keys (self , prefix , d ):
@@ -270,7 +282,7 @@ def _prefix_keys(self, prefix, d):
270
282
if not type (d ) == type ({}): return d
271
283
new_d = d .copy ()
272
284
for k ,v in d .items ():
273
- new_key = "{}:{}" .format (prefix , k )
285
+ new_key = u "{}:{}" .format (prefix , k )
274
286
new_v = v
275
287
if type (v ) == type ({}): new_v = self ._prefix_keys (prefix , v )
276
288
new_d [new_key ] = new_v
@@ -283,7 +295,7 @@ def _prep_data_for_soap(self, call, details):
283
295
Prepare the complete XML SOAP envelope
284
296
"""
285
297
data = xmltodict .unparse (self ._prefix_keys ('ns1' , { call : details }), pretty = False , full_document = False )
286
- soap_xml = """
298
+ soap_xml = u """
287
299
<?xml version="1.0" encoding="UTF-8"?>
288
300
<SOAP-ENV:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:Manager" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
289
301
<SOAP-ENV:Header/>
@@ -293,7 +305,10 @@ def _prep_data_for_soap(self, call, details):
293
305
</SOAP-ENV:Envelope>
294
306
""" .format (data ).strip ()
295
307
296
- return soap_xml
308
+ # convert any nil values to the proper format
309
+ soap_xml = re .sub (r'<([^>]+)></\1>' , r'<\1 xsi:nil="true" />' , soap_xml )
310
+
311
+ return soap_xml .encode ('utf-8' )
297
312
298
313
def log (self , message = '' , err = None , level = 'info' ):
299
314
"""
@@ -432,4 +447,112 @@ def _set_properties(self, api_response, log_func):
432
447
setattr (self , new_key , val )
433
448
except Exception , err :
434
449
if log_func :
435
- log_func ("Could not set property {} to value {} for object {}" .format (k , v , s ))
450
+ log_func ("Could not set property {} to value {} for object {}" .format (k , v , s ))
451
+ try :
452
+ setattr (self , log , log_func )
453
+ except : pass
454
+
455
+ def to_dict (self ):
456
+ """
457
+ Convert the object properties to API keypairs
458
+ """
459
+ result = {}
460
+
461
+ api_properties = translation .Terms .api_to_new .values ()
462
+
463
+ for p in dir (self ):
464
+ if p in api_properties :
465
+ key = translation .Terms .get_reverse (p )
466
+ val = getattr (self , p )
467
+ result [key ] = val
468
+
469
+ return result
470
+
471
+ class CoreList (list ):
472
+ def __init__ (self , * args ):
473
+ super (CoreList , self ).__init__ (args )
474
+ self ._exempt_from_find = []
475
+
476
+ def find (self , ** kwargs ):
477
+ """
478
+ Find any items where the values match the cumulative kwargs patterns and return their indices
479
+
480
+ If a keyword's value is a list, .find will match on any value for that keyword
481
+
482
+ .find(id=1)
483
+ >>> returns any item with a property 'id' and value in [1]
484
+ possibilities:
485
+ { 'id': 1, 'name': 'One'}
486
+ { 'id': 1, 'name': 'Two'}
487
+
488
+ .find(id=[1,2])
489
+ >>> returns any item with a property 'id' and value in [1,2]
490
+ possibilities:
491
+ { 'id': 1, 'name': 'One'}
492
+ { 'id': 2, 'name': 'One'}
493
+ { 'id': 1, 'name': 'Two'}
494
+ { 'id': 2, 'name': 'Two'}
495
+
496
+ .find(id=1, name='One')
497
+ >>> returns any item with a property 'id' and value in [1] AND a property 'name' and value in ['One']
498
+ possibilities:
499
+ { 'id': 1, 'name': 'One'}
500
+
501
+ .find(id=[1,2], name='One')
502
+ >>> returns any item with a property 'id' and value in [1,2] AND a property 'name' and value in ['One']
503
+ possibilities:
504
+ { 'id': 1, 'name': 'One'}
505
+ { 'id': 2, 'name': 'One'}
506
+
507
+ .find(id=[1,2], name=['One,Two'])
508
+ >>> returns any item with a property 'id' and value in [1,2] AND a property 'name' and value in ['One','Two']
509
+ possibilities:
510
+ { 'id': 1, 'name': 'One'}
511
+ { 'id': 2, 'name': 'One'}
512
+ { 'id': 1, 'name': 'Two'}
513
+ { 'id': 2, 'name': 'Two'}
514
+ """
515
+ results = []
516
+
517
+ if kwargs :
518
+ for item_id , item in enumerate (self ):
519
+ item_matches = False
520
+ for match_attr , match_attr_vals in kwargs .items ():
521
+ if not type (match_attr_vals ) == type ([]): match_attr_vals = [match_attr_vals ]
522
+
523
+ # does the current item have the property
524
+ attr_to_check = None
525
+ if match_attr in dir (item ):
526
+ attr_to_check = getattr (item , match_attr )
527
+ elif 'has_key' in dir (item ) and item .has_key (match_attr ):
528
+ attr_to_check = item [match_attr ]
529
+
530
+ if attr_to_check :
531
+ # does the property match the specified values?
532
+ for match_attr_val in match_attr_vals :
533
+ if type (attr_to_check ) in [type ('' ), type (u'' )]:
534
+ # string comparison
535
+ match = re .search (r'{}' .format (match_attr_val ), attr_to_check )
536
+ if match :
537
+ item_matches = True
538
+ break # and move on to the new kwarg
539
+ else :
540
+ item_matches = False
541
+ elif type (attr_to_check ) == type ([]):
542
+ # check for the match in the list
543
+ if match_attr_val in attr_to_check :
544
+ item_matches = True
545
+ break # and move on to the new kwarg
546
+ else :
547
+ item_matches = False
548
+ else :
549
+ # object comparison
550
+ if attr_to_check == match_attr_val :
551
+ item_matches = True
552
+ break # and move on to the new kwarg
553
+ else :
554
+ item_matches = False
555
+
556
+ if item_matches : results .append (item_id )
557
+
558
+ return results
0 commit comments