-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathssc.py
169 lines (136 loc) · 5.58 KB
/
ssc.py
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
'''
An utility which issues requests to remote Shapeshifter instances. Can be used in two ways:
1. by importing it in a python script and calling request();
2. by running it from the command line.
'''
import httplib
import mimetypes
import os
import sys
import threading
import traceback
__author__ = 'Maciek Makowski'
__version__ = '1.1.0'
default_port = 5457
def _encode_multipart_formdata(fields, files):
"""
fields is a sequence of (name, value) elements for regular form fields.
files is a sequence of (name, filename, value) elements for data to be uploaded as files
Return (content_type, body) ready for httplib.HTTP instance
Taken from http://code.activestate.com/recipes/146306/
"""
BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
CRLF = '\r\n'
L = []
for (key, value) in fields:
L.append('--' + BOUNDARY)
L.append('Content-Disposition: form-data; name="%s"' % key)
L.append('')
L.append(value)
for (key, filename, value) in files:
L.append('--' + BOUNDARY)
L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
L.append('Content-Type: %s' % _get_content_type(filename))
L.append('')
L.append(value)
L.append('--' + BOUNDARY + '--')
L.append('')
body = CRLF.join(L)
content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
return content_type, body
def _get_content_type(filename):
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
def _file_content(path):
with open(path, 'rb') as f: return f.read()
def _is_success_status(http_status):
return http_status < 400
class Result(object):
'''
A result of a request to Shapeshifter instance. The field 'success' indicates
if an invocation was succesful.
'''
def __init__(self, response=None, exception=None):
self.success = exception == None and response != None and _is_success_status(response.status)
self.response = response
self.exception = exception
def __str__(self):
return '%s: %s' % ('SUCCESS' if self.success else 'FAILURE',
self.__format_response() if self.response != None else self.__format_exception())
def __format_response(self):
resp_str = self.response.read()
if len(resp_str) == 0:
fmt_resp = ''
elif not '\n' in resp_str:
fmt_resp = ': %s' % resp_str
else:
fmt_resp = ':\n%s' % resp_str
return '%s, %s%s' % (self.response.status, self.response.reason, fmt_resp)
def __format_exception(self):
return self.exception
class _Requester(threading.Thread):
def __init__(self, target, method, uri, form_data, content_type):
threading.Thread.__init__(self)
self.target = target
self.method = method
self.uri = uri
self.form_data = form_data
self.content_type = content_type
self.result = None
def run(self):
try:
conn = httplib.HTTPConnection(self.target)
conn.request(self.method, self.uri, self.form_data, {'Content-type': self.content_type})
self.result = Result(response = conn.getresponse())
except Exception, e:
self.result = Result(exception = traceback.format_exc(e))
def request(targets, uri, method='POST', properties={}, files={}):
'''
Sends a request to Shapeshifter web servers.
- targets: a list of web server URLs, each in format "host:port" or "host", in which case the default
port will be assumed
- uri: the URI to request from each server
- method: the HTTP method to use
- properties: text fields to be submitted in the request
- files: files to be sent in the request; each entry should map a form id to a local file path, the file
will be read and its contents sent.
Returns a dictionary mapping server names (as specified in targets list) to Result objects.
'''
targets = [t if ':' in t else '%s:%s' % (t, default_port) for t in targets]
file_data = [(id, os.path.basename(path), _file_content(path)) for (id, path) in files.items()]
content_type, form_data = _encode_multipart_formdata(properties.items(), file_data)
requesters = []
for target in targets:
requester = _Requester(target, method, uri, form_data, content_type)
requesters.append(requester)
requester.start()
results = dict()
for requester in requesters:
requester.join()
results[requester.target] = requester.result
return results
def _print_usage():
print 'ssc targets method URI [mappings]'
def _split_form_data(data_map):
properties = dict()
files = dict()
for (key, value) in [tuple(m.split('=')) for m in data_map.split(',')]:
if value.startswith('file:'):
files[key] = value[5:]
else:
properties[key] = value
return (properties, files)
def _main():
if len(sys.argv) < 4:
_print_usage()
sys.exit(2)
targets = sys.argv[1].split(',')
method = sys.argv[2]
uri = sys.argv[3]
(properties, files) = _split_form_data(sys.argv[4]) if len(sys.argv) > 4 else ({}, {})
results = request(targets, uri, method, properties, files)
for (target, result) in results.items():
print '-- invocation result for %s --' % target
print result
print '----'
if __name__ == '__main__':
_main()