/[gli]/trunk/src/net/server/gliserv.py
Gentoo

Contents of /trunk/src/net/server/gliserv.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 950 - (show annotations) (download) (as text)
Sun Oct 9 17:54:40 2005 UTC (12 years, 4 months ago) by agaffney
File MIME type: text/x-python
File size: 14040 byte(s)
move handlers into subdirectory

1 #!/usr/bin/python
2
3 import sys
4 sys.path.append("../..")
5 import os
6 import posixpath
7 import BaseHTTPServer
8 import urllib
9 import shutil
10 import mimetypes
11 from StringIO import StringIO
12 from threading import *
13 import socket
14 import SocketServer
15 import SimpleXMLRPCServer
16 import mimetools
17 import GLIServerProfile
18 import traceback
19
20 class SharedInfo(object):
21
22 __shared_state = { 'client_state': {}, 'last_visitor': "", 'clients': [], 'profiles': [] }
23
24 def __init__(self):
25 self.__dict__ = self.__shared_state
26
27 class Params(dict):
28
29 def __getitem__(self, item):
30 try:
31 return dict.__getitem__(self, item)
32 except KeyError:
33 return ""
34
35 class GLIHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
36
37 def __init__(self, server_address):
38 self.port = server_address[1]
39 SocketServer.TCPServer.__init__(self, server_address, GLIHTTPRequestHandler)
40
41 class GLIHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
42
43 server_version = "GLIHTTP/0.1"
44
45 def __init__(self, request, client_address, parent):
46 self.shared_info = SharedInfo()
47 self.headers_out = []
48 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, client_address, parent)
49
50 def get_exception(self):
51 etype, value, tb = sys.exc_info()
52 s = traceback.format_exception(etype, value, tb)
53 content = "<pre>"
54 for line in s:
55 content += line
56 content += "</pre>"
57 return content
58
59 def wrap_in_template(self, content):
60 f = open("template.html", 'rb')
61 lines = f.readlines()
62 f.close()
63 for i in range(len(lines)):
64 if lines[i] == "Main content\n":
65 lines[i] = content
66 return "".join(lines)
67
68 def welcome(self):
69 return self.wrap_in_template("Do some shit on the left")
70
71 def showargs(self):
72 text = "These are the GET params you passed:<br><br><pre>"
73 text += str(self.get_params)
74 text += "</pre><br><br>These are the POST params you passed:<br><br><pre>"
75 text += str(self.post_params)
76 text += "</pre>"
77 return self.wrap_in_template(text)
78
79 def status(self):
80 return self.wrap_in_template("This is just a prototype, fool. There isn't anything to report")
81
82 def lastvisitor(self):
83 blah = "The last visitor was " + self.shared_info.last_visitor
84 self.shared_info.last_visitor = self.address_string()
85 return self.wrap_in_template(blah)
86
87 def showclients(self):
88 content = """
89 <h2>Clients</h2>
90 <table width="100%" cellpadding="0" cellpadding="0">
91 <tr>
92 <td><u>Name</u></td>
93 <td><u>MAC</u></td>
94 <td><u>Current IP</u></td>
95 <td><u>Post-install IP</u></td>
96 <td><u>Profile</u></td>
97 <td><u>Install progress</u></td>
98 </tr>
99 """
100 for client in self.shared_info.clients:
101 client_info = [client['name'], client['mac'], 'N/A', client['ip'], client['profile'], 'N/A']
102 if client['mac'] in self.shared_info.client_state:
103 client_info[2] = self.shared_info.client_state[client['mac']]['ip']
104 client_info[5] = self.shared_info.client_state[client['mac']]['install_status']
105 content += """
106 <tr>
107 <td>%s</td>
108 <td>%s</td>
109 <td>%s</td>
110 <td>%s</td>
111 <td>%s</td>
112 <td>%s</td>
113 </tr>
114 """ % tuple(client_info)
115 content += """
116 </table>
117 """
118 return self.wrap_in_template(content)
119
120 def parse_path(self):
121 self.get_params = Params()
122 self.post_params = Params()
123 pathparts = self.path.split("?")
124 self.path = pathparts[0]
125 if len(pathparts) > 1:
126 args = pathparts[1]
127 for arg in args.split("&"):
128 argparts = arg.split("=")
129 name = urllib.unquote(argparts[0])
130 if len(argparts) > 1:
131 data = urllib.unquote(argparts[1])
132 if name in self.get_params:
133 if isinstance(self.get_params[name], str):
134 self.get_params[name] = [self.get_params[name]]
135 self.get_params[name].append(data)
136 else:
137 self.get_params[name] = data
138 else:
139 self.get_params[name] = ""
140 else:
141 self.args = ""
142
143 def valid_boundary(self, s, _vb_pattern="^[ -~]{0,200}[!-~]$"):
144 import re
145 return re.match(_vb_pattern, s)
146
147 def parse_content_type(self, line):
148 plist = map(lambda x: x.strip(), line.split(';'))
149 key = plist.pop(0).lower()
150 pdict = {}
151 for p in plist:
152 i = p.find('=')
153 if i >= 0:
154 name = p[:i].strip().lower()
155 value = p[i+1:].strip()
156 if len(value) >= 2 and value[0] == value[-1] == '"':
157 value = value[1:-1]
158 value = value.replace('\\\\', '\\').replace('\\"', '"')
159 pdict[name] = value
160 return key, pdict
161
162 def do_HEAD(self):
163 """Serve a HEAD request."""
164 self.do_GET(head_only=True)
165
166 def do_POST(self, head_only=False):
167 self.parse_path()
168 maxlen = 0
169 ctype = self.headers.getheader('content-type')
170 if ctype == 'application/x-www-form-urlencoded':
171 clength = self.headers.getheader('content-length')
172 if clength:
173 try:
174 bytes = int(clength)
175 except ValueError:
176 pass
177 if maxlen and clength > maxlen:
178 raise ValueError, 'Maximum content length exceeded'
179 self.args = self.rfile.read(bytes)
180 if self.args:
181 for arg in self.args.split("&"):
182 argparts = arg.split("=")
183 name = urllib.unquote(argparts[0])
184 if len(argparts) > 1:
185 data = urllib.unquote(argparts[1])
186 if name in self.post_params:
187 if isinstance(self.post_params[name], str):
188 self.post_params[name] = [self.post_params[name]]
189 self.post_params[name].append(data)
190 else:
191 self.post_params[name] = data
192 else:
193 self.post_params[name] = ""
194 else:
195 # parse_multipart in /usr/lib/python2.4/cgi.py
196 ctype, pdict = self.parse_content_type(self.headers.getheader('content-type'))
197 boundary = ""
198 if 'boundary' in pdict:
199 boundary = pdict['boundary']
200 if not self.valid_boundary(boundary):
201 raise ValueError, ('Invalid boundary in multipart form: %r' % (boundary,))
202
203 nextpart = "--" + boundary
204 lastpart = "--" + boundary + "--"
205 partdict = {}
206 terminator = ""
207
208 while terminator != lastpart:
209 bytes = -1
210 data = None
211 if terminator:
212 # At start of next part. Read headers first.
213 headers = mimetools.Message(self.rfile)
214 clength = headers.getheader('content-length')
215 if clength:
216 try:
217 bytes = int(clength)
218 except ValueError:
219 pass
220 if bytes > 0:
221 if maxlen and bytes > maxlen:
222 raise ValueError, 'Maximum content length exceeded'
223 data = self.rfile.read(bytes)
224 else:
225 data = ""
226 # Read lines until end of part.
227 lines = []
228 while 1:
229 line = self.rfile.readline()
230 if not line:
231 terminator = lastpart # End outer loop
232 break
233 if line[:2] == "--":
234 terminator = line.strip()
235 if terminator in (nextpart, lastpart):
236 break
237 lines.append(line)
238 # Done with part.
239 if data is None:
240 continue
241 if bytes < 0:
242 if lines:
243 # Strip final line terminator
244 line = lines[-1]
245 if line[-2:] == "\r\n":
246 line = line[:-2]
247 elif line[-1:] == "\n":
248 line = line[:-1]
249 lines[-1] = line
250 data = "".join(lines)
251 line = headers['content-disposition']
252 if not line:
253 continue
254 key, params = self.parse_content_type(line)
255 if key != 'form-data':
256 continue
257 if 'name' in params:
258 name = params['name']
259 else:
260 continue
261 if name in partdict:
262 if isinstance(partdict[name], str):
263 partdict[name] = [partdict[name]]
264 partdict[name].append(data)
265 else:
266 partdict[name] = data
267 self.post_params = partdict
268 self.common_handler(head_only)
269
270 def do_GET(self, head_only=False):
271 self.parse_path()
272 self.common_handler(head_only)
273
274 def common_handler(self, head_only):
275 # paths = {
276 # '/': self.welcome,
277 # '/welcome': self.welcome,
278 # '/showargs': self.showargs,
279 # '/status': self.status,
280 # '/lastvisitor': self.lastvisitor,
281 # '/showclients': self.showclients,
282 # '/loadprofile': self.loadprofile,
283 # '/loadprofile2': self.loadprofile2,
284 # '/saveprofile': self.saveprofile,
285 # '/saveprofile2': self.saveprofile2
286 # }
287 paths = {
288 'ProfileHandler': [ '/loadprofile', '/loadprofile2', '/saveprofile', '/saveprofile2' ],
289 'welcome': [ '/welcome' ]
290 }
291 return_content = ""
292 for path in paths:
293 if self.path in paths[path]:
294 module = path
295 # Horrible hack until I figure out a better way to skip to sending the content
296 while 1:
297 try:
298 content_module = __import__("handlers/" + module)
299 module_obj = getattr(content_module, module)(self.get_params, self.post_params, self.headers_out, self.shared_info)
300 except AttributeError:
301 return_content = "Caught %s (%s) in module. Traceback:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], self.get_exception())
302 # return_content = "Unable to load module '%s'" % module
303 break
304 try:
305 function_obj = getattr(module_obj, 'handle')
306 except AttributeError:
307 return_content = "Cannot find function handle() in module '%s'" % module
308 break
309 try:
310 self.headers_out, return_content = function_obj(self.path)
311 except:
312 return_content = "Caught %s (%s) in module. Traceback:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], self.get_exception())
313 break
314 break
315 # return_content = self.wrap_in_template(return_content)
316 self.send_response(200)
317 if not self.headers_out:
318 self.headers_out.append(("Content-type", "text/html"))
319 self.headers_out.append(("Content-Length", len(return_content)))
320 for header in self.headers_out:
321 self.send_header(header[0], header[1])
322 self.end_headers()
323 self.wfile.write(return_content)
324 return
325 # No code handler...look for actual file
326 path = self.translate_path(self.path)
327 ctype = self.guess_type(path)
328 try:
329 f = open(path, 'rb')
330 except IOError:
331 self.send_error(404, "File not found")
332 return None
333 self.send_response(200)
334 self.send_header("Content-type", ctype)
335 self.send_header("Content-Length", str(os.fstat(f.fileno())[6]))
336 self.end_headers()
337 shutil.copyfileobj(f, self.wfile)
338 f.close()
339
340 def translate_path(self, path):
341 """Translate a /-separated PATH to the local filename syntax.
342
343 Components that mean special things to the local file system
344 (e.g. drive or directory names) are ignored. (XXX They should
345 probably be diagnosed.)
346
347 """
348 path = posixpath.normpath(urllib.unquote(path))
349 words = path.split('/')
350 words = filter(None, words)
351 path = os.getcwd()
352 for word in words:
353 drive, word = os.path.splitdrive(word)
354 head, word = os.path.split(word)
355 if word in (os.curdir, os.pardir): continue
356 path = os.path.join(path, word)
357 return path
358
359 def copyfile(self, source, outputfile):
360 shutil.copyfileobj(source, outputfile)
361
362 def guess_type(self, path):
363 base, ext = posixpath.splitext(path)
364 if ext in self.extensions_map:
365 return self.extensions_map[ext]
366 ext = ext.lower()
367 if ext in self.extensions_map:
368 return self.extensions_map[ext]
369 else:
370 return self.extensions_map['']
371
372 extensions_map = mimetypes.types_map.copy()
373 extensions_map.update({
374 '': 'application/octet-stream', # Default
375 '.py': 'text/plain',
376 '.c': 'text/plain',
377 '.h': 'text/plain',
378 })
379
380 def log_message(self, format, *args):
381 pass
382
383 class GLINetBe:
384
385 def __init__(self, loc):
386 self._path = loc
387 self.shared_info = SharedInfo()
388
389 def register_client(self, mac, ip):
390 self.shared_info.client_state[mac] = { 'ip': ip, 'install_status': "waiting for server", 'start_install': True }
391 for client in self.shared_info.clients:
392 if client['mac'] == mac: break
393 else:
394 self.shared_info.clients.append({ 'name': "", 'ip': "", 'mac': mac, 'profile': "" })
395 return True
396
397 def get_client_config(self, mac):
398 if not self.shared_info.client_state[mac]['start_install']:
399 return ""
400 for client in self.shared_info.clients:
401 if client['mac'] == mac:
402 if not client['profile']:
403 return None
404 for profile in self.shared_info.profiles:
405 if profile['name'] == client['profile']:
406 tmpfile = open(profile['ccxmlfile'], "r")
407 xml = "".join(tmpfile.readlines())
408 tmpfile.close()
409 return xml
410 return ""
411
412 def get_install_profile(self, mac):
413 if not self.shared_info.client_state[mac]['start_install']:
414 return ""
415 for client in self.shared_info.clients:
416 if client['mac'] == mac:
417 if not client['profile']:
418 return None
419 for profile in self.shared_info.profiles:
420 if profile['name'] == client['profile']:
421 tmpfile = open(profile['ipxmlfile'], "r")
422 xml = "".join(tmpfile.readlines())
423 tmpfile.close()
424 return xml
425 return ""
426
427 def update_client_status(self, mac, status):
428 self.shared_info.client_state[mac]['install_status'] = status
429 return True
430
431 def register():
432 host = ''
433 port = 8001
434 buf = 1024
435 addr = (host,port)
436 # Create socket and bind to address
437 UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
438 UDPSock.bind(addr)
439 UDPSock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
440
441 # Receive messages
442 while 1:
443 data, fromaddr = UDPSock.recvfrom(buf)
444 if not data:
445 print "Client has exited!"
446 # break #Serve forever
447 continue
448 else:
449 print "\nReceived message '" + data + "' from " + fromaddr[0] + ":" + str(fromaddr[1])
450
451 if data == "GLIAutoInstall":
452 data = "GLIAutoInstall version 1.0"
453 else:
454 data = "This is the GLIAutoInstall Service. Please make you application aware of this."
455 UDPSock.sendto(data, (fromaddr[0], fromaddr[1]))
456
457 # Close socket
458 UDPSock.close()
459 sys.exit(0)
460 return pid
461
462 def start_httpd():
463 server_address = ('', 8000)
464 # httpd = BaseHTTPServer.HTTPServer(server_address, GLIHTTPRequestHandler)
465 httpd = GLIHTTPServer(server_address)
466 httpd.serve_forever()
467
468 def start_xmlrpc():
469 server = SimpleXMLRPCServer.SimpleXMLRPCServer(('', 8002))
470 server.register_introspection_functions()
471 server.register_instance(GLINetBe("/tmp"))
472 server.serve_forever()
473
474 if __name__ == '__main__':
475 httpd_thread = Thread(target=start_httpd)
476 httpd_thread.setDaemon(True)
477 httpd_thread.start()
478 xmlrpc_thread = Thread(target=start_xmlrpc)
479 xmlrpc_thread.setDaemon(True)
480 xmlrpc_thread.start()
481 register()

Properties

Name Value
svn:eol-style native
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.20