some reworks

- rework configYaml
- rework router mechanism test
- move plugin and module files
This commit is contained in:
Bastian Schroll 2019-03-01 12:09:12 +01:00
parent 71d87b080f
commit a42676010e
19 changed files with 317 additions and 121 deletions

View file

@ -12,68 +12,52 @@
@file: configYaml.py @file: configYaml.py
@date: 27.02.2019 @date: 27.02.2019
@author: Bastian Schroll @author: Bastian Schroll
@description: Module for the configuration in yaml format @description: Module for the configuration in YAML format
""" """
import logging import logging
import yaml import yaml
logging.debug("- %s loaded", __name__) logging.debug("- %s loaded", __name__)
__sharePoints = {}
class ConfigYAML:
def loadConfigFile(configPath, sharePoint=""): def __init__(self, config=None):
"""!loads a given configuration self.__config = config
@param configPath: Path to the config file def __iter__(self):
@param sharePoint: If you want to share the config set name here for item in self.__config:
@return python dict of config or None""" if type(item) is list or type(item) is dict:
logging.debug("load config file from: %s", configPath) yield ConfigYAML(item)
try: else:
yield item
with open(configPath) as f: def __str__(self):
# use safe_load instead load return str(self.__config)
config = yaml.safe_load(f)
if sharePoint:
_shareConfig(config, sharePoint)
return config
except: # pragma: no cover
logging.exception("cannot load config file")
return None
def loadConfigFile(self, configPath):
"""!loads a given configuration
def loadConfigSharepoint(sharePoint): @param configPath: Path to the config file
"""!loads a given configuration from an sharepoint @return True or False"""
logging.debug("load config file from: %s", configPath)
try:
with open(configPath) as file:
# use safe_load instead load
self.__config = yaml.safe_load(file)
return True
except: # pragma: no cover
logging.exception("cannot load config file")
return False
@param sharePoint: Name of the sharepoint def get(self, *args, default=None):
@return python dict of config or None""" tmp = self.__config
try: try:
return __sharePoints[sharePoint] for arg in args:
except KeyError: tmp = tmp.get(arg, default)
logging.error("no sharePoint named: %s", sharePoint) if type(tmp) is list or type(tmp) is dict:
except: # pragma: no cover return ConfigYAML(tmp)
logging.exception("error while reading shared config") else:
return None return tmp
except AttributeError:
return default
def _shareConfig(config, sharePoint):
"""!Shares the configuration
Shares the local _config to the class wide global _sharedConfig
@param config: Python dict of the configuration
@param sharePoint: Name of the global share point
@return True or False"""
if sharePoint in __sharePoints:
logging.error("cannot share config - name is always in use: %s", sharePoint)
return False
else:
__sharePoints[sharePoint] = config
logging.debug("add config sharePoint: %s", sharePoint)
return True
def getAllSharepoints():
"""!Return a python dict of all set sharepoints
@return Sharepoint dict"""
return __sharePoints

View file

