-
Notifications
You must be signed in to change notification settings - Fork 195
Expand file tree
/
Copy pathtransport.py
More file actions
182 lines (133 loc) · 5.41 KB
/
transport.py
File metadata and controls
182 lines (133 loc) · 5.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
"""
SoftLayer.transports.transport
~~~~~~~~~~~~~~~~~~~~
Common functions for transporting API requests
:license: MIT, see LICENSE for more details.
"""
import base64
import json
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from SoftLayer import utils
def get_session(user_agent):
"""Sets up urllib sessions"""
client = requests.Session()
client.headers.update({
'Content-Type': 'application/json',
'User-Agent': user_agent,
})
retry = Retry(total=3, connect=1, backoff_factor=1)
adapter = HTTPAdapter(max_retries=retry)
client.mount('https://', adapter)
return client
# transports.Request does have a lot of instance attributes. :(
# pylint: disable=too-many-instance-attributes
class Request(object):
"""Transport request object."""
def __init__(self):
#: API service name. E.G. SoftLayer_Account
self.service = None
#: API method name. E.G. getObject
self.method = None
#: API Parameters.
self.args = tuple()
#: URL Parameters, used for the REST Transport
self.params = None
#: API headers, used for authentication, masks, limits, offsets, etc.
self.headers = {}
#: Transport user.
self.transport_user = None
#: Transport password.
self.transport_password = None
#: Transport headers.
self.transport_headers = {}
#: False -> Don't verify the SSL certificate
#: True -> Verify the SSL certificate
#: Path String -> Verify the SSL certificate with the .pem file at path
self.verify = None
#: Client certificate file path. (Used by X509Authentication)
self.cert = None
#: InitParameter/identifier of an object.
self.identifier = None
#: SoftLayer mask (dict or string).
self.mask = None
#: SoftLayer Filter (dict).
self.filter = None
#: Integer result limit.
self.limit = None
#: Integer result offset.
self.offset = None
#: Integer call start time
self.start_time = None
#: Integer call end time
self.end_time = None
#: String full url
self.url = None
#: String result of api call
self.result = None
#: String payload to send in
self.payload = None
#: Exception any exceptions that got caught
self.exception = None
def __repr__(self):
"""Prints out what this call is all about"""
pretty_mask = utils.clean_string(self.mask)
pretty_filter = self.filter
clean_args = self.args
# Passwords can show up here, so censor them before logging.
if self.method in ["performExternalAuthentication", "refreshEncryptedToken",
"getPortalLoginToken", "getEncryptedSessionToken"]:
clean_args = "*************"
param_string = (f"id={self.identifier}, mask='{pretty_mask}', filter='{pretty_filter}', args={clean_args}, "
f"limit={self.limit}, offset={self.offset}")
return "{service}::{method}({params})".format(
service=self.service, method=self.method, params=param_string)
def special_rest_params(self):
"""This method is to handle the edge case of SoftLayer_User_Employee::getEncryptedSessionToken
Added this method here since it was a little easier to change the data as needed this way.
"""
if self.method == "getEncryptedSessionToken" and self.service == "SoftLayer_User_Employee":
if len(self.args) < 3:
return
self.params = {"remoteToken": self.args[2]}
self.transport_user = self.args[0]
self.transport_password = self.args[1]
self.args = []
class SoftLayerListResult(list):
"""A SoftLayer API list result."""
def __init__(self, items=None, total_count=0):
#: total count of items that exist on the server. This is useful when
#: paginating through a large list of objects.
self.total_count = total_count
super().__init__(items)
def get_total_items(self):
"""A simple getter to totalCount, but its called getTotalItems since that is the header returned"""
return self.total_count
def _proxies_dict(proxy):
"""Makes a proxy dict appropriate to pass to requests."""
if not proxy:
return None
return {'http': proxy, 'https': proxy}
def _format_object_mask(objectmask):
"""Format the new style object mask.
This wraps the user mask with mask[USER_MASK] if it does not already
have one. This makes it slightly easier for users.
:param objectmask: a string-based object mask
"""
objectmask = objectmask.strip()
if (not objectmask.startswith('mask') and
not objectmask.startswith('[') and
not objectmask.startswith('filteredMask')):
objectmask = "mask[%s]" % objectmask
return objectmask
class ComplexEncoder(json.JSONEncoder):
"""ComplexEncoder helps jsonencoder deal with byte strings"""
def default(self, o):
"""Encodes o as JSON"""
# Base64 encode bytes type objects.
if isinstance(o, bytes):
base64_bytes = base64.b64encode(o)
return base64_bytes.decode("utf-8")
# Let the base class default method raise the TypeError
return json.JSONEncoder.default(self, o)