move manman to site

This commit is contained in:
Maciej Krüger 2022-05-24 11:26:11 +02:00 committed by Alexander List
parent 6f81325a79
commit 59482e53da
13 changed files with 0 additions and 711 deletions

View File

@ -1,13 +0,0 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-config-mode-manman-sync
PKG_VERSION:=2
include ../gluon.mk
define Package/gluon-config-mode-manman-sync
TITLE:=Sync location data from manman
DEPENDS:=+gluon-config-mode-core +gluon-manman-sync
endef
$(eval $(call BuildPackageGluon,gluon-config-mode-manman-sync))

View File

@ -1,36 +0,0 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2021-12-15 07:33+0100\n"
"Last-Translator: Maciej Krüger <maciej@xeredo.it>\n"
"Language-Team: German\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.0\n"
msgid "Enable ManMan sync"
msgstr "ManMan synchronisierung aktivieren"
msgid "ManMan location"
msgstr "ManMan Standort"
msgid "ManMan node (optional)"
msgstr "ManMan Knoten (optional)"
msgid "Required if multiple nodes in location"
msgstr "Erforderlich wenn mehrere Knoten am Standort"
msgid ""
"Sync configuration from ManMan "
"by entering ManMan location and node name here.<br>"
"This will automatically keep name, location and ips "
"in sync with the values specified in ManMan."
msgstr ""
"Synchronisiere die Konfiguration aus ManMan, indem du hier den ManMan "
"Standort-Namen und, optional, Knoten-Namen eingibst.\n"
"Dadurch werden Name, Standort und IP-Addressen automatisch mit den in "
"ManMan angegebenen Werten synchronisiert."

View File

@ -1,11 +0,0 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2015-08-12 23:30+0100\n"
"Last-Translator:Tobias Bernot <tqbs@airmail.cc>\n"
"Language-Team: French\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

View File

@ -1,21 +0,0 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Enable ManMan sync"
msgstr ""
msgid "ManMan location"
msgstr ""
msgid "ManMan node (optional)"
msgstr ""
msgid "Required if multiple nodes in location"
msgstr ""
msgid ""
"Sync configuration from ManMan "
"by entering ManMan location and node name here.<br>"
"This will automatically keep name, location and ips "
"in sync with the values specified in ManMan."
msgstr ""

View File

@ -1,17 +0,0 @@
local site_i18n = i18n 'gluon-site'
local uci = require('simple-uci').cursor()
local msg
if uci:get_bool('gluon-manman-sync', 'sync', 'enabled') then
msg = site_i18n._translate('gluon-config-mode:manman')
else
msg = site_i18n._translate('gluon-config-mode:no-manman')
end
if not msg then return end
renderer.render_string(msg, {
location_id = uci:get('gluon-manman-sync', 'sync', 'location_id')
})

View File

@ -1,46 +0,0 @@
return function(form, uci)
local pkg_i18n = i18n 'gluon-config-mode-manman-sync'
local msg = pkg_i18n.translate(
'Sync configuration from ManMan ' ..
'by entering ManMan location and node name here.<br>' ..
'This will automatically keep name, location and ips ' ..
'in sync with the values specified in ManMan.'
)
local s = form:section(Section, nil, msg)
local o
local manman = s:option(Flag, 'manman_sync', pkg_i18n.translate('Enable ManMan sync'))
manman.default = uci:get_bool('gluon-manman-sync', 'sync', 'enabled')
function manman:write(data)
uci:set('gluon-manman-sync', 'sync', 'enabled', data)
end
local id = s:option(Value, 'manman_location', pkg_i18n.translate('ManMan location'))
id:depends(manman, true)
id.default = uci:get('gluon-manman-sync', 'sync', 'location')
id.datatype = 'maxlength(16)'
function id:write(data)
-- clear ID, gets fetched from manman-sync
uci:set('gluon-manman-sync', 'sync', 'location_id', nil)
uci:set('gluon-manman-sync', 'sync', 'location', data)
end
local id = s:option(Value, 'manman_node', pkg_i18n.translate('ManMan node (optional)'), pkg_i18n.translate('Required if multiple nodes in location'))
id:depends(manman, true)
id.default = uci:get('gluon-manman-sync', 'sync', 'node')
id.datatype = 'maxlength(16)'
function id:write(data)
-- clear ID, gets fetched from manman-sync
uci:set('gluon-manman-sync', 'sync', 'node_id', nil)
uci:set('gluon-manman-sync', 'sync', 'node', data)
end
function s:write()
uci:save('gluon-manman-sync')
end
end

