increase default wait operation (sleep time, threshold interval) - avowedly greater inertance, but fewer system load by many jails resp. log files;

waiting with `wait_for` extended with verifying of active flag;
implemented better error handling in some multi-threaded routines;
shutdown of jails rewritten (faster and safer, does not breaks shutdown process if some error occurred);
pull/1557/head
sebres 2016-09-22 14:28:47 +02:00
parent 35ce1166b6
commit d153555a07
10 changed files with 130 additions and 79 deletions

View File

@ -281,9 +281,10 @@ class Actions(JailThread, Mapping):
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
while self.active: while self.active:
if self.idle: if self.idle:
time.sleep(self.sleeptime) Utils.wait_for(lambda: not self.active or not self.idle,
self.sleeptime * 10, self.sleeptime)
continue continue
if not Utils.wait_for(self.__checkBan, self.sleeptime): if not Utils.wait_for(lambda: not self.active or self.__checkBan(), self.sleeptime):
self.__checkUnBan() self.__checkUnBan()
self.__flushBan() self.__flushBan()

View File

@ -67,6 +67,7 @@ class RequestHandler(asynchat.async_chat):
# This method is called once we have a complete request. # This method is called once we have a complete request.
def found_terminator(self): def found_terminator(self):
try:
# Pop whole buffer # Pop whole buffer
message = self.__buffer message = self.__buffer
self.__buffer = [] self.__buffer = []
@ -84,6 +85,10 @@ class RequestHandler(asynchat.async_chat):
message = dumps(message, HIGHEST_PROTOCOL) message = dumps(message, HIGHEST_PROTOCOL)
# Sends the response to the client. # Sends the response to the client.
self.push(message + CSPROTO.END) self.push(message + CSPROTO.END)
except Exception as e: # pragma: no cover
logSys.error("Caught unhandled exception: %r", e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
def handle_error(self): def handle_error(self):
e1, e2 = formatExceptionInfo() e1, e2 = formatExceptionInfo()

View File

@ -501,6 +501,10 @@ class Filter(JailThread):
except Exception as e: except Exception as e:
logSys.error("Failed to process line: %r, caught exception: %r", line, e, logSys.error("Failed to process line: %r, caught exception: %r", line, e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
# incr common error counter:
self.commonError()
def commonError(self):
# incr error counter, stop processing (going idle) after 100th error : # incr error counter, stop processing (going idle) after 100th error :
self._errors += 1 self._errors += 1
# sleep a little bit (to get around time-related errors): # sleep a little bit (to get around time-related errors):

View File

@ -124,12 +124,13 @@ class FilterGamin(FileFilter):
while self.active: while self.active:
if self.idle: if self.idle:
# wait a little bit here for not idle, to prevent hi-load: # wait a little bit here for not idle, to prevent hi-load:
if not Utils.wait_for(lambda: not self.idle, if not Utils.wait_for(lambda: not self.active or not self.idle,
self.sleeptime * 10, self.sleeptime self.sleeptime * 10, self.sleeptime
): ):
self.ticks += 1 self.ticks += 1
continue continue
Utils.wait_for(self._handleEvents, self.sleeptime) Utils.wait_for(lambda: not self.active or self._handleEvents(),
self.sleeptime)
self.ticks += 1 self.ticks += 1
logSys.debug(self.jail.name + ": filter terminated") logSys.debug(self.jail.name + ": filter terminated")
return True return True

View File

@ -101,14 +101,15 @@ class FilterPoll(FileFilter):
logSys.log(6, "Woke up idle=%s with %d files monitored", logSys.log(6, "Woke up idle=%s with %d files monitored",
self.idle, self.getLogCount()) self.idle, self.getLogCount())
if self.idle: if self.idle:
if not Utils.wait_for(lambda: not self.idle, if not Utils.wait_for(lambda: not self.active or not self.idle,
self.sleeptime * 10, self.sleeptime self.sleeptime * 10, self.sleeptime
): ):
self.ticks += 1 self.ticks += 1
continue continue
# Get file modification # Get file modification
modlst = [] modlst = []
Utils.wait_for(lambda: self.getModified(modlst), self.sleeptime) Utils.wait_for(lambda: not self.active or self.getModified(modlst),
self.sleeptime)
for filename in modlst: for filename in modlst:
self.getFailures(filename) self.getFailures(filename)
self.__modified = True self.__modified = True
@ -162,7 +163,7 @@ class FilterPoll(FileFilter):
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
# increase file and common error counters: # increase file and common error counters:
self.__file404Cnt[filename] += 1 self.__file404Cnt[filename] += 1
self._errors += 1 self.commonError()
if self.__file404Cnt[filename] > 50: if self.__file404Cnt[filename] > 50:
logSys.warning("Too many errors. Remove file %r from monitoring process", filename) logSys.warning("Too many errors. Remove file %r from monitoring process", filename)
self.__file404Cnt[filename] = 0 self.__file404Cnt[filename] = 0

View File

@ -178,7 +178,7 @@ class FilterPyinotify(FileFilter):
# slow check events while idle: # slow check events while idle:
def __check_events(self, *args, **kwargs): def __check_events(self, *args, **kwargs):
if self.idle: if self.idle:
if Utils.wait_for(lambda: not self.idle, if Utils.wait_for(lambda: not self.active or not self.idle,
self.sleeptime * 10, self.sleeptime self.sleeptime * 10, self.sleeptime
): ):
pass pass

View File

@ -252,11 +252,20 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
while self.active: while self.active:
# wait for records (or for timeout in sleeptime seconds): # wait for records (or for timeout in sleeptime seconds):
self.__journal.wait(self.sleeptime) try:
## todo: find better method as wait_for to break (e.g. notify) journal.wait(self.sleeptime),
## don't use `journal.close()` for it, because in some python/systemd implementation it may
## cause abnormal program termination
#self.__journal.wait(self.sleeptime) != journal.NOP
##
## wait for entries without sleep in intervals, because "sleeping" in journal.wait:
Utils.wait_for(lambda: not self.active or \
self.__journal.wait(Utils.DEFAULT_SLEEP_INTERVAL) != journal.NOP,
self.sleeptime, 0.00001)
if self.idle: if self.idle:
# because journal.wait will returns immediatelly if we have records in journal, # because journal.wait will returns immediatelly if we have records in journal,
# just wait a little bit here for not idle, to prevent hi-load: # just wait a little bit here for not idle, to prevent hi-load:
if not Utils.wait_for(lambda: not self.idle, if not Utils.wait_for(lambda: not self.active or not self.idle,
self.sleeptime * 10, self.sleeptime self.sleeptime * 10, self.sleeptime
): ):
self.ticks += 1 self.ticks += 1
@ -285,7 +294,21 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
self.jail.putFailTicket(ticket) self.jail.putFailTicket(ticket)
except FailManagerEmpty: except FailManagerEmpty:
self.failManager.cleanup(MyTime.time()) self.failManager.cleanup(MyTime.time())
except Exception as e: # pragma: no cover
if not self.active: # if not active - error by stop...
break
logSys.error("Caught unhandled exception in main cycle: %r", e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
# incr common error counter:
self.commonError()
# close journal:
try:
if self.__journal:
self.__journal.close()
except Exception as e: # pragma: no cover
logSys.error("Close journal failed: %r", e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
logSys.debug((self.jail is not None and self.jail.name logSys.debug((self.jail is not None and self.jail.name
or "jailless") +" filter terminated") or "jailless") +" filter terminated")
return True return True

View File

@ -239,19 +239,31 @@ class Jail(object):
Once stated, also queries the persistent database to reinstate Once stated, also queries the persistent database to reinstate
any valid bans. any valid bans.
""" """
logSys.debug("Starting jail %r", self.name)
self.filter.start() self.filter.start()
self.actions.start() self.actions.start()
self.restoreCurrentBans() self.restoreCurrentBans()
logSys.info("Jail %r started", self.name) logSys.info("Jail %r started", self.name)
def stop(self): def stop(self, stop=True, join=True):
"""Stop the jail, by stopping filter and actions threads. """Stop the jail, by stopping filter and actions threads.
""" """
self.filter.stop() if stop:
self.actions.stop() logSys.debug("Stopping jail %r", self.name)
self.filter.join() for obj in (self.filter, self.actions):
self.actions.join() try:
logSys.info("Jail '%s' stopped" % self.name) ## signal to stop filter / actions:
if stop:
obj.stop()
## wait for end of threads:
if join:
if obj.isAlive():
obj.join()
except Exception as e:
logSys.error("Stop %r of jail %r failed: %s", obj, self.name, e,
exc_info=logSys.getEffectiveLevel()<=logging.DEBUG)
if join:
logSys.info("Jail %r stopped", self.name)
def isAlive(self): def isAlive(self):
"""Check jail "isAlive" by checking filter and actions threads. """Check jail "isAlive" by checking filter and actions threads.

View File

@ -210,11 +210,11 @@ class Server:
if self.__db is not None: if self.__db is not None:
self.__db.addJail(self.__jails[name]) self.__db.addJail(self.__jails[name])
def delJail(self, name, stop=True): def delJail(self, name, stop=True, join=True):
jail = self.__jails[name] jail = self.__jails[name]
if stop and jail.isAlive(): if join or jail.isAlive():
logSys.debug("Stopping jail %r" % name) jail.stop(stop=stop, join=join)
jail.stop() if join:
if self.__db is not None: if self.__db is not None:
self.__db.delJail(jail) self.__db.delJail(jail)
del self.__jails[name] del self.__jails[name]
@ -237,8 +237,12 @@ class Server:
def stopAllJail(self): def stopAllJail(self):
logSys.info("Stopping all jails") logSys.info("Stopping all jails")
with self.__lock: with self.__lock:
# 1st stop all jails (signal and stop actions/filter thread):
for name in self.__jails.keys(): for name in self.__jails.keys():
self.delJail(name, stop=True) self.delJail(name, stop=True, join=False)
# 2nd wait for end and delete jails:
for name in self.__jails.keys():
self.delJail(name, stop=False, join=True)
def reloadJails(self, name, opts, begin): def reloadJails(self, name, opts, begin):
if begin: if begin:

View File

@ -52,8 +52,8 @@ class Utils():
"""Utilities provide diverse static methods like executes OS shell commands, etc. """Utilities provide diverse static methods like executes OS shell commands, etc.
""" """
DEFAULT_SLEEP_TIME = 0.1 DEFAULT_SLEEP_TIME = 2
DEFAULT_SLEEP_INTERVAL = 0.01 DEFAULT_SLEEP_INTERVAL = 0.2
class Cache(object): class Cache(object):