@ -12,7 +12,7 @@
@file: pluginManager.py @file: pluginManager.py
@date: 08.01.2018 @date: 08.01.2018
@author: Bastian Schroll @author: Bastian Schroll
@description: Plugin manager class to load and call the plugins @description: Plugin manager class to load and call the plugin
@todo must be mostly refactored @todo must be mostly refactored
""" """
import logging import logging
@ -27,7 +27,7 @@ logging.debug("- %s loaded", __name__)
class PluginManager: class PluginManager:
"""!Plugin manager class to load, manage and call the plugins """!Plugin manager class to load, manage and call the plugin
@todo refactor the class and add documentation""" @todo refactor the class and add documentation"""
@ -37,7 +37,7 @@ class PluginManager:
self._pluginList = [] self._pluginList = []
def searchPluginDir(self): def searchPluginDir(self):
logging.debug("search for plugins in: %s", paths.PLUGIN_PATH) logging.debug("search for plugin in: %s", paths.PLUGIN_PATH)
for name in os.listdir(paths.PLUGIN_PATH): for name in os.listdir(paths.PLUGIN_PATH):
location = os.path.join(paths.PLUGIN_PATH, name) location = os.path.join(paths.PLUGIN_PATH, name)
@ -45,7 +45,7 @@ class PluginManager:
if not os.path.isdir(location) or not name + ".py" in os.listdir(location): if not os.path.isdir(location) or not name + ".py" in os.listdir(location):
continue continue
pluginPriority = self._config["plugins"][name] pluginPriority = self._config["plugin"][name]
if pluginPriority is None: if pluginPriority is None:
logging.warning("no entry in server config for plugin: %s", name) logging.warning("no entry in server config for plugin: %s", name)
@ -60,7 +60,7 @@ class PluginManager:
self._pluginList.sort(key=lambda x: x['pluginPriority'], reverse=True) self._pluginList.sort(key=lambda x: x['pluginPriority'], reverse=True)
def importAllPlugins(self): def importAllPlugins(self):
logging.debug("importing all plugins") logging.debug("importing all plugin")
for item in self._pluginList: for item in self._pluginList:
importPlugin = self._importPlugin(item["pluginName"]) importPlugin = self._importPlugin(item["pluginName"])
if importPlugin: if importPlugin:
@ -70,13 +70,13 @@ class PluginManager:
def _importPlugin(pluginName): def _importPlugin(pluginName):
logging.debug("import plugin: %s", pluginName) logging.debug("import plugin: %s", pluginName)
try: try:
return importlib.import_module("plugins." + pluginName + "." + pluginName) return importlib.import_module("plugin." + pluginName + "." + pluginName)
except: except:
logging.exception("error while loading plugin: %s", pluginName) logging.exception("error while loading plugin: %s", pluginName)
return False return False
def loadAllPlugins(self): def loadAllPlugins(self):
logging.debug("loading all plugins") logging.debug("loading all plugin")
for item in self._pluginList: for item in self._pluginList:
item["pluginObject"] = None # todo del or none ??? item["pluginObject"] = None # todo del or none ???
item["pluginObject"] = item["pluginImport"].BoswatchPlugin() item["pluginObject"] = item["pluginImport"].BoswatchPlugin()
@ -89,7 +89,7 @@ class PluginManager:
self.printEndStats() self.printEndStats()
def unloadAllPlugins(self): def unloadAllPlugins(self):
logging.debug("unload all plugins") logging.debug("unload all plugin")
for item in self._pluginList: for item in self._pluginList:
# todo del or None ??? # todo del or None ???
del item["pluginObject"] # delete plugin object to force __del__() running del item["pluginObject"] # delete plugin object to force __del__() running

View file

@ -53,7 +53,7 @@ class Router:
endpoint.call(bwPacket) endpoint.call(bwPacket)
# modules # module
double = Module("double") double = Module("double")
descriptor = Module("descriptor") descriptor = Module("descriptor")
# boswatch plugins # boswatch plugins
@ -63,7 +63,7 @@ mysql = Plugin("mysql")
Router1 = Router("R1") Router1 = Router("R1")
Router2 = Router("R2") Router2 = Router("R2")
# Router 1 modules # Router 1 module
Router1.addModule(double) Router1.addModule(double)
Router1.addModule(descriptor) Router1.addModule(descriptor)
Router1.addModule(double) Router1.addModule(double)
@ -74,7 +74,7 @@ Router1.addEndpoint(telegram)
Router1.addEndpoint(mysql) Router1.addEndpoint(mysql)
Router1.addEndpoint(Router2) Router1.addEndpoint(Router2)
# Router 2 modules # Router 2 module
Router2.addModule(double) Router2.addModule(double)
Router2.addModule(descriptor) Router2.addModule(descriptor)
# Router 2 endpoints # Router 2 endpoints

View file