View File

@ -1,24 +0,0 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-manman-sync
PKG_VERSION:=1
include ../gluon.mk
define Package/gluon-manman-sync
DEPENDS:=+gluon-core +micrond +luci-lib-ip +luci-lib-httpclient +gluon-lib-ecdsa
TITLE:=Sync configuration with data from manman
endef
define Package/gluon-manman-sync/install
$(Gluon/Build/Install)
# httpclient depends on util depends on template.parser & sys.zoneinfo which is from +luci-base
# +luci-base is quite big and unnesesarry. stubbing the files is enough to make it work.
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/template
touch $(1)/usr/lib/lua/luci/template/parser.lua
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/sys
touch $(1)/usr/lib/lua/luci/sys/zoneinfo.lua
endef
$(eval $(call BuildPackageGluon,gluon-manman-sync))

View File

@ -1,2 +0,0 @@
-- need_array_of(in_site({'manman', 'api'}), string(), false)
need_string(in_site({'manman', 'key'}), false)

View File

@ -1,14 +0,0 @@
package gluon-manman-sync
config sync 'sync'
option enabled '0'
option location_id ''
option node ''
option last_data_hash ''
option last_data ''
config rollback 'rollback'
option success '1'
option checked_at ''
option previous_hash ''
option new_hash ''

View File

@ -1,21 +0,0 @@
#!/usr/bin/lua
-- Setup a cron for manman-sync if enabled
local uci = require('simple-uci').cursor()
local urandom = io.open('/dev/urandom', 'r')
local seed1, seed2 = urandom:read(2):byte(1, 2)
math.randomseed(seed1*0x100 + seed2)
urandom:close()
-- Perform sync at a random time each hour
local minute = math.random(0, 59)
local f = io.open('/usr/lib/micron.d/manman-sync', 'w')
-- Only setup cron if enabled
-- Write file regardless to clear old cron
if uci:get_bool('gluon-manman-sync', 'sync', 'enabled') then
f:write(string.format('%i * * * * /usr/bin/manman-sync sync\n', minute))
end
f:close()

View File

