more cases covered, start in repair distinguish operations, on demand flag etc

pull/2588/head
sebres 2020-01-07 15:50:54 +01:00
parent b7fe33483a
commit 125da61bda
2 changed files with 58 additions and 22 deletions

View File

@ -484,7 +484,7 @@ class CommandAction(ActionBase):
""" """
return self._start() return self._start()
def _start(self, family='', forceStart=False): def _start(self, family=None, forceStart=False):
"""Executes the "actionstart" command. """Executes the "actionstart" command.
Replace the tags in the action command with actions properties Replace the tags in the action command with actions properties
@ -496,7 +496,7 @@ class CommandAction(ActionBase):
return True return True
elif not forceStart and self.__started.get(family): # pragma: no cover - normally unreachable elif not forceStart and self.__started.get(family): # pragma: no cover - normally unreachable
return True return True
family = [family] if family != '' else self._families family = [family] if family is not None else self._families
def _started(family, ret): def _started(family, ret):
if ret: if ret:
self._operationExecuted('<actionstop>', family, None) self._operationExecuted('<actionstop>', family, None)
@ -570,14 +570,14 @@ class CommandAction(ActionBase):
""" """
return self._stop() return self._stop()
def _stop(self, family=''): def _stop(self, family=None):
"""Executes the "actionstop" command. """Executes the "actionstop" command.
Replaces the tags in the action command with actions properties Replaces the tags in the action command with actions properties
and executes the resulting command. and executes the resulting command.
""" """
# collect started families, if started on demand (conditional): # collect started families, if started on demand (conditional):
if not family: if family is None:
family = [f for (f,v) in self.__started.iteritems() if v] family = [f for (f,v) in self.__started.iteritems() if v]
# if no started (on demand) actions: # if no started (on demand) actions:
if not family: return True if not family: return True
@ -812,26 +812,27 @@ class CommandAction(ActionBase):
realCmd = Utils.buildShellCmd(realCmd, varsDict) realCmd = Utils.buildShellCmd(realCmd, varsDict)
return realCmd return realCmd
def _invariantCheck(self, family='', beforeRepair=None): def _invariantCheck(self, family=None, beforeRepair=None, forceStart=True):
"""Executes a substituted `actioncheck` command. """Executes a substituted `actioncheck` command.
""" """
# for started action/family only (avoid check not started inet4 if inet6 gets broken): # for started action/family only (avoid check not started inet4 if inet6 gets broken):
if family != '' and family not in self.__started and not self.__started.get(''): if not forceStart and family is not None and family not in self.__started:
return True return 1
checkCmd = self._getOperation('<actioncheck>', family) checkCmd = self._getOperation('<actioncheck>', family)
if not checkCmd or self.executeCmd(checkCmd, self.timeout): if not checkCmd or self.executeCmd(checkCmd, self.timeout):
return True return 1
# if don't need repair/restore - just return: # if don't need repair/restore - just return:
if beforeRepair and not beforeRepair(): if beforeRepair and not beforeRepair():
return False return -1
self._logSys.error( self._logSys.error(
"Invariant check failed. Trying to restore a sane environment") "Invariant check failed. Trying to restore a sane environment")
# try to find repair command, if exists - exec it: # try to find repair command, if exists - exec it:
repairCmd = self._getOperation('<actionrepair>', family) repairCmd = self._getOperation('<actionrepair>', family)
if repairCmd: if repairCmd:
if not self.executeCmd(repairCmd, self.timeout): if not self.executeCmd(repairCmd, self.timeout):
self.__started[family] = 0
self._logSys.critical("Unable to restore environment") self._logSys.critical("Unable to restore environment")
return False return 0
self.__started[family] = 1 self.__started[family] = 1
else: else:
# no repair command, try to restart action... # no repair command, try to restart action...
@ -843,11 +844,11 @@ class CommandAction(ActionBase):
self._stop(family) self._stop(family)
except RuntimeError: # bypass error in stop (if start/check succeeded hereafter). except RuntimeError: # bypass error in stop (if start/check succeeded hereafter).
pass pass
self._start(family) self._start(family, forceStart=forceStart or not self._startOnDemand)
if not self.executeCmd(checkCmd, self.timeout): if self.__started.get(family) and not self.executeCmd(checkCmd, self.timeout):
self._logSys.critical("Unable to restore environment") self._logSys.critical("Unable to restore environment")
return False return 0
return True return 1
def _processCmd(self, cmd, aInfo=None): def _processCmd(self, cmd, aInfo=None):
"""Executes a command with preliminary checks and substitutions. """Executes a command with preliminary checks and substitutions.
@ -876,7 +877,7 @@ class CommandAction(ActionBase):
# conditional corresponding family of the given ip: # conditional corresponding family of the given ip:
try: try:
family = aInfo["family"] family = aInfo["family"]
except KeyError: except (KeyError, TypeError):
family = '' family = ''
# invariant check: # invariant check:
@ -887,8 +888,10 @@ class CommandAction(ActionBase):
self._logSys.error("Invariant check failed. Unban is impossible.") self._logSys.error("Invariant check failed. Unban is impossible.")
return False return False
return True return True
ret = self._invariantCheck(family, _beforeRepair) # check and repair if broken:
if not ret: ret = self._invariantCheck(family, _beforeRepair, forceStart=(cmd != '<actionunban>'))
# if not sane (and not restored) return:
if ret != 1:
return False return False
# Replace static fields # Replace static fields

