diff --git a/bin/fail2ban-testcases b/bin/fail2ban-testcases index 22a44a8b..606b0b06 100755 --- a/bin/fail2ban-testcases +++ b/bin/fail2ban-testcases @@ -67,6 +67,9 @@ def get_opt_parser(): Option('-f', "--fast", action="store_true", dest="fast", help="Try to increase speed of the tests, decreasing of wait intervals, memory database"), + Option('-i', "--ignore", action="store_true", + dest="negate_re", + help="negate [regexps] filter to ignore tests matched specified regexps"), Option("-t", "--log-traceback", action='store_true', help="Enrich log-messages with compressed tracebacks"), Option("--full-traceback", action='store_true', diff --git a/fail2ban/server/mytime.py b/fail2ban/server/mytime.py index 329e2465..816b04c4 100644 --- a/fail2ban/server/mytime.py +++ b/fail2ban/server/mytime.py @@ -98,6 +98,25 @@ class MyTime: else: return time.localtime(MyTime.myTime) + ## precreate/precompile primitives used in str2seconds: + + ## preparing expression: + _str2sec_prep = re.compile(r"(?i)(?<=[a-z])(\d)") + ## finally expression: + _str2sec_fini = re.compile(r"(\d)\s+(\d)") + ## wrapper for each sub part: + _str2sec_subpart = r"(?i)(?<=[\d\s])(%s)\b" + ## parts to be replaced - pair of (regexp x replacement): + _str2sec_parts = ( + (re.compile(_str2sec_subpart % r"days?|da|dd?"), "*"+str(24*60*60)), + (re.compile(_str2sec_subpart % r"weeks?|wee?|ww?"), "*"+str(7*24*60*60)), + (re.compile(_str2sec_subpart % r"months?|mon?"), "*"+str((365*3+366)*24*60*60/4/12)), + (re.compile(_str2sec_subpart % r"years?|yea?|yy?"), "*"+str((365*3+366)*24*60*60/4)), + (re.compile(_str2sec_subpart % r"seconds?|sec?|ss?"), "*"+str(1)), + (re.compile(_str2sec_subpart % r"minutes?|min?|mm?"), "*"+str(60)), + (re.compile(_str2sec_subpart % r"hours?|hou?|hh?"), "*"+str(60*60)), + ) + @staticmethod def str2seconds(val): """Wraps string expression like "1h 2m 3s" into number contains seconds (3723). @@ -120,13 +139,9 @@ class MyTime: if isinstance(val, (int, long, float, complex)): return val # replace together standing abbreviations, example '1d12h' -> '1d 12h': - val = re.sub(r"(?i)(?<=[a-z])(\d)", r" \1", val) + val = MyTime._str2sec_prep.sub(r" \1", val) # replace abbreviation with expression: - for rexp, rpl in ( - (r"days?|da|dd?", 24*60*60), (r"weeks?|wee?|ww?", 7*24*60*60), (r"months?|mon?", (365*3+366)*24*60*60/4/12), - (r"years?|yea?|yy?", (365*3+366)*24*60*60/4), - (r"seconds?|sec?|ss?", 1), (r"minutes?|min?|mm?", 60), (r"hours?|hou?|hh?", 60*60), - ): - val = re.sub(r"(?i)(?<=[\d\s])(%s)\b" % rexp, "*"+str(rpl), val) - val = re.sub(r"(\d)\s+(\d)", r"\1+\2", val); + for rexp, rpl in MyTime._str2sec_parts: + val = rexp.sub(rpl, val) + val = MyTime._str2sec_fini.sub(r"\1+\2", val) return eval(val) diff --git a/fail2ban/tests/utils.py b/fail2ban/tests/utils.py index 0ba0c7c6..0ca61183 100644 --- a/fail2ban/tests/utils.py +++ b/fail2ban/tests/utils.py @@ -55,6 +55,7 @@ class F2B(optparse.Values): def __init__(self, opts={}): self.__dict__ = opts.__dict__ if opts else { 'fast': False, 'memory_db':False, 'no_gamin': False, 'no_network': False, + "negate_re": False, } if self.fast: self.memory_db = True @@ -158,7 +159,8 @@ def gatherTests(regexps=None, opts=None): for test in suite: s = str(test) for r in self._regexps: - if r.search(s): + m = r.search(s) + if (m if not opts.negate_re else not m): matched.append(test) break for test in matched: