From bf75bba2810076fd492c7b15191268ca6f689fa6 Mon Sep 17 00:00:00 2001 From: NaruX Date: Fri, 21 Jul 2017 22:25:04 +0200 Subject: [PATCH] =?UTF-8?q?plcslave=20Thread=20startet=20automatisch,=20un?= =?UTF-8?q?abh=C3=A4ngig=20vom=20plc=20Thread=20ACL=20Liste=20f=C3=BCr=20p?= =?UTF-8?q?lcslave=20hinzugef=C3=BCgt=20plcslaveport=20als=20Parameter=20?= =?UTF-8?q?=C3=BCbergeben=20getconfig/setconfig=20auf=20neue=20Parameter?= =?UTF-8?q?=20angepasst=20re.match=20auf=20re.fullmatch=20ge=C3=A4ndert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/etc/revpipyload/revpipyload.conf | 3 + doc/revpipyload.html | 71 ++++++++- eric-revpipyload.api | 8 +- revpipyload.e4p | 4 +- revpipyload/revpipyload.py | 199 ++++++++++++++++++++------ setup.py | 2 +- 6 files changed, 235 insertions(+), 52 deletions(-) diff --git a/data/etc/revpipyload/revpipyload.conf b/data/etc/revpipyload/revpipyload.conf index 58ea476..ab69a8d 100644 --- a/data/etc/revpipyload/revpipyload.conf +++ b/data/etc/revpipyload/revpipyload.conf @@ -7,8 +7,11 @@ plcarguments= plcuid=1000 plcgid=1000 plcslave=0 +plcslaveacl= +plcslaveport = 55234 pythonversion=3 xmlrpc=0 +xmlrpcacl = xmlrpcport=55123 zeroonerror=0 zeroonexit=0 diff --git a/doc/revpipyload.html b/doc/revpipyload.html index 59ac401..590311d 100644 --- a/doc/revpipyload.html +++ b/doc/revpipyload.html @@ -32,7 +32,7 @@ begrenzt werden!

Global Attributes

- +
configrsc
picontrolreset
procimg
pyloadverion
rapcatalog
configrsc
picontrolreset
procimg
pyloadverion
rapcatalog
re_ipacl

Classes

@@ -61,6 +61,9 @@ Classes Functions + + + @@ -471,6 +474,12 @@ Methods + + + + + + @@ -664,6 +673,31 @@ Prueft ob das PLC Programm noch lauft.
True, wenn das PLC Programm noch lauft
+ +

+RevPiPyLoad.xml_plcslavestart

+xml_plcslavestart() +

+Startet den PLC Slave Server. +

+
Returns:
+
+Statuscode: + 0: erfolgreich gestartet + -1: Nicht aktiv in Konfiguration + -2: Laeuft bereits +
+
+

+RevPiPyLoad.xml_plcslavestop

+xml_plcslavestop() +

+Stoppt den PLC Slave Server. +

+
Returns:
+
+True, wenn stop erfolgreich +

RevPiPyLoad.xml_plcstart

@@ -830,10 +864,18 @@ Static Methods

RevPiSlave (Constructor)

-RevPiSlave() +RevPiSlave(acl, port=55234)

Instantiiert RevPiSlave-Klasse. -

+

+
acl
+
+Stringliste mit Leerstellen getrennt +
port
+
+Listen Port fuer plc Slaveserver +
+

RevPiSlave.newlogfile

newlogfile() @@ -890,7 +932,7 @@ Static Methods

RevPiSlaveDev (Constructor)

-RevPiSlaveDev(devcon, deadtime) +RevPiSlaveDev(devcon, deadtime, acl)

RevPiSlaveDev.run

@@ -900,6 +942,27 @@ RevPiSlaveDev.run RevPiSlaveDev.stopstop() +
Up
+

+ +

_ipmatch

+_ipmatch(ipaddress, dict_acl) +

+Prueft IP gegen ACL List und gibt ACL aus. +

