root / lib / asyncnotifier.py @ 784cd737
History | View | Annotate | Download (6 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 | b459a848 | Andrea Spadaccini | # pylint: disable=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 | e687ec01 | Michael Hanselmann | |
38 | a2c965ea | Guido Trotter | # We contributed the AsyncNotifier class back to python-pyinotify, and it's
|
39 | a2c965ea | Guido Trotter | # part of their codebase since version 0.8.7. This code can be removed once
|
40 | a2c965ea | Guido Trotter | # we'll be ready to depend on python-pyinotify >= 0.8.7
|
41 | a8a76bc2 | Guido Trotter | class AsyncNotifier(asyncore.file_dispatcher): |
42 | a8a76bc2 | Guido Trotter | """An asyncore dispatcher for inotify events.
|
43 | a8a76bc2 | Guido Trotter |
|
44 | a8a76bc2 | Guido Trotter | """
|
45 | b459a848 | Andrea Spadaccini | # pylint: disable=W0622,W0212
|
46 | 69b99987 | Michael Hanselmann | def __init__(self, watch_manager, default_proc_fun=None, map=None): |
47 | 69b99987 | Michael Hanselmann | """Initializes this class.
|
48 | 69b99987 | Michael Hanselmann |
|
49 | 69b99987 | Michael Hanselmann | This is a a special asyncore file_dispatcher that actually wraps a
|
50 | 69b99987 | Michael Hanselmann | pyinotify Notifier, making it asyncronous.
|
51 | a8a76bc2 | Guido Trotter |
|
52 | a8a76bc2 | Guido Trotter | """
|
53 | a8a76bc2 | Guido Trotter | if default_proc_fun is None: |
54 | 69b99987 | Michael Hanselmann | default_proc_fun = pyinotify.ProcessEvent() |
55 | 69b99987 | Michael Hanselmann | |
56 | a8a76bc2 | Guido Trotter | self.notifier = pyinotify.Notifier(watch_manager, default_proc_fun)
|
57 | 69b99987 | Michael Hanselmann | |
58 | a8a76bc2 | Guido Trotter | # here we need to steal the file descriptor from the notifier, so we can
|
59 | a8a76bc2 | Guido Trotter | # use it in the global asyncore select, and avoid calling the
|
60 | a8a76bc2 | Guido Trotter | # check_events() function of the notifier (which doesn't allow us to select
|
61 | a8a76bc2 | Guido Trotter | # together with other file descriptors)
|
62 | a8a76bc2 | Guido Trotter | self.fd = self.notifier._fd |
63 | a8a76bc2 | Guido Trotter | asyncore.file_dispatcher.__init__(self, self.fd, map) |
64 | a8a76bc2 | Guido Trotter | |
65 | a8a76bc2 | Guido Trotter | def handle_read(self): |
66 | a8a76bc2 | Guido Trotter | self.notifier.read_events()
|
67 | a8a76bc2 | Guido Trotter | self.notifier.process_events()
|
68 | c666f1f4 | Guido Trotter | |
69 | c666f1f4 | Guido Trotter | |
70 | e9c8deab | Guido Trotter | class ErrorLoggingAsyncNotifier(AsyncNotifier, |
71 | e9c8deab | Guido Trotter | daemon.GanetiBaseAsyncoreDispatcher): |
72 | e9c8deab | Guido Trotter | """An asyncnotifier that can survive errors in the callbacks.
|
73 | e9c8deab | Guido Trotter |
|
74 | e9c8deab | Guido Trotter | We define this as a separate class, since we don't want to make AsyncNotifier
|
75 | e9c8deab | Guido Trotter | diverge from what we contributed upstream.
|
76 | e9c8deab | Guido Trotter |
|
77 | e9c8deab | Guido Trotter | """
|
78 | e9c8deab | Guido Trotter | |
79 | e9c8deab | Guido Trotter | |
80 | e543a42f | Michael Hanselmann | class FileEventHandlerBase(pyinotify.ProcessEvent): |
81 | e543a42f | Michael Hanselmann | """Base class for file event handlers.
|
82 | e543a42f | Michael Hanselmann |
|
83 | e543a42f | Michael Hanselmann | @ivar watch_manager: Inotify watch manager
|
84 | c666f1f4 | Guido Trotter |
|
85 | c666f1f4 | Guido Trotter | """
|
86 | e543a42f | Michael Hanselmann | def __init__(self, watch_manager): |
87 | e543a42f | Michael Hanselmann | """Initializes this class.
|
88 | e543a42f | Michael Hanselmann |
|
89 | e543a42f | Michael Hanselmann | @type watch_manager: pyinotify.WatchManager
|
90 | e543a42f | Michael Hanselmann | @param watch_manager: inotify watch manager
|
91 | e543a42f | Michael Hanselmann |
|
92 | e543a42f | Michael Hanselmann | """
|
93 | b459a848 | Andrea Spadaccini | # pylint: disable=W0231
|
94 | e543a42f | Michael Hanselmann | # no need to call the parent's constructor
|
95 | e543a42f | Michael Hanselmann | self.watch_manager = watch_manager
|
96 | e543a42f | Michael Hanselmann | |
97 | e543a42f | Michael Hanselmann | def process_default(self, event): |
98 | e543a42f | Michael Hanselmann | logging.error("Received unhandled inotify event: %s", event)
|
99 | e543a42f | Michael Hanselmann | |
100 | e543a42f | Michael Hanselmann | def AddWatch(self, filename, mask): |
101 | e543a42f | Michael Hanselmann | """Adds a file watch.
|
102 | e543a42f | Michael Hanselmann |
|
103 | e543a42f | Michael Hanselmann | @param filename: Path to file
|
104 | e543a42f | Michael Hanselmann | @param mask: Inotify event mask
|
105 | e543a42f | Michael Hanselmann | @return: Result
|
106 | e543a42f | Michael Hanselmann |
|
107 | e543a42f | Michael Hanselmann | """
|
108 | e543a42f | Michael Hanselmann | result = self.watch_manager.add_watch(filename, mask)
|
109 | e543a42f | Michael Hanselmann | |
110 | e543a42f | Michael Hanselmann | ret = result.get(filename, -1)
|
111 | e543a42f | Michael Hanselmann | if ret <= 0: |
112 | e543a42f | Michael Hanselmann | raise errors.InotifyError("Could not add inotify watcher (%s)" % ret) |
113 | e543a42f | Michael Hanselmann | |
114 | e543a42f | Michael Hanselmann | return result[filename]
|
115 | c666f1f4 | Guido Trotter | |
116 | e543a42f | Michael Hanselmann | def RemoveWatch(self, handle): |
117 | e543a42f | Michael Hanselmann | """Removes a handle from the watcher.
|
118 | e543a42f | Michael Hanselmann |
|
119 | e543a42f | Michael Hanselmann | @param handle: Inotify handle
|
120 | e543a42f | Michael Hanselmann | @return: Whether removal was successful
|
121 | e543a42f | Michael Hanselmann |
|
122 | e543a42f | Michael Hanselmann | """
|
123 | e543a42f | Michael Hanselmann | result = self.watch_manager.rm_watch(handle)
|
124 | e543a42f | Michael Hanselmann | |
125 | e543a42f | Michael Hanselmann | return result[handle]
|
126 | e543a42f | Michael Hanselmann | |
127 | e543a42f | Michael Hanselmann | |
128 | e543a42f | Michael Hanselmann | class SingleFileEventHandler(FileEventHandlerBase): |
129 | e543a42f | Michael Hanselmann | """Handle modify events for a single file.
|
130 | e543a42f | Michael Hanselmann |
|
131 | e543a42f | Michael Hanselmann | """
|
132 | c666f1f4 | Guido Trotter | def __init__(self, watch_manager, callback, filename): |
133 | c666f1f4 | Guido Trotter | """Constructor for SingleFileEventHandler
|
134 | c666f1f4 | Guido Trotter |
|
135 | c666f1f4 | Guido Trotter | @type watch_manager: pyinotify.WatchManager
|
136 | c666f1f4 | Guido Trotter | @param watch_manager: inotify watch manager
|
137 | c666f1f4 | Guido Trotter | @type callback: function accepting a boolean
|
138 | c666f1f4 | Guido Trotter | @param callback: function to call when an inotify event happens
|
139 | c666f1f4 | Guido Trotter | @type filename: string
|
140 | c666f1f4 | Guido Trotter | @param filename: config file to watch
|
141 | c666f1f4 | Guido Trotter |
|
142 | c666f1f4 | Guido Trotter | """
|
143 | e543a42f | Michael Hanselmann | FileEventHandlerBase.__init__(self, watch_manager)
|
144 | e543a42f | Michael Hanselmann | |
145 | e543a42f | Michael Hanselmann | self._callback = callback
|
146 | e543a42f | Michael Hanselmann | self._filename = filename
|
147 | e543a42f | Michael Hanselmann | |
148 | e543a42f | Michael Hanselmann | self._watch_handle = None |
149 | c666f1f4 | Guido Trotter | |
150 | c666f1f4 | Guido Trotter | def enable(self): |
151 | e543a42f | Michael Hanselmann | """Watch the given file.
|
152 | c666f1f4 | Guido Trotter |
|
153 | c666f1f4 | Guido Trotter | """
|
154 | e543a42f | Michael Hanselmann | if self._watch_handle is not None: |
155 | e543a42f | Michael Hanselmann | return
|
156 | e543a42f | Michael Hanselmann | |
157 | ac96953d | Michael Hanselmann | # Different Pyinotify versions have the flag constants at different places,
|
158 | ac96953d | Michael Hanselmann | # hence not accessing them directly
|
159 | ac96953d | Michael Hanselmann | mask = (pyinotify.EventsCodes.ALL_FLAGS["IN_MODIFY"] |
|
160 | ac96953d | Michael Hanselmann | pyinotify.EventsCodes.ALL_FLAGS["IN_IGNORED"])
|
161 | e543a42f | Michael Hanselmann | |
162 | e543a42f | Michael Hanselmann | self._watch_handle = self.AddWatch(self._filename, mask) |
163 | c666f1f4 | Guido Trotter | |
164 | c666f1f4 | Guido Trotter | def disable(self): |
165 | e543a42f | Michael Hanselmann | """Stop watching the given file.
|
166 | c666f1f4 | Guido Trotter |
|
167 | c666f1f4 | Guido Trotter | """
|
168 | e543a42f | Michael Hanselmann | if self._watch_handle is not None and self.RemoveWatch(self._watch_handle): |
169 | e543a42f | Michael Hanselmann | self._watch_handle = None |
170 | c666f1f4 | Guido Trotter | |
171 | b459a848 | Andrea Spadaccini | # pylint: disable=C0103
|
172 | c666f1f4 | Guido Trotter | # this overrides a method in pyinotify.ProcessEvent
|
173 | c666f1f4 | Guido Trotter | def process_IN_IGNORED(self, event): |
174 | d021e478 | Guido Trotter | # Since we monitor a single file rather than the directory it resides in,
|
175 | d021e478 | Guido Trotter | # when that file is replaced with another one (which is what happens when
|
176 | d021e478 | Guido Trotter | # utils.WriteFile, the most normal way of updating files in ganeti, is
|
177 | d021e478 | Guido Trotter | # called) we're going to receive an IN_IGNORED event from inotify, because
|
178 | d021e478 | Guido Trotter | # of the file removal (which is contextual with the replacement). In such a
|
179 | d021e478 | Guido Trotter | # case we'll need to create a watcher for the "new" file. This can be done
|
180 | d021e478 | Guido Trotter | # by the callback by calling "enable" again on us.
|
181 | c666f1f4 | Guido Trotter | logging.debug("Received 'ignored' inotify event for %s", event.path)
|
182 | e543a42f | Michael Hanselmann | self._watch_handle = None |
183 | e543a42f | Michael Hanselmann | self._callback(False) |
184 | c666f1f4 | Guido Trotter | |
185 | b459a848 | Andrea Spadaccini | # pylint: disable=C0103
|
186 | c666f1f4 | Guido Trotter | # this overrides a method in pyinotify.ProcessEvent
|
187 | c666f1f4 | Guido Trotter | def process_IN_MODIFY(self, event): |
188 | d021e478 | Guido Trotter | # This gets called when the monitored file is modified. Note that this
|
189 | d021e478 | Guido Trotter | # doesn't usually happen in Ganeti, as most of the time we're just
|
190 | d021e478 | Guido Trotter | # replacing any file with a new one, at filesystem level, rather than
|
191 | d021e478 | Guido Trotter | # actually changing it. (see utils.WriteFile)
|
192 | c666f1f4 | Guido Trotter | logging.debug("Received 'modify' inotify event for %s", event.path)
|
193 | e543a42f | Michael Hanselmann | self._callback(True) |