diff --git a/fail2ban-testcases b/fail2ban-testcases index 41aca671..0160781d 100755 --- a/fail2ban-testcases +++ b/fail2ban-testcases @@ -119,6 +119,20 @@ tests.addTest(unittest.makeSuite(banmanagertestcase.AddFailure)) # ClientReader tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest)) +# Filter +tests.addTest(unittest.makeSuite(filtertestcase.IgnoreIP)) +tests.addTest(unittest.makeSuite(filtertestcase.LogFile)) +tests.addTest(unittest.makeSuite(filtertestcase.LogFileMonitor)) +tests.addTest(unittest.makeSuite(filtertestcase.GetFailures)) +tests.addTest(unittest.makeSuite(filtertestcase.DNSUtilsTests)) + +# DateDetector +tests.addTest(unittest.makeSuite(datedetectortestcase.DateDetectorTest)) + +# +# Extensive use-tests of different available filters backends +# + from server.filterpoll import FilterPoll filters = [FilterPoll] # always available @@ -127,8 +141,7 @@ filters = [FilterPoll] # always available # with good old unittest try: from server.filtergamin import FilterGamin - # That shmug plain doesn't work and stalls things ATM - # filters.append(FilterGamin) + filters.append(FilterGamin) except: pass @@ -142,22 +155,6 @@ for Filter_ in filters: tests.addTest(unittest.makeSuite( filtertestcase.get_monitor_failures_testcase(Filter_))) -# yoh: adding them (in particular datadetectortestscase before above -# get_monitor_failures_testcase's makes them fail (probably due -# to additional thread making it busier or smth like -# that)... TODO - -# Filter -tests.addTest(unittest.makeSuite(filtertestcase.IgnoreIP)) -tests.addTest(unittest.makeSuite(filtertestcase.LogFile)) -tests.addTest(unittest.makeSuite(filtertestcase.LogFileMonitor)) -tests.addTest(unittest.makeSuite(filtertestcase.GetFailures)) -tests.addTest(unittest.makeSuite(filtertestcase.DNSUtilsTests)) - -# DateDetector -tests.addTest(unittest.makeSuite(datedetectortestcase.DateDetectorTest)) - - # # Run the tests diff --git a/server/filtergamin.py b/server/filtergamin.py index 84148b1a..cff5aa54 100644 --- a/server/filtergamin.py +++ b/server/filtergamin.py @@ -17,14 +17,10 @@ # along with Fail2Ban; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# Author: Cyril Jaquier -# -# $Revision$ +# Author: Cyril Jaquier, Yaroslav Halchenko -__author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" -__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__author__ = "Cyril Jaquier, Yaroslav Halchenko" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko" __license__ = "GPL" from failmanager import FailManagerEmpty @@ -50,7 +46,7 @@ class FilterGamin(FileFilter): # # Initialize the filter object with default values. # @param jail the jail object - + def __init__(self, jail): FileFilter.__init__(self, jail) self.__modified = False @@ -63,9 +59,26 @@ class FilterGamin(FileFilter): logSys.debug("Got event: " + `event` + " for " + path) if event in (gamin.GAMCreated, gamin.GAMChanged, gamin.GAMExists): logSys.debug("File changed: " + path) - self.getFailures(path) self.__modified = True + self._process_file(path) + + + def _process_file(self, path): + """Process a given file + + TODO -- RF: + this is a common logic and must be shared/provided by FileFilter + """ + self.getFailures(path) + try: + while True: + ticket = self.failManager.toBan() + self.jail.putFailTicket(ticket) + except FailManagerEmpty: + self.failManager.cleanup(MyTime.time()) + self.dateDetector.sortTemplate() + self.__modified = False ## # Add a log file path @@ -80,7 +93,7 @@ class FilterGamin(FileFilter): # # @param path the log file to delete - def delLogPath(self, path): + def _delLogPath(self, path): self.monitor.stop_watch(path) ## @@ -92,30 +105,22 @@ class FilterGamin(FileFilter): def run(self): self.setActive(True) + # Gamin needs a loop to collect and dispatch events while self._isActive(): if not self.getIdle(): # We cannot block here because we want to be able to # exit. if self.monitor.event_pending(): self.monitor.handle_events() - - if self.__modified: - try: - while True: - ticket = self.failManager.toBan() - self.jail.putFailTicket(ticket) - except FailManagerEmpty: - self.failManager.cleanup(MyTime.time()) - self.dateDetector.sortTemplate() - self.__modified = False - time.sleep(self.getSleepTime()) - else: - time.sleep(self.getSleepTime()) - # Cleanup Gamin - self.__cleanup() + time.sleep(self.getSleepTime()) logSys.debug(self.jail.getName() + ": filter terminated") return True + + def stop(self): + super(FilterGamin, self).stop() + self.__cleanup() + ## # Desallocates the resources used by Gamin. diff --git a/testcases/filtertestcase.py b/testcases/filtertestcase.py index 4ddede8b..eade537f 100644 --- a/testcases/filtertestcase.py +++ b/testcases/filtertestcase.py @@ -47,6 +47,18 @@ def _killfile(f, name): except: pass +def _sleep_4_poll(): + """PollFilter relies on file timestamps - so we might need to + sleep to guarantee that they differ + """ + if sys.version_info[:2] <= (2,4): + # on old Python st_mtime is int, so we should give + # at least 1 sec so polling filter could detect + # the change + time.sleep(1.) + else: + time.sleep(0.1) + def _assert_equal_entries(utest, found, output, count=None): """Little helper to unify comparisons with the target entries @@ -201,12 +213,14 @@ class LogFileMonitor(unittest.TestCase): # but not any longer self.assertTrue(self.notModified()) self.assertTrue(self.notModified()) + _sleep_4_poll() # to guarantee freshier mtime for i in range(4): # few changes # unless we write into it self.file.write("line%d\n" % i) self.file.flush() self.assertTrue(self.isModified()) self.assertTrue(self.notModified()) + _sleep_4_poll() # to guarantee freshier mtime os.rename(self.name, self.name + '.old') # we are not signaling as modified whenever # it gets away @@ -214,6 +228,7 @@ class LogFileMonitor(unittest.TestCase): f = open(self.name, 'a') self.assertTrue(self.isModified()) self.assertTrue(self.notModified()) + _sleep_4_poll() f.write("line%d\n" % i) f.flush() self.assertTrue(self.isModified()) @@ -356,13 +371,7 @@ def get_monitor_failures_testcase(Filter_): # actions might be happening too fast in the tests, # sleep a bit to guarantee reliable time stamps if isinstance(self.filter, FilterPoll): - if sys.version_info[:2] <= (2,4): - # on old Python st_mtime is int, so we should give - # at least 1 sec so polling filter could detect - # the change - time.sleep(0.5) - else: - time.sleep(0.1) + _sleep_4_poll() def isEmpty(self, delay=0.4): # shorter wait time for not modified status