Большой бекап в маленьком MikroTik’е

Дело было вечером, делать было уже пора что-то с этим со всем…

Зайду из далека

Жил-был у меня CRS125-24G-1S-RM от одного неудачного проекта. На тот момент, я уже много чего слышал о сабже и его родителях, но как то юзать не решался, ибо forwarding на моём какинтоше и точка тупо-Link меня тогда вполне себе грели. Но пришло время собрать силы в кулак и…. распаковать зверушку. Юзаж ограничился добавлением masquerade в NAT, что превратило сабж в маршрутизатор, плюс пару штрихов и всё взлетело буквально в два клика, после чего последовали долгие месяцы любви и взаимопонимания.

Спустя неопределённое время, мой пытливый ум и руки полезли в скрипты RouterOS. Уже не припомню с какого перепуга мне понадобилось бэкапить конфиг, но тем не менее именно эта задача познакомила меня со скриптами на MikroTik’е, что в корне поменяло моё отношение к железу этой мамки.

Задача

Задача стояла достаточно просто, срезать бэкапы с системы в виде файла скрипта и файла бэкапа, плюс скрипты отдельных секций, таких как firewall, route пр. Все файлы нужно бережно складывать на плешку в заданное время с почтовым голубем извещением и аттачем основного бэкапа. По достижению заданного срока давности, старые бэкапы нужно было сносить.

Первые грабли

Как долго я удалял старые бэкапы руками… Это пожалуй бесило даже больше, чем отсутствие в скриптах RouterOS привычных мне функций join() и split(). Однако и это решаемо, в отличие от бага с полем from=" " в /tool email и отсутствием возможности множественных аттачментов к письму. Для справки: Письма не ходят с произвольно заполненным или отсутствующим полем from=" " (v6.37.4). Поле нужно указать именно вот так from=" " с пробелом в кавычках или from="<используемая@учётная.запись>" либо указать ящик на который отправляется почта.

Рецепт

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

Для начала объявляем переменные:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
:local history "7d";	# Как долго храним бэкапы. Указываем в секундах минутах часах или днях (s/m/h/d)
:local email "mailn@example.com"	# Куда спамить результатами
:local pathForBackups "disk1/backup/";	# Куда писать основной бэкап
:local pathForConfigs "disk1/etc/config/";	# Куда сохранять конфиги секций
:local pathForScripts "disk1/etc/script/";	# Куда сохранять бэкап скриптов
:local configsArray {"ip route";"ip firewall";"ip pool";"ip dhcp-server";"interface"};	# Перечисляем секции,
# которые будут бэкапиться по отдельности в виде скриптов. Пишем их полностью так, как они звучат до 
# команды "export". К примеру ip route export - в массив вписываем только ip route
:local nme [/system identity get name];	# Берём имя машинки
:local scriptName "Backup";	# Имя скрипта
:local mailSubject ([/system identity get name]."::".$scriptName);	# Формируем тему для письма
:local cdte ([/system clock get date]." ".[/system clock get time]);	# Дата и время

log info "$scriptName: Starting...";	# Логируем старт
/tool e-mail send from=" " to=$email subject=$mailSubject body="$mailSubject:: $cdte:: Started.";	# Спамим о старте
beep frequency=1975 length=100ms;	# Бип о начале
delay 150ms;

Функция конвертирует дату и время формата Feb/25/2017 14:18:12 в два варианта, первый — это количество секунд со дня творения каледраля от рождества Христова (это нам нужно для вычисления возраста файла) и второй — вот такого вида 25-02-2017-18-43 для формирования удобоваримых имён файлов.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
:local datr do={
	:local dfy 0;
	:local yar [pick $fdte 7 11];
	:local mth [pick $fdte 0 3];
	:local day [pick $fdte 4 6];
	:local hur [pick $fdte 12 14];
	:local min [pick $fdte 15 17];
	:local sec [pick $fdte 18 20];
	:local mts ("jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec");
	:local mtd {0;31;28;31;30;31;30;31;31;30;31;30;31};
	:local mtn ([find $mts $mth -1]+1);
	:local yrd (($yar*365)+($yar / 4));
	:local cmd ($mtd->$mtn);
	:local conA $mtn;
 
	do {
		set $conA ($conA-1);
		set $dfy ($dfy+($mtd->$conA));
		if ($conA=0) do={set $dfy ($dfy+$day);}
	} while ($conA>0);
 
	if ($mtn<=9) do={set mtn ("0".$mtn);}
 
	return {((($yrd+$dfy)*86400)+($hur*3600)+($min*60)+$sec);($day."-".$mtn."-".$yar."-".$hur."-".$min)};
}

Функция преобразовывает заданный период хранения бэкапов в секунды.