@ -34,7 +34,7 @@ else:
LOG_PATH = ROOT_PATH + "log/" LOG_PATH = ROOT_PATH + "log/"
CONFIG_PATH = ROOT_PATH + "config/" CONFIG_PATH = ROOT_PATH + "config/"
PLUGIN_PATH = ROOT_PATH + "plugins/" PLUGIN_PATH = ROOT_PATH + "plugin/"
CSV_PATH = ROOT_PATH + "csv/" CSV_PATH = ROOT_PATH + "csv/"
BIN_PATH = ROOT_PATH + "_bin/" BIN_PATH = ROOT_PATH + "_bin/"
TEST_PATH = ROOT_PATH + "test/" TEST_PATH = ROOT_PATH + "test/"

View file

@ -43,7 +43,7 @@ try:
import time import time
logging.debug("Import BOSWatch modules") logging.debug("Import BOSWatch modules")
from boswatch import configYaml from boswatch.configYaml import ConfigYAML
from boswatch.network.client import TCPClient from boswatch.network.client import TCPClient
from boswatch.network.broadcast import BroadcastClient from boswatch.network.broadcast import BroadcastClient
from boswatch.decoder.decoder import Decoder from boswatch.decoder.decoder import Decoder
@ -68,26 +68,26 @@ try:
parser.add_argument("-c", "--config", help="Name to configuration File", required=True) parser.add_argument("-c", "--config", help="Name to configuration File", required=True)
args = parser.parse_args() args = parser.parse_args()
bwConfig = configYaml.loadConfigFile(paths.CONFIG_PATH + args.config, "clientConfig") bwConfig = ConfigYAML()
if bwConfig is None: if not bwConfig.loadConfigFile(paths.CONFIG_PATH + args.config):
logging.error("cannot load config file") logging.error("cannot load config file")
exit(1)
except: # pragma: no cover except: # pragma: no cover
logging.exception("error occurred") logging.exception("error occurred")
exit(1) exit(1)
# ############################# begin client system # ############################# begin client system
try: try:
if bwConfig["client"]["useBroadcast"]: if bwConfig.get("client", "useBroadcast", default=False):
broadcastClient = BroadcastClient() broadcastClient = BroadcastClient()
if broadcastClient.getConnInfo(): if broadcastClient.getConnInfo():
ip = broadcastClient.serverIP ip = broadcastClient.serverIP
port = broadcastClient.serverPort port = broadcastClient.serverPort
else: else:
ip = bwConfig["server"]["ip"] ip = bwConfig.get("server", "ip", default="127.0.0.1")
port = bwConfig["server"]["port"] port = bwConfig.get("server", "port", default="8080")
bwClient = TCPClient() bwClient = TCPClient()
if bwClient.connect(ip, port): if bwClient.connect(ip, port):

View file

@ -35,7 +35,7 @@ except Exception as e: # pragma: no cover
try: try:
logging.debug("Import python modules") logging.debug("Import python module")
import argparse import argparse
logging.debug("- argparse") logging.debug("- argparse")
# following is temp for testing # following is temp for testing
@ -44,14 +44,14 @@ try:
import threading import threading
import queue import queue
logging.debug("Import BOSWatch modules") logging.debug("Import BOSWatch module")
from boswatch import configYaml from boswatch.configYaml import ConfigYAML
from boswatch.network.server import TCPServer from boswatch.network.server import TCPServer
from boswatch.packet.packet import Packet from boswatch.packet.packet import Packet
from boswatch.utils import header from boswatch.utils import header
from boswatch.network.broadcast import BroadcastServer from boswatch.network.broadcast import BroadcastServer
except: # pragma: no cover except: # pragma: no cover
logging.exception("cannot import modules") logging.exception("cannot import module")
exit(1) exit(1)
try: try:
@ -69,19 +69,21 @@ try:
parser.add_argument("-c", "--config", help="Name to configuration File", required=True) parser.add_argument("-c", "--config", help="Name to configuration File", required=True)
args = parser.parse_args() args = parser.parse_args()
bwConfig = configYaml.loadConfigFile(paths.CONFIG_PATH + args.config, "serverConfig") bwConfig = ConfigYAML()
if bwConfig is None: if not bwConfig.loadConfigFile(paths.CONFIG_PATH + args.config):
logging.error("cannot load config file") logging.error("cannot load config file")
exit(1)
except: # pragma: no cover except: # pragma: no cover
logging.exception("error occurred") logging.exception("error occurred")
exit(1) exit(1)
import router_test
# ############################# begin server system # ############################# begin server system
try: try:
if bwConfig["server"]["useBroadcast"]: if bwConfig.get("server", "useBroadcast", default=False):
bcServer = BroadcastServer() bcServer = BroadcastServer()
bcServer.start() bcServer.start()

