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