gluon-scheduled-domain-switch: add package (#1555)
This package allows to automatically switch to another domain, either at a given point in time or after the node was offline long enough.
This commit is contained in:
parent
131548580e
commit
c1b9ea2d9c
38
docs/package/gluon-scheduled-domain-switch.rst
Normal file
38
docs/package/gluon-scheduled-domain-switch.rst
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
gluon-scheduled-domain-switch
|
||||||
|
=============================
|
||||||
|
|
||||||
|
This package allows to switch a routers domain at a given point
|
||||||
|
in time. This is needed for switching between incompatible transport
|
||||||
|
protocols (e.g. 802.11s and IBSS or VXLAN).
|
||||||
|
|
||||||
|
Nodes will switch when the defined *switch-time* has passed. In case the node was
|
||||||
|
powered off while this was supposed to happen, it might not be able to aquire the
|
||||||
|
correct time. In this case, the node will switch after it has not seen any gateway
|
||||||
|
for a given period of time.
|
||||||
|
|
||||||
|
site.conf
|
||||||
|
---------
|
||||||
|
All those settings have to be defined exclusively in the domain, not the site.
|
||||||
|
|
||||||
|
domain_switch : optional (needed for domains to switch)
|
||||||
|
target_domain :
|
||||||
|
- target domain to switch to
|
||||||
|
switch_after_offline_mins :
|
||||||
|
- amount of time without reachable gateway to switch unconditionally
|
||||||
|
switch_time :
|
||||||
|
- UNIX epoch after which domain will be switched
|
||||||
|
connection_check_targets :
|
||||||
|
- array of IPv6 addresses which are probed to determine if the node is
|
||||||
|
connected to the mesh
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
domain_switch = {
|
||||||
|
target_domain = 'new_domain',
|
||||||
|
switch_after_offline_mins = 120,
|
||||||
|
switch_time = 1546344000, -- 01.01.2019 - 12:00 UTC
|
||||||
|
connection_check_targets = {
|
||||||
|
'2001:4860:4860::8888',
|
||||||
|
'2001:4860:4860::8844',
|
||||||
|
},
|
||||||
|
},
|
@ -3,15 +3,6 @@ need_string(in_site({'site_name'}))
|
|||||||
|
|
||||||
-- this_domain() returns nil when multidomain support is disabled
|
-- this_domain() returns nil when multidomain support is disabled
|
||||||
if this_domain() then
|
if this_domain() then
|
||||||
function need_domain_name(path)
|
|
||||||
need_string(path)
|
|
||||||
need(path, function(default_domain)
|
|
||||||
local f = io.open(os.getenv('IPKG_INSTROOT') .. '/lib/gluon/domains/' .. default_domain .. '.json')
|
|
||||||
if not f then return false end
|
|
||||||
f:close()
|
|
||||||
return true
|
|
||||||
end, nil, 'be a valid domain name')
|
|
||||||
end
|
|
||||||
need_domain_name(in_site({'default_domain'}))
|
need_domain_name(in_site({'default_domain'}))
|
||||||
|
|
||||||
need_table(in_domain({'domain_names'}), function(domain)
|
need_table(in_domain({'domain_names'}), function(domain)
|
||||||
|
@ -260,3 +260,12 @@ function foreach_radio(uci, f)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function get_uptime()
|
||||||
|
local uptime_file = readfile("/proc/uptime")
|
||||||
|
if uptime_file == nil then
|
||||||
|
-- Something went wrong reading "/proc/uptime"
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return tonumber(uptime_file:match('^[^ ]+'))
|
||||||
|
end
|
||||||
|
13
package/gluon-scheduled-domain-switch/Makefile
Normal file
13
package/gluon-scheduled-domain-switch/Makefile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=gluon-scheduled-domain-switch
|
||||||
|
PKG_VERSION:=1
|
||||||
|
|
||||||
|
include ../gluon.mk
|
||||||
|
|
||||||
|
define Package/gluon-scheduled-domain-switch
|
||||||
|
TITLE:=Allows scheduled migrations between domains
|
||||||
|
DEPENDS:=+gluon-core @GLUON_MULTIDOMAIN
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackageGluon,gluon-scheduled-domain-switch))
|
6
package/gluon-scheduled-domain-switch/check_site.lua
Normal file
6
package/gluon-scheduled-domain-switch/check_site.lua
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
if need_table(in_domain({'domain_switch'}), check_domain_switch, false) then
|
||||||
|
need_domain_name(in_domain({'domain_switch', 'target_domain'}))
|
||||||
|
need_number(in_domain({'domain_switch', 'switch_after_offline_mins'}))
|
||||||
|
need_number(in_domain({'domain_switch', 'switch_time'}))
|
||||||
|
need_string_array_match(in_domain({'domain_switch', 'connection_check_targets'}), '^[%x:]+$')
|
||||||
|
end
|
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
local json = require 'jsonc'
|
||||||
|
local site = require 'gluon.site'
|
||||||
|
local unistd = require 'posix.unistd'
|
||||||
|
|
||||||
|
local cronfile = "/usr/lib/micron.d/gluon-scheduled-domain-switch"
|
||||||
|
|
||||||
|
-- Check if domain switch is scheduled
|
||||||
|
if site.domain_switch() == nil then
|
||||||
|
-- In case no domain switch is scheduled, remove cronfile
|
||||||
|
os.remove(cronfile)
|
||||||
|
os.exit(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Only in case domain switch is scheduled
|
||||||
|
local f = io.open(cronfile, "w")
|
||||||
|
f:write("* * * * * /usr/bin/gluon-check-connection\n")
|
||||||
|
f:write("*/5 * * * * /usr/bin/gluon-switch-domain\n")
|
||||||
|
f:close()
|
36
package/gluon-scheduled-domain-switch/luasrc/usr/bin/gluon-check-connection
Executable file
36
package/gluon-scheduled-domain-switch/luasrc/usr/bin/gluon-check-connection
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
local unistd = require 'posix.unistd'
|
||||||
|
local util = require 'gluon.util'
|
||||||
|
local site = require 'gluon.site'
|
||||||
|
|
||||||
|
local offline_flag_file = "/tmp/gluon_offline"
|
||||||
|
local is_offline = true
|
||||||
|
|
||||||
|
-- Check if domain-switch is scheduled
|
||||||
|
if site.domain_switch() == nil then
|
||||||
|
-- Switch not applicable for current domain
|
||||||
|
os.exit(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check reachability of pre-defined targets
|
||||||
|
for _, ip in ipairs(site.domain_switch.connection_check_targets()) do
|
||||||
|
local exit_code = os.execute("ping -c 1 -w 10 " .. ip)
|
||||||
|
if exit_code == 0 then
|
||||||
|
is_offline = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_offline then
|
||||||
|
-- Check if we were previously offline
|
||||||
|
if unistd.access(offline_flag_file) then
|
||||||
|
os.exit(0)
|
||||||
|
end
|
||||||
|
-- Create offline flag
|
||||||
|
local f = io.open(offline_flag_file, "w")
|
||||||
|
f:write(tostring(util.get_uptime()))
|
||||||
|
f:close()
|
||||||
|
else
|
||||||
|
os.remove(offline_flag_file)
|
||||||
|
end
|
67
package/gluon-scheduled-domain-switch/luasrc/usr/bin/gluon-switch-domain
Executable file
67
package/gluon-scheduled-domain-switch/luasrc/usr/bin/gluon-switch-domain
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
local uci = require('simple-uci').cursor()
|
||||||
|
local unistd = require 'posix.unistd'
|
||||||
|
local util = require 'gluon.util'
|
||||||
|
local site = require 'gluon.site'
|
||||||
|
|
||||||
|
-- Returns true if node was offline long enough to perform domain switch
|
||||||
|
function switch_after_min_reached()
|
||||||
|
if not unistd.access("/tmp/gluon_offline") then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local switch_after_sec = site.domain_switch.switch_after_offline_mins() * 60
|
||||||
|
|
||||||
|
local current_uptime = util.get_uptime()
|
||||||
|
if current_uptime == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = util.readfile("/tmp/gluon_offline")
|
||||||
|
if f == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local offline_since = tonumber(f)
|
||||||
|
|
||||||
|
local offline_time_sec = current_uptime - offline_since
|
||||||
|
|
||||||
|
if offline_time_sec > switch_after_sec then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns true in case switch time has passed
|
||||||
|
function switch_time_passed()
|
||||||
|
local current_time = os.time()
|
||||||
|
local switch_time = site.domain_switch.switch_time()
|
||||||
|
|
||||||
|
return switch_time < current_time
|
||||||
|
end
|
||||||
|
|
||||||
|
if site.domain_switch() == nil then
|
||||||
|
-- Switch not applicable for current domain
|
||||||
|
print("No domain switch defined for the current domain.")
|
||||||
|
os.exit(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
local current_domain = uci:get("gluon", "core", "domain")
|
||||||
|
local target_domain = site.domain_switch.target_domain()
|
||||||
|
|
||||||
|
if target_domain == current_domain then
|
||||||
|
-- Current and target domain are equal
|
||||||
|
print("Domain '" .. target_domain .. "' equals current domain.")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not switch_after_min_reached() and not switch_time_passed() then
|
||||||
|
-- Neither switch-time passed nor switch_after_min reached
|
||||||
|
os.exit(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
uci:set("gluon", "core", "domain", target_domain)
|
||||||
|
uci:commit("gluon")
|
||||||
|
|
||||||
|
os.execute("gluon-reconfigure")
|
||||||
|
os.execute("reboot")
|
@ -305,6 +305,15 @@ function need_array_of(path, array, required)
|
|||||||
return need_array(path, function(e) need_one_of(e, array) end, required)
|
return need_array(path, function(e) need_one_of(e, array) end, required)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function need_domain_name(path)
|
||||||
|
need_string(path)
|
||||||
|
need(path, function(domain_name)
|
||||||
|
local f = io.open(os.getenv('IPKG_INSTROOT') .. '/lib/gluon/domains/' .. domain_name .. '.json')
|
||||||
|
if not f then return false end
|
||||||
|
f:close()
|
||||||
|
return true
|
||||||
|
end, nil, 'be a valid domain name')
|
||||||
|
end
|
||||||
|
|
||||||
local check = assert(loadfile())
|
local check = assert(loadfile())
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user