2017-12-16 18:59:33 +01:00
|
|
|
|
# pyruse is intended as a replacement to both fail2ban and epylog
|
2018-01-31 11:58:12 +01:00
|
|
|
|
# Copyright © 2017–2018 Y. Gablin
|
2017-12-16 18:59:33 +01:00
|
|
|
|
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
|
2018-02-01 08:30:35 +01:00
|
|
|
|
from pyruse import base, config, log, module
|
2017-12-15 19:36:50 +01:00
|
|
|
|
|
|
|
|
|
class Workflow:
|
|
|
|
|
def __init__(self, actions):
|
2018-01-31 12:58:26 +01:00
|
|
|
|
self._withDebug = config.Config().asMap().get("debug", False)
|
2017-12-15 19:36:50 +01:00
|
|
|
|
seen = {}
|
|
|
|
|
dangling = []
|
|
|
|
|
firstStep = None
|
|
|
|
|
for label in actions:
|
|
|
|
|
if not label in seen:
|
2018-02-24 13:05:57 +01:00
|
|
|
|
(entryPoint, seen, newDangling) = self._initChain(actions, label, seen, (label,))
|
2017-12-15 19:36:50 +01:00
|
|
|
|
if firstStep is None:
|
|
|
|
|
firstStep = entryPoint
|
|
|
|
|
elif len(dangling) > 0:
|
|
|
|
|
for setter in dangling:
|
|
|
|
|
setter(entryPoint)
|
|
|
|
|
dangling = newDangling
|
2018-01-31 15:13:03 +01:00
|
|
|
|
self.firstStep = firstStep
|
2017-12-15 19:36:50 +01:00
|
|
|
|
|
2018-02-24 13:05:57 +01:00
|
|
|
|
def _initChain(self, actions, label, seen, wholeChain):
|
2017-12-15 19:36:50 +01:00
|
|
|
|
dangling = []
|
|
|
|
|
previousSetter = None
|
|
|
|
|
firstStep = None
|
|
|
|
|
isPreviousDangling = False
|
|
|
|
|
isThenCalled = False
|
2018-01-31 12:58:26 +01:00
|
|
|
|
for stepNum, step in enumerate(actions[label]):
|
2017-12-15 19:36:50 +01:00
|
|
|
|
if isThenCalled:
|
|
|
|
|
break
|
|
|
|
|
mod = module.get(step)
|
|
|
|
|
obj = mod.module
|
2018-01-31 12:58:26 +01:00
|
|
|
|
if self._withDebug:
|
|
|
|
|
obj.setStepName(label + '[' + str(stepNum) + ']')
|
2018-01-31 11:58:12 +01:00
|
|
|
|
if mod.thenRun:
|
|
|
|
|
(seen, dangling) = \
|
2018-02-24 13:05:57 +01:00
|
|
|
|
self._branchToChain(
|
|
|
|
|
obj.setNextStep, mod.thenRun, wholeChain,
|
|
|
|
|
actions, seen, dangling)
|
2018-01-31 11:58:12 +01:00
|
|
|
|
isThenCalled = True
|
|
|
|
|
if mod.isFilter:
|
2017-12-15 19:36:50 +01:00
|
|
|
|
if mod.elseRun:
|
|
|
|
|
(seen, dangling) = \
|
2018-02-24 13:05:57 +01:00
|
|
|
|
self._branchToChain(
|
|
|
|
|
obj.setAltStep, mod.elseRun, wholeChain,
|
|
|
|
|
actions, seen, dangling)
|
2017-12-15 19:36:50 +01:00
|
|
|
|
else:
|
|
|
|
|
dangling.append(obj.setAltStep)
|
2018-01-31 11:58:12 +01:00
|
|
|
|
isPreviousDangling = mod.isFilter and not isThenCalled
|
2017-12-15 19:36:50 +01:00
|
|
|
|
if previousSetter:
|
|
|
|
|
previousSetter(obj)
|
|
|
|
|
else:
|
|
|
|
|
firstStep = obj
|
|
|
|
|
previousSetter = obj.setNextStep
|
|
|
|
|
if isPreviousDangling:
|
|
|
|
|
dangling.append(previousSetter)
|
2018-02-01 08:30:35 +01:00
|
|
|
|
seen[label] = firstStep if len(dangling) == 0 else None
|
2017-12-15 19:36:50 +01:00
|
|
|
|
return (firstStep, seen, dangling)
|
|
|
|
|
|
2018-02-24 13:05:57 +01:00
|
|
|
|
def _branchToChain(self, parentSetter, branchName, wholeChain, actions, seen, dangling):
|
|
|
|
|
if branchName in wholeChain:
|
|
|
|
|
raise RecursionError("Loop found in actions: %s\n" % str(wholeChain + (branchName,)))
|
|
|
|
|
elif branchName in seen and seen[branchName] is not None:
|
2017-12-15 19:36:50 +01:00
|
|
|
|
parentSetter(seen[branchName])
|
|
|
|
|
elif branchName in actions:
|
|
|
|
|
(entryPoint, seen, newDangling) = \
|
2018-02-24 13:05:57 +01:00
|
|
|
|
self._initChain(actions, branchName, seen, wholeChain + (branchName,))
|
2017-12-15 19:36:50 +01:00
|
|
|
|
parentSetter(entryPoint)
|
|
|
|
|
dangling.extend(newDangling)
|
|
|
|
|
else:
|
|
|
|
|
raise ValueError("Action chain not found: %s\n" % branchName)
|
|
|
|
|
return (seen, dangling)
|