gluon-check-connection: initial commit

This commit adds a new package which can be used for scheduled
connectivity checks.
This commit is contained in:
CodeFetch 2020-12-29 02:24:22 +01:00
parent c97be1e18a
commit 9e5775865c
9 changed files with 269 additions and 1 deletions

View File

@ -59,6 +59,7 @@ Several Freifunk communities in Germany use Gluon as the foundation of their Fre
:caption: Packages
:maxdepth: 1
package/gluon-check-connection
package/gluon-client-bridge
package/gluon-config-mode-domain-select
package/gluon-ebtables-filter-multicast

View File

@ -0,0 +1,69 @@
gluon-check-connection
======================
This package adds a script that checks if at least one connection to IPv6 hosts
defined as *target groups* is working using the ping command.
The script is called once every minute by ``micrond``.
For example one can define a group of *local* targets to check if a connection
to hosts in the mesh network is possible (e.g. time or update servers) and
*global* targets for checking if a connection to the global internet is possible.
Currently only IPv6 addresses are supported.
This is e.g. used by the *gluon-scheduled-domain-switch* package
site.conf
---------
Target groups can be pre-defined in the domain config.
::
check_connection = {
targets = {
targets_local = {
'fe80::dead:c0de:1',
'fe80::bad:c0de:1',
'fe80::dead:c0de:2',
'fe80::bad:c0de:2',
},
targets_global = {
'2620:0:ccc::2', -- OpenDNS
'2001:4860:4860::8888', -- Google DNS
'2600::1', -- Sprint DNS
'2620:0:ccd::2', -- OpenDNS
'2001:4860:4860::8844', -- Google DNS
'2600::2', -- Sprint DNS
},
},
},
Defining target groups in the site.conf will overwrite existing ones with the same
name when performing a *sysupgrade* or by triggering *gluon-reconfigure*.
Configuration via UCI
---------------------
Packages can use gluon-check-connection to be triggered after connection checks.
For this they can define the following *script* attributes:
script : an entry for defining the ping target
enabled :
- a boolean defining whether the target will be considered
interval :
- the interval to execute the trigger script (in minutes - defaults to 1)
command :
- the command to execute
groups :
- the target groups array on which the ping test will be performed on
onchange :
- if set true the command is only being executed on a state change or always otherwise
trigger :
- on which the command is being executed (``offline``, ``online`` or unset for both)
*target* groups can be defined with the following attributes:
target : an entry for defining the IPv6 adress to ping
hosts :
- array containing the IPv6 addresses to perform the ping test on

View File

@ -516,7 +516,6 @@ config_mode \: optional
},
},
roles \: optional
Optional role definitions. Nodes will announce their role inside the mesh.
This will allow in the backend to distinguish between normal, backbone and

View File

@ -0,0 +1,21 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-check-connection
PKG_VERSION:=1
include ../gluon.mk
define Package/$(PKG_NAME)
TITLE:=Checks if a node can ping definable targets
DEPENDS:=+gluon-core +micrond @GLUON_MULTIDOMAIN
endef
define Package/$(PKG_NAME)/description
Script to check if there is a connection to any gateway and also if there is
a connection to the global internet. This script is called once every minute
by ``micrond``. It will trigger scripts which will be executed if a
connection state changes or after a definable interval after which
connections checks have been performed.
endef
$(eval $(call BuildPackageGluon,$(PKG_NAME)))

View File

@ -0,0 +1,5 @@
local function check_target(t)
need_string_array_match(t, '^[%x:]+$', false)
end
need_table(in_domain({'check_connection', 'targets'}), check_target, false)

View File

@ -0,0 +1 @@
* * * * * /usr/sbin/gluon-check-connection

View File

@ -0,0 +1,14 @@
#!/usr/bin/lua
local site = require 'gluon.site'
local uci = require('simple-uci').cursor()
for group, hosts in pairs(site.check_connection.targets()) do
uci:delete('gluon-check-connection', 'target', group)
uci:section('gluon-check-connection', 'target', group, {
hosts = hosts
})
end
uci:save('gluon-check-connection')

View File

@ -0,0 +1,158 @@
#!/usr/bin/lua
local unistd = require 'posix.unistd'
local util = require 'gluon.util'
local uci = require('simple-uci').cursor()
-- Minimal uptime (in minutes) before the checks start
local min_uptime = 5
local offline_flag_file_prefix = '/tmp/gluon-offline-'
local firstrun_file = '/tmp/gluon-check-connection-firstrun'
local lastrun_file = '/tmp/gluon-check-connection-lastrun'
local function lock(file)
exec('lock', file)
end
local function unlock(file)
exec('lock', '-u', file)
end
local function shuffle(tbl)
new_tbl = {}
for i, ele in ipairs(tbl) do
table.insert(new_tbl, math.random(1, #new_tbl+1), ele)
end
return new_tbl
end
local function ping_hosts(hosts)
for _, host in ipairs(hosts) do
if 0 == os.execute("ping -c 1 -w 10 " .. host) then
return true
end
end
return false
end
local function check_connection(group, old_state, hosts)
local offline_flag_file = offline_flag_file_prefix .. group
local targets = shuffle(hosts)
if ping_hosts(targets) then
if not old_state then
util.log(group .. 'connectivity available again')
os.remove(offline_flag_file)
end
return true
end
if old_state then
util.log(group .. ' connectivity lost')
io.open(offline_flag_file, "w"):write(tostring(util.get_uptime()))
end
return false
end
local uptime = math.floor(util.get_uptime() / 60)
if uptime < min_uptime then
os.exit(0)
end
if not lock('/var/lock/gluon-check-connection.lock') then
util.log('Unable to set lock. Is an old instance still running?')
os.exit(0)
end
math.randomseed(uptime)
local firstrun = uptime
local lastrun = uptime
if unistd.access(firstrun_file) and unistd.access(lastrun_file) then
firstrun = tonumber(util.readfile(firstrun_file))
lastrun = tonumber(util.readfile(lastrun_file))
else
io.open(firstrun_file, "w"):write(tostring(uptime))
end
local runtime = lastrun - firstrun
local scripts = {}
uci:foreach('gluon-check-connection', 'script', function(script)
if not script['enabled'] then return end
if not runtime or uptime - lastrun >= (tonumber(script['interval']) or 1) then
table.insert(scripts, script)
end
end)
local groups = {}
uci:foreach('gluon-check-connection', 'target', function(group)
-- do not perform connection checks for groups which are not in use
for _, script in ipairs(scripts) do
if util.contains(script['groups'], group['.name']) then
groups[group['.name']] = group['hosts']
break
end
end
end)
local old_states = {}
for group, _ in pairs(groups) do
if unistd.access(offline_flag_file_prefix .. group) then
old_states[group] = false
else
old_states[group] = true
end
end
local states = {}
for group, hosts in pairs(groups) do
states[group] = check_connection(group, old_states[group], hosts)
end
for _, script in ipairs(scripts) do
local state_changed = false
local state_offline = false
local state_online = false
for _, group in ipairs(script['groups']) do
if nil ~= states[group] then
if states[group] ~= old_states[group] then
state_changed = true
end
if states[group] then
state_online = true
else
state_offline = true
end
end
end
if not runtime or state_changed or not script['onchange'] then
local do_run = not script['trigger']
if script['trigger'] == 'online' and state_online then
do_run = true
end
if script['trigger'] == 'offline' and state_offline then
do_run = true
end
if do_run then
util.exec(script['command'])
end
end
end
unlock('/var/lock/gluon-check-connection.lock')
io.open(lastrun_file, "w"):write(tostring(math.floor(util.get_uptime() / 60)))