Scripting: how to find last backup

Задача: каким-то образом сделали бэкап, например, ткнув Backup в Winbox или выполнив в терминале команду:

/system backup save

Затем где-то в скриптах нам понадобилось найти этот самый файл с бэкапом.

Считаем, что время в роутере настроено правильно и, желательно, синхронизируется по Интернет с NTP сервером. Иначе есть риск того, что все бэкапы датированы 1970м годом :/)

На первый взгляд несложно – ведь мы легко можем найти нужный файл в WinBox. Даже если их очень много – то сортируем их по дате создание (Creation Time):

files

А через консоль?

Выполнив

/files print

видим, что файлы выводятся в порядке их создания:

files-2

То есть достаточно просто вывести имя последнего.

Это и будет решение 1.

Решение 1.

Для того, чтобы увидеть, что мы можем в скриптах получить от какой команды – после имени команды просто введите знак вопроса.

Так как вики от Микротик частенько запаздывают с версиями %), то научимся искать необходимые данные сами.

1. Смотрим, какие свойства мы можем читать:

/file ?

дает, что мы можем вызывать команды edit, find, get, print, remove, set. Команду print мы уже использовали, но для скрипта она не очень полезна – она выводит текст на консоль, а в качестве результата в скрипт возвращает nil.

Можем воспользоваться комадной find:

/file find

Хм, на экране пусто. И это правльно – ибо теперь всё с точностью наоборот, команда ничего не выводит на терминал, но возвращает в скрипт результат как array (массив то есть, теорию все прочитали?) . Чтобы увидеть воочию, воспользуемся командой вывода на консоль :put :

:put [/file find]

Результат правда малочитаем для нас, у меня он выглядит так:

*d0066;*d0218;*d0231;*d0246;*d0247;*d026b;*d05bb;*d05ec;*d05ed;*d0a67;*d0a68;*d0ad2;*d0b9f;*d0ba8;*d0bd8;*d0c19;*d0cd9;*d0cf5

* обозначает, что это ссылка на какой-то другой объект, ид (уникальный идентификатор) которого типа dNNNN.

Теперь посмотрим какой-то объект с помощью команды get:

:put [/file get]

Хм, просит ввести какой-то номер. Это порядковый номер #, который мы видели в первом столбце, когда выполняли команду /file print. Вот он для чего оказывается %). Что ж, глянем свойства последнего бекапа – у него номер 17:

:put [/file get 17]

В результате получаем:

.id=*d0cf5;creation-time=nov/05/2016<tab>18:49:43;name=CCR-Home-20161105-1849.backup;size=376748;type=backup

где <tab> – символ табуляции.

Что для нас тут важно:

  • бекапы имеют тип backup. Вот здесь и видим то, как вики Микротика отстали от жизни %) – http://wiki.mikrotik.com/wiki/Manual:System/File – у них это параметр под именем file type;
  • имя бекапа хранится в name (логично);
  • id – тот самый уникальный ид, который мы видели выше;
  • creation-time – Время создания бэкапа.

Теперь осталось просто найти номер (или ид) последнего бекапа.

:put [:len [/file find]]

выдает

18

Всё ок? А не всегда. А вдруг последний по дате не бекап, а какой-то лог-файл?

Значит считаем только бэкапы (type=backup):

:put [:len [/file find type=backup]]

выдает

10

Кто желает потренироваться в последовательном счете, может удостовериться, что на скриншоте в самом деле 10 бекапов.

Значит нам бэкап с номером… 9 %) Ибо нумерация с нуля!

:put [/file get 9]

Угх… что-то страшное. А почему? Потому что мы не сказали, что нам нужен именно бэкап с номером 9!

Итак, узнаем ид нужного бэкапа:

:put ([/file find type=backup]->9)

у меня это *d0cf5

далее – ведь нам хочется его имя – узнаем его имя имея ид:

:put [/file get *d0cf5 name]

Вуаля.

Теперь всё то же самое, но в виде одного скрипта. Я назвал его func_getlastbackup и дал при создании только одно право (policy) – read. С policy аккуратнее – по умолчанию Winbox дает абсолютно все права и если потом будете вызывать из Scheduler или другого скрипта, у которого меньше прав, то скрипт не выполнится. Посему давайте конкретным скриптам только те права, которые ему нужны. В частности read позволяет только читать параметры – нам этого достаточно:

#####################################
# Script: func_getlastbackup 
# Input: None 
# Output: return name of the last backup 
#####################################

#local variables
:local numBackup
:local idBackup
:local nameBackup

#get last backup number
:set numBackup ([:len [/file find type=backup]]-1)
#:put $numBackup

#get id of needed backup 
:set idBackup ([/file find type=backup]->$numBackup)
#put $idBackup

#return nil if none backups
:if ($numBackup<0) do={:return nil}
#find name of backup
:set nameBackup [/file get $idBackup name]
#:put $nameBackup

#return found result - for case of functions
:return $nameBackup

последняя команда позволяет:

А) вызвать скрипт из консоли и увидеть результат:

/tool> /system script run func_getlastbackup

выдало у меня

CCR-Home-20161105-1849.backup

Правильно, как ни странно.

Б) позволяет вызвать этот скрипт из другого скрипта как функцию!

Зачем в скрипте строка

:if ($numBackup<0) do={:return nil}

?

Без нее скрипт вроде тоже работает. Но так только кажется и все хорошо выглядит при вызове из консоли:

no such item

Ибо будет попытка найти бекап с номером -1 и будет выдана ошибка (ее мы и видим в консоли) и скрипт прекратит свое выполнение. И если этот скрипт вызывался из другого – то выполнение и того остановится (скорее всего – смотря как вызывать). В любом случае лучше не оставлять такие вещи на волю случая и предохраниться %)

Я для случая, если бекапа нет (то есть тогда переменная $numBackup будет равна
0-1=-1), то возвращаем значение nil и корректно завершаем скрипт. Тогда вызывающий скрипт сможет проверить результат и принять решение, что ему делать дальше.

Это было Решение 1.

Значит будет Решение 2?

Решение 2.

Тут вот интересный вопрос возник – а что делать если

/files print

по какой-то причине выдаст бэкапы не по порядку их создания? Может ли такое быть? Может. Например, бэкапы заливались на роутер отдельно и не в порядке их создания…

Продолжение следует…