View file

@ -12,44 +12,29 @@ server:
name: BW3 Server # name of the BW3 Server instance name: BW3 Server # name of the BW3 Server instance
useBroadcast: no # serve server ip on broadcast request useBroadcast: no # serve server ip on broadcast request
## here you can enable needed plugins
## 0 is disabled
## all greater than 0 enable the plugin
## the higher the number the earlier the plugin is called on alarm
## we call ist Plugin Prioority
plugins:
template: 1
filter:
doubleFilter:
maxEntry: 30
ignoreTime: 10
checkMsg: no
alarmRouter: alarmRouter:
- Router 1 - Router 1
- Router 2
router: router:
- name: Router 1 - name: Router 1
route: route:
- type: module - type: module
name: doubleFilter name: template_module
config: config:
maxEntry: 30 maxEntry: 30
ignoreTime: 10 ignoreTime: 10
checkMsg: no checkMsg: no
- type: router
name: Router 2
- type: plugin - type: plugin
name: mysql name: template_plugin
config: config:
user: test user: test
pass: test pass: test
db: test db: test
- type: router
name: Router 2
- name: Router 2 - name: Router 2
route: route:
- type: module
name: template_module

View file

@ -18,17 +18,15 @@
import logging import logging
import time import time
from boswatch import configYaml
logging.debug("- %s loaded", __name__) logging.debug("- %s loaded", __name__)
class DoubleFilter: class DoubleFilter:
"""!Double Filter Class""" """!Double Filter Class"""
def __init__(self): def __init__(self, config):
"""!init""" """!init"""
self._config = configYaml.loadConfigSharepoint("serverConfig")["filter"]["doubleFilter"] self._config = config
self._filterLists = {} self._filterLists = {}
def filter(self, bwPacket): def filter(self, bwPacket):

103
module/module.py Normal file
View file

