Revision 81010134 daemons/ganeti-noded
b/daemons/ganeti-noded | ||
---|---|---|
28 | 28 |
import sys |
29 | 29 |
import resource |
30 | 30 |
import traceback |
31 |
import BaseHTTPServer |
|
32 |
import simplejson |
|
31 | 33 |
|
32 | 34 |
from optparse import OptionParser |
33 | 35 |
|
... | ... | |
40 | 42 |
from ganeti import ssconf |
41 | 43 |
from ganeti import utils |
42 | 44 |
|
43 |
from twisted.spread import pb |
|
44 |
from twisted.internet import reactor |
|
45 |
from twisted.cred import checkers, portal |
|
46 |
from OpenSSL import SSL |
|
47 | 45 |
|
48 |
|
|
49 |
class ServerContextFactory: |
|
50 |
"""SSL context factory class that uses a given certificate. |
|
51 |
|
|
52 |
""" |
|
53 |
@staticmethod |
|
54 |
def getContext(): |
|
55 |
"""Return a customized context. |
|
56 |
|
|
57 |
The context will be set to use our certificate. |
|
58 |
|
|
59 |
""" |
|
60 |
ctx = SSL.Context(SSL.TLSv1_METHOD) |
|
61 |
ctx.use_certificate_file(constants.SSL_CERT_FILE) |
|
62 |
ctx.use_privatekey_file(constants.SSL_CERT_FILE) |
|
63 |
return ctx |
|
64 |
|
|
65 |
class ServerObject(pb.Avatar): |
|
46 |
class ServerObject(BaseHTTPServer.BaseHTTPRequestHandler): |
|
66 | 47 |
"""The server implementation. |
67 | 48 |
|
68 | 49 |
This class holds all methods exposed over the RPC interface. |
69 | 50 |
|
70 | 51 |
""" |
71 |
def __init__(self, name): |
|
72 |
self.name = name |
|
73 |
|
|
74 |
def perspectiveMessageReceived(self, broker, message, args, kw): |
|
75 |
"""Custom message dispatching function. |
|
76 |
|
|
77 |
This function overrides the pb.Avatar function in order to provide |
|
78 |
a simple form of exception passing (as text only). |
|
52 |
def do_PUT(self): |
|
53 |
"""Handle a post request. |
|
79 | 54 |
|
80 | 55 |
""" |
81 |
args = broker.unserialize(args, self) |
|
82 |
kw = broker.unserialize(kw, self) |
|
83 |
method = getattr(self, "perspective_%s" % message) |
|
84 |
tb = None |
|
85 |
state = None |
|
56 |
path = self.path |
|
57 |
if path.startswith("/"): |
|
58 |
path = path[1:] |
|
59 |
mname = "perspective_%s" % path |
|
60 |
if not hasattr(self, mname): |
|
61 |
self.send_error(404) |
|
62 |
return False |
|
63 |
|
|
64 |
method = getattr(self, mname) |
|
86 | 65 |
try: |
87 |
state = method(*args, **kw) |
|
88 |
except: |
|
89 |
tb = traceback.format_exc() |
|
66 |
body_length = int(self.headers.get('Content-Length', '0')) |
|
67 |
except ValueError: |
|
68 |
self.send_error(400, 'No Content-Length header or invalid format') |
|
69 |
return False |
|
90 | 70 |
|
91 |
return broker.serialize((tb, state), self, method, args, kw) |
|
71 |
try: |
|
72 |
body = self.rfile.read(body_length) |
|
73 |
except socket.error, err: |
|
74 |
logger.Error("Socket error while reading: %s" % str(err)) |
|
75 |
return |
|
76 |
try: |
|
77 |
params = simplejson.loads(body) |
|
78 |
result = method(params) |
|
79 |
payload = simplejson.dumps(result) |
|
80 |
except Exception, err: |
|
81 |
self.send_error(500, "Error: %s" % str(err)) |
|
82 |
return False |
|
83 |
self.send_response(200) |
|
84 |
self.send_header('Content-Length', str(len(payload))) |
|
85 |
self.end_headers() |
|
86 |
self.wfile.write(payload) |
|
87 |
return True |
|
88 |
|
|
89 |
def log_message(self, format, *args): |
|
90 |
"""Log a request to the log. |
|
91 |
|
|
92 |
This is the same as the parent, we just log somewhere else. |
|
93 |
|
|
94 |
""" |
|
95 |
msg = ("%s - - [%s] %s\n" % |
|
96 |
(self.address_string(), |
|
97 |
self.log_date_time_string(), |
|
98 |
format % args)) |
|
99 |
logger.Debug(msg) |
|
92 | 100 |
|
93 | 101 |
# the new block devices -------------------------- |
94 | 102 |
|
... | ... | |
487 | 495 |
return utils.TestDelay(duration) |
488 | 496 |
|
489 | 497 |
|
490 |
class MyRealm: |
|
491 |
"""Simple realm that forwards all requests to a ServerObject. |
|
492 |
|
|
493 |
""" |
|
494 |
__implements__ = portal.IRealm |
|
495 |
|
|
496 |
def requestAvatar(self, avatarId, mind, *interfaces): |
|
497 |
"""Return an avatar based on our ServerObject class. |
|
498 |
|
|
499 |
""" |
|
500 |
if pb.IPerspective not in interfaces: |
|
501 |
raise NotImplementedError |
|
502 |
return pb.IPerspective, ServerObject(avatarId), lambda:None |
|
503 |
|
|
504 |
|
|
505 | 498 |
def ParseOptions(): |
506 | 499 |
"""Parse the command line options. |
507 | 500 |
|
... | ... | |
550 | 543 |
logger.SetupLogging(twisted_workaround=True, debug=options.debug, |
551 | 544 |
program="ganeti-noded") |
552 | 545 |
|
553 |
p = portal.Portal(MyRealm()) |
|
554 |
p.registerChecker( |
|
555 |
checkers.InMemoryUsernamePasswordDatabaseDontUse(master_node=pwdata)) |
|
556 |
reactor.listenSSL(port, pb.PBServerFactory(p), ServerContextFactory()) |
|
557 |
reactor.run() |
|
546 |
httpd = BaseHTTPServer.HTTPServer(('', port), ServerObject) |
|
547 |
httpd.serve_forever() |
|
558 | 548 |
|
559 | 549 |
|
560 | 550 |
def createDaemon(): |
Also available in: Unified diff