root / lib / asyncnotifier.py @ 0f9c08dc
History | View | Annotate | Download (5.1 kB)
1 | a8a76bc2 | Guido Trotter | #
|
---|---|---|---|
2 | a8a76bc2 | Guido Trotter | #
|
3 | a8a76bc2 | Guido Trotter | |
4 | a8a76bc2 | Guido Trotter | # Copyright (C) 2009 Google Inc.
|
5 | a8a76bc2 | Guido Trotter | #
|
6 | a8a76bc2 | Guido Trotter | # This program is free software; you can redistribute it and/or modify
|
7 | a8a76bc2 | Guido Trotter | # it under the terms of the GNU General Public License as published by
|
8 | a8a76bc2 | Guido Trotter | # the Free Software Foundation; either version 2 of the License, or
|
9 | a8a76bc2 | Guido Trotter | # (at your option) any later version.
|
10 | a8a76bc2 | Guido Trotter | #
|
11 | a8a76bc2 | Guido Trotter | # This program is distributed in the hope that it will be useful, but
|
12 | a8a76bc2 | Guido Trotter | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | a8a76bc2 | Guido Trotter | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | a8a76bc2 | Guido Trotter | # General Public License for more details.
|
15 | a8a76bc2 | Guido Trotter | #
|
16 | a8a76bc2 | Guido Trotter | # You should have received a copy of the GNU General Public License
|
17 | a8a76bc2 | Guido Trotter | # along with this program; if not, write to the Free Software
|
18 | a8a76bc2 | Guido Trotter | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | a8a76bc2 | Guido Trotter | # 02110-1301, USA.
|
20 | a8a76bc2 | Guido Trotter | |
21 | a8a76bc2 | Guido Trotter | |
22 | a8a76bc2 | Guido Trotter | """Asynchronous pyinotify implementation"""
|
23 | a8a76bc2 | Guido Trotter | |
24 | a8a76bc2 | Guido Trotter | |
25 | a8a76bc2 | Guido Trotter | import asyncore |
26 | c666f1f4 | Guido Trotter | import logging |
27 | a8a76bc2 | Guido Trotter | |
28 | ad54f3d2 | Guido Trotter | try:
|
29 | 7260cfbe | Iustin Pop | # pylint: disable-msg=E0611
|
30 | 6af8a903 | Iustin Pop | from pyinotify import pyinotify |
31 | ad54f3d2 | Guido Trotter | except ImportError: |
32 | 6af8a903 | Iustin Pop | import pyinotify |
33 | ad54f3d2 | Guido Trotter | |
34 | e9c8deab | Guido Trotter | from ganeti import daemon |
35 | c666f1f4 | Guido Trotter | from ganeti import errors |
36 | a8a76bc2 | Guido Trotter | |
37 | a2c965ea | Guido Trotter | # We contributed the AsyncNotifier class back to python-pyinotify, and it's
|
38 | a2c965ea | Guido Trotter | # part of their codebase since version 0.8.7. This code can be removed once
|
39 | a2c965ea | Guido Trotter | # we'll be ready to depend on python-pyinotify >= 0.8.7
|
40 | a8a76bc2 | Guido Trotter | class AsyncNotifier(asyncore.file_dispatcher): |
41 | a8a76bc2 | Guido Trotter | """An asyncore dispatcher for inotify events.
|
42 | a8a76bc2 | Guido Trotter |
|
43 | a8a76bc2 | Guido Trotter | """
|
44 | 7260cfbe | Iustin Pop | # pylint: disable-msg=W0622,W0212
|
45 | 69b99987 | Michael Hanselmann | def __init__(self, watch_manager, default_proc_fun=None, map=None): |
46 | 69b99987 | Michael Hanselmann | """Initializes this class.
|
47 | 69b99987 | Michael Hanselmann |
|
48 | 69b99987 | Michael Hanselmann | This is a a special asyncore file_dispatcher that actually wraps a
|
49 | 69b99987 | Michael Hanselmann | pyinotify Notifier, making it asyncronous.
|
50 | a8a76bc2 | Guido Trotter |
|
51 | a8a76bc2 | Guido Trotter | """
|
52 | a8a76bc2 | Guido Trotter | if default_proc_fun is None: |
53 | 69b99987 | Michael Hanselmann | default_proc_fun = pyinotify.ProcessEvent() |
54 | 69b99987 | Michael Hanselmann | |
55 | a8a76bc2 | Guido Trotter | self.notifier = pyinotify.Notifier(watch_manager, default_proc_fun)
|
56 | 69b99987 | Michael Hanselmann | |
57 | a8a76bc2 | Guido Trotter | # here we need to steal the file descriptor from the notifier, so we can
|
58 | a8a76bc2 | Guido Trotter | # use it in the global asyncore select, and avoid calling the
|
59 | a8a76bc2 | Guido Trotter | # check_events() function of the notifier (which doesn't allow us to select
|
60 | a8a76bc2 | Guido Trotter | # together with other file descriptors)
|
61 | a8a76bc2 | Guido Trotter | self.fd = self.notifier._fd |
62 | a8a76bc2 | Guido Trotter | asyncore.file_dispatcher.__init__(self, self.fd, map) |
63 | a8a76bc2 | Guido Trotter | |
64 | a8a76bc2 | Guido Trotter | def handle_read(self): |
65 | a8a76bc2 | Guido Trotter | self.notifier.read_events()
|
66 | a8a76bc2 | Guido Trotter | self.notifier.process_events()
|
67 | c666f1f4 | Guido Trotter | |
68 | c666f1f4 | Guido Trotter | |
69 | e9c8deab | Guido Trotter | class ErrorLoggingAsyncNotifier(AsyncNotifier, |
70 | e9c8deab | Guido Trotter | daemon.GanetiBaseAsyncoreDispatcher): |
71 | e9c8deab | Guido Trotter | """An asyncnotifier that can survive errors in the callbacks.
|
72 | e9c8deab | Guido Trotter |
|
73 | e9c8deab | Guido Trotter | We define this as a separate class, since we don't want to make AsyncNotifier
|
74 | e9c8deab | Guido Trotter | diverge from what we contributed upstream.
|
75 | e9c8deab | Guido Trotter |
|
76 | e9c8deab | Guido Trotter | """
|
77 | e9c8deab | Guido Trotter | |
78 | e9c8deab | Guido Trotter | |
79 | c666f1f4 | Guido Trotter | class SingleFileEventHandler(pyinotify.ProcessEvent): |
80 | c666f1f4 | Guido Trotter | """Handle modify events for a single file.
|
81 | c666f1f4 | Guido Trotter |
|
82 | c666f1f4 | Guido Trotter | """
|
83 | c666f1f4 | Guido Trotter | |
84 | c666f1f4 | Guido Trotter | def __init__(self, watch_manager, callback, filename): |
85 | c666f1f4 | Guido Trotter | """Constructor for SingleFileEventHandler
|
86 | c666f1f4 | Guido Trotter |
|
87 | c666f1f4 | Guido Trotter | @type watch_manager: pyinotify.WatchManager
|
88 | c666f1f4 | Guido Trotter | @param watch_manager: inotify watch manager
|
89 | c666f1f4 | Guido Trotter | @type callback: function accepting a boolean
|
90 | c666f1f4 | Guido Trotter | @param callback: function to call when an inotify event happens
|
91 | c666f1f4 | Guido Trotter | @type filename: string
|
92 | c666f1f4 | Guido Trotter | @param filename: config file to watch
|
93 | c666f1f4 | Guido Trotter |
|
94 | c666f1f4 | Guido Trotter | """
|
95 | c666f1f4 | Guido Trotter | # pylint: disable-msg=W0231
|
96 | c666f1f4 | Guido Trotter | # no need to call the parent's constructor
|
97 | c666f1f4 | Guido Trotter | self.watch_manager = watch_manager
|
98 | c666f1f4 | Guido Trotter | self.callback = callback
|
99 | c666f1f4 | Guido Trotter | self.mask = pyinotify.EventsCodes.ALL_FLAGS["IN_IGNORED"] | \ |
100 | c666f1f4 | Guido Trotter | pyinotify.EventsCodes.ALL_FLAGS["IN_MODIFY"]
|
101 | c666f1f4 | Guido Trotter | self.file = filename
|
102 | c666f1f4 | Guido Trotter | self.watch_handle = None |
103 | c666f1f4 | Guido Trotter | |
104 | c666f1f4 | Guido Trotter | def enable(self): |
105 | c666f1f4 | Guido Trotter | """Watch the given file
|
106 | c666f1f4 | Guido Trotter |
|
107 | c666f1f4 | Guido Trotter | """
|
108 | c666f1f4 | Guido Trotter | if self.watch_handle is None: |
109 | c666f1f4 | Guido Trotter | result = self.watch_manager.add_watch(self.file, self.mask) |
110 | c666f1f4 | Guido Trotter | if not self.file in result or result[self.file] <= 0: |
111 | c666f1f4 | Guido Trotter | raise errors.InotifyError("Could not add inotify watcher") |
112 | c666f1f4 | Guido Trotter | else:
|
113 | c666f1f4 | Guido Trotter | self.watch_handle = result[self.file] |
114 | c666f1f4 | Guido Trotter | |
115 | c666f1f4 | Guido Trotter | def disable(self): |
116 | c666f1f4 | Guido Trotter | """Stop watching the given file
|
117 | c666f1f4 | Guido Trotter |
|
118 | c666f1f4 | Guido Trotter | """
|
119 | c666f1f4 | Guido Trotter | if self.watch_handle is not None: |
120 | c666f1f4 | Guido Trotter | result = self.watch_manager.rm_watch(self.watch_handle) |
121 | c666f1f4 | Guido Trotter | if result[self.watch_handle]: |
122 | c666f1f4 | Guido Trotter | self.watch_handle = None |
123 | c666f1f4 | Guido Trotter | |
124 | c666f1f4 | Guido Trotter | # pylint: disable-msg=C0103
|
125 | c666f1f4 | Guido Trotter | # this overrides a method in pyinotify.ProcessEvent
|
126 | c666f1f4 | Guido Trotter | def process_IN_IGNORED(self, event): |
127 | d021e478 | Guido Trotter | # Since we monitor a single file rather than the directory it resides in,
|
128 | d021e478 | Guido Trotter | # when that file is replaced with another one (which is what happens when
|
129 | d021e478 | Guido Trotter | # utils.WriteFile, the most normal way of updating files in ganeti, is
|
130 | d021e478 | Guido Trotter | # called) we're going to receive an IN_IGNORED event from inotify, because
|
131 | d021e478 | Guido Trotter | # of the file removal (which is contextual with the replacement). In such a
|
132 | d021e478 | Guido Trotter | # case we'll need to create a watcher for the "new" file. This can be done
|
133 | d021e478 | Guido Trotter | # by the callback by calling "enable" again on us.
|
134 | c666f1f4 | Guido Trotter | logging.debug("Received 'ignored' inotify event for %s", event.path)
|
135 | c666f1f4 | Guido Trotter | self.watch_handle = None |
136 | 32b1efa1 | Guido Trotter | self.callback(False) |
137 | c666f1f4 | Guido Trotter | |
138 | c666f1f4 | Guido Trotter | # pylint: disable-msg=C0103
|
139 | c666f1f4 | Guido Trotter | # this overrides a method in pyinotify.ProcessEvent
|
140 | c666f1f4 | Guido Trotter | def process_IN_MODIFY(self, event): |
141 | d021e478 | Guido Trotter | # This gets called when the monitored file is modified. Note that this
|
142 | d021e478 | Guido Trotter | # doesn't usually happen in Ganeti, as most of the time we're just
|
143 | d021e478 | Guido Trotter | # replacing any file with a new one, at filesystem level, rather than
|
144 | d021e478 | Guido Trotter | # actually changing it. (see utils.WriteFile)
|
145 | c666f1f4 | Guido Trotter | logging.debug("Received 'modify' inotify event for %s", event.path)
|
146 | 32b1efa1 | Guido Trotter | self.callback(True) |
147 | c666f1f4 | Guido Trotter | |
148 | c666f1f4 | Guido Trotter | def process_default(self, event): |
149 | c666f1f4 | Guido Trotter | logging.error("Received unhandled inotify event: %s", event) |