1
2
3
4
5
6
7
8
9
10
:local hgen do={
	:local rlt;
	:local dgt [pick $hist 0 ([len $hist]-1)];
	:local ltr [pick $hist ([len $hist]-1) [len $hist]];
	if ($ltr="s") do={set $rlt $dgt;}
	if ($ltr="m") do={set $rlt ($dgt*60);}
	if ($ltr="h") do={set $rlt ($dgt*3600);}
	if ($ltr="d") do={set $rlt ($dgt*86400);}
	return $rlt;
}

Функция удаляет старые бэкапы путём выборки по файлам и выполнения простого действия: если сегодняшняя дата, минус дата создания файла, больше чем заданный период, то файл удаляется.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
:local delo do={
	:local far [/file find name~"@".$nme];
	:local conB ([len $far]);
	do {
		set $conB ($conB-1);
		:local ftm ([$datr fdte=[/file get number=($far->$conB) value-name=creation-time]]->0);
		:local bdte ([$datr fdte=[/file get number=($far->$conB) value-name=creation-time]]->1);
		if (($cdte-$ftm)>($hgen-10)) do={	# Отнимаем 10 секунд от значения, иначе самый старый бекап не будет удаляться.
			log info "$scriptName: Deleting old backups: $bdte";
			/file remove number=($far->$conB);
			beep frequency=1568 length=10ms;
			delay 100ms;
		} else={
			beep frequency=1975 length=10ms;
			delay 100ms;
		}
	} while ($conB>0);
}

Функция создаёт скрипты для каждой из перечисленных в массиве секций {"ip route";"ip firewall";"ip pool";"ip dhcp-server";"interface"}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
:local cspt do={
	:local cbn;
	:local conC ([len $cba]);
	do {
		set $conC ($conC-1);
		beep frequency=1975 length=1000ms;
		set $cbn [($cba->$conC)];
		log info "$scriptName: Create $cbn script";
		execute "/$cbn export file=\"$pfc$cbn.rsc\"";
		beep frequency=1975 length=10ms;
		log info "$scriptName: Done";
		delay 100ms;
	} while ($conC>0);
}

После успешного объявления переменных и функций, весело звучим, создаём шаблон имени для файлов бэкапа, бэкапимся и удаляем старые файлы.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
beep frequency=1975 length=100ms;
delay 1s;
 
:local bfn ($pathForBackups.([$datr fdte=$cdte]->1)."@".$nme);	# Шаблон имени файлов

log info "$scriptName: Create system backup";	# Основной бэкап
/system backup save name=($bfn.".backup");
beep frequency=1975 length=10ms;
log info "$scriptName: Done";
delay 100ms;
 
log info "$scriptName: Create system script";	# Основной бэкап в скрипт
/export compact file=($bfn.".rsc");
beep frequency=1975 length=10ms;
log info "$scriptName: Done";
delay 100ms;
 
log info "$scriptName: Create Scripts script";	# Бэкап скриптов
/system script export file=($pathForScripts."scripts.rsc");
beep frequency=1975 length=10ms;
log info "$scriptName: Done";
delay 100ms;
 
 
[$cspt pfc=$pathForConfigs cba=$configsArray scriptName=$scriptName]	# Бэкапим заданные секции

[$delo nme=$nme cdte=([$datr fdte=$cdte]->0) datr=$datr hgen=[$hgen hist=$history] scriptName=$scriptName]	# Удаляем старые файлы

В финале мероприятия объявляем глобально дату последнего бэкапа, сколько раз отработал скрипт, весело пищим и спамим результатом на заданный почтовый ящик.

1
2
3
4
5
6
7
8
:global BackupLastTime $cdte;
:global BackupTimes ($BackupTimes+1);
beep frequency=1760 length=20ms;
delay 150ms;
beep frequency=1760 length=20ms;
delay 100ms;
/tool e-mail send from=" " to=$email subject=$mailSubject body="$mailSubject:: $[/system clock get date] $[/system clock get time]:: Success!\n" file=($bfn.".backup");
log info "$scriptName: Exit";

Теперь осталось сохранить код с нужным именем и добавить в Scheduler строку запуска скрипта /system script run Backup, предварительно настроив время запуска.

MikroTik scheduler
MikroTik scheduler

Что стоит бекапить в первую очередь и как потом использовать резервные копии, расскажет Дмитрий Скромнов в русскоязычном онлайн-курсе по MikroTik для самостоятельного изучения. Серия курсов по MikroTik и RouterOS основана на официальной программе MTCNA, однако содержит намного больше полезной информации. 162 видеоурока и большая практическая задача, разбитая на 45 лабораторных работ. Время на изучение неограниченно – все материалы передаются бессрочно и их можно пересматривать сколько нужно. Первые 25 уроков можно посмотреть бесплатно, оставив заявку на странице курса.

Скрипт полностью:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
 
