mirror of
https://github.com/vvzvlad/vestasync.git
synced 2024-12-27 04:31:01 +03:00
init commit
This commit is contained in:
commit
4a87b682a0
32
README.md
Normal file
32
README.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Система запускает две службы:
|
||||||
|
|
||||||
|
## Восстановление MAC-адресов (apply_macs)
|
||||||
|
Служба apply_macs отвечает за применение MAC-адресов к сетевым интерфейсам при загрузке системы.
|
||||||
|
Эта служба считывает MAC-адреса из файлов, расположенных в каталоге /mnt/data/etc/vestasync/macs/, если они есть, и присваивает их соответствующим интерфейсам, таким как eth0, eth1, wlan0 и т. д. Это используется, если на контроллер был восстанновлен созданный бекап, чтобы сохранять MAC-адреса старого контроллера, и соотвественно, адрес, выданный DHCP.
|
||||||
|
Для изменения MAC-адресов на изначальные надо просто удалить все файлы и перезагрузиться:
|
||||||
|
```
|
||||||
|
rm -rf /mnt/data/etc/vestasync/macs/*
|
||||||
|
reboot
|
||||||
|
```
|
||||||
|
Или, если надо сделать это временно, остановить службу:
|
||||||
|
```systemctl stop apply_macs.service```
|
||||||
|
Обратно запустить: ``` systemctl start apply_macs.service```
|
||||||
|
Узнать статус: ```systemctl status apply_macs.service```
|
||||||
|
|
||||||
|
## Автоматическое версионирование и деплой конфигов (pushgit)
|
||||||
|
|
||||||
|
Служба pushgit, работает в паре с таймером pushgit.timer.
|
||||||
|
Они обеспечивают автоматическое сохранение конфигов в репозиторий Git на удаленном сервере ежедневно.
|
||||||
|
Это позволяет сохранять изменения в файлах и версионировать их, что упрощает управление конфигурационными файлами и предотвращает потерю данных при их случайном изменении или удалении.
|
||||||
|
Чтобы отключить сохранение, надо остановить службу: ```systemctl stop pushgit.timer```
|
||||||
|
Запуск и проверка статуса аналогично предыдущек:
|
||||||
|
|
||||||
|
Запустить: ``` systemctl start pushgit.timer```
|
||||||
|
Узнать статус: ```systemctl status pushgit.timer```
|
7
apply_macs.service
Normal file
7
apply_macs.service
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Apply MAC addresses to network interfaces
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/bin/apply_macs.sh
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
10
apply_macs.sh
Normal file
10
apply_macs.sh
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
macs_dir="/mnt/data/etc/vestasync/macs"
|
||||||
|
|
||||||
|
for mac_file in "$macs_dir"/*; do
|
||||||
|
ifname=$(basename "$mac_file")
|
||||||
|
if [ -f "$mac_file" ]; then
|
||||||
|
mac_address=$(cat "$mac_file")
|
||||||
|
ip link set "$ifname" address "$mac_address"
|
||||||
|
fi
|
||||||
|
done
|
9
pushgit.service
Normal file
9
pushgit.service
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Push git changes daily
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/mnt/data/etc/pushgit.sh
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
5
pushgit.sh
Normal file
5
pushgit.sh
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
cd /mnt/data/etc/ > /dev/null 2>&1 || true
|
||||||
|
git add . > /dev/null 2>&1 || true
|
||||||
|
git commit -m "$(date)" > /dev/null 2>&1 || true
|
||||||
|
git push -u origin master > /dev/null 2>&1 || true
|
10
pushgit.timer
Normal file
10
pushgit.timer
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Run pushgit.service daily
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=daily
|
||||||
|
Persistent=true
|
||||||
|
Unit=pushgit.service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
13
requirements.txt
Normal file
13
requirements.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
bcrypt==4.0.1
|
||||||
|
certifi==2022.12.7
|
||||||
|
cffi==1.15.1
|
||||||
|
charset-normalizer==3.1.0
|
||||||
|
cryptography==40.0.2
|
||||||
|
fabric==3.0.0
|
||||||
|
idna==3.4
|
||||||
|
invoke==2.0.0
|
||||||
|
paramiko==3.1.0
|
||||||
|
pycparser==2.21
|
||||||
|
PyNaCl==1.5.0
|
||||||
|
requests==2.28.2
|
||||||
|
urllib3==1.26.15
|
212
vestasync.py
Executable file
212
vestasync.py
Executable file
@ -0,0 +1,212 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from fabric import task, Connection
|
||||||
|
from invoke.exceptions import UnexpectedExit
|
||||||
|
from io import StringIO
|
||||||
|
import requests
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
device_user = "root"
|
||||||
|
gitea_user = "vestasync"
|
||||||
|
device_short_sn = ""
|
||||||
|
|
||||||
|
cmd_parser = argparse.ArgumentParser(description='Process command line arguments.')
|
||||||
|
cmd_parser.add_argument('--cmd', help='Command (prepare, update, restore)', required=True)
|
||||||
|
cmd_args = cmd_parser.parse_known_args()
|
||||||
|
|
||||||
|
cmd_args = cmd_args[0]
|
||||||
|
|
||||||
|
main_parser = argparse.ArgumentParser(description='Process command line arguments.')
|
||||||
|
if cmd_args.cmd == "restore":
|
||||||
|
main_parser.add_argument('--source_hostname', help='Source device hostname', required=True)
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
args = main_parser.parse_known_args()
|
||||||
|
args = args[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def parse_address(address):
|
||||||
|
pattern = r'^(?P<protocol>http|https)://(?P<host>[^:]+):(?P<port>\d+)(/.*|)$'
|
||||||
|
match = re.match(pattern, address)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
return match.group('protocol'), match.group('host'), match.group('port')
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid address format")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_short_sn(c):
|
||||||
|
global device_short_sn
|
||||||
|
device_short_sn = c.run('wb-gen-serial -s', hide=True).stdout.strip()
|
||||||
|
if device_short_sn is None:
|
||||||
|
raise ValueError("Both device_new_name and device_short_sn must be provided")
|
||||||
|
|
||||||
|
def set_hostname(c):
|
||||||
|
c.run(f'hostnamectl set-hostname {args.device_new_name}-{device_short_sn}')
|
||||||
|
|
||||||
|
def restore_hostname(c):
|
||||||
|
c.run(f'hostnamectl set-hostname `cat /mnt/data/etc/vestasync/hostname`')
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_packages_wb(c):
|
||||||
|
c.run('apt update')
|
||||||
|
c.run('apt install git apt-transport-https ca-certificates htop sudo mc wget curl jq zip gzip tar -y')
|
||||||
|
c.run('apt-get -y autoremove')
|
||||||
|
c.run('apt-get -y clean')
|
||||||
|
c.run('apt-get -y autoclean ')
|
||||||
|
|
||||||
|
|
||||||
|
def configure_git(c):
|
||||||
|
hostname = c.run('hostname', hide=True).stdout.strip()
|
||||||
|
c.run(f'git config --global user.name vestasync_wb_{hostname}')
|
||||||
|
c.run(f'git config --global user.email "vestasync@fake.mail"')
|
||||||
|
c.run(f'git config --global init.defaultBranch "master"')
|
||||||
|
|
||||||
|
def create_repo(c):
|
||||||
|
hostname = c.run('hostname', hide=True).stdout.strip()
|
||||||
|
headers = {'Authorization': f'token {args.gitea_token}', 'Content-Type': 'application/json'}
|
||||||
|
data = {"name": hostname, "private": False}
|
||||||
|
response = requests.post(f'{args.vestasync_gitea_protocol}://{args.vestasync_gitea_host}:{args.vestasync_gitea_port}/api/v1/user/repos', headers=headers, json=data)
|
||||||
|
if response.status_code == 201: # 201 - Created, ожидаемый код успешного создания репозитория
|
||||||
|
print("Repository created successfully.")
|
||||||
|
elif response.status_code == 409: # 409 - Conflict, репозиторий уже существует
|
||||||
|
print("Error: Repository already exists.")
|
||||||
|
print("Exiting...")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
print(f"Error: Unexpected HTTP status code {response.status_code}")
|
||||||
|
print("Exiting...")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def init_repo(c):
|
||||||
|
hostname = c.run('hostname', hide=True).stdout.strip()
|
||||||
|
#c.run('cd /mnt/data/etc/ && git init')
|
||||||
|
#c.run(f'cd /mnt/data/etc/ && git remote add origin {args.vestasync_gitea_protocol}://{gitea_user}:{args.gitea_token}@{args.vestasync_gitea_host}:{args.vestasync_gitea_port}/{gitea_user}/{hostname}.git')
|
||||||
|
|
||||||
|
|
||||||
|
def create_autogit_sh(c):
|
||||||
|
c.run('echo "#!/usr/bin/env sh" > /mnt/data/etc/pushgit.sh')
|
||||||
|
c.run('echo "cd /mnt/data/etc/" >> /mnt/data/etc/pushgit.sh')
|
||||||
|
c.run('echo "git add ." >> /mnt/data/etc/pushgit.sh')
|
||||||
|
c.run('''echo 'git commit -m "$(date)"' >> /mnt/data/etc/pushgit.sh''')
|
||||||
|
c.run('echo "git push -u origin master" >> /mnt/data/etc/pushgit.sh')
|
||||||
|
c.run('chmod +x /mnt/data/etc/pushgit.sh')
|
||||||
|
c.run('(crontab -l; echo "0 0 * * * /mnt/data/etc/pushgit.sh > /dev/null 2>&1 ") | crontab -')
|
||||||
|
|
||||||
|
def git_clone(c):
|
||||||
|
c.run(f'mkdir /mnt/data/{args.source_hostname}_etc ', hide=True)
|
||||||
|
c.run(f'git clone {args.vestasync_gitea_protocol}://{gitea_user}:{args.gitea_token}@{args.vestasync_gitea_host}:{args.vestasync_gitea_port}/{gitea_user}/{args.source_hostname}.git /mnt/data/{args.source_hostname}_etc')
|
||||||
|
|
||||||
|
def copy_etc(c):
|
||||||
|
current_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||||
|
archive_name = f"backup_{current_date}.tar.gz"
|
||||||
|
print(f"Remove old .git...")
|
||||||
|
c.run(f"rm -rf /mnt/data/etc/.git")
|
||||||
|
print(f"Create backup: /mnt/data/{archive_name}")
|
||||||
|
c.run(f"tar -czvf /mnt/data/{archive_name} -C /mnt/data etc", hide=True)
|
||||||
|
|
||||||
|
files_and_folders = c.run("find /mnt/data/WB2-A3TBJXLS_etc", hide=True).stdout.strip().split('\n')
|
||||||
|
files_and_folders = [item for item in files_and_folders if ".git" not in item]
|
||||||
|
|
||||||
|
for item in files_and_folders:
|
||||||
|
dest_item = item.replace(f"/{args.source_hostname}_etc/", "/etc/")
|
||||||
|
if c.run(f"test -f {item}", hide=True, warn=True).ok:
|
||||||
|
c.run(f"cat {item} > {dest_item}")
|
||||||
|
elif c.run(f"test -d {item}", hide=True, warn=True).ok:
|
||||||
|
c.run(f"mkdir -p {dest_item}")
|
||||||
|
print(f"Restore: {item} -> {dest_item}")
|
||||||
|
|
||||||
|
print(f"Сopy source .git...")
|
||||||
|
c.run(f"cp -R /mnt/data/{args.source_hostname}_etc/.git /mnt/data/etc/.git")
|
||||||
|
|
||||||
|
print(f"Remove source etc...")
|
||||||
|
c.run(f"rm -rf /mnt/data/{args.source_hostname}_etc")
|
||||||
|
|
||||||
|
print(f"Restore completed")
|
||||||
|
|
||||||
|
def ppush_the_repo(c):
|
||||||
|
c.run('cd /mnt/data/etc/ && git add .', hide=True)
|
||||||
|
try:
|
||||||
|
c.run('cd /mnt/data/etc/ && git commit -m "$(date)"', hide=True)
|
||||||
|
except UnexpectedExit as e:
|
||||||
|
if 'nothing to commit' in e.result.stdout:
|
||||||
|
print("Nothing to commit, exit")
|
||||||
|
else:
|
||||||
|
print(f"Error: {e.result.stderr}")
|
||||||
|
c.run('cd /mnt/data/etc/ && git push -u origin master', hide=True)
|
||||||
|
|
||||||
|
|
||||||
|
def save_mac_in_cfg(c):
|
||||||
|
hostname = c.run('hostname', hide=True).stdout.strip()
|
||||||
|
interfaces_info = c.run("ip -j a", hide=True).stdout.strip()
|
||||||
|
interfaces_data = json.loads(interfaces_info)
|
||||||
|
c.run("mkdir -p /mnt/data/etc/vestasync/macs")
|
||||||
|
for interface in interfaces_data:
|
||||||
|
ifname = interface["ifname"]
|
||||||
|
if re.match(r'^(eth|wlan)', ifname):
|
||||||
|
mac_address = interface["address"]
|
||||||
|
c.run(f"echo {mac_address} > /mnt/data/etc/vestasync/macs/{ifname}")
|
||||||
|
c.run(f'echo {hostname} > /mnt/data/etc/vestasync/hostname')
|
||||||
|
|
||||||
|
|
||||||
|
def device_prepare():
|
||||||
|
with Connection(host=args.device_ip, user=device_user) as 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)
|
||||||
|
ppush_the_repo(c)
|
||||||
|
|
||||||
|
def device_update():
|
||||||
|
with Connection(host=args.device_ip, user=device_user) as c:
|
||||||
|
ppush_the_repo(c)
|
||||||
|
|
||||||
|
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)
|
||||||
|
ppush_the_repo(c)
|
||||||
|
restore_hostname(c)
|
||||||
|
create_autogit_sh(c)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
args.vestasync_gitea_protocol, args.vestasync_gitea_host, args.vestasync_gitea_port = parse_address(args.gitea_address)
|
||||||
|
del args.gitea_address
|
||||||
|
except ValueError as e:
|
||||||
|
print(e)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
if cmd_args.cmd == "prepare":
|
||||||
|
device_prepare()
|
||||||
|
if cmd_args.cmd == "update":
|
||||||
|
device_update()
|
||||||
|
if cmd_args.cmd == "restore":
|
||||||
|
device_restore()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user