Following on from discussion in Emoncms V9.9.6 Stable merge (pre-merge check) - #31 by borpin, I’ve made some changes to service-runner to try and deal with different EmonCMS prefixes and multiple instances of EmonCMS running against the same redis server.
Before I create a PR I thought I’d post it here for those who may be able to test it further - i.e. those with either non-EmonPi installs, or multi-instance EmonCMS installs.
#!/usr/bin/python
## Used to update log viewer window in Emoncms admin
# Used in conjunction with: service-runner-update.sh and Emoncms admin module
import sys
import re
import redis
import subprocess
import time
import signal
import os
# Edit this list if you add additional EmonCMS instances or change the instance name from the
# default of "" for an EmonPi, or "emoncms" for a self-install from GitHub.
# Ensure you add the trailing colon as shown below for "emoncms:"!
instancelist = "", "emoncms:"
def handle_sigterm(sig, frame):
print("Got Termination signal, exiting")
sys.exit(0)
# Setup the signal handler to gracefully exit
signal.signal(signal.SIGTERM, handle_sigterm)
signal.signal(signal.SIGINT, handle_sigterm)
def connect_redis():
while True:
try:
server = redis.Redis()
if server.ping():
print("Connected to redis-server")
# need to wait until EmonCMS has started
return server
except redis.exceptions.ConnectionError:
print("Unable to connect to redis-server, sleeping for 30s")
sys.stdout.flush()
time.sleep(30)
print("Starting service-runner")
sys.stdout.flush()
server = connect_redis()
while True:
try:
# Check for the existence of a redis 'service-runner' key
for instance in instancelist:
if server.exists(instance + 'service-runner'):
# We've got one, now to turn it into a cmdline
flag = server.lpop(instance + 'service-runner')
print("Got flag: %s\n" % flag)
sys.stdout.flush()
script, logfile = flag.split('>')
cmdstring = "{s} > {l} 2>&1".format(s=script, l=logfile)
print("STARTING: " + cmdstring)
sys.stdout.flush()
# Got a cmdline, now run it.
subprocess.call(cmdstring, shell=True)
if not (os.path.isfile(logfile)):
f = open(logfile, 'a')
f.close()
print("COMPLETE: " + cmdstring)
sys.stdout.flush()
except redis.exceptions.ConnectionError:
print("Connection to redis-server lost, attempting to reconnect")
sys.stdout.flush()
server = connect_redis()
except SystemExit:
# If the sys.exit(0) from the interrupt handler gets caught here,
# just break from the while True: and let the script exit normally.
break
except:
print("Exception occurred", sys.exc_info()[0])
sys.exit(1)
time.sleep(0.2)
Save the above into a file somewhere called whatever you like… sr.py
will do if you’re a lazy typer
Make sure the standard service-runner is stopped using:
sudo systemctl stop service-runner
From the folder you saved the above script into, run:
sudo python sr.py
(replace sr.py with whatever name you chose)
Confirm it finds your instance(s) in the output printed to the console - something like:
pi@emonpi(ro):~$ sudo python sr.py
Starting service-runner
Connected to redis-server
Found unnamed EmonCMS instance []
Then try using the backup module or one of the other Web Admin tasks that utilise service-runner and confirm they get run (again, checking the output in the console). You should see something like this:
Got flag: /home/pi/backup/emoncms-export.sh /tmp/emoncms-flag-export>/home/pi/data/emoncms-export.log
STARTING: /home/pi/backup/emoncms-export.sh /tmp/emoncms-flag-export > /home/pi/data/emoncms-export.log 2>&1
COMPLETE: /home/pi/backup/emoncms-export.sh /tmp/emoncms-flag-export > /home/pi/data/emoncms-export.log 2>&1
Terminate service runner using Ctrl-C
After you’ve tested sufficiently, make sure you restart the actual service-runner with:
sudo systemctl start service-runner
I also realise that as written, service-runner will only run a single command at a time, so if a backup is initiated and then the user changes pages to the Admin page and initiates an update, the update will not start until the backup completes.
Likewise if an update is triggered from two instances of EmonCMS, they will execute serially, which may cause confusion in the user interface, particularly of the one initiated second.
There are certainly ways around this but they add complexity and I’m open to opinions on whether this complexity is actually necessary before diving in and adding it.