Пребацивање community.openwrt са shell-а на ucode
Ansible-ом управљам малом флотом OpenWrt рутера: рутери у облаку, VPS и
кутије код куће. Алат за то је
community.openwrt,
наследник старе улоге gekmihesg.openwrt. Њена предност је што рутеру
није потребан Python: модули су били shell скрипте које је контролер
копирао преко SSH-а и покретао у BusyBox-у самог рутера.
То ради, али споро — и управо ме је брзина натерала да га препишем. У
shell модулу uci свака опција је значила још неколико позива uci и
jsonfilter, а мојој mesh мрежи треба много опција на сваком рутеру — по
један задатак и гомила процеса за сваку. Уз то, JSON се склапа од shell
стрингова, escaping је минско поље, а такве модуле је тешко тестирати.
Зашто ucode
На сваком OpenWrt уређају већ постоји бољи runtime:
ucode, мали језик сличан JavaScript-у у
ком је написан firewall4. Има уграђен JSON и везе ка fs, uci и ubus,
па модул чита и пише конфигурацију директно, уместо да за сваку вредност
покреће спољну команду.
Шта се десило
- Почео сам са UCI модулом на ucode-у и заједничком библиотеком помоћних функција (#264). Затворен је у корист мањих, фокусираних pull request-ова.
- Превод самог модула
uciушао је као #280. Главна новина јеoperations: цела серија измена (обриши застареле секције, направи или ажурирај нове) примењује се редом у једном позиву, уместо по један задатак за сваку опцију. Додаје иredact_keys, да тајне не би биле видљиве у diff испису. - Издање 1.9.0 је прво издање колекције засновано на ucode-у уместо на
shell скриптама: 17 модула, од
apkиcopyдоserviceиuci.
Следеће: WireGuard и AmneziaWG
Моја кућна mesh мрежа ради на WireGuard и AmneziaWG тунелима, и до сада је
сва логика око њих живела у великој Ansible улози. Нови модули
wg_interface и wg_peer
(#281,
на ревизији) преузимају логику на страни уређаја: генерисање кључева,
заједничке PSK, пирове и брисање интерфејса који више нису потребни. Већ их
користим из свог форка, а као интеграциони тестови служе Molecule сценарији
на правом firmware-у рутера у виртуелним машинама.
Неколико замки у ucode-у
- Модул добија аргументе као путању до JSON фајла у
ARGV[0]; оставите у фајлу маркерWANT_JSONда би их Ansible тако прослеђивао. - Стрингови и низови немају
.length— користите глобалнуlength(x). - Објекти се пореде по референци, па за откривање измена треба сопствена рекурзивна провера једнакости.
sprintf("%J", obj)је најбржи начин да се испише JSON.