@ -0,0 +1,103 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""!
____ ____ ______ __ __ __ _____
/ __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ /
/ __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ <
/ /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ /
/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/
German BOS Information Script
by Bastian Schroll
@file: module.py
@date: 01.03.2019
@author: Bastian Schroll
@description: Module main class to inherit
"""
import logging
import time
logging.debug("- %s loaded", __name__)
class Module:
"""!Main module class"""
_modulesActive = 0
def __init__(self, moduleName, config):
"""!init preload some needed locals and then call onLoad() directly"""
self._moduleName = moduleName
self.config = config
self._modulesActive += 1
# for time counting
self._cumTime = 0
self._moduleTime = 0
self._tmpTime = 0
self._tmpTime = 0
# for statistics
self._runCount = 0
self._moduleErrorCount = 0
logging.debug("[%s] onLoad()", moduleName)
self.onLoad()
def __del__(self):
"""!Destructor calls onUnload() directly"""
logging.debug("[%s] onUnload()", self._moduleName)
self._modulesActive -= 1
self.onUnload()
def _run(self, bwPacket):
"""!start an rund of the module.
@param bwPacket: A BOSWatch packet instance
@return bwPacket or False"""
self._runCount += 1
logging.debug("[%s] run #%d", self._moduleName, self._runCount)
self._tmpTime = time.time()
try:
logging.debug("[%s] doWork()", self._moduleName)
bwPacket = self.doWork(bwPacket)
except:
self._moduleErrorCount += 1
logging.exception("[%s] alarm error", self._moduleName)
self._moduleTime = time.time() - self._tmpTime
self._cumTime += self._moduleTime
self._endTime = time.time()
logging.debug("[%s] took %0.3f seconds", self._moduleName, self._moduleTime)
return bwPacket
def _getStatistics(self):
"""!Returns statistical information's from last module run
@return Statistics as pyton dict"""
stats = {"runCount": self._runCount,
"cumTime": self._cumTime,
"moduleTime": self._moduleTime,
"moduleErrorCount": self._moduleErrorCount}
return stats
def onLoad(self):
"""!Called by import of the module
Must be inherit"""
pass
def doWork(self, bwPacket):
"""!Called module run
Must be inherit
@param bwPacket: bwPacket instance"""
logging.warning("no functionality in module %s", self._moduleName)
def onUnload(self):
"""!Called by destruction of the module
Must be inherit"""
pass

47
module/template_module.py Normal file
View file

@ -0,0 +1,47 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""!
____ ____ ______ __ __ __ _____
/ __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ /
/ __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ <
/ /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ /
/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/
German BOS Information Script
by Bastian Schroll
@file: template_module.py
@date: 01.03.2019
@author: Bastian Schroll
@description: Template Module File
"""
import logging
from module.module import Module
# ###################### #
# Custom plugin includes #
# ###################### #
logging.debug("- %s loaded", __name__)
class BoswatchModule(Module):
"""!Description of the Module"""
def __init__(self, config):
"""!Do not change anything here!"""
super().__init__(__name__, config) # you can access the config DICT by 'self._config'
def onLoad(self):
"""!Called by import of the plugin"""
pass
def doWork(self, bwPacket):
"""!start an rund of the module.
@param bwPacket: A BOSWatch packet instance
@return bwPacket or False"""
return bwPacket
def onUnload(self):
"""!Called by destruction of the plugin"""
pass

2
plugin/__init__.py Normal file
View file

@ -0,0 +1,2 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

View file

@ -17,8 +17,6 @@
import logging import logging
import time import time
from boswatch.utils import paths
from boswatch import configYaml
from boswatch.utils import wildcard from boswatch.utils import wildcard
logging.debug("- %s loaded", __name__) logging.debug("- %s loaded", __name__)
@ -29,9 +27,10 @@ class Plugin:
_pluginsActive = 0 _pluginsActive = 0
def __init__(self, pluginName): def __init__(self, pluginName, config):
"""!init preload some needed locals and then call onLoad() directly""" """!init preload some needed locals and then call onLoad() directly"""
self._pluginName = pluginName self._pluginName = pluginName
self.config = config
self._pluginsActive += 1 self._pluginsActive += 1
# to save the packet while alarm is running for other functions # to save the packet while alarm is running for other functions
@ -52,11 +51,6 @@ class Plugin:
self._alarmErrorCount = 0 self._alarmErrorCount = 0
self._teardownErrorCount = 0 self._teardownErrorCount = 0
if paths.fileExist(paths.PLUGIN_PATH + pluginName + "/" + pluginName + ".yaml"):
self.config = configYaml.loadConfigFile(paths.PLUGIN_PATH + pluginName + "/" + pluginName + ".yaml")
else:
logging.debug("no config for %s found", pluginName)
logging.debug("[%s] onLoad()", pluginName) logging.debug("[%s] onLoad()", pluginName)
self.onLoad() self.onLoad()
@ -126,6 +120,8 @@ class Plugin:
# logging.debug("- alarm: %0.2f sec.", self._alarmTime) # logging.debug("- alarm: %0.2f sec.", self._alarmTime)
# logging.debug("- teardown: %0.2f sec.", self._teardownTime) # logging.debug("- teardown: %0.2f sec.", self._teardownTime)
return None
def _getStatistics(self): def _getStatistics(self):
"""!Returns statistical information's from last plugin run """!Returns statistical information's from last plugin run

