Collectd Plugin #13

Merged
sqozz merged 3 commits from cfr34k/sem6000:collectd into master 2020-05-02 19:26:20 +02:00
2 changed files with 47 additions and 1 deletions
Showing only changes of commit de3b7026a8 - Show all commits

View file

@ -40,6 +40,8 @@ Add or adjust the configuration for your collectds Python plugin as follows:
<Module collectd_sem6000> <Module collectd_sem6000>
Address "12:34:56:78:90:ab" Address "12:34:56:78:90:ab"
SocketName "FirstSocket" SocketName "FirstSocket"
ReadTimeout 30
SuspendTime 300
</Module> </Module>
<Module collectd_sem6000> <Module collectd_sem6000>
Address "ab:cd:ef:13:37:42" Address "ab:cd:ef:13:37:42"
@ -49,6 +51,14 @@ Add or adjust the configuration for your collectds Python plugin as follows:
</Plugin> </Plugin>
``` ```
`ReadTimeout` and `SuspendTime` control whats happening when a device is
unavailable. If no value could be retrieved for `ReadTimeout` seconds, the
plugin does not retry for `SuspendTime` seconds. After that, normal operation
is resumed. This procedure ensures that an unreachable device does not block
other devices (too often) in the current single-threaded architecture.
If not specified, `ReadTimeout` is 30 seconds and `SuspendTime` is 5 minutes.
Make sure that everything listed in `requirements.txt` is available to the user Make sure that everything listed in `requirements.txt` is available to the user
running collectd. running collectd.

View file

@ -3,6 +3,7 @@
# vim: noet ts=2 sw=2 sts=2 # vim: noet ts=2 sw=2 sts=2
import os import os
import time
import collectd import collectd
from sem6000 import SEMSocket from sem6000 import SEMSocket
@ -25,6 +26,12 @@ def config_func(cfg):
if key in ['address', 'socketname']: if key in ['address', 'socketname']:
config[key] = value config[key] = value
if key == 'readtimeout':
config['readtimeout'] = int(value)
if key == 'suspendtime':
config['suspendtime'] = int(value)
if 'address' not in config.keys(): if 'address' not in config.keys():
collectd.error('sem6000: address must be set') collectd.error('sem6000: address must be set')
return return
@ -32,7 +39,19 @@ def config_func(cfg):
if 'socketname' not in config.keys(): if 'socketname' not in config.keys():
config['socketname'] = config['address'].replace(':', '') config['socketname'] = config['address'].replace(':', '')
instances.append( {'config': config, 'socket': None} ) if 'readtimeout' not in config.keys():
config['readtimeout'] = 30
if 'suspendtime' not in config.keys():
config['suspendtime'] = 300
instances.append( {
'config': config,
'socket': None,
'suspended': False,
'lastsuccess': 0,
'resumetime': 0
} )
def read_func(): def read_func():
global instances global instances
@ -40,6 +59,14 @@ def read_func():
for inst in instances: for inst in instances:
config = inst['config'] config = inst['config']
if inst['suspended']:
if time.time() < inst['resumetime']:
continue
else:
collectd.info("sem6000: Device {} waking up.".format(config['address']))
inst['suspended'] = False
inst['lastsuccess'] = time.time()
try: try:
if inst['socket'] == None: if inst['socket'] == None:
collectd.info("sem6000: Connecting to {}...".format(config['address'])) collectd.info("sem6000: Connecting to {}...".format(config['address']))
@ -52,6 +79,13 @@ def read_func():
collectd.warning("sem6000: Exception caught: {}".format(e)) collectd.warning("sem6000: Exception caught: {}".format(e))
collectd.warning("sem6000: Restarting on next cycle...") collectd.warning("sem6000: Restarting on next cycle...")
if inst['lastsuccess'] < time.time() - config['readtimeout']:
collectd.error("sem6000: no successful communication with {} for {:.1f} seconds. Suspending device for {:.1f} seconds.".format(
config['address'], config['readtimeout'], config['suspendtime']))
inst['suspended'] = True
inst['resumetime'] = time.time() + config['suspendtime']
if inst['socket'] != None: if inst['socket'] != None:
inst['socket'].disconnect() inst['socket'].disconnect()
inst['socket'] = None inst['socket'] = None
@ -61,6 +95,8 @@ def read_func():
if socket != None and socket.voltage != 0: if socket != None and socket.voltage != 0:
collectd.debug("Uploading values for {}".format(socket.mac_address)) collectd.debug("Uploading values for {}".format(socket.mac_address))
inst['lastsuccess'] = time.time()
val = collectd.Values(plugin = 'sem6000-{}'.format(config['socketname'])) val = collectd.Values(plugin = 'sem6000-{}'.format(config['socketname']))
Review

I'd prefer to use the BT mac instead of a string as plugin name here. There's a good chance we can read the name of the socket over BT and expose it later with this plugin which would allow a mapping even if the name changes.

I'd prefer to use the BT mac instead of a string as plugin name here. There's a good chance we can read the name of the socket over BT and expose it later with this plugin which would allow a mapping even if the name changes.
Review

Are you sure that the name is actually stored on the socket? My feeling is that that’s only a mapping in the app.

I prefer to have a string in the plugin name, because it indicates the purpose of the measurements (which can change as devices are reused). If you don't like it, just copy the address into SocketName 😉 in the collectd config.

Are you sure that the name is actually stored on the socket? My feeling is that that’s only a mapping in the app. I prefer to have a string in the plugin name, because it indicates the purpose of the measurements (which can change as devices are reused). If you don't like it, just copy the address into `SocketName` :wink: in the collectd config.
val.type = 'voltage' val.type = 'voltage'