Для роутеров Mikrotik, достаточно регулярно выходят обновления. Обновляться можно через меню System -> Packages -> Check_For_Updates, но когда устройств пара десятков и в эфир они выходят не регулярно — это не удобно. Ясно, что процесс нуждается в автоматизации.
Подготовка
Как подсказывает документация, если в корне файловой системы роутера лежит .npk файл с версией (зашита в заголовке файла, считывается автоматом) выше, чем установленная, то после перезагрузки по команде /system reboot (proper shutdown, как её называет лог) прошивка автоматически обновится.
Mikrotik
На роутере создаём скрипт следующего содержания:
:local url "176.28.64.20:16002/mikrotik/update"
:local user "cgb-mikrot"
:local pass "Pr0v1s10"
:local serial [/system routerboard get value-name="serial-number"]
:local osver [:pick [/system resource get value-name=version] 0 [:find [/system resource get value-name=version] " "]]
if ( [/system routerboard get value-name=upgrade-firmware] != [/system routerboard get value-name=current-firmware] ) do={ /system routerboard upgrade
:put "y"
/system reboot
:put "y"
}
if ( [:len [/file find name=routeros-mipsbe.npk]]=1 ) do={ /file remove routeros-mipsbe.npk }
:do { /tool fetch dst-path=routeros-mipsbe.npk mode=https user=$user password=$pass [:put ("https://$url\?serial=$serial&osver=$osver)]
/system reboot
:put "y"
} on-error={ /file remove routeros-mipsbe.npk }
Здесь, получаем серийник, текущую версию прошивки, проверяем соответствие установленной версии загрузчика и доступной и если они не равны делаем обновление и перезагружаем устройство.
Далее, серверу передаётся набор следующих параметров: osver — текущая версия прошивки, serial — серийный номер роутера.
Если на сервере есть версия свежее, чем osver, то сервер отдаёт файл для скачивания, после успешного завершения которой, Mikrotik уходит в перезагрузку, иначе — ошибку 404.
Сервер
На стороне сервера поднимаем Web-сервер с поддержкой CGI и пишем скрипт:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import cgi
import re
import requests
from bs4 import BeautifulSoup
def not_found():
print('Status: 404 Not Found', end='\r\n\r\n')
exit()
def version2int(version, multfactor=100):
if not version:
return 0
version_int = 0
mult = 1
for i in reversed(re.findall(r'\d+', str(version))):
version_int = version_int + (int(i) * mult)
mult = mult * multfactor
return version_int
fw_dir = '/usr/local/www/mikrotik/firmware/'
params = cgi.FieldStorage()
osver = params.getfirst('osver')
if len(sys.argv) > 1 and sys.argv[1] == 'fetch':
r = requests.get('https://mikrotik.com/download', verify=False)
soup = BeautifulSoup(r.text.encode("utf-8"))
table = soup.find('table', {'class': 'table downloadTable'})
thead = table.find('thead').find_all('th')
bf_row = 0
for col in thead:
if 'Bugfix only' in col.text:
break
bf_row += 1
else:
not_found()
tbody = table.find('tbody')
mips = 0
for i in tbody.find_all('tr'):
if 'MIPS' in i.text:
mips = 1
continue
if mips:
link = 'https:' + i.find_all('td')[bf_row].select('a')[0]['href']
filename = link.split('/')[-1]
for f in os.listdir(path=fw_dir):
if f == filename:
not_found()
filereq = requests.get(link, stream = True)
with open(fw_dir + filename, 'wb') as f:
for chunk in filereq.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
f.close()
not_found()
not_found()
if not osver:
not_found()
osver_int = version2int(osver)
avl_dict = {i: version2int(i) for i in os.listdir(path=fw_dir)}
avl_newest = [(k, avl_dict[k]) for k in sorted(avl_dict, key=avl_dict.get, reverse=True)][0]
if avl_newest[1] <= osver_int:
not_found()
print('Content-type: application/octet-stream; name=' + avl_newest[0], end='\r\n')
print('Content-Length: ' + str(os.path.getsize(fw_dir + avl_newest[0])), end='\r\n')
print('Content-Disposition: attachment; filename=' + avl_newest[0], end='\r\n')
print('Content-Transfer-Encoding: binary', end='\r\n')
print('Accept-Ranges: bytes', end='\r\n\n')
fd = open(fw_dir + avl_newest[0], 'rb')
content = fd.read()
sys.stdout.flush()
sys.stdout.buffer.write(content)
fd.close()
Тут смотрим, если скрипт запустили из консоли с параметром "fetch", то парсим страницу загрузок сайта Mikrotik'а, в таблице ищем самую верхнюю прошивку с архитектурой MIPS, из ветки Bugfix only (как-то были проблемы после ручного обновления на Current, а тут можно автоматически положить все устройства, так что - онли баг фикс), смотрим нет ли у нас уже такого файла и если нет - качаем, выходим.
Если скрипт запущен через CGI: ищем новейшую версию на сервере, сравниваем с версией на устройстве и, в случае различия в пользу версии на сервере, отдаём файл.
P.S: Для сравнения версий используется преобразование в число функцией version2int. Например версия 6.38.7 преобразуется к 63807, как показал беглый просмотр сайта mikrotik.com ветка Bugfix only придерживается такого формата уже лет 7.
Еще вариант скрипта автоматического обновления + резервные копии
https://github.com/beeyev/Mikrotik-RouterOS-automatic-backup-and-update