@ -1,422 +0,0 @@
#!/usr/bin/lua
local uci = require('simple-uci').cursor()
local ip = require 'luci.ip' -- luci-lib-ip
local fetch = require 'luci.httpclient'
local json = require 'luci.jsonc'
local ecdsa = require 'gluon.ecdsa'
local site = require 'gluon.site'
local pretty_hostname = require 'pretty_hostname'
local hostname = pretty_hostname.get(uci)
local manapi = site.manman.api()
local mankey = site.manman.key()
-- CLI lib from olsrd-cli (TODO: move to it's own lib package)
-- DO NOT EDIT HERE, JUST COPY FROM THERE
function printf(...)
print(string.format(...))
end
-- Print contents of `tbl`, with indentation.
-- `indent` sets the initial level of indentation.
-- src https://gist.github.com/xytis/5361405
function tprint (tbl, indent)
if not indent then indent = 0 end
for k, v in pairs(tbl) do
formatting = string.rep(' ', indent) .. k .. ': '
if type(v) == 'table' then
print(formatting)
tprint(v, indent + 1)
else
print(formatting .. tostring(v))
end
end
end
-- src https://stackoverflow.com/a/24823383/3990041
function table.slice(tbl, first, last, step)
local sliced = {}
for i = first or 1, last or #tbl, step or 1 do
sliced[#sliced+1] = tbl[i]
end
return sliced
end
-- CLI lib
function exec_cmd(args, sub)
if sub[args[1]] == nil then
return cmd_err('does not exist')
else
local cmd = sub[args[1]]
if cmd[3] ~= nil and #args > 1 then
exec_cmd(table.slice(args, 2), cmd[3])
else
cmd[1](unpack(table.slice(args, 2)))
end
end
end
function list_cmd(level, sub)
for key, cmd in pairs(sub) do
printf('%s%s: %s', string.rep(' ', level), key, cmd[2])
if cmd[3] ~= nil then
list_cmd(level + 1, cmd[3])
end
end
end
function show_help()
printf('Usage: %s <command>', arg[0])
list_cmd(1, sub)
end
function cmd_err(msg, no_show_help)
-- since argv0 is at... well... 0... even though this is lua...
--- ...slice just returns arg without argv0 as the for starts at 1
printf('Error: Command "%s" %s', table.concat(table.slice(arg), ' '), msg)
if not no_show_help then
printf('')
show_help()
end
os.exit(2)
end
function dummy()
cmd_err('requires a subcommand')
end
-- ///
-- NOTE: these will have mesh_ appended for static-ip
local mappings = {
wifi = 'radio0',
tunnel = 'vpn',
eth = 'other',
lan = 'other',
wan = 'uplink',
single = 'uplink',
}
-- https://stackoverflow.com/a/1647577/3990041
function string:split(pat)
pat = pat or '%s+'
local st, g = 1, self:gmatch("()("..pat..")")
local function getter(segs, seps, sep, cap1, ...)
st = sep and seps + #sep
return self:sub(segs, (seps or 0) - 1), cap1 or sep, ...
end
return function() if st then return getter(st, g()) end end
end
-- https://gist.github.com/Uradamus/10323382
local function shuffle(tbl)
for i = #tbl, 2, -1 do
local j = math.random(i)
tbl[i], tbl[j] = tbl[j], tbl[i]
end
return tbl
end
local function fetch_signed_json(remote, url)
print('GET ' .. url)
local code, res, result = fetch.request_raw(remote .. url)
if code < 1 then
print('E: failed to fetch')
return 1
end
if code == 404 then
print('E: location does not exist')
return 2
end
if code ~= 200 then
print('E: got status code ' .. code)
return 1
end
-- cloudflare's reverse proxies send http chunked responses with chunk sizes
-- for whatever reasons the chunk size gets smashed into the result
-- this is a hack to fish it out, it is irrelevant on unaffected reverse proxies
local j_start = result:find('{')
local j_end = (result:reverse()):find("}")
result = string.sub(result, j_start, (1 - j_end) > 0 and (1 - j_end) or nil)
local sig = res.headers['x-ecdsa']
local ts = res.headers['x-ecdsa-ts']
if not sig or not ts then
print('E: provided response is not signed')
return 1
end
local data = ts .. '@' .. result
if not ecdsa.verify(data, sig, mankey) then
print('E: signature invalid or not signed with expected key')
return 1
end
local obj = json.parse(result)
if obj == nil then
print('E: failed to parse json data')
return 1
end
return false, obj
end
local function do_manman_sync()
if not uci:get('gluon-manman-sync', 'sync', 'location_id') and not uci:get('gluon-manman-sync', 'sync', 'location') then
print('E: manman location missing')
return 2
end
-- check manman reachability, abort if not reachable
local working_remote
for _, remote in ipairs(shuffle(manapi)) do
if not working_remote then -- don't try other remotes if we got one that works
print('Trying remote ' .. remote)
local success, a, b, c = pcall(function() return fetch.request_raw(remote .. '/') end)
if not success then
print('E: couldnt reach manman: ' .. a)
else
if a ~= 200 then
print('E: couldnt reach manman - unexpected fetch result', a, b, c)
else
working_remote = remote
end
end
end
end
if not working_remote then
print('E: couldnt reach any manapi server, giving up')
return 1
end
-- try to fetch data
print('Fetching manman data...')
local location_id = uci:get('gluon-manman-sync', 'sync', 'location_id')
local location = uci:get('gluon-manman-sync', 'sync', 'location')
if not location_id and location then
local err, resp = fetch_signed_json(working_remote, '/find_location_by_name/' .. location)
if err then
return err
end
if not resp.id then
printf('E: location %s is invalid (404)', location)
return 2
end
uci:set('gluon-manman-sync', 'sync', 'location_id', resp.id)
location_id = resp.id
end
local err, location = fetch_signed_json(working_remote, '/location/show/' .. location_id)
if err then
return err
end
print('Syncing with location ' .. location.location.name)
uci:set('gluon-manman-sync', 'sync', 'location', location.location.name)
if uci:get('gluon-manman-sync', 'sync', 'last_data') and json.stringify(location) == uci:get('gluon-manman-sync', 'sync', 'last_data') then
print('Nothing changed, skipping sync')
return 0
end
local local_router_id
for id, _ in string.split(hostname, '-') do
if id then
local_router_id = id
end
end
local local_node_name = uci:get('gluon-manman-sync', 'sync', 'node') or local_router_id
local local_node = uci:get('gluon-manman-sync', 'sync', 'node_id') or local_router_id
local node
local should_hostname
if #location.nodes > 1 then
for _, potential_node in ipairs(location.nodes) do
if (local_node ~= nil and tostring(potential_node.id) == local_node) or (local_node_name ~= nil and potential_node.name == local_node_name) then
node = potential_node
should_hostname = location.location.name .. '-' .. node.name
end
end
else
node = location.nodes[1]
should_hostname = location.location.name
end
-- save node data to update name and persist ID
uci:set('gluon-manman-sync', 'sync', 'node_id', node.id)
uci:set('gluon-manman-sync', 'sync', 'node', node.name)
if node == nil then
print('E: unable to find matching node (selector "' .. node .. '")')
return 2
end
print('Syncing data for node ' .. node.name)
if hostname ~= should_hostname then
print('Renaming node to ' .. should_hostname)
pretty_hostname.set(uci, should_hostname)
end
local owner = uci:get_first('gluon-node-info', 'owner')
uci:set('gluon-node-info', owner, 'contact', location.administrator.email)
local _location = uci:get_first('gluon-node-info', 'location')
uci:set('gluon-node-info', _location, 'share_location', '1')
uci:set('gluon-node-info', _location, 'latitude', location.location.lat)
uci:set('gluon-node-info', _location, 'longitude', location.location.long)
-- check if anything changed since last time
-- if yes, apply changes and do gluon-reload
local has_changes = false
-- Use this when changing something that needs a reload and/or rollback (not the hostname)
local function set(a, b, c, d, isbool)
local curval
if isbool then
curval = uci:get_bool(a, b, c)
else
curval = uci:get(a, b, c)
end
if curval ~= d then
uci:set(a, b, c, d)
print(' Value', a, b, c, 'changed to', d, 'was', curval)
has_changes = true
else
print(' Value', a, b, c, 'unchanged', d)
end
end
local has_tunnel = false
for _, net in ipairs(node.interfaces) do
local net_name = net.name
if net_name == 'tunnel' or net_name == 'vpn' or net_name == 'mesh_vpn' then
has_tunnel = true
end
local net_mapped = mappings[net_name] or net_name
if not string.find(net_mapped, '_') then
net_mapped = 'mesh_' .. net_mapped
end
local cidr = ip.new(net.ip, net.netmask):string()
print('Syncing ' .. net_name .. ' as ' .. net_mapped .. ' to ' .. cidr)
set('gluon-static-ip', net_mapped, 'ip4', cidr)
end
print('Syncing mesh vpn: ' .. (has_tunnel and 'on' or 'off'))
set('gluon', 'mesh_vpn', 'enabled', has_tunnel, true)
uci:set('gluon-manman-sync', 'sync', 'last_data', json.stringify(location))
uci:save('system')
uci:save('gluon')
uci:save('gluon-manman-sync')
uci:save('gluon-static-ip')
uci:save('gluon-node-info')
os.execute('exec uci commit')
if has_changes then
print('Applying changes...')
os.execute('exec gluon-reconfigure')
os.execute('exec gluon-reload')
else
print('No settings changes, no reason to reload')
end
end
local function maybe_sync()
if uci:get_bool('gluon-manman-sync', 'sync', 'enabled') then
do_manman_sync()
else
print('manman-sync not enabled, skipping')
end
end
local function enable_sync(location, node)
if not uci:get('gluon-manman-sync', 'sync', 'location') and not uci:get('gluon-manman-sync', 'sync', 'location_id') and not location then
cmd_err('requires at least a location (No location was found in config)', true)
end
if location then
printf('Config location %s', location)
uci:set('gluon-manman-sync', 'sync', 'location', location)
-- clear ID, gets fetched from manman-sync
uci:set('gluon-manman-sync', 'sync', 'location_id', nil)
end
if node then
printf('Config node %s', node)
uci:set('gluon-manman-sync', 'sync', 'node', node)
-- clear ID, gets fetched from manman-sync
uci:set('gluon-manman-sync', 'sync', 'node_id', nil)
end
if not uci:get_bool('gluon-manman-sync', 'sync', 'enabled') then
uci:set('gluon-manman-sync', 'sync', 'enabled', true)
print('Enabled sync.')
print('Trigger with manman-sync sync')
else
print('Sync already enabled.')
end
uci:save('gluon-manman-sync')
end
local function disable_sync()
if uci:get_bool('gluon-manman-sync', 'sync', 'enabled') then
uci:set('gluon-manman-sync', 'sync', 'enabled', false)
print('Disabled sync.')
uci:save('gluon-manman-sync')
end
end
local function show_info()
tprint({
enabled = uci:get_bool('gluon-manman-sync', 'sync', 'enabled'),
location = string.format('%s (id %s)',
uci:get('gluon-manman-sync', 'sync', 'location') or '[none]',
uci:get('gluon-manman-sync', 'sync', 'location_id') or '[will autodetect]'
),
node = string.format('%s (id %s)',
uci:get('gluon-manman-sync', 'sync', 'node') or '[none - use only available or error]',
uci:get('gluon-manman-sync', 'sync', 'node_id') or '[will autodetect]'
),
})
end
sub = {
info = { show_info, 'Show manman-sync configuration' },
help = { show_help, 'Show help' },
sync = { maybe_sync, 'Trigger a manman-sync' },
force_sync = { do_manman_sync, 'Trigger a manman-sync, despite being disabled' },
enable = { enable_sync, 'Enable sync. [<location>] [<node>]' },
disable = { disable_sync, 'Disable sync.' }
}
exec_cmd(table.slice(arg), sub)

View File

@ -1,6 +0,0 @@
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci

View File

@ -1,78 +0,0 @@
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <respondd.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <uci.h>
#include <string.h>
static struct json_object * get_autoupdater(void) {
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return NULL;
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (uci_load(ctx, "gluon-manman-sync", &p))
goto error;
struct uci_section *s = uci_lookup_section(ctx, p, "sync");
if (!s)
goto error;
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "location_id", gluonutil_wrap_string(uci_lookup_option_string(ctx, s, "location_id")));
const char *enabled = uci_lookup_option_string(ctx, s, "enabled");
json_object_object_add(ret, "enabled", json_object_new_boolean(enabled && !strcmp(enabled, "1")));
uci_free_context(ctx);
return ret;
error:
uci_free_context(ctx);
return NULL;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "manman", get_autoupdater());
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{}
};