+
ipaddress
+
+zum pruefen +
dict_acl
+
+ACL Dict gegen die IP zu pruefen ist +
+
+
Returns:
+
+int() ACL Wert oder -1 wenn nicht gefunden +
+
Up


diff --git a/eric-revpipyload.api b/eric-revpipyload.api index 3c7b587..c2279f9 100644 --- a/eric-revpipyload.api +++ b/eric-revpipyload.api @@ -49,6 +49,8 @@ revpipyload.RevPiPyLoad.xml_getprocimg?4() revpipyload.RevPiPyLoad.xml_plcdownload?4(mode="tar", pictory=False) revpipyload.RevPiPyLoad.xml_plcexitcode?4() revpipyload.RevPiPyLoad.xml_plcrunning?4() +revpipyload.RevPiPyLoad.xml_plcslavestart?4() +revpipyload.RevPiPyLoad.xml_plcslavestop?4() revpipyload.RevPiPyLoad.xml_plcstart?4() revpipyload.RevPiPyLoad.xml_plcstop?4() revpipyload.RevPiPyLoad.xml_plcupload?4(filedata, filename) @@ -62,13 +64,15 @@ revpipyload.RevPiPyLoad?1() revpipyload.RevPiSlave.newlogfile?4() revpipyload.RevPiSlave.run?4() revpipyload.RevPiSlave.stop?4() -revpipyload.RevPiSlave?1() +revpipyload.RevPiSlave?1(acl, port=55234) revpipyload.RevPiSlaveDev.run?4() revpipyload.RevPiSlaveDev.stop?4() -revpipyload.RevPiSlaveDev?1(devcon, deadtime) +revpipyload.RevPiSlaveDev?1(devcon, deadtime, acl) +revpipyload._ipmatch?5(ipaddress, dict_acl) revpipyload._zeroprocimg?5() revpipyload.configrsc?7 revpipyload.picontrolreset?7 revpipyload.procimg?7 revpipyload.pyloadverion?7 revpipyload.rapcatalog?7 +revpipyload.re_ipacl?7 diff --git a/revpipyload.e4p b/revpipyload.e4p index eff7f78..bd9eb3b 100644 --- a/revpipyload.e4p +++ b/revpipyload.e4p @@ -1,7 +1,7 @@ - + en_US @@ -9,7 +9,7 @@ Python3 Console Dieser Loader wird über das Init-System geladen und führt das angegebene Pythonprogramm aus. Es ist für den RevolutionPi gedacht um automatisch das SPS-Programm zu starten. - 0.4.2 + 0.5.0 Sven Sager akira@narux.de diff --git a/revpipyload/revpipyload.py b/revpipyload/revpipyload.py index 7033e9e..92095d5 100755 --- a/revpipyload/revpipyload.py +++ b/revpipyload/revpipyload.py @@ -38,12 +38,11 @@ import signal import socket import subprocess import tarfile -import warnings import zipfile from concurrent import futures from configparser import ConfigParser from json import loads as jloads -from re import match as rematch +from re import fullmatch as refullmatch from shutil import rmtree from sys import stdout as sysstdout from tempfile import mkstemp @@ -56,9 +55,26 @@ from xmlrpc.server import SimpleXMLRPCServer configrsc = None picontrolreset = "/opt/KUNBUS/piControlReset" procimg = "/dev/piControl0" -pyloadverion = "0.4.2" +pyloadverion = "0.5.0" rapcatalog = None +re_ipacl = "(([\\d\\*]{1,3}\\.){3}[\\d\\*]{1,3},[0-1] ?)+" + + +def _ipmatch(ipaddress, dict_acl): + """Prueft IP gegen ACL List und gibt ACL aus. + + @param ipaddress zum pruefen + @param dict_acl ACL Dict gegen die IP zu pruefen ist + @return int() ACL Wert oder -1 wenn nicht gefunden + + """ + for aclip in sorted(dict_acl, reverse=True): + regex = aclip.replace(".", "\\.").replace("*", "\\d{1,3}") + if refullmatch(regex, ipaddress) is not None: + return str(dict_acl[aclip]) + return "-1" + def _zeroprocimg(): """Setzt Prozessabbild auf NULL.""" @@ -439,16 +455,26 @@ class RevPiPlc(Thread): class RevPiSlave(Thread): - def __init__(self): - """Instantiiert RevPiSlave-Klasse.""" + def __init__(self, acl, port=55234): + """Instantiiert RevPiSlave-Klasse. + @param acl Stringliste mit Leerstellen getrennt + @param port Listen Port fuer plc Slaveserver""" super().__init__() self.deadtime = 0.5 self._evt_exit = Event() self.exitcode = None - self.th_dev = [] + self._port = port + self._th_dev = [] self.zeroonerror = False self.zeroonexit = False + # ACLs aufbereiten + self.dict_acl = {} + for host in acl.split(): + aclsplit = host.split(",") + self.dict_acl[aclsplit[0]] = \ + "0" if len(aclsplit) == 1 else aclsplit[1] + def newlogfile(self): """Konfiguriert die FileHandler auf neue Logdatei.""" pass @@ -460,9 +486,8 @@ class RevPiSlave(Thread): self.so = socket.socket(socket.AF_INET, socket.SOCK_STREAM) while not self._evt_exit.is_set(): try: - self.so.bind(("", 55234)) + self.so.bind(("", self._port)) except: - warnings.warn("can not bind socket - retry", Warning) proginit.logger.warning("can not bind socket - retry") self._evt_exit.wait(1) else: @@ -480,20 +505,30 @@ class RevPiSlave(Thread): proginit.logger.exception("after accept") continue - # Thread starten - th = RevPiSlaveDev(tup_sock, self.deadtime) - th.start() - self.th_dev.append(th) + # ACL prüfen + aclstatus = _ipmatch(tup_sock[1][0], self.dict_acl) + if aclstatus == "-1": + tup_sock[0].close() + proginit.logger.warning( + "host ip '{}' does not match revpiacl - disconnect" + "".format(tup_sock[1][0]) + ) + else: + # Thread starten + th = RevPiSlaveDev(tup_sock, self.deadtime, aclstatus) + th.start() + self._th_dev.append(th) + + # TODO: tote Threads entfernen # Alle Threads beenden - for th in self.th_dev: + for th in self._th_dev: th.stop() # Socket schließen self.so.close() self.exitcode = 0 - warnings.resetwarnings() proginit.logger.debug("leave RevPiSlave.run()") def stop(self): @@ -508,8 +543,9 @@ class RevPiSlave(Thread): class RevPiSlaveDev(Thread): - def __init__(self, devcon, deadtime): + def __init__(self, devcon, deadtime, acl): super().__init__() + self._acl = acl self.daemon = True self._deadtime = deadtime self._devcon, self._addr = devcon @@ -522,8 +558,15 @@ class RevPiSlaveDev(Thread): def run(self): proginit.logger.debug("enter RevPiSlaveDev.run()") - msgcli = [b'DATA', b'PICT', b'SEND'] - proginit.logger.info("connected from {}".format(self._addr)) + proginit.logger.info( + "got new connection from host {} with acl {}".format( + self._addr, self._acl) + ) + + # CMDs anhand ACL aufbauen + msgcli = [b'DATA', b'PICT'] + if self._acl == "1": + msgcli.append(b'SEND') # Prozessabbild öffnen fh_proc = open(procimg, "r+b", 0) @@ -717,10 +760,33 @@ class RevPiPyLoad(): self.globalconfig["DEFAULT"].get("plcworkdir", ".") self.plcslave = \ int(self.globalconfig["DEFAULT"].get("plcslave", 0)) + + # PLC Slave ACL laden und prüfen + plcslaveacl = \ + self.globalconfig["DEFAULT"].get("plcslaveacl", "") + if len(plcslaveacl) > 0 and refullmatch(re_ipacl, plcslaveacl) is None: + self.plcslaveacl = "" + proginit.logger.warning("can not load plcslaveacl - wrong format") + else: + self.plcslaveacl = plcslaveacl + + self.plcslaveport = \ + int(self.globalconfig["DEFAULT"].get("plcslaveport", 55234)) self.pythonver = \ int(self.globalconfig["DEFAULT"].get("pythonversion", 3)) self.xmlrpc = \ int(self.globalconfig["DEFAULT"].get("xmlrpc", 0)) + + # XML ACL laden und prüfen + # TODO: xmlrpcacl auswerten + xmlrpcacl = \ + self.globalconfig["DEFAULT"].get("xmlrpcacl", "") + if len(xmlrpcacl) > 0 and refullmatch(re_ipacl, xmlrpcacl) is None: + self.xmlrpcacl = "" + proginit.logger.warning("can not load xmlrpcacl - wrong format") + else: + self.xmlrpcacl = xmlrpcacl + self.zeroonerror = \ int(self.globalconfig["DEFAULT"].get("zeroonerror", 1)) self.zeroonexit = \ @@ -731,6 +797,10 @@ class RevPiPyLoad(): # PLC Thread konfigurieren self.plc = self._plcthread() + if self.plcslave: + self.th_plcslave = RevPiSlave(self.plcslaveacl, self.plcslaveport) + else: + self.th_plcslave = None # XMLRPC-Server Instantiieren und konfigurieren if self.xmlrpc >= 1: @@ -791,6 +861,10 @@ class RevPiPyLoad(): self.xml_plcupload, "plcupload") self.xsrv.register_function( self.xml_plcuploadclean, "plcuploadclean") + self.xsrv.register_function( + self.xml_plcslavestart, "plcslavestart") + self.xsrv.register_function( + self.xml_plcslavestop, "plcslavestop") self.xsrv.register_function( lambda: os.system(picontrolreset), "resetpicontrol") self.xsrv.register_function( @@ -816,31 +890,24 @@ class RevPiPyLoad(): proginit.logger.debug("enter RevPiPyLoad._plcthread()") th_plc = None - if self.plcslave: - # Slaveausfuehrung übergeben - th_plc = RevPiSlave() - th_plc.zeroonerror = self.zeroonerror - th_plc.zeroonexit = self.zeroonexit + # Prüfen ob Programm existiert + if not os.path.exists(os.path.join(self.plcworkdir, self.plcprog)): + proginit.logger.error("plc file does not exists {}".format( + os.path.join(self.plcworkdir, self.plcprog) + )) + return None - else: - # Prüfen ob Programm existiert - if not os.path.exists(os.path.join(self.plcworkdir, self.plcprog)): - proginit.logger.error("plc file does not exists {}".format( - os.path.join(self.plcworkdir, self.plcprog) - )) - return None - - proginit.logger.debug("create PLC watcher") - th_plc = RevPiPlc( - os.path.join(self.plcworkdir, self.plcprog), - self.plcarguments, - self.pythonver - ) - th_plc.autoreload = self.autoreload - th_plc.gid = int(self.globalconfig["DEFAULT"].get("plcgid", 65534)) - th_plc.uid = int(self.globalconfig["DEFAULT"].get("plcuid", 65534)) - th_plc.zeroonerror = self.zeroonerror - th_plc.zeroonexit = self.zeroonexit + proginit.logger.debug("create PLC watcher") + th_plc = RevPiPlc( + os.path.join(self.plcworkdir, self.plcprog), + self.plcarguments, + self.pythonver + ) + th_plc.autoreload = self.autoreload + th_plc.gid = int(self.globalconfig["DEFAULT"].get("plcgid", 65534)) + th_plc.uid = int(self.globalconfig["DEFAULT"].get("plcuid", 65534)) + th_plc.zeroonerror = self.zeroonerror + th_plc.zeroonexit = self.zeroonexit proginit.logger.debug("leave RevPiPyLoad._plcthread()") return th_plc @@ -939,6 +1006,10 @@ class RevPiPyLoad(): self.tpe = futures.ThreadPoolExecutor(max_workers=1) self.tpe.submit(self.xsrv.serve_forever) + if self.plcslave: + # Slaveausfuehrung übergeben + self.th_plcslave.start() + if self.autostart: proginit.logger.debug("starting revpiplc-thread") if self.plc is not None: @@ -971,8 +1042,14 @@ class RevPiPyLoad(): proginit.logger.info("stopping revpipyload") self._exit = True + if self.th_plcslave is not None and self.th_plcslave.is_alive(): + proginit.logger.debug("stopping revpi slave thread") + self.th_plcslave.stop() + self.th_plcslave.join() + proginit.logger.debug("revpi slave thread successfully closed") + if self.plc is not None and self.plc.is_alive(): - proginit.logger.debug("stopping revpiplc-thread") + proginit.logger.debug("stopping revpiplc thread") self.plc.stop() self.plc.join() proginit.logger.debug("revpiplc thread successfully closed") @@ -996,8 +1073,11 @@ class RevPiPyLoad(): dc["plcprogram"] = self.plcprog dc["plcarguments"] = self.plcarguments dc["plcslave"] = self.plcslave + dc["plcslaveacl"] = self.plcslaveacl + dc["plcslaveport"] = self.plcslaveport dc["pythonversion"] = self.pythonver dc["xmlrpc"] = self.xmlrpc + dc["xmlrpcacl"] = self.xmlrpcacl dc["xmlrpcport"] = \ self.globalconfig["DEFAULT"].get("xmlrpcport", 55123) dc["zeroonerror"] = self.zeroonerror @@ -1170,8 +1250,11 @@ class RevPiPyLoad(): "plcprogram": ".+", "plcarguments": ".*", "plcslave": "[01]", + "plcslaveacl": re_ipacl, + "plcslaveport": "[0-9]{,5}", "pythonversion": "[23]", "xmlrpc": "[0-3]", + "xmlrpcacl": re_ipacl, "xmlrpcport": "[0-9]{,5}", "zeroonerror": "[01]", "zeroonexit": "[01]" @@ -1180,7 +1263,7 @@ class RevPiPyLoad(): # Werte übernehmen for key in keys: if key in dc: - if rematch(keys[key], str(dc[key])) is None: + if refullmatch(keys[key], str(dc[key])) is None: proginit.logger.error( "got wrong setting '{}' with value '{}'".format( key, dc[key] @@ -1276,6 +1359,36 @@ class RevPiPyLoad(): else: return False + def xml_plcslavestart(self): + """Startet den PLC Slave Server. + + @return Statuscode: + 0: erfolgreich gestartet + -1: Nicht aktiv in Konfiguration + -2: Laeuft bereits + + """ + if self.plcslave: + if self.th_plcslave is not None and self.th_plcslave.is_alive(): + return -2 + else: + self.th_plcslave = RevPiSlave( + self.plcslaveacl, self.plcslaveport + ) + self.th_plcslave.start() + return 0 + else: + return -1 + + def xml_plcslavestop(self): + """Stoppt den PLC Slave Server. + @return True, wenn stop erfolgreich""" + if self.th_plcslave is not None: + self.th_plcslave.stop() + return True + else: + return False + if __name__ == "__main__": root = RevPiPyLoad() diff --git a/setup.py b/setup.py index 64cce8f..2a9f059 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ setup( license="LGPLv3", name="revpipyload", - version="0.4.2", + version="0.5.0", scripts=["data/revpipyload"],
_ipmatchPrueft IP gegen ACL List und gibt ACL aus.
_zeroprocimg Setzt Prozessabbild auf NULL.
xml_plcrunning Prueft ob das PLC Programm noch lauft.
xml_plcslavestartStartet den PLC Slave Server.
xml_plcslavestopStoppt den PLC Slave Server.
xml_plcstart Startet das PLC Programm.