X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/0b678558f489d136558f41ab883e0a183c30711c..acd9ff9e995bb1dd7314ce7923aa4e5e888019a1:/daemons/ganeti-confd diff --git a/daemons/ganeti-confd b/daemons/ganeti-confd index 671dbd2..3a685c7 100755 --- a/daemons/ganeti-confd +++ b/daemons/ganeti-confd @@ -26,14 +26,19 @@ It uses UDP+HMAC for authentication with a global cluster key. """ +# pylint: disable-msg=C0103 +# C0103: Invalid name ganeti-confd + import os import sys import logging -import asyncore -import socket -import pyinotify import time -import errno + +try: + # pylint: disable-msg=E0611 + from pyinotify import pyinotify +except ImportError: + import pyinotify from optparse import OptionParser @@ -43,7 +48,6 @@ from ganeti.confd import server as confd_server from ganeti import constants from ganeti import errors from ganeti import daemon -from ganeti import ssconf class ConfdAsyncUDPServer(daemon.AsyncUDPSocket): @@ -66,7 +70,7 @@ class ConfdAsyncUDPServer(daemon.AsyncUDPSocket): self.port = port self.processor = processor self.bind((bind_address, port)) - logging.debug("listening on ('%s':%d)" % (bind_address, port)) + logging.debug("listening on ('%s':%d)", bind_address, port) # this method is overriding a daemon.AsyncUDPSocket method def handle_datagram(self, payload_in, ip, port): @@ -84,95 +88,6 @@ class ConfdAsyncUDPServer(daemon.AsyncUDPSocket): logging.error("Reply too big to fit in an udp packet.") -class ConfdInotifyEventHandler(pyinotify.ProcessEvent): - - def __init__(self, watch_manager, callback, - file=constants.CLUSTER_CONF_FILE): - """Constructor for ConfdInotifyEventHandler - - @type watch_manager: L{pyinotify.WatchManager} - @param watch_manager: ganeti-confd inotify watch manager - @type callback: function accepting a boolean - @param callback: function to call when an inotify event happens - @type file: string - @param file: config file to watch - - """ - # no need to call the parent's constructor - self.watch_manager = watch_manager - self.callback = callback - self.mask = pyinotify.EventsCodes.IN_IGNORED | \ - pyinotify.EventsCodes.IN_MODIFY - self.file = file - self.watch_handle = None - - def enable(self): - """Watch the given file - - """ - if self.watch_handle is None: - result = self.watch_manager.add_watch(self.file, self.mask) - if not self.file in result or result[self.file] <= 0: - raise errors.InotifyError("Could not add inotify watcher") - else: - self.watch_handle = result[self.file] - - def disable(self): - """Stop watching the given file - - """ - if self.watch_handle is not None: - result = self.watch_manager.rm_watch(self.watch_handle) - if result[self.watch_handle]: - self.watch_handle = None - - def process_IN_IGNORED(self, event): - # Due to the fact that we monitor just for the cluster config file (rather - # than for the whole data dir) when the file is replaced with another one - # (which is what happens normally in ganeti) we're going to receive an - # IN_IGNORED event from inotify, because of the file removal (which is - # contextual with the replacement). In such a case we need to create - # another watcher for the "new" file. - logging.debug("Received 'ignored' inotify event for %s" % event.path) - self.watch_handle = None - - try: - # Since the kernel believes the file we were interested in is gone, it's - # not going to notify us of any other events, until we set up, here, the - # new watch. This is not a race condition, though, since we're anyway - # going to realod the file after setting up the new watch. - self.callback(False) - except errors.ConfdFatalError, err: - logging.critical("Critical error, shutting down: %s" % err) - sys.exit(constants.EXIT_FAILURE) - except: - # we need to catch any exception here, log it, but proceed, because even - # if we failed handling a single request, we still want the confd to - # continue working. - logging.error("Unexpected exception", exc_info=True) - - def process_IN_MODIFY(self, event): - # This gets called when the config file is modified. Note that this doesn't - # usually happen in Ganeti, as the config file is normally replaced by a - # new one, at filesystem level, rather than actually modified (see - # utils.WriteFile) - logging.debug("Received 'modify' inotify event for %s" % event.path) - - try: - self.callback(True) - except errors.ConfdFatalError, err: - logging.critical("Critical error, shutting down: %s" % err) - sys.exit(constants.EXIT_FAILURE) - except: - # we need to catch any exception here, log it, but proceed, because even - # if we failed handling a single request, we still want the confd to - # continue working. - logging.error("Unexpected exception", exc_info=True) - - def process_default(self, event): - logging.error("Received unhandled inotify event: %s" % event) - - class ConfdConfigurationReloader(object): """Logic to control when to reload the ganeti configuration @@ -197,9 +112,13 @@ class ConfdConfigurationReloader(object): self.last_notification = 0 # Asyncronous inotify handler for config changes + cfg_file = constants.CLUSTER_CONF_FILE self.wm = pyinotify.WatchManager() - self.inotify_handler = ConfdInotifyEventHandler(self.wm, self.OnInotify) - self.notifier = asyncnotifier.AsyncNotifier(self.wm, self.inotify_handler) + self.inotify_handler = asyncnotifier.SingleFileEventHandler(self.wm, + self.OnInotify, + cfg_file) + notifier_class = asyncnotifier.ErrorLoggingAsyncNotifier + self.notifier = notifier_class(self.wm, self.inotify_handler) self.timer_handle = None self._EnableTimer() @@ -318,21 +237,28 @@ class ConfdConfigurationReloader(object): self._ResetTimer() -def CheckConfd(options, args): +def CheckConfd(_, args): """Initial checks whether to run exit with a failure. """ + if args: # confd doesn't take any arguments + print >> sys.stderr, ("Usage: %s [-f] [-d] [-b ADDRESS]" % sys.argv[0]) + sys.exit(constants.EXIT_FAILURE) + # TODO: collapse HMAC daemons handling in daemons GenericMain, when we'll # have more than one. - if not os.path.isfile(constants.HMAC_CLUSTER_KEY): - print >> sys.stderr, "Need HMAC key %s to run" % constants.HMAC_CLUSTER_KEY + if not os.path.isfile(constants.CONFD_HMAC_KEY): + print >> sys.stderr, "Need HMAC key %s to run" % constants.CONFD_HMAC_KEY sys.exit(constants.EXIT_FAILURE) -def ExecConfd(options, args): +def ExecConfd(options, _): """Main confd function, executed with PID file held """ + # TODO: clarify how the server and reloader variables work (they are + # not used) + # pylint: disable-msg=W0612 mainloop = daemon.Mainloop() # Asyncronous confd UDP server @@ -343,7 +269,7 @@ def ExecConfd(options, args): # If enabling the processor has failed, we can still go on, but confd will # be disabled logging.warning("Confd is starting in disabled mode") - pass + server = ConfdAsyncUDPServer(options.bind_address, options.port, processor) # Configuration reloader