root / commissioning / clients / http.py @ 9f1a1bd0
History | View | Annotate | Download (7.8 kB)
1 |
#!/usr/bin/env python
|
---|---|
2 |
|
3 |
from httplib import HTTPConnection, HTTPException |
4 |
from urlparse import urlparse |
5 |
from commissioning import Callpoint |
6 |
|
7 |
import logging |
8 |
|
9 |
from json import loads as json_loads, dumps as json_dumps |
10 |
|
11 |
|
12 |
def init_logger_file(name, level='DEBUG'): |
13 |
logger = logging.getLogger(name) |
14 |
handler = logging.FileHandler(name + '.log')
|
15 |
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
16 |
handler.setFormatter(formatter) |
17 |
logger.addHandler(handler) |
18 |
level = getattr(logging, level, logging.DEBUG)
|
19 |
logger.setLevel(level) |
20 |
return logger
|
21 |
|
22 |
def init_logger_stdout(name, level='DEBUG'): |
23 |
logger = logging.getLogger(name) |
24 |
from sys import stdout |
25 |
handler = logging.StreamHandler(stdout) |
26 |
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
27 |
handler.setFormatter(formatter) |
28 |
logger.addHandler(handler) |
29 |
level = getattr(logging, level, logging.DEBUG)
|
30 |
logger.setLevel(level) |
31 |
return logger
|
32 |
|
33 |
|
34 |
class NULLConnection(object): |
35 |
|
36 |
def __init__(self, host, port): |
37 |
self.host = host
|
38 |
self.port = port
|
39 |
|
40 |
def request(self, *args): |
41 |
return None |
42 |
|
43 |
def getresponse(self, *args): |
44 |
return None |
45 |
|
46 |
|
47 |
class quota_holder_allocation(object): |
48 |
client = None
|
49 |
serial = None
|
50 |
|
51 |
def __init__(self, *allocations, **pre_allocations): |
52 |
allocations = canonify_list_of_allocations(allocations) |
53 |
pre_allocations = pre_allocations.items() |
54 |
pre_allocations = [(name, pre, 0) for name, pre in pre_allocations] |
55 |
pre_allocations = canonify_list_of_allocations(pre_allocations) |
56 |
allocations.extend(pre_allocations) |
57 |
self.allocations = allocations
|
58 |
|
59 |
if self.client is None: |
60 |
self.client = QuotaholderClient.instance()
|
61 |
|
62 |
def __call__(self, func): |
63 |
def wrapped(*args, **kw): |
64 |
client = self.client
|
65 |
serial = client.get_provision(self.allocations)
|
66 |
try:
|
67 |
ret = func(*args, **kw) |
68 |
except Exception: |
69 |
client.recall_provision(serial) |
70 |
raise
|
71 |
else:
|
72 |
client.commit_provision(serial) |
73 |
|
74 |
return ret
|
75 |
|
76 |
return wrapped
|
77 |
|
78 |
def __enter__(self): |
79 |
self.serial = self.client.get_provision(self.allocations) |
80 |
|
81 |
def __exit__(self, exctype, exc, traceback): |
82 |
serial = self.serial
|
83 |
client = self.client
|
84 |
|
85 |
if exctype is None: |
86 |
client.commit_provision(serial) |
87 |
else:
|
88 |
client.recall_provision(serial) |
89 |
raise
|
90 |
|
91 |
|
92 |
class GenericHTTPClient(Callpoint): |
93 |
"""Synchronous http client for quota holder API"""
|
94 |
|
95 |
appname = 'http'
|
96 |
_http_client = None
|
97 |
quota_holder_allocation = quota_holder_allocation |
98 |
|
99 |
def init_connection(self, connection): |
100 |
self.url = connection
|
101 |
|
102 |
class quota_holder_allocator_class(quota_holder_allocation): |
103 |
client = self
|
104 |
|
105 |
self.quota_holder_allocation = quota_holder_allocator_class
|
106 |
self.logger = logging.getLogger(self.appname) |
107 |
|
108 |
@classmethod
|
109 |
def instance(cls): |
110 |
client = cls._http_client |
111 |
if client is None: |
112 |
url = "http://127.0.0.1:8000/%s/%s" % (cls.appname, cls.version)
|
113 |
client = cls(url) |
114 |
self._http_client = client
|
115 |
|
116 |
return client
|
117 |
|
118 |
def commit(self): |
119 |
return
|
120 |
|
121 |
def rollback(self): |
122 |
return
|
123 |
|
124 |
def debug(self, fmt, *args): |
125 |
self.logger.debug(fmt % args)
|
126 |
|
127 |
def do_make_call(self, api_call, data): |
128 |
url = urlparse(self.url)
|
129 |
scheme = url.scheme |
130 |
Connection = HTTPConnection |
131 |
if scheme == 'http': |
132 |
port = 80
|
133 |
elif scheme == 'https': |
134 |
port = 443
|
135 |
elif scheme == 'null': |
136 |
Connection = NULLConnection |
137 |
else:
|
138 |
raise ValueError("Unsupported scheme %s" % (scheme,)) |
139 |
|
140 |
path = url.path.strip('/')
|
141 |
path = ('/' + path + '/' + api_call) if path else ('/' + api_Call) |
142 |
|
143 |
netloc = url.netloc.rsplit(':', 1) |
144 |
netloclen = len(netloc)
|
145 |
if netloclen == 1: |
146 |
host = netloc[0]
|
147 |
elif netloclen == 2: |
148 |
host, port = netloc |
149 |
else:
|
150 |
msg = "Unsupported network location type '%s'" % (netloc,)
|
151 |
raise ValueError(msg) |
152 |
|
153 |
self.debug("Connecting to %s:%s\n>>>", host, port) |
154 |
conn = Connection(host, port) |
155 |
|
156 |
if (api_call.startswith('list') or |
157 |
api_call.startswith('get') or |
158 |
api_call.startswith('read')):
|
159 |
|
160 |
method = 'GET'
|
161 |
else:
|
162 |
method = 'POST'
|
163 |
|
164 |
json_data = self.json_dumps(data)
|
165 |
self.debug("%s %s\n%s\n<<<\n", method, path, json_data) |
166 |
|
167 |
req = conn.request(method, path, body=json_data) |
168 |
resp = conn.getresponse() |
169 |
self.debug(">>>\nStatus: %s", resp.status) |
170 |
|
171 |
for name, value in resp.getheaders(): |
172 |
self.debug("%s: %s", name, value) |
173 |
|
174 |
body = ''
|
175 |
while 1: |
176 |
s = resp.read() |
177 |
if not s: |
178 |
break
|
179 |
body += s |
180 |
|
181 |
self.debug("\n%s\n<<<\n", body) |
182 |
|
183 |
status = int(resp.status)
|
184 |
if status == 200: |
185 |
if body:
|
186 |
body = json_loads(body) |
187 |
return body
|
188 |
else:
|
189 |
return body
|
190 |
|
191 |
raise IOError("Call Failed", str(resp.status)) |
192 |
|
193 |
API_Callpoint = GenericHTTPClient |
194 |
|
195 |
|
196 |
def main(): |
197 |
from sys import argv, stdout |
198 |
from os.path import basename, expanduser |
199 |
from time import time |
200 |
from commissioning import get_callpoint |
201 |
|
202 |
progname = basename(argv[0])
|
203 |
if progname == 'http.py': |
204 |
if len(argv) < 2: |
205 |
usage = "./http.py <appname> <app args...>"
|
206 |
print(usage) |
207 |
raise SystemExit |
208 |
|
209 |
argv = argv[1:]
|
210 |
progname = basename(argv[0])
|
211 |
|
212 |
init_logger_stdout(progname) |
213 |
|
214 |
pointname = 'clients.' + progname
|
215 |
API_Callpoint = get_callpoint(pointname, automake='http')
|
216 |
api = API_Callpoint.api_spec |
217 |
|
218 |
usage = "API Calls:\n\n"
|
219 |
|
220 |
for call_name in sorted(api.call_names()): |
221 |
canonical = api.input_canonical(call_name) |
222 |
argstring = canonical.tostring(multiline=1, showopts=0) |
223 |
usage += call_name + '.' + argstring + '\n\n' |
224 |
|
225 |
import argparse |
226 |
parser = argparse.ArgumentParser ( |
227 |
formatter_class = argparse.RawDescriptionHelpFormatter, |
228 |
description = "%s http client" % (progname,),
|
229 |
epilog = usage, |
230 |
) |
231 |
|
232 |
urlhelp = 'set %s server base url' % (progname,)
|
233 |
parser.add_argument('--url', type=str, dest='url', |
234 |
action='store', help=urlhelp)
|
235 |
|
236 |
jsonhelp = 'intepret data as json'
|
237 |
parser.add_argument('--json', dest='json_data', action='store_false', |
238 |
default=True, help=jsonhelp)
|
239 |
|
240 |
callhelp = 'api call to perform'
|
241 |
parser.add_argument('api_call', type=str, action='store', nargs=1, |
242 |
help=callhelp) |
243 |
|
244 |
arghelp = 'data to provide to api call'
|
245 |
parser.add_argument('data', type=str, action='store', nargs='?', |
246 |
help=callhelp) |
247 |
|
248 |
urlfilepath = expanduser('~/.qholderrc')
|
249 |
|
250 |
def get_url(): |
251 |
try:
|
252 |
with open(urlfilepath) as f: |
253 |
url = f.read() |
254 |
except Exception: |
255 |
m = "Cannot load url from %s. Try --url." % (urlfilepath,)
|
256 |
raise ValueError(m) |
257 |
return url
|
258 |
|
259 |
def set_url(url): |
260 |
url = url.strip('/')
|
261 |
with open(urlfilepath, "w") as f: |
262 |
f.write(url) |
263 |
print "Base URL set to '%s'" % (url,) |
264 |
|
265 |
args = parser.parse_args(argv[1:])
|
266 |
|
267 |
api_call = args.api_call[0]
|
268 |
api.input_canonical(api_call) |
269 |
|
270 |
if args.url:
|
271 |
set_url(args.url) |
272 |
|
273 |
url = get_url() |
274 |
|
275 |
data = args.data |
276 |
|
277 |
if data == '-': |
278 |
from sys import stdin |
279 |
data = stdin.read() |
280 |
|
281 |
if not data: |
282 |
data = None
|
283 |
|
284 |
client = API_Callpoint(url) |
285 |
print(client.make_call_from_json(api_call, data)) |
286 |
|
287 |
|
288 |
if __name__ == '__main__': |
289 |
main() |
290 |
|