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 |
|