Допиливаем…
Зачем?
Как и в предидущем ремейке продолжаем глобализировать код с применением библиотеки Edelweiss. Логика и цели остаются теми же что и в оригинале, за исключением того, что все функции берутся из библиотеки. Пересобрал я этот скрипт уже достаточно давно, месяцев эдак 6 назад, но вот руки только сейчас дошли до публикации.
Русскоязычный онлайн-курс по MikroTik от нашего коллеги Дмитрия Скромнова. Здесь можно изучить MikroTik и RouterOS самостоятельно по курсу «Настройка оборудования MikroTik». Курс основан на официальной программе MTCNA, но содержит больше информации. Это 162 видеоурока и большая практическая задача, разбитая на 45 лабораторных работ. Время на изучение неограниченно – все материалы передаются бессрочно и их можно пересматривать сколько нужно. Первые 25 уроков можно посмотреть бесплатно, оставив заявку на странице курса.
Рецепт
Не будем повторяться, просто скрипт полностью с пояснениями и небольшими дополнениями. Напоминаю о том, что скрипт нужно добавить в Sheduler. Более полное описание работы и всех тонкостей можно найти здесь
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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | beep frequency=1760 length=10ms; :local scriptName "Failover"; # Задаем имя скрипта :if ([len [/system script job find where script=$scriptName]]>1) do={ # Проверяем на наличее уже запущенного # скрипта. Если скрипт уже запущен, выходим, если нет, работаем. log error "$scriptName: Single instance"; beep frequency=440 length=1000ms; } else={ delay 60s; # При старте вместе с системой лучше подождать, иначе возможны грабли, проверено. :local globalIPArray {"8.8.8.8";"77.88.8.8";"193.58.251.251";"114.114.115.115"}; # Определяем адреса # которые скрипт пингует для проверки интернет соединения. Кол-во адресов может быть любое. В примере указано 4 # публичных DNS сервера (Google, Yandex, SkyDNS, 114dns). :local localIPArray {"10.0.0.4";"10.0.1.4";"10.0.2.4";"172.16.0.4";"192.168.0.4"}; # Определяем адреса # которые скрипт пингует для проверки локального соединения. Кол-во адресов может быть любое. :local restoreDelays {600;7200;21600;43200;86400}; # Определяем тайм-ауты восстановления аплинка в # секундах. Кол-во и значения могут быть любые. :local filePath "disk1/var/log/full_log.0.txt"; # Указываем файл высылаемый на почту при сбое. :local email "mail@example.com"; # Куда спамить письмами. :local displayOnRestore "uplink0"; # Что отображаем на дисплее в нормальном режиме. :local displayOnError "bond1.0"; # Что отображаем на дисплее при сбое. :local uplink "uplink0"; # Указываем интерфейс аплинка. :local pingCount 2; # Кол-во пингов на один IP. :local conA 0; :local conB 0; :local conD 0; :local conC 0; :local once 0; :local pingResA 0; :local pingResB 0; :local mailSubject ([/system identity get name]."::".$scriptName); # Тема отправляемого E-Mail. # Вводим глобальные переменные для отображения текущего статуса в environment. :global FailoverState 0; # Отображает текущий статус скрипта, всего их 5, от 0 до 4. :global UplinkIPCurrentScan "waiting..."; # Отображает IP в интернет который скрипт пингует в # данный момент. :global LocalIPCurrentScan "waiting..."; # Отображает локальный IP который скрипт пингует в # данный момент. :global RestoreDelay 0; # Номер следующего тайм-аута восстановления аплинка. :global RestoreDelayTime "0 s"; # Длительность следующего тайм-аута. :global RestoreDelayTimeRemaining "0 s"; # Отображает оставшеся время до восстановления аплинка # без учёта длительности основного цикла. :global UplinkDrops 0; # Количество потерянных пакетов на аплинке. :global UplinkDisabled "false"; # Статус аплинк интерфейса. :global UplinkFailoverTimes 0; # Кол-во падений аплинка. :global UplinkFailoverLastTime "-"; # Дата и время последнего падения аплинка. :global UplinkRestoreTimes 0; # Кол-во восстановлений аплинка. :global UplinkRestoreLastTime "-"; # Дата и время последнего восстановления аплинка. :global LocalDrops 0; # Количество потерянных пакетов в локалке. :global LocalDisabled "false"; # Статус локального интерфейса. :global LocalFailoverTimes 0; # Кол-во падений локалки. :global LocalFailoverLastTime "-"; # Дата и время последнего падения локалки. :global LocalRestoreTimes 0; # Кол-во восстановлений локалки. :global LocalRestoreLastTime "-"; # Дата и время последнего восстановления локалки. # Запускаем библиотеку и объявляем функции. /system script run "Edelweiss"; :global mlFrm; :global cuDte; :global beMel; :global foDte; :global ifSta; :global ifDis; :global ifRun; # Функция изменяет параметр edge для всех портов мостов. Это лечит известные грабли с мостиками. # Примеры: # [$brdgPt brdgEd="auto"]; - Изменяет значение параметра edge на заданный (в примере на auto); :local brdgPt do={ :local brdgPtCount [/interface bridge port print count-only;]; do { set $brdgPtCount ($brdgPtCount-1); :local F [/interface bridge port print brief]; if ([/interface bridge port get $brdgPtCount edge] != $brdgDo) do={ execute "/interface bridge port set edge=$brdgEd numbers=$brdgPtCount"; } } while ($brdgPtCount>0); } lcd backlight state=on; # Включаем дисплей. [$brdgPt brdgDo="yes"]; # "Подготавливаем" мосты. [/interface enable $uplink;] # Включаем аплинк. lcd interface display $displayOnRestore; # Отображаем заданный интерфейс. [$ifRun Name=$scriptName Type="vrrp" Disabled="no"]; # Поднимаем VRRP интерфейсы [$beMel Frequency=({"1975.0"; "1760.0"; "1568.0"; "1318.5"; "1046.5"}) Delay=({"100"}) Length=({"100"})]; # Звучим о старте. /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Started.\n" file=$filePath; # Шлём на почту файл (текущий лог) и сообщение о старте. if ($FailoverState=0) do={ # Проверяем текущий статус, если "0", то стартует основной цикл. Именно он держит скрипт # в режиме постоянно активного процесса. [$beMel Frequency=({"1975"; "1975"}) Delay=({"1000"; "150"}) Length=({"100"})]; # Звучим о старте процесса. do { if ($conC<1) do={ # Если тайм-аут отсутствует (меньше 1). if ([/interface get $uplink running]=false) do={ # Проверяем состояние интерфейса. if ($once=0) do={ # Если выполняется впервые - действуем. set $once 1; # Отмечаем как выполненное. log info "$scriptName: Uplink IF DOWN"; set $UplinkFailoverLastTime "$[$cuDte]"; set $UplinkFailoverTimes ($UplinkFailoverTimes+1); log info "$scriptName: state changed $FailoverState->1"; set $FailoverState 1; # Устанавливаем статус "1". [$beMel Frequency=({"1046.5"; "1318.5"; "1568.0"; "1760.0"; "1975.0"}) Delay=({"150"}) Length=({"150"})]; [$ifDis Name=$scriptName Type="l2tp-out" Disabled="yes"]; # Отключаем L2TP интерфейсы. [$ifDis Name=$scriptName Type="pptp-out" Disabled="yes"]; # Отключаем PPTP интерфейсы. [$ifRun Name=$scriptName Type="vrrp" Disabled="yes"]; # Отключаем VRRP интерфейсы. [$brdgPt brdgDo="yes"]; # Дёргаем мосты. log info "$scriptName: state changed $FailoverState->4"; set $FailoverState 4; # Устанавливаем статус "4". lcd backlight state=off; # Тушим подсветку сигнализируя тем самым о проблеме. # Шлём на почту файл (текущий лог) и сообщение о падении физики. /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Uplink IF DOWN.\n" file=$filePath; } } else={ if ($FailoverState=4) do={ # Проверяем статус "4". if ($once=1) do={ # Если выполнено предидущее действие - стартуем. set $once 0; # Сбрасываем счётчик выполнения. log info "$scriptName: Uplink IF UP"; set $UplinkRestoreLastTime "$[$cuDte]"; set $UplinkRestoreTimes ($UplinkRestoreTimes+1); log info "$scriptName: state changed $FailoverState->3"; set $FailoverState 3; # Устанавливаем статус "3". [$beMel Frequency=({"1975.0"; "1760.0"; "1568.0"; "1318.5"; "1046.5"}) Delay=({"150"}) Length=({"150"})]; [$ifRun Name=$scriptName Type="vrrp" Disabled="no"]; # Включаем VRRP интерфейсы. if ([$ifSta Type="vrrp"]=true) do={ # Если все VRRP интерфейсы активны. [$brdgPt brdgEd="auto"]; # "Будим" мосты. [$ifDis Name=$scriptName Type="pptp-out" Disabled="no"]; # Включаем PPTP интерфейсы. [$ifDis Name=$scriptName Type="l2tp-out" Disabled="no"]; # Включаем L2TP интерфейсы. } log info "$scriptName: state changed $FailoverState->0"; set $FailoverState 0; # Устанавливаем статус "0". lcd backlight state=on; # Включаем дисплей для наглядности. # Шлём на почту файл (текущий лог) и сообщение о восттановлении физики. /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Uplink IF UP.\n" file=$filePath; } } } if ($FailoverState<4) do={ # Если статус меньше "4" то работаем. set $conA 0; set $pingResA 0; set $conB ([len $globalIPArray]-1); # Определяем длинну массива с IP адресами. do { set $conA ($conA+$pingCount); # Считаем количество пингов. set $UplinkIPCurrentScan [($globalIPArray->$conB)]; # Берём из массива IP адрес. set $pingResA ($pingResA+[ping $UplinkIPCurrentScan count=$pingCount]); # Считаем кол-во ответов. set $conB ($conB-1); # Уменьшакем счётчик цикла. delay "$(([len $globalIPArray]*$pingCount)*100) ms"; # Тайм-аут. Счиается из расчёта 100 мс на один пинг. } while ($conB>-1); set $UplinkDrops ($UplinkDrops+($conA-$pingResA)); # Считаем количество потерянных пакетов. } if ($pingResA=0) do={ # Если результат пинга равен нулю. if ($FailoverState=0) do={ # Если статус "0". log info "$scriptName: connection to the Internet lost"; set $UplinkFailoverLastTime "$[$cuDte]"; set $UplinkFailoverTimes ($UplinkFailoverTimes+1); log info "$scriptName: state changed $FailoverState->1"; set $FailoverState 1; # Устанавливаем статус "1". # Звучим о потере соединения. [$beMel Frequency=({"1046.5"; "1318.5"; "1568.0"; "1760.0"; "1975.0"}) Delay=({"150"}) Length=({"150"})]; [$ifDis Name=$scriptName Type="l2tp-out" Disabled="yes"]; # Отключаем L2TP интерфейсы. [$ifDis Name=$scriptName Type="pptp-out" Disabled="yes"]; # Отключаем PPTP интерфейсы. [$ifRun Name=$scriptName Type="vrrp" Disabled="yes"]; # Отключаем VRRP интерфейсы. [$brdgPt brdgDo="yes"]; # "Будим" мосты. log info "$scriptName: state changed $FailoverState->2"; set $FailoverState 2; # Устанавливаем статус "2". lcd backlight state=off; # Тушим подсветку сигнализируя тем самым о проблеме. # Шлём на почту файл (текущий лог) и сообщение об отсутствии подключения. /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Connection to the Internet lost."; } } else={ # Если результат пинга не равен нулю. if ($FailoverState=2) do={ # Если статус "2". log info "$scriptName: connection to the Internet established"; set $UplinkRestoreLastTime "$[$cuDte]"; set $UplinkRestoreTimes ($UplinkRestoreTimes+1); log info "$scriptName: state changed $FailoverState->3"; set $FailoverState 3; # Устанавливаем статус "3". # Звучим о восттановлении соединения. [$beMel Frequency=({"1975.0"; "1760.0"; "1568.0"; "1318.5"; "1046.5"}) Delay=({"150"}) Length=({"150"})]; [$ifRun Name=$scriptName Type="vrrp" Disabled="no"]; # Включаем VRRP интерфейсы. if ([$ifSta Type="vrrp"]=true) do={ # Если все VRRP интерфейсы активны. [$brdgPt brdgEd="auto"]; # "Будим" мосты. [$ifDis Name=$scriptName Type="pptp-out" Disabled="no"]; # Включаем PPTP интерфейсы. [$ifDis Name=$scriptName Type="l2tp-out" Disabled="no"]; # Включаем L2TP интерфейсы. } log info "$scriptName: state changed $FailoverState->0"; set $FailoverState 0; # Устанавливаем статус "0". lcd backlight state=on; # Включаем дисплей для наглядности. # Шлём на почту файл (текущий лог) и сообщение о восстановлении подключения. # /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Connection to the Internet established."; } } # Принцип работы тайм-аутов следующий. Если "падений" более одного, то устанавливаем первый тайм-аут из заданных в массиве # и устанавливаем счётчик тайм-аутов равным единице. По истечению тайм-аута, пробуем использовать аплинк. Если попытка удалась, # то ждём истечения времени заданного в последней позиции массива для сброса счётчика тайм-аутов, если нет, то устанавливаем # следующий тайм-аут из массива и увеличиваем значение счётчика на единицу. Если все тайм-ауты израсходованы, то отключаем # аплинк-интерфейс и отсылаем письмо об ошибке. if ($FailoverState=2) do={ # Если статус "2". if ($UplinkFailoverTimes>1) do={ # Если "падений" более одного. set $conC 1; # Устанавливаем наличие тайм-аута. set $rdLe [len $restoreDelays]; # Определяем длинну массива с тайм-аутами. set $RestoreDelay ($RestoreDelay+1); # Устанавливаем номер следующего тайм-аута. # Если номер следующего тайм-аута больше длинны массива с тайм-аутами. if ($RestoreDelay>$rdLe) do={ set $FailoverState 4; # Устанавливаем статус "4". # Устанавливаем время следующего восстановления "бесконечность". set $RestoreDelayTime "infinit" set $UplinkDisabled "true"; # Устанавливаем статус аплинка "отключен". [/interface disable $uplink;] # Отключаем аплинк интерфейс. # Отправляем письмо о том, что аплинк-интерфейс отключен. /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Error occurred! Uplink interface $uplink disabled.\n" file=$filePath; } } } if ($FailoverState=0) do={ # Если статус "0". if ($RestoreDelay>0) do={ # Если номер следующего тайм-аута больше нуля. # Если ( "текущая дата и время" минус "дата и время последнего восстановления аплинка" ) больше чем # "последний тайм-аут в массиве тайма-утов". if ((([$foDte Date=[$cuDte]]->0)-([$foDte Date=$UplinkRestoreLastTime]->0))>[($restoreDelays->($rdLe-1))]) do={ set $RestoreDelay 0; # Устанавливаем номер следующего тайм-аута "0". set $RestoreDelayTime "0 s"; # Устанавливаем время следующего восстановления "0 секунд". } } } # Если "падений" более нуля, устанавливаем время следующего восстановления из массива тайм-аутов под номером # следующего тайм-аута. if ($UplinkFailoverTimes>0) do={set $RestoreDelayTime "$[($restoreDelays->$RestoreDelay)] s";} } else={ # Если тайм-аут присутствует (больше 0). # Если номер следующего тайм-аута больше нуля. if ($RestoreDelay>0) do={ # Если ( "текущая дата и время" минус "дата и время последнего "падения" аплинка" ) больше чем # "текущее значение тайм-аута" в массиве тайма-утов, устанавливаем что тайм-аут отсутствует (значение "0"). if ((([$foDte Date=[$cuDte]]->0)-([$foDte Date=$UplinkFailoverLastTime]->0))>[($restoreDelays->($RestoreDelay-1))]) do={set $conC 0;} # Если номер следующего тайм-аута равен нулю или меньше, устанавливаем что тайм-аут отсутствует (значение "0"). } else={set $conC 0;} } # Вычисляем оставшееся время до окончания тайм-аута. set $rdtrs ((([$foDte Date=$UplinkFailoverLastTime]->0)+[($restoreDelays->($RestoreDelay-1))])-([$foDte Date=[$cuDte]]->0)); # Если оставшееся время до окончания тайм-аута больше нуля, то устанавливаем значение оставшегося времени равным оставшемуся времени, # если нет, то устанавливаем значение оставшегося времени равным нулю (выполняется один раз за цикл). if ($rdtrs>0) do={set $RestoreDelayTimeRemaining "$rdtrs s";} else={set $RestoreDelayTimeRemaining "0 s";} set $vicS [$ifSta Type="vrrp"]; # "Запоминаем" статус VRRP интерфейсов. if ([$ifSta Type="vrrp"]=true) do={ # Если все VRRP интерфейсы активны. [$brdgPt brdgEd="auto"]; # "Будим" мосты. [$ifDis Name=$scriptName Type="pptp-out" Disabled="no"]; # Включаем PPTP интерфейсы. [$ifDis Name=$scriptName Type="l2tp-out" Disabled="no"]; # Включаем L2TP интерфейсы. } else={ [$brdgPt brdgEd="yes"]; # "Будим" мосты. [$ifDis Name=$scriptName Type="l2tp-out" Disabled="yes"]; # Отключаем L2TP интерфейсы. [$ifDis Name=$scriptName Type="pptp-out" Disabled="yes"]; # Отключаем PPTP интерфейсы. } set $conD ([len $localIPArray]-1); # Определяем длинну массива с IP адресами. do { if ([$ifSta Type="vrrp"]=$vicS) do={ # Если изменений в конфигурации VRRP интерфейсов нет. set $LocalIPCurrentScan [($localIPArray->$conD)]; # Берём из массива IP адрес. set $pingResB [ping $LocalIPCurrentScan count=$pingCount]; # Считаем кол-во ответов. set $LocalDrops ($LocalDrops+($pingCount-$pingResB)); # Считаем количество потерянных пакетов. set $conD ($conD-1); delay "$(([len $localIPArray]*$pingCount)*100) ms"; # Тайм-аут. Счиается из расчёта 100 мс на один пинг. if ($pingResB=0) do={ # Если результат пинга равен нулю. if ([/ip dhcp-server get $LocalIPCurrentScan disabled]=true) do={ # Если текущий DHCP сервер не активен. # Звучим об активации текущего встроенного DHCP сервера. [$beMel Frequency=({"1046.5"; "1568.0"; "1046.5"; "1568.5"}) Delay=({"150"}) Length=({"150"})]; log info "$scriptName: connection to $LocalIPCurrentScan lost"; [/ip dhcp-server enable $LocalIPCurrentScan;] # Активируем текущий DHCP сервер. log info "$scriptName: enable dhcp-server $LocalIPCurrentScan"; set $LocalFailoverLastTime "$[$cuDte]"; set $LocalFailoverTimes ($LocalFailoverTimes+1); lcd interface display $displayOnError; } } else={ # Если результат пинга не равен нулю. if ($LocalFailoverTimes<20) do={ # Если потерь соединений меньше 20. if ([/ip dhcp-server get $LocalIPCurrentScan disabled]=false) do={ # Если текущий DHCP сервер активен. # Звучим об отключении текущего встроенного DHCP сервера. [$beMel Frequency=({"1975.0"; "1760.0"; "1568.0"; "1318.5"; "1046.5"}) Delay=({"100"}) Length=({"100"})]; log info "$scriptName: connection to $LocalIPCurrentScan established"; [/ip dhcp-server disable $LocalIPCurrentScan;] # Отключаем текущий DHCP сервер. log info "$scriptName: disable dhcp-server $LocalIPCurrentScan"; set $LocalRestoreLastTime "$[$cuDte]"; set $LocalRestoreTimes ($LocalRestoreTimes+1); lcd interface display $displayOnRestore; } } else={ # Если потерь соединений больше 20. set $LocalDisabled "true"; # Отключаем локальные сервисы. # Шлём на почту файл (текущий лог) и сообщение о том что локальные сервисы отключены. /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Multiple errors! Local services disabled!\n" file=$filePath; set $conD -1; # Завершаем цикл. } } } else={ # Если произошли изменения в конфигурации VRRP интерфейсов. set $conD -1; # Завершаем цикл. } } while ($conD>-1); } while (true); } # В случае сбоя в работе основного цикла, шлём письмо о сбое прикрепляя файл (текущий лог). /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Error occurred! Unexpected end of cycle.\n" file=$filePath; }; |
Коротко о статусах
0 — Работа в штатном режиме.
1 — Утеряно соединение с интернет, переход в режим отключения интерфейсов.
2 — Интерфейсы отключены, режим ожидания возобновления соединенеия.
3 — Соединение с интернет восстановлено, переход в режим включения интерфейсов.
4 — В аплинк интерфейс не подключен кабель или проблемы с физическим соединением.
Напоминаю, что активным шлюзом является VRRP интерфейс, а это означает что он там, где есть интернет. Соответственно, если прописать статический маршрут для IP почтового сервера и DNS на IP VRRP интерфейса, то почта будет отправлена через активное соединение на соседнем маршрутизаторе.
Дополнительно хочу добавить, что для отправки почты в том виде, в котором это происходит в данном скрипте, необходимо внести соответствующие настройки в Tools -> Email.
Узнать подробно о работе VRRP и других протоколов, лучших практиках построения отказоустойчивых кластеров и систем высокой доступности, можно из русскоязычного онлайн-курса по 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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | beep frequency=1760 length=10ms; :local scriptName "Failover"; :if ([len [/system script job find where script=$scriptName]]>1) do={ log error "$scriptName: Single instance"; beep frequency=440 length=1000ms; } else={ delay 60s; :local globalIPArray {"8.8.8.8";"77.88.8.8";"193.58.251.251";"114.114.115.115"}; :local localIPArray {"10.0.0.4";"10.0.1.4";"10.0.2.4";"172.16.0.4";"192.168.0.4"}; :local restoreDelays {600;7200;21600;43200;86400}; :local filePath "disk1/var/log/full_log.0.txt"; :local email "mail@example.com"; :local displayOnRestore "uplink0"; :local displayOnError "bond1.0"; :local uplink "uplink0"; :local pingCount 2; :local conA 0; :local conB 0; :local conD 0; :local conC 0; :local once 0; :local pingResA 0; :local pingResB 0; :local mailSubject ([/system identity get name]."::".$scriptName); :global FailoverState 0; :global UplinkIPCurrentScan "waiting..."; :global LocalIPCurrentScan "waiting..."; :global RestoreDelay 0; :global RestoreDelayTime "0 s"; :global RestoreDelayTimeRemaining "0 s"; :global UplinkDrops 0; :global UplinkDisabled "false"; :global UplinkFailoverTimes 0; :global UplinkFailoverLastTime "-"; :global UplinkRestoreTimes 0; :global UplinkRestoreLastTime "-"; :global LocalDrops 0; :global LocalDisabled "false"; :global LocalFailoverTimes 0; :global LocalFailoverLastTime "-"; :global LocalRestoreTimes 0; :global LocalRestoreLastTime "-"; /system script run "Edelweiss"; :global mlFrm; :global cuDte; :global beMel; :global foDte; :global ifSta; :global ifDis; :global ifRun; :local brdgPt do={ :local brdgPtCount [/interface bridge port print count-only;]; do { set $brdgPtCount ($brdgPtCount-1); :local F [/interface bridge port print brief]; if ([/interface bridge port get $brdgPtCount edge] != $brdgDo) do={ execute "/interface bridge port set edge=$brdgEd numbers=$brdgPtCount"; } } while ($brdgPtCount>0); } lcd backlight state=on; [$brdgPt brdgDo="yes"]; [/interface enable $uplink;] lcd interface display $displayOnRestore; [$ifRun Name=$scriptName Type="vrrp" Disabled="no"]; [$beMel Frequency=({"1975.0"; "1760.0"; "1568.0"; "1318.5"; "1046.5"}) Delay=({"100"}) Length=({"100"})]; /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Started.\n" file=$filePath; if ($FailoverState=0) do={ [$beMel Frequency=({"1975"; "1975"}) Delay=({"1000"; "150"}) Length=({"100"})]; do { if ($conC<1) do={ if ([/interface get $uplink running]=false) do={ if ($once=0) do={ set $once 1; log info "$scriptName: Uplink IF DOWN"; set $UplinkFailoverLastTime "$[$cuDte]"; set $UplinkFailoverTimes ($UplinkFailoverTimes+1); log info "$scriptName: state changed $FailoverState->1"; set $FailoverState 1; [$beMel Frequency=({"1046.5"; "1318.5"; "1568.0"; "1760.0"; "1975.0"}) Delay=({"150"}) Length=({"150"})]; [$ifDis Name=$scriptName Type="l2tp-out" Disabled="yes"]; [$ifDis Name=$scriptName Type="pptp-out" Disabled="yes"]; [$ifRun Name=$scriptName Type="vrrp" Disabled="yes"]; [$brdgPt brdgDo="yes"]; log info "$scriptName: state changed $FailoverState->4"; set $FailoverState 4; lcd backlight state=off; /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Uplink IF DOWN.\n" file=$filePath; } } else={ if ($FailoverState=4) do={ if ($once=1) do={ set $once 0; log info "$scriptName: Uplink IF UP"; set $UplinkRestoreLastTime "$[$cuDte]"; set $UplinkRestoreTimes ($UplinkRestoreTimes+1); log info "$scriptName: state changed $FailoverState->3"; set $FailoverState 3; [$beMel Frequency=({"1975.0"; "1760.0"; "1568.0"; "1318.5"; "1046.5"}) Delay=({"150"}) Length=({"150"})]; [$ifRun Name=$scriptName Type="vrrp" Disabled="no"]; if ([$ifSta Type="vrrp"]=true) do={ [$brdgPt brdgEd="auto"]; [$ifDis Name=$scriptName Type="pptp-out" Disabled="no"]; [$ifDis Name=$scriptName Type="l2tp-out" Disabled="no"]; } log info "$scriptName: state changed $FailoverState->0"; set $FailoverState 0; lcd backlight state=on; /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Uplink IF UP.\n" file=$filePath; } } } if ($FailoverState<4) do={ set $conA 0; set $pingResA 0; set $conB ([len $globalIPArray]-1); do { set $conA ($conA+$pingCount); set $UplinkIPCurrentScan [($globalIPArray->$conB)]; set $pingResA ($pingResA+[ping $UplinkIPCurrentScan count=$pingCount]); set $conB ($conB-1); delay "$(([len $globalIPArray]*$pingCount)*100) ms"; } while ($conB>-1); set $UplinkDrops ($UplinkDrops+($conA-$pingResA)); } if ($pingResA=0) do={ if ($FailoverState=0) do={ log info "$scriptName: connection to the Internet lost"; set $UplinkFailoverLastTime "$[$cuDte]"; set $UplinkFailoverTimes ($UplinkFailoverTimes+1); log info "$scriptName: state changed $FailoverState->1"; set $FailoverState 1; [$beMel Frequency=({"1046.5"; "1318.5"; "1568.0"; "1760.0"; "1975.0"}) Delay=({"150"}) Length=({"150"})]; [$ifDis Name=$scriptName Type="l2tp-out" Disabled="yes"]; [$ifDis Name=$scriptName Type="pptp-out" Disabled="yes"]; [$ifRun Name=$scriptName Type="vrrp" Disabled="yes"]; [$brdgPt brdgDo="yes"]; log info "$scriptName: state changed $FailoverState->2"; set $FailoverState 2; lcd backlight state=off; /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Connection to the Internet lost."; } } else={ if ($FailoverState=2) do={ log info "$scriptName: connection to the Internet established"; set $UplinkRestoreLastTime "$[$cuDte]"; set $UplinkRestoreTimes ($UplinkRestoreTimes+1); log info "$scriptName: state changed $FailoverState->3"; set $FailoverState 3; [$beMel Frequency=({"1975.0"; "1760.0"; "1568.0"; "1318.5"; "1046.5"}) Delay=({"150"}) Length=({"150"})]; [$ifRun Name=$scriptName Type="vrrp" Disabled="no"]; if ([$ifSta Type="vrrp"]=true) do={ [$brdgPt brdgEd="auto"]; [$ifDis Name=$scriptName Type="pptp-out" Disabled="no"]; [$ifDis Name=$scriptName Type="l2tp-out" Disabled="no"]; } log info "$scriptName: state changed $FailoverState->0"; set $FailoverState 0; lcd backlight state=on; # /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Connection to the Internet established."; } } if ($FailoverState=2) do={ if ($UplinkFailoverTimes>1) do={ set $conC 1; set $rdLe [len $restoreDelays]; set $RestoreDelay ($RestoreDelay+1); if ($RestoreDelay>$rdLe) do={ set $FailoverState 4; set $RestoreDelayTime "infinit" set $UplinkDisabled "true"; [/interface disable $uplink;] /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Error occurred! Uplink interface $uplink disabled.\n" file=$filePath; } } } if ($FailoverState=0) do={ if ($RestoreDelay>0) do={ if ((([$foDte Date=[$cuDte]]->0)-([$foDte Date=$UplinkRestoreLastTime]->0))>[($restoreDelays->($rdLe-1))]) do={ set $RestoreDelay 0; set $RestoreDelayTime "0 s"; } } } if ($UplinkFailoverTimes>0) do={set $RestoreDelayTime "$[($restoreDelays->$RestoreDelay)] s";} } else={ if ($RestoreDelay>0) do={ if ((([$foDte Date=[$cuDte]]->0)-([$foDte Date=$UplinkFailoverLastTime]->0))>[($restoreDelays->($RestoreDelay-1))]) do={set $conC 0;} } else={set $conC 0;} } set $rdtrs ((([$foDte Date=$UplinkFailoverLastTime]->0)+[($restoreDelays->($RestoreDelay-1))])-([$foDte Date=[$cuDte]]->0)); if ($rdtrs>0) do={set $RestoreDelayTimeRemaining "$rdtrs s";} else={set $RestoreDelayTimeRemaining "0 s";} set $vicS [$ifSta Type="vrrp"]; if ([$ifSta Type="vrrp"]=true) do={ [$brdgPt brdgEd="auto"]; [$ifDis Name=$scriptName Type="pptp-out" Disabled="no"]; [$ifDis Name=$scriptName Type="l2tp-out" Disabled="no"]; } else={ [$brdgPt brdgEd="yes"]; [$ifDis Name=$scriptName Type="l2tp-out" Disabled="yes"]; [$ifDis Name=$scriptName Type="pptp-out" Disabled="yes"]; } set $conD ([len $localIPArray]-1); do { if ([$ifSta Type="vrrp"]=$vicS) do={ set $LocalIPCurrentScan [($localIPArray->$conD)]; set $pingResB [ping $LocalIPCurrentScan count=$pingCount]; set $LocalDrops ($LocalDrops+($pingCount-$pingResB)); set $conD ($conD-1); delay "$(([len $localIPArray]*$pingCount)*100) ms"; if ($pingResB=0) do={ if ([/ip dhcp-server get $LocalIPCurrentScan disabled]=true) do={ [$beMel Frequency=({"1046.5"; "1568.0"; "1046.5"; "1568.5"}) Delay=({"150"}) Length=({"150"})]; log info "$scriptName: connection to $LocalIPCurrentScan lost"; [/ip dhcp-server enable $LocalIPCurrentScan;] log info "$scriptName: enable dhcp-server $LocalIPCurrentScan"; set $LocalFailoverLastTime "$[$cuDte]"; set $LocalFailoverTimes ($LocalFailoverTimes+1); lcd interface display $displayOnError; } } else={ if ($LocalFailoverTimes<20) do={ if ([/ip dhcp-server get $LocalIPCurrentScan disabled]=false) do={ [$beMel Frequency=({"1975.0"; "1760.0"; "1568.0"; "1318.5"; "1046.5"}) Delay=({"100"}) Length=({"100"})]; log info "$scriptName: connection to $LocalIPCurrentScan established"; [/ip dhcp-server disable $LocalIPCurrentScan;] log info "$scriptName: disable dhcp-server $LocalIPCurrentScan"; set $LocalRestoreLastTime "$[$cuDte]"; set $LocalRestoreTimes ($LocalRestoreTimes+1); lcd interface display $displayOnRestore; } } else={ set $LocalDisabled "true"; /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Multiple errors! Local services disabled!\n" file=$filePath; set $conD -1; } } } else={ set $conD -1; } } while ($conD>-1); } while (true); } /tool e-mail send from=$mlFrm to=$email subject=$mailSubject body="$mailSubject:: $[$cuDte]:: Error occurred! Unexpected end of cycle.\n" file=$filePath; }; |
У меня только один вопрос. Вы от провайдера по 2 кабеля загоняете или кабель в свитч, там вланом до одного микротика и вланом до второго. Иначе я не вижу логики отказоустойчивости при одновременной работе и распределении траффика (load balancing)
Здравствуйте. Нет, два провайдера, по одному в каждый маршрутизатор. И распределения нагрузки здесь нет, только отказоустойчивость.
Добрый день!
А зачем для резервирования собирать кластер, вместо банальной маркировки пакетов с несколькими шлюзами и т.д. внутри одной коробки?
Добрый. Для того, чтобы когда одна «коробка» перестаёт работать, вторая брала на себя её задачи. Для «дома» оно конечно не нужно, можно и дистансами дефроутов отработать в «одной коробке», но здесь стояла совершенно иная задача.