/[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 940 - (show annotations) (download) (as text)
Sat Oct 1 18:31:32 2005 UTC (12 years, 9 months ago) by agaffney
File MIME type: text/x-python
File size: 15293 byte(s)
  no XMLRPC functions return None
  src/net/client/gliclient.py:
  various typo and type mismatch fixes
  exit after exception

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

Properties

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

  ViewVC Help
Powered by ViewVC 1.1.20