View File

@ -228,8 +228,12 @@ class ExecuteActions(LogCaptureTestCase):
all=True, wait=True) all=True, wait=True)
# check should fail (so cause stop/start): # check should fail (so cause stop/start):
self.pruneLog('[test-phase 1] simulate inconsistent env') self.pruneLog('[test-phase 1a] simulate inconsistent irreparable env by unban')
act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1' act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1'
self.__actions.removeBannedIP('2001:db8::1')
self.assertLogged('Invariant check failed. Unban is impossible.',
wait=True)
self.pruneLog('[test-phase 1b] simulate inconsistent irreparable env by flush')
self.__actions._Actions__flushBan() self.__actions._Actions__flushBan()
self.assertLogged( self.assertLogged(
"stdout: %r" % 'ip flush inet4', "stdout: %r" % 'ip flush inet4',
@ -238,7 +242,7 @@ class ExecuteActions(LogCaptureTestCase):
'No flush occured, do consistency check', 'No flush occured, do consistency check',
'Invariant check failed. Trying to restore a sane environment', 'Invariant check failed. Trying to restore a sane environment',
"stdout: %r" % 'ip stop', # same for both families "stdout: %r" % 'ip stop', # same for both families
'Unable to restore environment', 'Failed to flush bans',
all=True, wait=True) all=True, wait=True)
# check succeeds: # check succeeds:
@ -283,11 +287,12 @@ class ExecuteActions(LogCaptureTestCase):
all=True) all=True)
def testActionsConsistencyCheckDiffFam(self): def testActionsConsistencyCheckDiffFam(self):
# same as testActionsConsistencyCheck, but different start/stop commands for both families # same as testActionsConsistencyCheck, but different start/stop commands for both families and repair on unban
act = self.defaultAction({'start':' <family>', 'check':' <family>', 'flush':' <family>', 'stop':' <family>'}) act = self.defaultAction({'start':' <family>', 'check':' <family>', 'flush':' <family>', 'stop':' <family>'})
# flush for inet6 is intentionally "broken" here - test no unhandled except and invariant check: # flush for inet6 is intentionally "broken" here - test no unhandled except and invariant check:
act['actionflush?family=inet6'] = act.actionflush + '; exit 1' act['actionflush?family=inet6'] = act.actionflush + '; exit 1'
act.actionstart_on_demand = True act.actionstart_on_demand = True
act.actionrepair_on_unban = True
self.__actions.start() self.__actions.start()
self.assertNotLogged("stdout: %r" % 'ip start') self.assertNotLogged("stdout: %r" % 'ip start')
@ -301,8 +306,36 @@ class ExecuteActions(LogCaptureTestCase):
all=True, wait=True) all=True, wait=True)
# check should fail (so cause stop/start): # check should fail (so cause stop/start):
self.pruneLog('[test-phase 1] simulate inconsistent env')
act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1' act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1'
self.pruneLog('[test-phase 1a] simulate inconsistent irreparable env by unban')
self.__actions.removeBannedIP('2001:db8::1')
self.assertLogged('Invariant check failed. Trying to restore a sane environment',
"stdout: %r" % 'ip stop inet6',
all=True, wait=True)
self.assertNotLogged(
"stdout: %r" % 'ip start inet6', # start on demand (not on repair)
"stdout: %r" % 'ip stop inet4', # family inet4 is not affected
"stdout: %r" % 'ip start inet4',
all=True)
self.pruneLog('[test-phase 1b] simulate inconsistent irreparable env by ban')
self.assertEqual(self.__actions.addBannedIP('2001:db8::1'), 1)
self.assertLogged('Invariant check failed. Trying to restore a sane environment',
"stdout: %r" % 'ip stop inet6',
"stdout: %r" % 'ip start inet6',
"stdout: %r" % 'ip check inet6',
'Unable to restore environment',
'Failed to execute ban',
all=True, wait=True)
self.assertNotLogged(
"stdout: %r" % 'ip stop inet4', # family inet4 is not affected
"stdout: %r" % 'ip start inet4',
all=True)
act['actioncheck?family=inet6'] = act.actioncheck
self.assertEqual(self.__actions.addBannedIP('2001:db8::2'), 1)
act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1'
self.pruneLog('[test-phase 1c] simulate inconsistent irreparable env by flush')
self.__actions._Actions__flushBan() self.__actions._Actions__flushBan()
self.assertLogged( self.assertLogged(
"stdout: %r" % 'ip flush inet4', "stdout: %r" % 'ip flush inet4',
@ -311,7 +344,7 @@ class ExecuteActions(LogCaptureTestCase):
'No flush occured, do consistency check', 'No flush occured, do consistency check',
'Invariant check failed. Trying to restore a sane environment', 'Invariant check failed. Trying to restore a sane environment',
"stdout: %r" % 'ip stop inet6', "stdout: %r" % 'ip stop inet6',
'Unable to restore environment', 'Failed to flush bans in jail',
all=True, wait=True) all=True, wait=True)
# start/stop should be called for inet6 only: # start/stop should be called for inet6 only:
self.assertNotLogged( self.assertNotLogged(