:local history "7d";
:local email "mailn@example.com";
:local pathForBackups "disk1/backup/";
:local pathForConfigs "disk1/etc/config/";
:local pathForScripts "disk1/etc/script/";
:local configsArray {"ip route";"ip firewall";"ip pool";"ip dhcp-server";"interface"};
:local nme [/system identity get name];
:local scriptName "Backup";
:local mailSubject ([/system identity get name]."::".$scriptName);
:local cdte ([/system clock get date]." ".[/system clock get time]);
 
log info "$scriptName: Starting...";
/tool e-mail send from=" " to=$email subject=$mailSubject body="$mailSubject:: $cdte:: Started.";
beep frequency=1975 length=100ms;
delay 150ms;
 
:local datr do={
	:local dfy 0;
	:local yar [pick $fdte 7 11];
	:local mth [pick $fdte 0 3];
	:local day [pick $fdte 4 6];
	:local hur [pick $fdte 12 14];
	:local min [pick $fdte 15 17];
	:local sec [pick $fdte 18 20];
	:local mts ("jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec");
	:local mtd {0;31;28;31;30;31;30;31;31;30;31;30;31};
	:local mtn ([find $mts $mth -1]+1);
	:local yrd (($yar*365)+($yar / 4));
	:local cmd ($mtd->$mtn);
	:local conA $mtn;
 
	do {
		set $conA ($conA-1);
		set $dfy ($dfy+($mtd->$conA));
		if ($conA=0) do={set $dfy ($dfy+$day);}
	} while ($conA>0);
 
	if ($mtn<=9) do={set mtn ("0".$mtn);}
 
	return {((($yrd+$dfy)*86400)+($hur*3600)+($min*60)+$sec);($day."-".$mtn."-".$yar."-".$hur."-".$min)};
}
 
 
:local hgen do={
	:local rlt;
	:local dgt [pick $hist 0 ([len $hist]-1)];
	:local ltr [pick $hist ([len $hist]-1) [len $hist]];
	if ($ltr="s") do={set $rlt $dgt;}
	if ($ltr="m") do={set $rlt ($dgt*60);}
	if ($ltr="h") do={set $rlt ($dgt*3600);}
	if ($ltr="d") do={set $rlt ($dgt*86400);}
	return $rlt;
}
 
 
:local delo do={
	:local far [/file find name~"@".$nme];
	:local conB ([len $far]);
	do {
		set $conB ($conB-1);
		:local ftm ([$datr fdte=[/file get number=($far->$conB) value-name=creation-time]]->0);
		:local bdte ([$datr fdte=[/file get number=($far->$conB) value-name=creation-time]]->1);
		if (($cdte-$ftm)>($hgen-10)) do={
			log info "$scriptName: Deleting old backups: $bdte";
			/file remove number=($far->$conB);
			beep frequency=1568 length=10ms;
			delay 100ms;
		} else={
			beep frequency=1975 length=10ms;
			delay 100ms;
		}
	} while ($conB>0);
}
 
:local cspt do={
	:local cbn;
	:local conC ([len $cba]);
	do {
		set $conC ($conC-1);
		beep frequency=1975 length=1000ms;
		set $cbn [($cba->$conC)];
		log info "$scriptName: Create $cbn script";
		execute "/$cbn export file=\"$pfc$cbn.rsc\"";
		beep frequency=1975 length=10ms;
		log info "$scriptName: Done";
		delay 100ms;
	} while ($conC>0);
}
 
 
beep frequency=1975 length=100ms;
delay 1s;
 
:local bfn ($pathForBackups.([$datr fdte=$cdte]->1)."@".$nme);
 
log info "$scriptName: Create system backup";
/system backup save name=($bfn.".backup");
beep frequency=1975 length=10ms;
log info "$scriptName: Done";
delay 100ms;
 
log info "$scriptName: Create system script";
/export compact file=($bfn.".rsc");
beep frequency=1975 length=10ms;
log info "$scriptName: Done";
delay 100ms;
 
log info "$scriptName: Create Scripts script";
/system script export file=($pathForScripts."scripts.rsc");
beep frequency=1975 length=10ms;
log info "$scriptName: Done";
delay 100ms;
 
 
[$cspt pfc=$pathForConfigs cba=$configsArray scriptName=$scriptName]
 
[$delo nme=$nme cdte=([$datr fdte=$cdte]->0) datr=$datr hgen=[$hgen hist=$history] scriptName=$scriptName]
 
:global BackupLastTime $cdte;
:global BackupTimes ($BackupTimes+1);
beep frequency=1760 length=20ms;
delay 150ms;
beep frequency=1760 length=20ms;
delay 100ms;
/tool e-mail send from=" " to=$email subject=$mailSubject body="$mailSubject:: $[/system clock get date] $[/system clock get time]:: Success!\n" file=($bfn.".backup");
log info "$scriptName: Exit";

Большой бекап в маленьком MikroTik’е: 2 комментария

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.