From a3a08cd89007254154f94ff2e9b7ff5faefd2a81 Mon Sep 17 00:00:00 2001 From: vvzvlad Date: Mon, 17 Apr 2023 15:23:53 +0700 Subject: [PATCH] add: test conn, multihosts, refactoring --- README.md | 30 +++++++- vestasync.py | 197 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 150 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index cc0fa7b..0165b54 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,8 @@ pip install -r requirements.txt ### install — установка на контроллер Команда `install` выполняет подготовительные действия — устанавливает ПО, создает гит-репозитарий, устанавливает службы (подробнее в разделе "Службы"). -Эту команду надо выполнять, указывая в ```device_ip``` исходный контроллер (который будет стоять в проде инсталляции) перед началом эксплуатации (если выполнять ее еще до настройки, то бонусом получим сохранение конфигов и wb-rules в гите во время разработки и ПНР). Эта команда выполняется на контроллере один раз. При повторном запуске она перезапишет файлы скриптов и сервисов, этим можно пользоваться для обновления скриптов на существующих контроллерах, если вышла новая версия Vestasync. +Эту команду надо выполнять, указывая в ```device_ip``` исходный контроллер (который будет стоять в проде инсталляции) перед началом эксплуатации (если выполнять ее еще до настройки, то бонусом получим сохранение конфигов и wb-rules в гите во время разработки и ПНР). Эта команда выполняется на контроллере один раз. + Пример запуска (запускается на локальной машине, адрес контроллера указывается в ```device_ip```): @@ -55,13 +56,25 @@ pip install -r requirements.txt --gitea_address http://192.168.1.101:3001/ --device_new_name WB2 --gitea_token de8a2eaee0d2f27746157c2fd563815f932d671c` +--user_cmd user_cmd.sh ``` ```--cmd install```: означает, что надо установить Vestasync на контроллер и подготовить его к созданию бекапа ```--device_ip```: IP-адрес контроллера ```--gitea_address```: адрес Gitea-сервера, куда будет отправлен бекап в виде "http://192.168.1.101:3001/" ```--device_new_name```: имя контроллера, из которого вместе с SN будет сформировано название контроллера, которое запишется в хостнейм и будет служить именем репозитария с конфигами -```--gitea_token```: токен для авторизации на Gitea-сервере (получается в интерфейсе) +```--gitea_token```: токен для авторизации на Gitea-сервере (получается в интерфейсе Gitea) +```--user_cmd```: файл sh с командами, которые надо выполняить на контроллере для его настройки под ваши задачи (указывать необязательно). В нем можно описать любые команды, которыми вам надо конфигурировать контроллер: например, установка ключа SSH, установка таймзоны и локали, и так далее. Пример файла — ```user_cmd.sh```: + +``` +#!/usr/bin/env sh +timedatectl set-timezone Asia/Krasnoyarsk +localectl set-locale LANG=en_GB.UTF-8 +service ntp stop +ntpdate pool.ntp.org +service ntp start +hwclock --systohc --localtime +``` ### restore — восстановление из бекапа @@ -117,7 +130,18 @@ reboot ### Пользовательские команды -В папке системы лежит файл "user_cmd.sh", внутри которого можно описать любые команды, которыми вам надо конфигурировать контроллер: например, установка ключа SSH, установка таймзоны и локали, и так далее. Если вы не хотите ничего устанавливать, оставьте его пустым. +В папке системы лежит файл "user_cmd.sh", Если вы не хотите ничего устанавливать, оставьте его пустым. + +### Обновление скриптов +При повторном запуске команда ```install``` перезапишет файлы скриптов и сервисов для обновления скриптов на существующих контроллерах, если вышла новая версия Vestasync. +В этом случае в ```--device_ip``` можно передать несколько IP-адресов: + +``` +./vestasync.py --cmd install --device_ip 192.168.98.92 192.168.98.85 --gitea_address http://192.168.98.101:3001/ --device_new_name WB1 --gitea_token de8a2eaee0d2f27746157c2fd563815f932d670c +``` + +Обратите внимание, что устанавливать Vestasync на несколько контроллеров лучше с помощью скрипта ниже, потому что при указании набора из нескольких адресов ```device_ip``` с командой ```install``` у них будет одинаковые имена хостов (```--device_new_name WB1```), отличающееся только серийным номером: WB1-AFYATAO7, WB1-A3TBJXLS и так далее. + ### Множественный запуск diff --git a/vestasync.py b/vestasync.py index b877765..b9800f2 100755 --- a/vestasync.py +++ b/vestasync.py @@ -9,6 +9,7 @@ import re import sys import datetime import json +import socket device_user = "root" @@ -28,8 +29,8 @@ else: main_parser.add_argument('--device_new_name', help='Device new name', required=True) main_parser.add_argument('--gitea_address', help='Gitea address string', required=True) main_parser.add_argument('--gitea_token', help='Gitea token', required=True) -main_parser.add_argument('--device_ip', help='Device IP', required=True) - +main_parser.add_argument('--device_ip', help='Device IP(s)', required=True, nargs='+', type=str) +main_parser.add_argument('--user_cmd', help='User commands file') args = main_parser.parse_known_args() args = args[0] @@ -53,6 +54,8 @@ def get_short_sn(c): def set_hostname(c): c.run(f'hostnamectl set-hostname {args.device_new_name}-{device_short_sn}') + hostname = c.run('hostname', hide=True).stdout.strip() + return hostname def save_hostname(c): c.run(f'echo $(hostname) > /mnt/data/etc/vestasync/hostname') @@ -103,53 +106,75 @@ def copy_wb_rule(c): c.put("./files/vestasync.js", wb_rule_path) def create_automac_systemd(c): - apply_macs_script_path = "/usr/local/bin/apply_macs.sh" - c.run(f"rm {apply_macs_script_path} || true") - c.put("./files/apply_macs.sh", apply_macs_script_path) - c.run(f"chmod +x {apply_macs_script_path}") + #disable + for service in ['apply_macs.service']: + c.run(f'systemctl disable {service} || true') - apply_macs_service_path = "/etc/systemd/system/apply_macs.service" - c.run(f"rm {apply_macs_service_path} || true") - c.put("./files/apply_macs.service", apply_macs_service_path) + file_paths = { #local path: remote path + './files/apply_macs.sh': '/usr/local/bin/apply_macs.sh', + './files/apply_macs.service': '/etc/systemd/system/apply_macs.service', + } - c.run("systemctl disable apply_macs.service || true") + for local_path, remote_path in file_paths.items(): + c.run(f"rm {remote_path} || true") + c.put(local_path, remote_path) + c.run(f"chmod +x {remote_path}") + + #reload c.run("systemctl daemon-reload") - c.run("systemctl enable apply_macs.service") + + #enable and start + for service in ['apply_macs.service']: + c.run(f'systemctl enable {service}') + c.run(f'systemctl start {service}') + + + #check statuses + for service in ['apply_macs.service']: + active = c.run(f'systemctl is-active {service} || true', hide=True).stdout.strip() + enabled = c.run(f'systemctl is-enabled {service} || true', hide=True).stdout.strip() + print(f"{service}: {active}, {enabled}") + + + def create_autogit_systemd(c): - pushgit_script_path = "/usr/local/bin/pushgit.sh" - c.run(f"rm {pushgit_script_path} || true") - c.put("./files/pushgit.sh", pushgit_script_path) - c.run(f"chmod +x {pushgit_script_path}") - - pushgit_inotify_script_path = "/usr/local/bin/pushgit_inotify.sh" - c.run(f"rm {pushgit_inotify_script_path} || true") - c.put("./files/pushgit.sh", pushgit_inotify_script_path) - c.run(f"chmod +x {pushgit_inotify_script_path}") - - pushgit_inotify_service_path = "/etc/systemd/system/pushgit_inotify.service" - c.run(f"rm {pushgit_inotify_service_path} || true") - c.put("./files/pushgit_inotify.service", pushgit_inotify_service_path) - c.run(f"chmod +x {pushgit_inotify_service_path}") - - pushgit_service_path = "/etc/systemd/system/pushgit.service" - c.run(f"rm {pushgit_service_path} || true") - c.put("./files/pushgit.service", pushgit_service_path) - c.run(f"chmod +x {pushgit_service_path}") - - pushgit_timer_path = "/etc/systemd/system/pushgit.timer" - c.run(f"rm {pushgit_timer_path} || true") - c.put("./files/pushgit.timer", pushgit_timer_path) - c.run(f"chmod +x {pushgit_timer_path}") + #disable + for service in ['pushgit.timer', 'pushgit_inotify.service']: + c.run(f'systemctl disable {service} || true') - c.run("systemctl disable pushgit.timer || true") - c.run("systemctl disable pushgit_inotify.service || true") + #delete old files, copy new files, chmod +x + file_paths = { #local path: remote path + './files/pushgit.sh': '/usr/local/bin/pushgit.sh', + './files/pushgit_inotify.sh': '/usr/local/bin/pushgit_inotify.sh', + './files/pushgit_inotify.service': '/etc/systemd/system/pushgit_inotify.service', + './files/pushgit.service': '/etc/systemd/system/pushgit.service', + './files/pushgit.timer': '/etc/systemd/system/pushgit.timer' + } + + for local_path, remote_path in file_paths.items(): + c.run(f"rm {remote_path} || true") + c.put(local_path, remote_path) + c.run(f"chmod +x {remote_path}") + + #reload c.run("systemctl daemon-reload") - c.run("systemctl enable pushgit.timer") - c.run("systemctl start pushgit.timer") - c.run("systemctl enable pushgit_inotify.service") - c.run("systemctl start pushgit_inotify.service") + + #enable and start + for service in ['pushgit.timer', 'pushgit_inotify.service']: + c.run(f'systemctl enable {service}') + c.run(f'systemctl start {service}') + + + #check statuses + for service in ['pushgit.timer', 'pushgit_inotify.service', 'pushgit.service']: + active = c.run(f'systemctl is-active {service} || true', hide=True).stdout.strip() + enabled = c.run(f'systemctl is-enabled {service} || true', hide=True).stdout.strip() + print(f"{service}: {active}, {enabled}") + + + def reboot(c): c.run("reboot > /dev/null 2>&1 || true") @@ -219,42 +244,66 @@ def check_vestasync_installed(c): result = c.run(f"test -d {vestasync_path}", warn=True) return result.ok -def device_install(): - with Connection(host=args.device_ip, user=device_user, connect_kwargs={"password": "wirenboard"}) as c: - if not check_vestasync_installed(c): - prepare_packages_wb(c) - configure_git(c) - get_short_sn(c) - set_hostname(c) - create_repo(c) - init_repo(c) - ppush_the_repo(c) - save_mac_in_cfg(c) - save_hostname(c) - copy_wb_rule(c) - ppush_the_repo(c) - create_automac_systemd(c) - create_autogit_systemd(c) - run_user_cmd(c) - reboot(c) - else: - print("Found vestasync! Update...") - copy_wb_rule(c) - create_automac_systemd(c) - create_autogit_systemd(c) +def device_install(c): + print("Found vestasync! Update...") + copy_wb_rule(c) + create_automac_systemd(c) + create_autogit_systemd(c) + print("Update vestasync complete\n") + +def device_update(c): + print("Not found vestasync! Install...") + prepare_packages_wb(c) + configure_git(c) + get_short_sn(c) + set_hostname(c) + create_repo(c) + init_repo(c) + ppush_the_repo(c) + save_mac_in_cfg(c) + hostname = save_hostname(c) + copy_wb_rule(c) + ppush_the_repo(c) + create_automac_systemd(c) + create_autogit_systemd(c) + run_user_cmd(c) + reboot(c) + print(f"Install vestasync complete (hostname {hostname}), reboot target device\n") + +def device_install_or_update(): + print(f"Install/update command on host(s) {', '.join(args.device_ip)}") + for device_ip in args.device_ip: + with Connection(host=device_ip, user=device_user, connect_kwargs={"password": "wirenboard"}) as c: + print(f"\nConnect to {device_ip} as {device_user}..") + try: + if not check_vestasync_installed(c): + device_update(c) + else: + device_install(c) + except socket.timeout: + print(f"Failed to connect to the host {device_ip}") def device_restore(): - with Connection(host=args.device_ip, user=device_user) as c: - #prepare_packages_wb(c) - #configure_git(c) - #git_clone(c) - #copy_etc(c) - #restore_hostname(c) - #ppush_the_repo(c) + for device_ip in args.device_ip: + with Connection(host=device_ip, user=device_user) as c: + print(f"\nConnect to {device_ip} as {device_user}..") + try: + if check_vestasync_installed(c): + prepare_packages_wb(c) + configure_git(c) + git_clone(c) + copy_etc(c) + restore_hostname(c) + ppush_the_repo(c) + create_autogit_systemd(c) + create_automac_systemd(c) + except socket.timeout: + print(f"Failed to connect to the host {device_ip}") + + + - create_autogit_systemd(c) - create_automac_systemd(c) if __name__ == '__main__': @@ -266,7 +315,7 @@ if __name__ == '__main__': exit(1) if cmd_args.cmd == "install": - device_install() + device_install_or_update() if cmd_args.cmd == "restore": device_restore()