Revision 073c31a5 daemons/ganeti-rapi
b/daemons/ganeti-rapi | ||
---|---|---|
31 | 31 |
import sys |
32 | 32 |
import os |
33 | 33 |
import os.path |
34 |
import errno |
|
34 | 35 |
|
35 | 36 |
try: |
36 | 37 |
from pyinotify import pyinotify # pylint: disable-msg=E0611 |
... | ... | |
103 | 104 |
@param filename: Path to file |
104 | 105 |
|
105 | 106 |
""" |
107 |
logging.info("Reading users file at %s", filename) |
|
106 | 108 |
try: |
107 |
contents = utils.ReadFile(filename) |
|
108 |
except EnvironmentError, err: |
|
109 |
logging.warning("Error while reading %s: %s", filename, err) |
|
110 |
return False |
|
109 |
try: |
|
110 |
contents = utils.ReadFile(filename) |
|
111 |
except EnvironmentError, err: |
|
112 |
self._users = None |
|
113 |
if err.errno == errno.ENOENT: |
|
114 |
logging.warning("No users file at %s", filename) |
|
115 |
else: |
|
116 |
logging.warning("Error while reading %s: %s", filename, err) |
|
117 |
return False |
|
111 | 118 |
|
112 |
try: |
|
113 | 119 |
users = http.auth.ParsePasswordFile(contents) |
120 |
|
|
114 | 121 |
except Exception, err: # pylint: disable-msg=W0703 |
115 | 122 |
# We don't care about the type of exception |
116 | 123 |
logging.error("Error while parsing %s: %s", filename, err) |
117 | 124 |
return False |
118 | 125 |
|
119 | 126 |
self._users = users |
127 |
|
|
120 | 128 |
return True |
121 | 129 |
|
122 | 130 |
def _GetRequestContext(self, req): |
... | ... | |
229 | 237 |
return serializer.DumpJson(result) |
230 | 238 |
|
231 | 239 |
|
232 |
class FileWatcher:
|
|
233 |
def __init__(self, filename, cb):
|
|
240 |
class FileEventHandler(asyncnotifier.FileEventHandlerBase):
|
|
241 |
def __init__(self, wm, path, cb):
|
|
234 | 242 |
"""Initializes this class. |
235 | 243 |
|
236 |
@type filename: string |
|
237 |
@param filename: File to watch |
|
244 |
@param wm: Inotify watch manager |
|
245 |
@type path: string |
|
246 |
@param path: File path |
|
238 | 247 |
@type cb: callable |
239 | 248 |
@param cb: Function called on file change |
240 | 249 |
|
241 | 250 |
""" |
242 |
self._filename = filename |
|
251 |
asyncnotifier.FileEventHandlerBase.__init__(self, wm) |
|
252 |
|
|
243 | 253 |
self._cb = cb |
254 |
self._filename = os.path.basename(path) |
|
244 | 255 |
|
245 |
wm = pyinotify.WatchManager()
|
|
246 |
self._handler = asyncnotifier.SingleFileEventHandler(wm, self._OnInotify,
|
|
247 |
filename)
|
|
248 |
asyncnotifier.AsyncNotifier(wm, default_proc_fun=self._handler)
|
|
249 |
self._handler.enable()
|
|
256 |
# Class '...' has no 'IN_...' member, pylint: disable-msg=E1103
|
|
257 |
mask = (pyinotify.EventsCodes.IN_CLOSE_WRITE |
|
|
258 |
pyinotify.EventsCodes.IN_DELETE |
|
|
259 |
pyinotify.EventsCodes.IN_MOVED_FROM |
|
|
260 |
pyinotify.EventsCodes.IN_MOVED_TO)
|
|
250 | 261 |
|
251 |
def _OnInotify(self, notifier_enabled): |
|
252 |
"""Called upon update of the RAPI users file by pyinotify. |
|
262 |
self._handle = self.AddWatch(os.path.dirname(path), mask) |
|
253 | 263 |
|
254 |
@type notifier_enabled: boolean
|
|
255 |
@param notifier_enabled: whether the notifier is still enabled
|
|
264 |
def process_default(self, event):
|
|
265 |
"""Called upon inotify event.
|
|
256 | 266 |
|
257 | 267 |
""" |
258 |
logging.info("Reloading modified %s", self._filename) |
|
268 |
if event.name == self._filename: |
|
269 |
logging.debug("Received inotify event %s", event) |
|
270 |
self._cb() |
|
271 |
|
|
272 |
|
|
273 |
def SetupFileWatcher(filename, cb): |
|
274 |
"""Configures an inotify watcher for a file. |
|
259 | 275 |
|
260 |
self._cb() |
|
276 |
@type filename: string |
|
277 |
@param filename: File to watch |
|
278 |
@type cb: callable |
|
279 |
@param cb: Function called on file change |
|
261 | 280 |
|
262 |
# Renable the watch again if we'd an atomic update of the file (e.g. mv) |
|
263 |
if not notifier_enabled: |
|
264 |
self._handler.enable() |
|
281 |
""" |
|
282 |
wm = pyinotify.WatchManager() |
|
283 |
handler = FileEventHandler(wm, filename, cb) |
|
284 |
asyncnotifier.AsyncNotifier(wm, default_proc_fun=handler) |
|
265 | 285 |
|
266 | 286 |
|
267 | 287 |
def CheckRapi(options, args): |
... | ... | |
294 | 314 |
ssl_verify_peer=False, |
295 | 315 |
request_executor_class=JsonErrorRequestExecutor) |
296 | 316 |
|
297 |
if os.path.exists(constants.RAPI_USERS_FILE): |
|
298 |
# Setup file watcher (it'll be driven by asyncore) |
|
299 |
FileWatcher(constants.RAPI_USERS_FILE, |
|
300 |
compat.partial(server.LoadUsers, constants.RAPI_USERS_FILE)) |
|
317 |
# Setup file watcher (it'll be driven by asyncore) |
|
318 |
SetupFileWatcher(constants.RAPI_USERS_FILE, |
|
319 |
compat.partial(server.LoadUsers, constants.RAPI_USERS_FILE)) |
|
301 | 320 |
|
302 | 321 |
server.LoadUsers(constants.RAPI_USERS_FILE) |
303 | 322 |
|
304 | 323 |
# pylint: disable-msg=E1101 |
305 | 324 |
# it seems pylint doesn't see the second parent class there |
306 | 325 |
server.Start() |
326 |
|
|
307 | 327 |
return (mainloop, server) |
308 | 328 |
|
329 |
|
|
309 | 330 |
def ExecRapi(options, args, prep_data): # pylint: disable-msg=W0613 |
310 | 331 |
"""Main remote API function, executed with the PID file held. |
311 | 332 |
|
Also available in: Unified diff