View file

@ -9,13 +9,13 @@
German BOS Information Script German BOS Information Script
by Bastian Schroll by Bastian Schroll
@file: template.py @file: template_module.py
@date: 14.01.2018 @date: 14.01.2018
@author: Bastian Schroll @author: Bastian Schroll
@description: Template Plugin File @description: Template Plugin File
""" """
import logging import logging
from boswatch.plugin.plugin import Plugin from plugin.plugin import Plugin
# ###################### # # ###################### #
# Custom plugin includes # # Custom plugin includes #
@ -27,10 +27,9 @@ logging.debug("- %s loaded", __name__)
class BoswatchPlugin(Plugin): class BoswatchPlugin(Plugin):
"""!Description of the Plugin""" """!Description of the Plugin"""
def __init__(self): def __init__(self, config):
"""!Do not change anything here except the PLUGIN NAME in the super() call""" """!Do not change anything here!"""
# PLEASE SET YOU PLUGIN NAME HERE !!!! super().__init__(__name__, config) # you can access the config DICT by 'self._config'
super().__init__("template")
def onLoad(self): def onLoad(self):
"""!Called by import of the plugin""" """!Called by import of the plugin"""

View file

@ -1,4 +0,0 @@
example:
string: Hello World!
bool: true
integer: 12

84
router_test.py Normal file
View file

@ -0,0 +1,84 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import logging
from boswatch.configYaml import ConfigYAML
from boswatch.packet.packet import Packet
import importlib
import copy
class Router:
def __init__(self, name):
self.__name = name
self.__route = []
logging.debug("add new router: %s", self.__name)
def addRoute(self, route):
logging.debug("[%s] add route: %s", self.__name, route)
self.__route.append(route)
def call(self, bwPacket):
for call in self.__route:
logging.debug("[%s] -> run route: %s", self.__name, call)
bwPacket_tmp = call(copy.deepcopy(bwPacket)) # todo is deepcopy here right?
if bwPacket_tmp is None: # returning None doesnt change the bwPacket
continue
if bwPacket is False: # returning False stops the router immediately
logging.debug("[%s] stopped", self.__name)
break
bwPacket = bwPacket_tmp
logging.debug("[%s] <- route returned: %s", self.__name, bwPacket)
logging.debug("[%s] ended", self.__name)
return bwPacket
def showRoute(self):
logging.debug("[%s] internal route", self.__name)
for call in self.__route:
logging.debug(" - %s", call)
config = ConfigYAML()
config.loadConfigFile("config/server.yaml")
routerList = {}
for router in config.get("router"):
routerList[router.get("name")] = Router(router.get("name"))
for router in config.get("router"):
for route in router.get("route"):
if route.get("type") == "plugin":
importedFile = importlib.import_module(route.get("type") + "." + route.get("name"))
loadedClass = importedFile.BoswatchPlugin(route.get("config"))
routerList[router.get("name")].addRoute(loadedClass._run)
elif route.get("type") == "module":
importedFile = importlib.import_module(route.get("type") + "." + route.get("name"))
loadedClass = importedFile.BoswatchModule(route.get("config"))
routerList[router.get("name")].addRoute(loadedClass._run)
elif route.get("type") == "router":
routerList[router.get("name")].addRoute(routerList[route.get("name")].call)
print()
print(routerList)
print()
for router in routerList:
routerList[router].showRoute()
print()
bwPack = Packet("{'timestamp': 1551421020.9004176, 'mode': 'zvei', 'zvei': '12345'}")
for alaRouter in config.get("alarmRouter"):
routerList[str(alaRouter)].call(bwPack)
exit(0)