mirror of
https://github.com/beeyev/Mikrotik-RouterOS-automatic-backup-and-update.git
synced 2025-07-15 08:24:37 +02:00
Update script version to 25.04.12; enhance backup and update functionality with improved logging and email notifications
This commit is contained in:
parent
feb4f7b950
commit
69d331c070
2 changed files with 0 additions and 1060 deletions
315
v2.rsc
315
v2.rsc
|
@ -1,315 +0,0 @@
|
||||||
# Script name: BackupAndUpdate
|
|
||||||
#
|
|
||||||
#----------SCRIPT INFORMATION---------------------------------------------------
|
|
||||||
#
|
|
||||||
# Script: Mikrotik RouterOS automatic backup & update
|
|
||||||
# Version: 24.06.04
|
|
||||||
# Created: 07/08/2018
|
|
||||||
# Updated: 04/06/2024
|
|
||||||
# Author: Alexander Tebiev
|
|
||||||
# Website: https://github.com/beeyev
|
|
||||||
# You can contact me by e-mail at tebiev@mail.com
|
|
||||||
#
|
|
||||||
# IMPORTANT!
|
|
||||||
# Minimum supported RouterOS version is v6.43.7
|
|
||||||
#
|
|
||||||
#----------MODIFY THIS SECTION AS NEEDED----------------------------------------
|
|
||||||
## Notification e-mail
|
|
||||||
## (Make sure you have configured Email settings in Tools -> Email)
|
|
||||||
:local emailAddress "zzt.tzz@gmail.com"
|
|
||||||
|
|
||||||
## Script mode, possible values: backup, osupdate, osnotify.
|
|
||||||
# backup - Only backup will be performed. (default value, if none provided)
|
|
||||||
#
|
|
||||||
# osupdate - The script will install a new RouterOS version if it is available.
|
|
||||||
# It will also create backups before and after update process (it does not matter what value `forceBackup` is set to)
|
|
||||||
# Email will be sent only if a new RouterOS version is available.
|
|
||||||
# Change parameter `forceBackup` if you need the script to create backups every time when it runs (even when no updates were found).
|
|
||||||
#
|
|
||||||
# osnotify - The script will send email notifications only (without backups) if a new RouterOS update is available.
|
|
||||||
# Change parameter `forceBackup` if you need the script to create backups every time when it runs.
|
|
||||||
:local scriptMode "osupdate"
|
|
||||||
|
|
||||||
## Additional parameter if you set `scriptMode` to `osupdate` or `osnotify`
|
|
||||||
# Set `true` if you want the script to perform backup every time its fired, whatever script mode is set.
|
|
||||||
:local forceBackup false
|
|
||||||
|
|
||||||
## Backup encryption password, no encryption if no password.
|
|
||||||
:local backupPassword ""
|
|
||||||
|
|
||||||
## If true, passwords will be included in exported config.
|
|
||||||
:local sensitiveDataInConfig true
|
|
||||||
|
|
||||||
## Update channel. Possible values: stable, long-term, testing, development
|
|
||||||
:local updateChannel "stable"
|
|
||||||
|
|
||||||
## Installs only patch versions of RouterOS updates.
|
|
||||||
## Works only if you set scriptMode to "osupdate"
|
|
||||||
## Means that new update will be installed only if MAJOR and MINOR version numbers remained the same as currently installed RouterOS.
|
|
||||||
## Example: v6.43.6 => major.minor.PATCH
|
|
||||||
## Script will send information if new version is greater than just patch.
|
|
||||||
:local installOnlyPatchUpdates false
|
|
||||||
|
|
||||||
## If true, device public IP address information will be included into the email message
|
|
||||||
:local detectPublicIpAddress true
|
|
||||||
|
|
||||||
## Allow anonymous statistics collection. (script mode, device model, OS version)
|
|
||||||
:local allowAnonymousStatisticsCollection true
|
|
||||||
|
|
||||||
##------------------------------------------------------------------------------------------##
|
|
||||||
# !!!! DO NOT CHANGE ANYTHING BELOW THIS LINE, IF YOU ARE NOT SURE WHAT YOU ARE DOING !!!! #
|
|
||||||
##------------------------------------------------------------------------------------------##
|
|
||||||
|
|
||||||
:local scriptVersion "24.06.04"
|
|
||||||
|
|
||||||
#Script messages prefix
|
|
||||||
:local SMP "Bkp&Upd:"
|
|
||||||
|
|
||||||
:log info "\n$SMP script \"Mikrotik RouterOS automatic backup & update\" v.$scriptVersion started."
|
|
||||||
:log info "$SMP Script Mode: `$scriptMode`, forceBackup: `$forceBackup`"
|
|
||||||
|
|
||||||
|
|
||||||
###########
|
|
||||||
|
|
||||||
# Get current system time and date
|
|
||||||
:local rawTime [/system clock get time]
|
|
||||||
:local rawDate [/system clock get date]
|
|
||||||
|
|
||||||
# Current time in specific format `hh-mm-ss`
|
|
||||||
:local currentTime ([:pick $rawTime 0 2] . "-" . [:pick $rawTime 3 5] . "-" . [:pick $rawTime 6 8])
|
|
||||||
|
|
||||||
# Current date `YYYY-MM-DD` or `YYYY-Mon-DD`, will be defined later in the script
|
|
||||||
:local currentDate "undefined"
|
|
||||||
|
|
||||||
# Check if the date is in the old format, it should not start with a number
|
|
||||||
:if ([:len [:tonum [:pick $rawDate 0 1]]] = 0) do={
|
|
||||||
# Convert old format `nov/11/2023` → `2023-nov-11`
|
|
||||||
:set currentDate ([:pick $rawDate 7 11] . "-" . [:pick $rawDate 0 3] . "-" . [:pick $rawDate 4 6])
|
|
||||||
} else={
|
|
||||||
# Use new format as is `YYYY-MM-DD`
|
|
||||||
:set currentDate $rawDate
|
|
||||||
}
|
|
||||||
|
|
||||||
# Combine date and time → `YYYY-MM-DD-hh-mm-ss` or `YYYY-Mon-11-hh-mm-ss`
|
|
||||||
:local currentDateTime ($currentDate . "-" . $currentTime)
|
|
||||||
|
|
||||||
:local isSoftBased false
|
|
||||||
:local boardName [/system resource get board-name]
|
|
||||||
|
|
||||||
# Check if board name contains "CHR" or starts with "x86"
|
|
||||||
:if ([:len [:find $boardName "CHR"]] > 0 or [:pick $boardName 0 3] = "x86") do={
|
|
||||||
:set isSoftBased true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
############### vvvvvvvvv GLOBALS vvvvvvvvv ###############
|
|
||||||
# Function converts standard mikrotik build versions intto the number.
|
|
||||||
# Possible arguments: paramOsVer
|
|
||||||
# Example:
|
|
||||||
# :put [$buGlobalFuncGetOsVerNum paramOsVer="6.49.2"] # Result will be: 64902
|
|
||||||
# :put [$buGlobalFuncGetOsVerNum paramOsVer=[/system routerboard get current-firmware]]
|
|
||||||
:global buGlobalFuncGetOsVerNum do={
|
|
||||||
#Script messages prefix
|
|
||||||
:local SMP "Bkp&Upd:"
|
|
||||||
:log info ("$SMP global function buGlobalFuncGetOsVerNum started. Input: `$paramOsVer`")
|
|
||||||
|
|
||||||
:local osVer $paramOsVer
|
|
||||||
:local allowedChars "0123456789."
|
|
||||||
:local i 0
|
|
||||||
:local c ""
|
|
||||||
|
|
||||||
if ([:len $osVer] < 1 or [:len $osVer] > 10) do={
|
|
||||||
:local errMesg "$SMP getOsVerNum: invalid version string length, given version: `$osVer`"
|
|
||||||
:log error $errMesg
|
|
||||||
:error $errMesg
|
|
||||||
}
|
|
||||||
|
|
||||||
# validate that each character is a digit or a dot
|
|
||||||
:for i from=0 to=([:len $osVer] - 1) do={
|
|
||||||
:set c [:pick $osVer $i]
|
|
||||||
:if ([:len [:find $allowedChars $c]] = 0) do={
|
|
||||||
:local errMesg "$SMP invalid version string, invalid character: `$c`, given version: `$osVer`"
|
|
||||||
:log error $errMesg
|
|
||||||
:error $errMesg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:local major ""
|
|
||||||
:local minor "00"
|
|
||||||
:local patch "00"
|
|
||||||
|
|
||||||
:if ([:find $osVer "."] >= 0) do={
|
|
||||||
:set major [:pick $osVer 0 [:find $osVer "."]]
|
|
||||||
:local rest [:pick $osVer ([:find $osVer "."] + 1) [:len $osVer]]
|
|
||||||
|
|
||||||
:if ([:find $rest "."] >= 0) do={
|
|
||||||
:set minor [:pick $rest 0 [:find $rest "."]]
|
|
||||||
:set patch [:pick $rest ([:find $rest "."] + 1) [:len $rest]]
|
|
||||||
} else={
|
|
||||||
:set minor $rest
|
|
||||||
}
|
|
||||||
} else={:set major $osVer}
|
|
||||||
|
|
||||||
:if ([:len $minor] = 1) do={:set minor ("0" . $minor)}
|
|
||||||
|
|
||||||
:if ([:len $patch] = 1) do={:set patch ("0" . $patch)}
|
|
||||||
|
|
||||||
:local result ($major . $minor . $patch)
|
|
||||||
|
|
||||||
:log info ("$SMP global function buGlobalFuncGetOsVerNum finished. Result: `$result`")
|
|
||||||
|
|
||||||
:return $result
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function creates backups (system and config) and returns array of names of created files.
|
|
||||||
# Possible arguments:
|
|
||||||
# `backupName` | string | backup file name, without extension!
|
|
||||||
# `backupPassword` | string |
|
|
||||||
# `sensitiveDataInConfig` | boolean |
|
|
||||||
# Example:
|
|
||||||
# :put [$buGlobalFuncCreateBackups backupName="daily-backup"]
|
|
||||||
:global buGlobalFuncCreateBackups do={
|
|
||||||
#Script messages prefix
|
|
||||||
:local SMP "Bkp&Upd:"
|
|
||||||
:log info ("$SMP global function `buGlobalFuncCreateBackups` started, input: `$backupName`")
|
|
||||||
|
|
||||||
# validate required parameter: backupName
|
|
||||||
:if ([:typeof $backupName] != "str" or [:len $backupName] = 0) do={
|
|
||||||
:local errMesg "$SMP parameter 'backupName' is required and must be a non-empty string"
|
|
||||||
:log error $errMesg
|
|
||||||
:error $errMesg
|
|
||||||
}
|
|
||||||
|
|
||||||
:local backupFileSys "$backupName.backup"
|
|
||||||
:local backupFileConfig "$backupName.rsc"
|
|
||||||
:local backupNames {$backupFileSys;$backupFileConfig}
|
|
||||||
|
|
||||||
## Perform system backup
|
|
||||||
:if ([:len $backupPassword] = 0) do={
|
|
||||||
:log info ("$SMP starting backup without password, backup name: `$backupName`")
|
|
||||||
/system backup save dont-encrypt=yes name=$backupName
|
|
||||||
} else={
|
|
||||||
:log info ("$SMP starting backup with password, backup name: `$backupName`")
|
|
||||||
/system backup save password=$backupPassword name=$backupName
|
|
||||||
}
|
|
||||||
|
|
||||||
:log info ("$SMP system backup created: `$backupFileSys`")
|
|
||||||
|
|
||||||
## Export config file
|
|
||||||
:if ($sensitiveDataInConfig = true) do={
|
|
||||||
:log info ("$SMP starting export config with sensitive data, backup name: `$backupName`")
|
|
||||||
# Since RouterOS v7 it needs to be explicitly set that we want to export sensitive data
|
|
||||||
:if ([:pick [/system package update get installed-version] 0 1] < 7) do={
|
|
||||||
:execute "/export compact terse file=$backupName"
|
|
||||||
} else={
|
|
||||||
:execute "/export compact show-sensitive terse file=$backupName"
|
|
||||||
}
|
|
||||||
} else={
|
|
||||||
:log info ("$SMP starting export config without sensitive data, backup name: `$backupName`")
|
|
||||||
/export compact hide-sensitive terse file=$backupName
|
|
||||||
}
|
|
||||||
|
|
||||||
:log info ("$SMP Config export complete: `$backupFileConfig`")
|
|
||||||
:log info ("$SMP Waiting a little to ensure file is written")
|
|
||||||
|
|
||||||
:delay 20s
|
|
||||||
|
|
||||||
:log info ("$SMP global function `buGlobalFuncCreateBackups` finished. Created backups: `$backupNames`")
|
|
||||||
|
|
||||||
:return $backupNames
|
|
||||||
}
|
|
||||||
|
|
||||||
# Global variable to track current update step
|
|
||||||
:global buGlobalVarUpdateStep
|
|
||||||
############### ^^^^^^^^^ GLOBALS ^^^^^^^^^ ###############
|
|
||||||
|
|
||||||
:local deviceOsVerInst [/system package update get installed-version];
|
|
||||||
:local deviceOsVerInstNum [$buGlobalFuncGetOsVerNum paramOsVer=$deviceOsVerInst];
|
|
||||||
:local deviceIdentityName [/system identity get name];
|
|
||||||
:local deviceIdentityNameShort [:pick $deviceIdentityName 0 18]
|
|
||||||
:local deviceUpdateChannel [/system package update get channel];
|
|
||||||
|
|
||||||
:local deviceRbModel "CloudHostedRouter";
|
|
||||||
:local deviceRbSerialNumber "--";
|
|
||||||
:local deviceRbCurrentFw "--";
|
|
||||||
:local deviceRbUpgradeFw "--";
|
|
||||||
|
|
||||||
:if ($isSoftBased = false) do={
|
|
||||||
:set deviceRbModel [/system routerboard get model];
|
|
||||||
:set deviceRbSerialNumber [/system routerboard get serial-number];
|
|
||||||
:set deviceRbCurrentFw [/system routerboard get current-firmware];
|
|
||||||
:set deviceRbUpgradeFw [/system routerboard get upgrade-firmware];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
:local mailBodyDeviceInfo ""
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\n\nDevice information:")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nIdentity: $deviceIdentityName")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nModel: $deviceRbModel")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nSerial number: $deviceRbSerialNumber")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nCurrent RouterOS: $deviceOsVerInst ($[/system package update get channel]) $[/system resource get build-time]")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nCurrent routerboard FW: $deviceRbCurrentFw")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nDevice uptime: $[/system resource get uptime]")
|
|
||||||
|
|
||||||
# default and fallback public IP detection services
|
|
||||||
:local ipAddressDetectServiceDefault "https://ipv4.mikrotik.ovh/"
|
|
||||||
:local ipAddressDetectServiceFallback "https://api.ipify.org/"
|
|
||||||
|
|
||||||
# default values, to be set later
|
|
||||||
:local publicIpAddress "not-detected"
|
|
||||||
:local telemetryDataQuery ""
|
|
||||||
|
|
||||||
:local updateStep $buGlobalVarUpdateStep
|
|
||||||
:do {/system script environment remove buGlobalVarUpdateStep} on-error={}
|
|
||||||
:if ([:len $updateStep] = 0) do={
|
|
||||||
:set updateStep 1
|
|
||||||
}
|
|
||||||
|
|
||||||
## IP address detection & anonymous statistics collection
|
|
||||||
:if ($updateStep = 1 or $updateStep = 3) do={
|
|
||||||
:if ($updateStep = 3) do={
|
|
||||||
:log info ("$SMP Waiting for one minute before continuing to the final step.")
|
|
||||||
:delay 1m
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ($detectPublicIpAddress = true or $allowAnonymousStatisticsCollection = true) do={
|
|
||||||
:if ($allowAnonymousStatisticsCollection = true) do={
|
|
||||||
:set telemetryDataQuery ("\?mode=" . $scriptMode . "&osver=" . $deviceOsVerInst . "&model=" . $deviceRbModel. "&step=" . $updateStep)
|
|
||||||
}
|
|
||||||
|
|
||||||
:do {:set publicIpAddress ([/tool fetch http-method="get" url=($ipAddressDetectServiceDefault . $telemetryDataQuery) output=user as-value]->"data")} on-error={
|
|
||||||
:if ($detectPublicIpAddress = true) do={
|
|
||||||
:log warning "$SMP Could not detect public IP address using default detection service: `$ipAddressDetectServiceDefault`"
|
|
||||||
:log warning "$SMP Trying to detect public ip using fallback detection service: `$ipAddressDetectServiceFallback`"
|
|
||||||
|
|
||||||
:do {
|
|
||||||
:set publicIpAddress ([/tool fetch http-method="get" url=$ipAddressDetectServiceFallback output=user as-value]->"data")
|
|
||||||
} on-error={
|
|
||||||
:log warning "$SMP Could not detect public IP address using fallback detection service: `$ipAddressDetectServiceFallback`"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:set publicIpAddress ([:pick $publicIpAddress 0 15])
|
|
||||||
|
|
||||||
:if ($detectPublicIpAddress = true) do={
|
|
||||||
# truncate IP to max 15 characters (basic safety)
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nPublic IP address: $publicIpAddress")
|
|
||||||
:log info "$SMP Public IP address detected: `$publicIpAddress`"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## STEP ONE: Creating backups, checking for new RouterOs version and sending email with backups,
|
|
||||||
## Steps 2 and 3 are fired only if script is set to automatically update device and if a new RouterOs version is available.
|
|
||||||
:if ($updateStep = 1) do={
|
|
||||||
:log info ("$SMP Performing the first step.");
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Remove functions from global environment to keep it fresh and clean.
|
|
||||||
:do {/system script environment remove buGlobalFuncGetOsVerNum} on-error={}
|
|
||||||
:do {/system script environment remove buGlobalFuncCreateBackups} on-error={}
|
|
745
v3.rsc
745
v3.rsc
|
@ -1,745 +0,0 @@
|
||||||
# Script name: BackupAndUpdate
|
|
||||||
#
|
|
||||||
#----------SCRIPT INFORMATION---------------------------------------------------
|
|
||||||
#
|
|
||||||
# Script: Mikrotik RouterOS automatic backup & update
|
|
||||||
# Version: 25.04.12
|
|
||||||
# Created: 07/08/2018
|
|
||||||
# Updated: 12/04/2025
|
|
||||||
# Author: Alexander Tebiev
|
|
||||||
# Website: https://github.com/beeyev
|
|
||||||
# You can contact me by e-mail at tebiev@mail.com
|
|
||||||
#
|
|
||||||
# IMPORTANT!
|
|
||||||
# Minimum supported RouterOS version is v6.43.7
|
|
||||||
#
|
|
||||||
#----------MODIFY THIS SECTION AS NEEDED----------------------------------------
|
|
||||||
## Notification e-mail
|
|
||||||
## (Make sure you have configured Email settings in Tools -> Email)
|
|
||||||
:local emailAddress "zzt.tzz@gmail.com"
|
|
||||||
|
|
||||||
## Script mode, possible values: backup, osupdate, osnotify.
|
|
||||||
# backup - Only backup will be performed. (default value, if none provided)
|
|
||||||
#
|
|
||||||
# osupdate - The script will install a new RouterOS version if it is available.
|
|
||||||
# It will also create backups before and after update process (it does not matter what value `forceBackup` is set to)
|
|
||||||
# Email will be sent only if a new RouterOS version is available.
|
|
||||||
# Change parameter `forceBackup` if you need the script to create backups every time when it runs (even when no updates were found).
|
|
||||||
#
|
|
||||||
# osnotify - The script will send email notifications only (without backups) if a new RouterOS update is available.
|
|
||||||
# Change parameter `forceBackup` if you need the script to create backups every time when it runs.
|
|
||||||
:local scriptMode "osnotify"
|
|
||||||
|
|
||||||
## Additional parameter if you set `scriptMode` to `osupdate` or `osnotify`
|
|
||||||
# Set `true` if you want the script to perform backup every time its fired, whatever script mode is set.
|
|
||||||
:local forceBackup false
|
|
||||||
|
|
||||||
## Backup encryption password, no encryption if no password.
|
|
||||||
:local backupPassword ""
|
|
||||||
|
|
||||||
## If true, passwords will be included in exported config.
|
|
||||||
:local sensitiveDataInConfig true
|
|
||||||
|
|
||||||
## Update channel. Possible values: stable, long-term, testing, development
|
|
||||||
:local updateChannel "stable"
|
|
||||||
|
|
||||||
## Install only patch updates (requires scriptMode = "osupdate")
|
|
||||||
## Works only for `stable` and `long-term` channels.
|
|
||||||
## Update will run only if MAJOR and MINOR versions match the current one
|
|
||||||
## Example: current = v6.43.2 → v6.43.6 = allowed, v6.44.1 = skipped
|
|
||||||
## Script will send information if new version is greater than just patch.
|
|
||||||
:local installOnlyPatchUpdates false
|
|
||||||
|
|
||||||
## If true, device public IP address information will be included into the email message
|
|
||||||
:local detectPublicIpAddress true
|
|
||||||
|
|
||||||
## Allow anonymous statistics collection. (script mode and generic non-sensitive device info)
|
|
||||||
:local allowAnonymousStatisticsCollection true
|
|
||||||
|
|
||||||
##------------------------------------------------------------------------------------------##
|
|
||||||
# !!!! DO NOT CHANGE ANYTHING BELOW THIS LINE, IF YOU ARE NOT SURE WHAT YOU ARE DOING !!!! #
|
|
||||||
##------------------------------------------------------------------------------------------##
|
|
||||||
|
|
||||||
:local scriptVersion "25.04.12"
|
|
||||||
|
|
||||||
# default and fallback public IP detection services
|
|
||||||
:local ipAddressDetectServiceDefault "https://ipv4.mikrotik.ovh/"
|
|
||||||
:local ipAddressDetectServiceFallback "https://api.ipify.org/"
|
|
||||||
|
|
||||||
#Script messages prefix
|
|
||||||
:local SMP "Bkp&Upd:"
|
|
||||||
|
|
||||||
:local exitErrorMessage "$SMP script stopped due to an error. Please check logs for more details."
|
|
||||||
:log info "\n\n$SMP Script \"Mikrotik RouterOS automatic backup & update\" v.$scriptVersion started."
|
|
||||||
:log info "$SMP Script Mode: `$scriptMode`, Update channel: `$updateChannel`, Force backup: `$forceBackup`, Install only patch updates: `$installOnlyPatchUpdates`"
|
|
||||||
|
|
||||||
############### vvvvvvvvv FUNCTIONS vvvvvvvvv ###############
|
|
||||||
|
|
||||||
# Function: FuncGetRunningOsVersion
|
|
||||||
# ----------------------------
|
|
||||||
# Returns currently running RouterOS version
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# :put [$FuncGetRunningOsVersion] # Output: 6.48.1
|
|
||||||
:local FuncGetRunningOsVersion do={
|
|
||||||
:local runningOsAndChannel [/system resource get version]
|
|
||||||
|
|
||||||
:local spacePos [:find $runningOsAndChannel " "]
|
|
||||||
:if ([:len $spacePos] = 0) do={
|
|
||||||
:log error "Bkp&Upd: Could not extract installed OS version string: `$runningOsAndChannel`. Script stopped."
|
|
||||||
:error "Bkp&Upd: script stopped due to an error. Please check logs for more details."
|
|
||||||
}
|
|
||||||
|
|
||||||
:local versionOnly [:pick $runningOsAndChannel 0 $spacePos]
|
|
||||||
|
|
||||||
:return $versionOnly
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function: FuncGetRunningOsChannel
|
|
||||||
# ----------------------------
|
|
||||||
# Returns currently running RouterOS channel (stable, long-term, testing, development)
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# :put [$FuncGetRunningOsChannel] # Output: stable
|
|
||||||
:local FuncGetRunningOsChannel do={
|
|
||||||
:local runningOsAndChannel [/system resource get version]
|
|
||||||
:local errorMessage "Bkp&Upd: Could not extract installed OS channel from version string: `$runningOsAndChannel`. Script stopped."
|
|
||||||
:local exitErrorMessage "Bkp&Upd: script stopped due to an error. Please check logs for more details."
|
|
||||||
|
|
||||||
:local open [:find $runningOsAndChannel "("]
|
|
||||||
:if ([:len $open] = 0) do={
|
|
||||||
:log error ($errorMessage . " (1)")
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
:local rest [:pick $runningOsAndChannel ($open+1) [:len $runningOsAndChannel]]
|
|
||||||
|
|
||||||
:local close [:find $rest ")"]
|
|
||||||
:if ([:len $close] = 0) do={
|
|
||||||
:log error ($errorMessage . " (2)")
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
:local channel [:pick $rest 0 $close]
|
|
||||||
:if ([:len $channel] = 0) do={
|
|
||||||
:log error ($errorMessage . " (3)")
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
:return $channel
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function: FuncIsPatchUpdateOnly
|
|
||||||
# ----------------------------
|
|
||||||
# Determines if two RouterOS version strings differ only by the patch version.
|
|
||||||
#
|
|
||||||
# Parameters:
|
|
||||||
# `version1` | string | The first version string (e.g., "6.2.1").
|
|
||||||
# `version2` | string | The second version string (e.g., "6.2.4").
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# boolean | true if only the patch versions differ; false otherwise.
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# :put [$FuncIsPatchUpdateOnly "6.2.1" "6.2.4"] # Output: true
|
|
||||||
# :put [$FuncIsPatchUpdateOnly "6.2.1" "6.3.1"] # Output: false
|
|
||||||
:local FuncIsPatchUpdateOnly do={
|
|
||||||
:local ver1 $1
|
|
||||||
:local ver2 $2
|
|
||||||
|
|
||||||
# Internal function to extract the major and minor components from a version string.
|
|
||||||
:local extractMajorMinor do={
|
|
||||||
:local ver $1
|
|
||||||
:local dot1 [:find $ver "."]
|
|
||||||
:if ($dot1 = -1) do={ :return $ver }
|
|
||||||
|
|
||||||
:local major [:pick $ver 0 $dot1]
|
|
||||||
:local rest [:pick $ver ($dot1 + 1) [:len $ver]]
|
|
||||||
:local dot2 [:find $rest "."]
|
|
||||||
:local minor $rest
|
|
||||||
:if ($dot2 >= 0) do={ :set minor [:pick $rest 0 $dot2] }
|
|
||||||
|
|
||||||
:return ($major . "." . $minor)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Compare the major and minor components of both version strings.
|
|
||||||
:if ([$extractMajorMinor $ver1] = [$extractMajorMinor $ver2]) do={
|
|
||||||
:return true
|
|
||||||
}
|
|
||||||
:return false
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function creates backups (system and config) and returns array of names of created files.
|
|
||||||
# Possible arguments:
|
|
||||||
# `backupName` | string | backup file name, without extension!
|
|
||||||
# `backupPassword` | string |
|
|
||||||
# `sensitiveDataInConfig` | boolean |
|
|
||||||
# Example:
|
|
||||||
# :put [$FuncCreateBackups "daily-backup"]
|
|
||||||
:local FuncCreateBackups do={
|
|
||||||
:local backupName $1
|
|
||||||
:local backupPassword $2
|
|
||||||
:local sensitiveDataInConfig $3
|
|
||||||
|
|
||||||
#Script messages prefix
|
|
||||||
:local SMP "Bkp&Upd:"
|
|
||||||
:local exitErrorMessage "$SMP script stopped due to an error. Please check logs for more details."
|
|
||||||
:log info ("$SMP global function `FuncCreateBackups` started, input: `$backupName`")
|
|
||||||
|
|
||||||
# validate required parameter: backupName
|
|
||||||
:if ([:typeof $backupName] != "str" or [:len $backupName] = 0) do={
|
|
||||||
:log error "$SMP parameter 'backupName' is required and must be a non-empty string"
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
:local backupFileSys "$backupName.backup"
|
|
||||||
:local backupFileConfig "$backupName.rsc"
|
|
||||||
:local backupNames {$backupFileSys;$backupFileConfig}
|
|
||||||
|
|
||||||
## Perform system backup
|
|
||||||
:if ([:len $backupPassword] = 0) do={
|
|
||||||
:log info ("$SMP starting backup without password, backup name: `$backupName`")
|
|
||||||
/system backup save dont-encrypt=yes name=$backupName
|
|
||||||
} else={
|
|
||||||
:log info ("$SMP starting backup with password, backup name: `$backupName`")
|
|
||||||
/system backup save password=$backupPassword name=$backupName
|
|
||||||
}
|
|
||||||
|
|
||||||
:log info ("$SMP system backup created: `$backupFileSys`")
|
|
||||||
|
|
||||||
## Export config file
|
|
||||||
:if ($sensitiveDataInConfig = true) do={
|
|
||||||
:log info ("$SMP starting export config with sensitive data, backup name: `$backupName`")
|
|
||||||
# Since RouterOS v7 it needs to be explicitly set that we want to export sensitive data
|
|
||||||
:if ([:pick [/system resource get version] 0 1] < 7) do={
|
|
||||||
:execute "/export compact terse file=$backupName"
|
|
||||||
} else={
|
|
||||||
:execute "/export compact show-sensitive terse file=$backupName"
|
|
||||||
}
|
|
||||||
} else={
|
|
||||||
:log info ("$SMP starting export config without sensitive data, backup name: `$backupName`")
|
|
||||||
/export compact hide-sensitive terse file=$backupName
|
|
||||||
}
|
|
||||||
|
|
||||||
:log info ("$SMP Config export complete: `$backupFileConfig`")
|
|
||||||
:log info ("$SMP Waiting a little to ensure backup files are written")
|
|
||||||
|
|
||||||
:delay 20s
|
|
||||||
|
|
||||||
:if ([:len [/file find name=$backupFileSys]] > 0) do={
|
|
||||||
:log info ("$SMP system backup file successfully saved to the file system: `$backupFileSys`")
|
|
||||||
} else={
|
|
||||||
:log error ("$SMP system backup was not created, file does not exist: `$backupFileSys`")
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([:len [/file find name=$backupFileConfig]] > 0) do={
|
|
||||||
:log info ("$SMP config backup file successfully saved to the file system: `$backupFileConfig`")
|
|
||||||
} else={
|
|
||||||
:log error ("$SMP config backup was not created, file does not exist: `$backupFileConfig`")
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
:log info ("$SMP global function `FuncCreateBackups` finished. Created backups, system: `$backupFileSys`, config: `$backupFileConfig`")
|
|
||||||
|
|
||||||
:return $backupNames
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function: FuncSendEmailSafe
|
|
||||||
# ---------------------------
|
|
||||||
# Sends an email and checks if it was sent successfully.
|
|
||||||
#
|
|
||||||
# Parameters:
|
|
||||||
# $1 - to (email address)
|
|
||||||
# $2 - subject
|
|
||||||
# $3 - body
|
|
||||||
# $4 - file attachments (optional; pass "" if not needed)
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# :do {
|
|
||||||
# $FuncSendEmailSafe "admin@domain.com" "Backup Done" "Backup complete." "backup1.backup"
|
|
||||||
# } on-error={
|
|
||||||
# :log error "Email failed to send"
|
|
||||||
# }
|
|
||||||
:local FuncSendEmailSafe do={
|
|
||||||
|
|
||||||
:local emailTo $1
|
|
||||||
:local emailSubject $2
|
|
||||||
:local emailBody $3
|
|
||||||
:local emailAttachments $4
|
|
||||||
|
|
||||||
:local SMP "Bkp&Upd:"
|
|
||||||
:local exitErrorMessage "$SMP script stopped due to an error. Please check logs for more details."
|
|
||||||
|
|
||||||
:log info "$SMP Attempting to send email to `$emailTo`"
|
|
||||||
|
|
||||||
# SAFETY: wait for any previously queued email to finish
|
|
||||||
:local waitTimeoutPre 60
|
|
||||||
:local waitCounterPre 0
|
|
||||||
:while (([/tool e-mail get last-status] = "resolving-dns" or [/tool e-mail get last-status] = "in-progress")) do={
|
|
||||||
:if ($waitCounterPre >= $waitTimeoutPre) do={
|
|
||||||
:log error "$SMP Email send aborted: previous send did not complete after $waitTimeoutPre seconds"
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
:log info "$SMP Waiting for previous email to finish (status: $[/tool e-mail get last-status])..."
|
|
||||||
:delay 1s
|
|
||||||
:set waitCounterPre ($waitCounterPre + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Send the email
|
|
||||||
:do {
|
|
||||||
/tool e-mail send to=$emailTo subject=$emailSubject body=$emailBody file=$emailAttachments
|
|
||||||
} on-error={
|
|
||||||
:log error "$SMP Email send command failed to execute. Check logs and verify email settings."
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
# Wait for send status to change from "in-progress" / "resolving-dns"
|
|
||||||
:local waitTimeout 60
|
|
||||||
:local waitCounter 0
|
|
||||||
:local emailStatus ""
|
|
||||||
:log info "$SMP Waiting for email to be sent, timeout in `$waitTimeout` seconds..."
|
|
||||||
:while ($waitCounter < $waitTimeout) do={
|
|
||||||
:set emailStatus [/tool e-mail get last-status]
|
|
||||||
:if ($emailStatus != "in-progress" and $emailStatus != "resolving-dns") do={
|
|
||||||
:log info "$SMP Email send status received: $emailStatus"
|
|
||||||
|
|
||||||
# exit loop
|
|
||||||
:set waitCounter $waitTimeout
|
|
||||||
} else={
|
|
||||||
:delay 1s
|
|
||||||
:set waitCounter ($waitCounter + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Final decision based on last status
|
|
||||||
:if ($emailStatus = "succeeded") do={
|
|
||||||
:log info "$SMP Email successfully sent to `$emailTo`"
|
|
||||||
} else={
|
|
||||||
:log error "$SMP Email failed to send. Status: `$emailStatus`. Check logs for more details and verify email settings."
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Global variable to track current update step
|
|
||||||
# They need to be initialized here first to be available in the script
|
|
||||||
:global buGlobalVarTargetOsVersion
|
|
||||||
|
|
||||||
:global buGlobalVarScriptStep
|
|
||||||
:local scriptStep $buGlobalVarScriptStep
|
|
||||||
:do {/system script environment remove buGlobalVarScriptStep} on-error={}
|
|
||||||
:if ([:len $scriptStep] = 0) do={
|
|
||||||
:set scriptStep 1
|
|
||||||
}
|
|
||||||
############### ^^^^^^^^^ FUNCTIONS ^^^^^^^^^ ###############
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Initial validation
|
|
||||||
#
|
|
||||||
|
|
||||||
## Check email settings
|
|
||||||
:if ([:len $emailAddress] < 3) do={
|
|
||||||
:log error ("$SMP Script parameter `\$emailAddress` is not set, or contains invalid value. Script stopped.")
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
# Values will be defined later in the script
|
|
||||||
:local emailServer ""
|
|
||||||
:local emailFromAddress [/tool e-mail get from]
|
|
||||||
|
|
||||||
:log info "$SMP Validating email settings..."
|
|
||||||
:do {
|
|
||||||
:set emailServer [/tool e-mail get server]
|
|
||||||
} on-error={
|
|
||||||
# This is a workaround for the RouterOS v7.12 and older versions
|
|
||||||
:set emailServer [/tool e-mail get address]
|
|
||||||
}
|
|
||||||
:if ($emailServer = "0.0.0.0") do={
|
|
||||||
:log error ("$SMP Email server address is not correct: `$emailServer`, please check `Tools -> Email`. Script stopped.");
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
:if ([:len $emailFromAddress] < 3) do={
|
|
||||||
:log error ("$SMP Email configuration FROM address is not correct: `$emailFromAddress`, please check `Tools -> Email`. Script stopped.");
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
# Script mode validation
|
|
||||||
:if ($scriptMode != "backup" and $scriptMode != "osupdate" and $scriptMode != "osnotify") do={
|
|
||||||
:log error ("$SMP Script parameter `\$scriptMode` is not set, or contains invalid value: `$scriptMode`. Script stopped.")
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
# Update channel validation
|
|
||||||
:if ($updateChannel != "stable" and $updateChannel != "long-term" and $updateChannel != "testing" and $updateChannel != "development") do={
|
|
||||||
:log error ("$SMP Script parameter `\$updateChannel` is not set, or contains invalid value: `$updateChannel`. Script stopped.")
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if the script is set to install only patch updates and if the update channel is valid
|
|
||||||
:if ($scriptMode = "osupdate" and $installOnlyPatchUpdates = true) do={
|
|
||||||
:if ($updateChannel != "stable" and $updateChannel != "long-term") do={
|
|
||||||
:log error ("$SMP Script is set to install only patch updates, but the update channel is not valid: `$updateChannel`. Only `stable` and `long-term` channels supported. Script stopped.")
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
:local susRunningOsChannel [$FuncGetRunningOsChannel]
|
|
||||||
|
|
||||||
:if ($susRunningOsChannel != "stable" and $susRunningOsChannel != "long-term") do={
|
|
||||||
:log error ("$SMP Script is set to install only patch updates, but the installed RouterOS version is not from `stable` or `long-term` channel: `$susRunningOsChannel`. Script stopped.")
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Get current system date and time
|
|
||||||
#
|
|
||||||
:local rawTime [/system clock get time]
|
|
||||||
:local rawDate [/system clock get date]
|
|
||||||
|
|
||||||
## Current time in specific format `hh-mm-ss`
|
|
||||||
:local currentTime ([:pick $rawTime 0 2] . "-" . [:pick $rawTime 3 5] . "-" . [:pick $rawTime 6 8])
|
|
||||||
|
|
||||||
## Current date `YYYY-MM-DD` or `YYYY-Mon-DD`, will be defined later in the script
|
|
||||||
:local currentDate "undefined"
|
|
||||||
|
|
||||||
## Check if the date is in the old format, it should not start with a number
|
|
||||||
:if ([:len [:tonum [:pick $rawDate 0 1]]] = 0) do={
|
|
||||||
# Convert old format `nov/11/2023` → `2023-nov-11`
|
|
||||||
:set currentDate ([:pick $rawDate 7 11] . "-" . [:pick $rawDate 0 3] . "-" . [:pick $rawDate 4 6])
|
|
||||||
} else={
|
|
||||||
# Use new format as is `YYYY-MM-DD`
|
|
||||||
:set currentDate $rawDate
|
|
||||||
}
|
|
||||||
|
|
||||||
## Combine date and time → `YYYY-MM-DD-hh-mm-ss` or `YYYY-Mon-DD-hh-mm-ss`
|
|
||||||
:local currentDateTime ($currentDate . "-" . $currentTime)
|
|
||||||
|
|
||||||
#####
|
|
||||||
|
|
||||||
:local deviceBoardName [/system resource get board-name]
|
|
||||||
|
|
||||||
## Check if it's a cloud hosted router or a hardware based device
|
|
||||||
:local isCloudHostedRouter false;
|
|
||||||
:if ([:pick $deviceBoardName 0 3] = "CHR" or [:pick $deviceBoardName 0 3] = "x86") do={
|
|
||||||
:set isCloudHostedRouter true;
|
|
||||||
};
|
|
||||||
|
|
||||||
:local deviceIdentityName [/system identity get name];
|
|
||||||
:local deviceIdentityNameShort [:pick $deviceIdentityName 0 18]
|
|
||||||
|
|
||||||
:local deviceRbModel "CloudHostedRouter";
|
|
||||||
:local deviceRbSerialNumber "--"
|
|
||||||
:local deviceRbCurrentFw "--"
|
|
||||||
:local deviceRbUpgradeFw "--"
|
|
||||||
|
|
||||||
:if ($isCloudHostedRouter = false) do={
|
|
||||||
:set deviceRbModel [/system routerboard get model]
|
|
||||||
:set deviceRbSerialNumber [/system routerboard get serial-number]
|
|
||||||
:set deviceRbCurrentFw [/system routerboard get current-firmware]
|
|
||||||
:set deviceRbUpgradeFw [/system routerboard get upgrade-firmware]
|
|
||||||
};
|
|
||||||
|
|
||||||
:local runningOsChannel [$FuncGetRunningOsChannel]
|
|
||||||
:local runningOsVersion [$FuncGetRunningOsVersion]
|
|
||||||
:local deviceOsVerAndChannelRunning [/system resource get version]
|
|
||||||
|
|
||||||
:local backupNameTemplate "backup_v$runningOsVersion_$runningOsChannel_$currentDateTime"
|
|
||||||
:local backupNameBeforeUpdate "backup_before_update_$backupNameTemplate"
|
|
||||||
:local backupNameAfterUpdate "backup_after_update_$backupNameTemplate"
|
|
||||||
|
|
||||||
## Email body template
|
|
||||||
|
|
||||||
:local mailSubjectPrefix "$SMP Device - `$deviceIdentityNameShort`"
|
|
||||||
|
|
||||||
:local mailBodyCopyright "Mikrotik RouterOS automatic backup & update (ver. $scriptVersion) \nhttps://github.com/beeyev/Mikrotik-RouterOS-automatic-backup-and-update"
|
|
||||||
:local changelogUrl "Check RouterOS changelog: https://mikrotik.com/download/changelogs/"
|
|
||||||
|
|
||||||
:local mailBodyDeviceInfo ""
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "Device information:")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\n---------------------")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nName: $deviceIdentityName")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nModel: $deviceRbModel")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nBoard: $deviceBoardName")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nSerial number: $deviceRbSerialNumber")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nRouterOS version: v$deviceOsVerAndChannelRunning")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nBuild time: $[/system resource get build-time]")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nRouterboard FW: $deviceRbCurrentFw")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nDevice date-time: $rawDate $rawTime ($[/system clock get time-zone-name ])")
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nUptime: $[/system resource get uptime]")
|
|
||||||
# IP address will be appended later if needed
|
|
||||||
|
|
||||||
:local mailAttachments [:toarray ""]
|
|
||||||
|
|
||||||
## IP address detection & anonymous statistics collection
|
|
||||||
:if ($scriptStep = 1 or $scriptStep = 3) do={
|
|
||||||
:if ($scriptStep = 3) do={
|
|
||||||
:log info ("$SMP Waiting for one minute before continuing to the final step.")
|
|
||||||
:delay 1m
|
|
||||||
}
|
|
||||||
# default values, to be set later
|
|
||||||
:local publicIpAddress "not-detected"
|
|
||||||
:local telemetryDataQuery ""
|
|
||||||
|
|
||||||
:if ($detectPublicIpAddress = true or $allowAnonymousStatisticsCollection = true) do={
|
|
||||||
:if ($allowAnonymousStatisticsCollection = true) do={
|
|
||||||
:set telemetryDataQuery ("\?mode=" . $scriptMode . "&scriptver=" . $scriptVersion . "&updatechannel=" . $updateChannel . "&osver=" . $runningOsVersion . "&step=" . $scriptStep . "&forcebackup=" . $forceBackup . "&onlypatchupdates=" . $installOnlyPatchUpdates . "&model=" . $deviceRbModel . "&deviceboard=" . $deviceBoardName)
|
|
||||||
}
|
|
||||||
|
|
||||||
:do {:set publicIpAddress ([/tool fetch http-method="get" url=($ipAddressDetectServiceDefault . $telemetryDataQuery) output=user as-value]->"data")} on-error={
|
|
||||||
:if ($detectPublicIpAddress = true) do={
|
|
||||||
:log warning "$SMP Could not detect public IP address using default detection service: `$ipAddressDetectServiceDefault`"
|
|
||||||
:log warning "$SMP Trying to detect public IP using fallback detection service: `$ipAddressDetectServiceFallback`"
|
|
||||||
:do {
|
|
||||||
:set publicIpAddress ([/tool fetch http-method="get" url=$ipAddressDetectServiceFallback output=user as-value]->"data")
|
|
||||||
} on-error={
|
|
||||||
:log warning "$SMP Could not detect public IP address using fallback detection service: `$ipAddressDetectServiceFallback`"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:set publicIpAddress ([:pick $publicIpAddress 0 15])
|
|
||||||
|
|
||||||
:if ($detectPublicIpAddress = true) do={
|
|
||||||
# truncate IP to max 15 characters (basic safety)
|
|
||||||
:set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\nPublic IP address: $publicIpAddress")
|
|
||||||
:log info "$SMP Public IP address detected: `$publicIpAddress`"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
## STEP ONE: Creating backups, checking for new RouterOs version and sending email with backups,
|
|
||||||
## Steps 2 and 3 are fired only if script is set to automatically update device and if a new RouterOs version is available.
|
|
||||||
:if ($scriptStep = 1) do={
|
|
||||||
:local routerOsVersionAvailable "0.0.0"
|
|
||||||
:local isNewOsUpdateAvailable false
|
|
||||||
:local isLatestOsAlreadyInstalled true
|
|
||||||
:local isOsNeedsToBeUpdated false
|
|
||||||
:local isUpdateCheckSucceeded false
|
|
||||||
:local isEmailNeedsToBeSent false
|
|
||||||
|
|
||||||
:local mailSubjectPartAction ""
|
|
||||||
:local mailPtBodyAction ""
|
|
||||||
|
|
||||||
:local mailPtSubjectBackup ""
|
|
||||||
:local mailPtBodyBackup ""
|
|
||||||
|
|
||||||
# Checking for new RouterOS version
|
|
||||||
:if ($scriptMode = "osupdate" or $scriptMode = "osnotify") do={
|
|
||||||
log info ("$SMP Setting update channel to `$updateChannel`")
|
|
||||||
/system package update set channel=$updateChannel
|
|
||||||
log info ("$SMP Checking for new RouterOS version. Current installed version is: `$runningOsVersion`")
|
|
||||||
/system package update check-for-updates
|
|
||||||
|
|
||||||
# Wait for 5 seconds to allow the system to check for updates
|
|
||||||
:delay 5s;
|
|
||||||
|
|
||||||
:local packageUpdateStatus "undefined"
|
|
||||||
|
|
||||||
:set routerOsVersionAvailable [/system package update get latest-version]
|
|
||||||
:set packageUpdateStatus [/system package update get status]
|
|
||||||
|
|
||||||
:if ($packageUpdateStatus = "New version is available") do={
|
|
||||||
:log info ("$SMP New RouterOS version is available: `$routerOsVersionAvailable`")
|
|
||||||
:set isNewOsUpdateAvailable true
|
|
||||||
:set isLatestOsAlreadyInstalled false
|
|
||||||
:set isUpdateCheckSucceeded true
|
|
||||||
:set isEmailNeedsToBeSent true
|
|
||||||
|
|
||||||
:set mailSubjectPartAction "New RouterOS available"
|
|
||||||
:set mailPtBodyAction "New RouterOS version is available, current version: v$runningOsVersion, new version: v$routerOsVersionAvailable. \n$changelogUrl"
|
|
||||||
} else={
|
|
||||||
:if ($packageUpdateStatus = "System is already up to date") do={
|
|
||||||
:log info ("$SMP No new RouterOS version is available, the latest version is already installed: `v$runningOsVersion`")
|
|
||||||
:set isUpdateCheckSucceeded true
|
|
||||||
|
|
||||||
:set mailSubjectPartAction "No os update available"
|
|
||||||
:set mailPtBodyAction "No new RouterOS version is available, the latest version is already installed: `v$runningOsVersion`"
|
|
||||||
} else={
|
|
||||||
:log error ("$SMP Failed to check for new RouterOS version. Package check status: `$packageUpdateStatus`")
|
|
||||||
:set isEmailNeedsToBeSent true
|
|
||||||
|
|
||||||
:set mailSubjectPartAction "Error unable to check new os version"
|
|
||||||
:set mailPtBodyAction "An error occurred while checking for a new RouterOS version.\nStatus returned: `$packageUpdateStatus`\n\nPlease review the logs on the device for more details and verify internet connectivity."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Checking if the script needs to install new RouterOS version
|
|
||||||
:if ($scriptMode = "osupdate" and $isNewOsUpdateAvailable = true) do={
|
|
||||||
:if ($installOnlyPatchUpdates = true) do={
|
|
||||||
:if ([$FuncIsPatchUpdateOnly $runningOsVersion $routerOsVersionAvailable] = true) do={
|
|
||||||
:log info "$SMP New RouterOS version is available, and it is a patch update. Current version: v$runningOsVersion, new version: v$routerOsVersionAvailable"
|
|
||||||
:set isOsNeedsToBeUpdated true
|
|
||||||
} else={
|
|
||||||
:log info "$SMP The script will not install this update, because it is not a patch update. Current version: v$runningOsVersion, new version: v$routerOsVersionAvailable"
|
|
||||||
:set mailPtBodyAction ($mailPtBodyAction . "\nThis update will not be installed, because the script is set to install only patch updates.")
|
|
||||||
}
|
|
||||||
} else={
|
|
||||||
:set isOsNeedsToBeUpdated true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Checking If the script needs to create a backup
|
|
||||||
:if ($forceBackup = true or $scriptMode = "backup" or $isOsNeedsToBeUpdated = true) do={
|
|
||||||
:log info ("$SMP Starting backup process.")
|
|
||||||
|
|
||||||
:set isEmailNeedsToBeSent true
|
|
||||||
|
|
||||||
:local backupName $backupNameTemplate
|
|
||||||
|
|
||||||
# This means it's the first step where we create a backup before the update process
|
|
||||||
:if ($isOsNeedsToBeUpdated = true) do={
|
|
||||||
:set backupName $backupNameBeforeUpdate
|
|
||||||
|
|
||||||
#Email body if the purpose of the script is to update the device
|
|
||||||
:set mailSubjectPartAction "Update preparation"
|
|
||||||
:set mailPtBodyAction ($mailPtBodyAction . "\nThe update process for device '$deviceIdentityName' is scheduled to upgrade RouterOS from version v.$runningOsVersion to version v.$routerOsVersionAvailable (Update channel: $updateChannel)")
|
|
||||||
:set mailPtBodyAction ($mailPtBodyAction . "\nPlease note: The update will proceed only after a successful backup.")
|
|
||||||
:set mailPtBodyAction ($mailPtBodyAction . "\nA final report with detailed information will be sent once the update process is completed.")
|
|
||||||
:set mailPtBodyAction ($mailPtBodyAction . "\nIf you do not receive a second email within the next 10 minutes, there may be an issue. Please check your device logs for further information.")
|
|
||||||
}
|
|
||||||
|
|
||||||
:do {
|
|
||||||
:set mailAttachments [$FuncCreateBackups $backupName $backupPassword $sensitiveDataInConfig];
|
|
||||||
|
|
||||||
:set mailPtSubjectBackup "Backup created"
|
|
||||||
:set mailPtBodyBackup "System backups have been successfully created and attached to this email."
|
|
||||||
} on-error={
|
|
||||||
#failed to create backup
|
|
||||||
:set isOsNeedsToBeUpdated false
|
|
||||||
|
|
||||||
:set mailPtSubjectBackup "Backup failed"
|
|
||||||
:set mailPtBodyBackup "The script failed to create backups. Please check device logs for more details."
|
|
||||||
|
|
||||||
:log warning "$SMP Failed to create backup files. Update process will be cancelled, if the script is set to update the device."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ($isEmailNeedsToBeSent = true) do={
|
|
||||||
:log info "$SMP Preparing to send email..."
|
|
||||||
|
|
||||||
:local mailStep1Subject $mailSubjectPrefix
|
|
||||||
:local mailStep1Body ""
|
|
||||||
|
|
||||||
# Assemble email subject
|
|
||||||
:if ($mailSubjectPartAction != "") do={:set mailStep1Subject ($mailStep1Subject . " - " . $mailSubjectPartAction)}
|
|
||||||
:if ($mailPtSubjectBackup != "") do={:set mailStep1Subject ($mailStep1Subject . " - " . $mailPtSubjectBackup)}
|
|
||||||
# Assemble email body
|
|
||||||
:if ($mailPtBodyAction != "") do={:set mailStep1Body ($mailStep1Body . $mailPtBodyAction . "\n\n")}
|
|
||||||
:if ($mailPtBodyBackup != "") do={:set mailStep1Body ($mailStep1Body . $mailPtBodyBackup . "\n\n")}
|
|
||||||
|
|
||||||
:set mailStep1Body ($mailStep1Body . $mailBodyDeviceInfo . "\n\n" . $mailBodyCopyright)
|
|
||||||
|
|
||||||
# Send email with backup files attached
|
|
||||||
:do {$FuncSendEmailSafe $emailAddress $mailStep1Subject $mailStep1Body $mailAttachments} on-error={
|
|
||||||
:set isOsNeedsToBeUpdated false
|
|
||||||
:log error "$SMP The script will not proceed with the update process, because the email was not sent."
|
|
||||||
#:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([:len $mailAttachments] > 0) do={
|
|
||||||
:log info "$SMP Cleaning up backup files from the file system..."
|
|
||||||
/file remove $mailAttachments;
|
|
||||||
:delay 2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ($isOsNeedsToBeUpdated = true) do={
|
|
||||||
:log info "$SMP everything is ready to install new RouterOS, going to start the update process and reboot the device."
|
|
||||||
:do {
|
|
||||||
:local nextStep 2
|
|
||||||
:if ($isCloudHostedRouter = true) do={
|
|
||||||
:log info "$SMP The device is a cloud hosted router, the second step updating the Routerboard firmware will be skipped."
|
|
||||||
:set nextStep 3
|
|
||||||
}
|
|
||||||
|
|
||||||
:local scheduledCommand (":delay 5s; /system scheduler remove BKPUPD-NEXT-BOOT-TASK; :global buGlobalVarScriptStep $nextStep; :global buGlobalVarTargetOsVersion \"$routerOsVersionAvailable\"; :delay 10s; /system script run BackupAndUpdate;")
|
|
||||||
/system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=$scheduledCommand start-time=startup interval=0
|
|
||||||
|
|
||||||
/system package update install
|
|
||||||
} on-error={
|
|
||||||
# Failed to install new RouterOS version, remove the scheduled task
|
|
||||||
:do {/system scheduler remove BKPUPD-NEXT-BOOT-TASK} on-error={}
|
|
||||||
|
|
||||||
:log error "$SMP Failed to install new RouterOS version. Please check device logs for more details."
|
|
||||||
|
|
||||||
:local mailUpdateErrorSubject ($mailSubjectPrefix . " - Update failed")
|
|
||||||
:local mailUpdateErrorBody "The script was unable to install new RouterOS version. Please check device logs for more details."
|
|
||||||
|
|
||||||
# Send email with error message
|
|
||||||
$FuncSendEmailSafe $emailAddress $mailUpdateErrorSubject $mailUpdateErrorBody ""
|
|
||||||
|
|
||||||
:error $exitErrorMessage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
## STEP TWO: (after first reboot) routerboard firmware upgrade
|
|
||||||
## Steps 2 and 3 are fired only if script is set to automatically update device and if new RouterOs is available.
|
|
||||||
:if ($scriptStep = 2) do={
|
|
||||||
:log info "$SMP The script is in the second step, updating Routerboard firmware."
|
|
||||||
|
|
||||||
:log info "$SMP Upgrading routerboard firmware from v.$deviceRbCurrentFw to v.$deviceRbUpgradeFw"
|
|
||||||
|
|
||||||
/system routerboard upgrade
|
|
||||||
## Wait until the upgrade is completed
|
|
||||||
:delay 2s
|
|
||||||
:log info "$SMP routerboard upgrade process was completed, going to reboot in a moment!";
|
|
||||||
|
|
||||||
## Set scheduled task to send final report on the next boot, task will be deleted when it is done. (That is why you should keep original script name)
|
|
||||||
/system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=":delay 5s; /system scheduler remove BKPUPD-NEXT-BOOT-TASK; :global buGlobalVarScriptStep 3; :global buGlobalVarTargetOsVersion \"$buGlobalVarTargetOsVersion\"; :delay 10s; /system script run BackupAndUpdate;" start-time=startup interval=0
|
|
||||||
|
|
||||||
## Reboot system to boot with new firmware
|
|
||||||
/system reboot;
|
|
||||||
}
|
|
||||||
|
|
||||||
## STEP THREE: Last step (after second reboot) sending final report
|
|
||||||
## Steps 2 and 3 are fired only if script is set to automatically update device and if new RouterOs is available.
|
|
||||||
## This step is executed after some delay
|
|
||||||
:if ($scriptStep = 3) do={
|
|
||||||
:log info ("$SMP The script is in the third step, sending final report.")
|
|
||||||
|
|
||||||
:local targetOsVersion $buGlobalVarTargetOsVersion
|
|
||||||
:do {/system script environment remove buGlobalVarTargetOsVersion} on-error={}
|
|
||||||
:if ([:len $targetOsVersion] = 0) do={
|
|
||||||
:log warning "$SMP Something is wrong, the script was unable to get the target updated OS version from the global variable."
|
|
||||||
}
|
|
||||||
|
|
||||||
:local mailStep3Subject $mailSubjectPrefix
|
|
||||||
:local mailStep3Body ""
|
|
||||||
|
|
||||||
:if ($targetOsVersion = $runningOsVersion) do={
|
|
||||||
:log info "$SMP The script has successfully verified that the new RouterOS version was installed, target version: `$targetOsVersion`, current version: `$runningOsVersion`"
|
|
||||||
|
|
||||||
:set mailStep3Subject ($mailStep3Subject . " - Update completed - Backup created")
|
|
||||||
:set mailStep3Body ($mailStep3Body . "RouterOS and routerboard upgrade process was completed")
|
|
||||||
:set mailStep3Body ($mailStep3Body . "\nNew RouterOS version: v.$targetOsVersion, routerboard firmware: v.$deviceRbCurrentFw")
|
|
||||||
:set mailStep3Body ($mailStep3Body . "\n$changelogUrl")
|
|
||||||
:set mailStep3Body ($mailStep3Body . "\nBackups of the upgraded system are in the attachment of this email.")
|
|
||||||
:set mailStep3Body ($mailStep3Body . "\n\n" . $mailBodyDeviceInfo . "\n\n" . $mailBodyCopyright)
|
|
||||||
|
|
||||||
:set mailAttachments [$FuncCreateBackups $backupNameAfterUpdate $backupPassword $sensitiveDataInConfig];
|
|
||||||
} else={
|
|
||||||
:log error "$SMP The script was unable to verify that the new RouterOS version was installed, target version: `$targetOsVersion`, current version: `$runningOsVersion`"
|
|
||||||
:set mailStep3Subject ($mailStep3Subject . " - Update failed")
|
|
||||||
|
|
||||||
:set mailStep3Body ($mailStep3Body . "The script was unable to verify that the new RouterOS version was installed, target version: `$targetOsVersion`, current version: `$runningOsVersion`")
|
|
||||||
:set mailStep3Body ($mailStep3Body . "\nPlease check device logs for more details.")
|
|
||||||
:set mailStep3Body ($mailStep3Body . "\n\n" . $mailBodyDeviceInfo . "\n\n" . $mailBodyCopyright)
|
|
||||||
}
|
|
||||||
|
|
||||||
$FuncSendEmailSafe $emailAddress $mailStep3Subject $mailStep3Body $mailAttachments
|
|
||||||
|
|
||||||
:if ([:len $mailAttachments] > 0) do={
|
|
||||||
:log info "$SMP Cleaning up backup files from the file system..."
|
|
||||||
/file remove $mailAttachments;
|
|
||||||
:delay 2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
:log info "$SMP Final report email sent successfully, and the script has finished."
|
|
||||||
}
|
|
||||||
|
|
||||||
:log info "$SMP the script has finished, script step: `$scriptStep` \n\n"
|
|
Loading…
Add table
Add a link
Reference in a new issue