]> git.llucax.com Git - software/pymin.git/blob - pymin/pymindaemon.py
Bugfix: call ProcessInfo.stop() in the right way (without parameters).
[software/pymin.git] / pymin / pymindaemon.py
1 # vim: set encoding=utf-8 et sw=4 sts=4 :
2
3 r"""
4 Python Administration Daemon.
5
6 Python Administration Daemon is an modular, extensible administration tool
7 to administrate a set of services remotely (or localy) throw a simple
8 command-line.
9 """
10
11 import signal
12 import socket
13
14 from pymin.dispatcher import handler
15 from pymin import dispatcher
16 from pymin import eventloop
17 from pymin import serializer
18 from pymin import procman
19
20 class PyminDaemon(eventloop.EventLoop):
21     r"""PyminDaemon(root, bind_addr) -> PyminDaemon instance
22
23     This class is well suited to run as a single process. It handles
24     signals for controlled termination (SIGINT and SIGTERM), as well as
25     a user signal to reload the configuration files (SIGUSR1).
26
27     root - the root handler. This is passed directly to the Dispatcher.
28
29     bind_addr - is a tuple of (ip, port) where to bind the UDP socket to.
30
31     Here is a simple usage example:
32
33     >>> from pymin import dispatcher
34     >>> class Root(dispatcher.Handler):
35             @handler('Test command.')
36             def test(self, *args):
37                 print 'test:', args
38     >>> PyminDaemon(Root(), ('', 9999)).run()
39     """
40
41     def __init__(self, root, bind_addr=('', 9999), timer=1):
42         r"""Initialize the PyminDaemon object.
43
44         See PyminDaemon class documentation for more info.
45         """
46         # Timer timeout time
47         self.timer = timer
48         # Create and bind socket
49         sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
50         sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
51         sock.bind(bind_addr)
52         # Signal handling
53         def quit(loop, signum):
54             print "Shuting down ..."
55             loop.stop() # tell main event loop to stop
56         def reload_config(loop, signum):
57             print "Reloading configuration..."
58             # TODO iterate handlers list propagating reload action
59         def timer(loop, signum):
60             loop.handle_timer()
61             signal.alarm(loop.timer)
62         def child(loop, signum):
63             procman.sigchild_handler(signum)
64         # Create EventLoop
65         eventloop.EventLoop.__init__(self, sock, signals={
66                 signal.SIGINT: quit,
67                 signal.SIGTERM: quit,
68                 signal.SIGUSR1: reload_config,
69                 signal.SIGALRM: timer,
70                 signal.SIGCHLD: child,
71             })
72         # Create Dispatcher
73         #TODO root.pymin = PyminHandler()
74         self.dispatcher = dispatcher.Dispatcher(root)
75
76     def handle(self):
77         r"handle() -> None :: Handle incoming events using the dispatcher."
78         (msg, addr) = self.file.recvfrom(65535)
79         try:
80             result = self.dispatcher.dispatch(unicode(msg, 'utf-8'))
81             if result is not None:
82                 result = serializer.serialize(result)
83             response = u'OK '
84         except dispatcher.Error, e:
85             result = unicode(e) + u'\n'
86             response = u'ERROR '
87         except Exception, e:
88             import traceback
89             result = u'Internal server error\n'
90             traceback.print_exc() # TODO logging!
91             response = u'ERROR '
92         if result is None:
93             response += u'0\n'
94         else:
95             response += u'%d\n%s' % (len(result), result)
96         self.file.sendto(response.encode('utf-8'), addr)
97
98     def handle_timer(self):
99         r"handle_timer() -> None :: Call handle_timer() on handlers."
100         self.dispatcher.root.handle_timer()
101
102     def run(self):
103         r"run() -> None :: Run the event loop (shortcut to loop())"
104         # Start the timer
105         self.handle_timer()
106         signal.alarm(self.timer)
107         # Loop
108         try:
109             return self.loop()
110         except eventloop.LoopInterruptedError, e:
111             pass
112
113 if __name__ == '__main__':
114
115     class Root(dispatcher.Handler):
116         @handler(u"Print all the arguments, return nothing.")
117         def test(self, *args):
118             print 'test:', args
119         @handler(u"Echo the message passed as argument.")
120         def echo(self, message):
121             print 'echo:', message
122             return message
123
124     PyminDaemon(Root()).run()
125