diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index b77fe0e..a888ef0 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,2 @@ # These are supported funding model platforms -ko_fi: beeyev -liberapay: beeyev -buy_me_a_coffee: beeyev +custom: https://buymeacoffee.com/beeyev diff --git a/BackupAndUpdate.rsc b/BackupAndUpdate.rsc index b66b094..b6ed63a 100644 --- a/BackupAndUpdate.rsc +++ b/BackupAndUpdate.rsc @@ -3,9 +3,9 @@ #----------SCRIPT INFORMATION--------------------------------------------------- # # Script: Mikrotik RouterOS automatic backup & update -# Version: 25.04.12 +# Version: 24.06.04 # Created: 07/08/2018 -# Updated: 12/04/2025 +# Updated: 04/06/2024 # Author: Alexander Tebiev # Website: https://github.com/beeyev # You can contact me by e-mail at tebiev@mail.com @@ -15,7 +15,7 @@ # #----------MODIFY THIS SECTION AS NEEDED---------------------------------------- ## Notification e-mail -## (Make sure you have configured Email settings in Tools -> Email) +## (Make sure you have configurated Email settings in Tools -> Email) :local emailAddress "yourmail@example.com"; ## Script mode, possible values: backup, osupdate, osnotify. @@ -28,718 +28,472 @@ # # 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" +: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 +# Set `true` if you want the script to perform backup every time it's 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 +:local sensitiveDataInConfig true; ## Update channel. Possible values: stable, long-term, testing, development -:local updateChannel "stable" +: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 +## 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 +:local installOnlyPatchUpdates false; ## If true, device public IP address information will be included into the email message -:local detectPublicIpAddress true +:local detectPublicIpAddress true; -## Allow anonymous statistics collection. (script mode and generic non-sensitive device info) -:local allowAnonymousStatisticsCollection 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 "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`" +:log info "\r\n$SMP script \"Mikrotik RouterOS automatic backup & update\" started."; +:log info "$SMP Script Mode: $scriptMode, forceBackup: $forceBackup"; -############### 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 +# Check email settings +:if ([:len $emailAddress] = 0) do={ + :log error ("$SMP \$emailAddress variable is empty. Script stopped."); + :error "$SMP bye!"; +} +:local emailServer "" +:do { + :set emailServer [/tool e-mail get server]; +} on-error={ + # Old of getting email server before the RouterOS v7.12 + :log info "$SMP Checking email server using old command `/tool e-mail get address`"; + :set emailServer [/tool e-mail get address]; +} +:if ($emailServer = "0.0.0.0") do={ + :log error ("$SMP Email server address is not correct, please check Tools -> Email. Script stopped."); + :error "$SMP bye!"; +} +:if ([:len [/tool e-mail get from]] = 0 or [/tool e-mail get from] = "<>") do={ + :log error ("$SMP Email configuration FROM address is not correct, please check Tools -> Email. Script stopped."); + :error "$SMP bye!"; } -# Function: FuncGetRunningOsChannel -# ---------------------------- -# Returns currently running RouterOS channel (stable, long-term, testing, development) -# + +#Check if proper identity name is set +if ([:len [/system identity get name]] = 0 or [/system identity get name] = "MikroTik") do={ + :log warning ("$SMP Please set identity name of your device (System -> Identity), keep it short and informative."); +}; + +############### vvvvvvvvv GLOBALS vvvvvvvvv ############### +# Function converts standard mikrotik build versions to the number. +# Possible arguments: paramOsVer # 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." +# :put [$buGlobalFuncGetOsVerNum paramOsVer=[/system routerboard get current-RouterOS]]; +# Result will be: 64301, because current RouterOS version is: 6.43.1 +:global buGlobalFuncGetOsVerNum do={ + :local osVer $paramOsVer; + :local osVerNum; + :local osVerMicroPart; + :local zro 0; + :local tmp; - :local open [:find $runningOsAndChannel "("] - :if ([:len $open] = 0) do={ - :log error ($errorMessage . " (1)") - :error $exitErrorMessage + # Replace word `beta` with dot + :local isBetaPos [:tonum [:find $osVer "beta" 0]]; + :if ($isBetaPos > 1) do={ + :set osVer ([:pick $osVer 0 $isBetaPos] . "." . [:pick $osVer ($isBetaPos + 4) [:len $osVer]]); + } + # Replace word `rc` with dot + :local isRcPos [:tonum [:find $osVer "rc" 0]]; + :if ($isRcPos > 1) do={ + :set osVer ([:pick $osVer 0 $isRcPos] . "." . [:pick $osVer ($isRcPos + 2) [:len $osVer]]); } - :local rest [:pick $runningOsAndChannel ($open+1) [:len $runningOsAndChannel]] + :local dotPos1 [:find $osVer "." 0]; - :local close [:find $rest ")"] - :if ([:len $close] = 0) do={ - :log error ($errorMessage . " (2)") - :error $exitErrorMessage + :if ($dotPos1 > 0) do={ + + # AA + :set osVerNum [:pick $osVer 0 $dotPos1]; + + :local dotPos2 [:find $osVer "." $dotPos1]; + #Taking minor version, everything after first dot + :if ([:len $dotPos2] = 0) do={:set tmp [:pick $osVer ($dotPos1+1) [:len $osVer]];} + #Taking minor version, everything between first and second dots + :if ($dotPos2 > 0) do={:set tmp [:pick $osVer ($dotPos1+1) $dotPos2];} + + # AA 0B + :if ([:len $tmp] = 1) do={:set osVerNum "$osVerNum$zro$tmp";} + # AA BB + :if ([:len $tmp] = 2) do={:set osVerNum "$osVerNum$tmp";} + + :if ($dotPos2 > 0) do={ + :set tmp [:pick $osVer ($dotPos2+1) [:len $osVer]]; + # AA BB 0C + :if ([:len $tmp] = 1) do={:set osVerNum "$osVerNum$zro$tmp";} + # AA BB CC + :if ([:len $tmp] = 2) do={:set osVerNum "$osVerNum$tmp";} + } else={ + # AA BB 00 + :set osVerNum "$osVerNum$zro$zro"; + } + } else={ + # AA 00 00 + :set osVerNum "$osVer$zro$zro$zro$zro"; } - :local channel [:pick $rest 0 $close] - :if ([:len $channel] = 0) do={ - :log error ($errorMessage . " (3)") - :error $exitErrorMessage - } - - :return $channel + :return $osVerNum; } -# 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. +# Function creates backups (system and config) and returns array with names # 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 +# :put [$buGlobalFuncCreateBackups name="daily-backup"]; +:global buGlobalFuncCreateBackups do={ + :log info ("$SMP Global function \"buGlobalFuncCreateBackups\" was fired."); - #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`") + :local backupFileSys "$backupName.backup"; + :local backupFileConfig "$backupName.rsc"; + :local backupNames {$backupFileSys;$backupFileConfig}; - # 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 + ## Make 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 + /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 + /system backup save password=$backupPassword name=$backupName; } + :log info ("$SMP System backup created. $backupFileSys"); - :log info ("$SMP system backup created: `$backupFileSys`") - - ## Export config file + ## 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" + :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" + :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 + /export compact hide-sensitive terse file=$backupName; } + :log info ("$SMP Config file was exported. $backupFileConfig, the script execution will be paused for a moment."); - :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 + #Delay after creating backups + :delay 20s; + :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={ +:global buGlobalVarUpdateStep; +############### ^^^^^^^^^ GLOBALS ^^^^^^^^^ ############### - :local emailTo $1 - :local emailSubject $2 - :local emailBody $3 - :local emailAttachments $4 +:local scriptVersion "24.06.04"; - :local SMP "Bkp&Upd:" - :local exitErrorMessage "$SMP script stopped due to an error. Please check logs for more details." +# Current time `hh-mm-ss` +:local currentTime ([:pick [/system clock get time] 0 2] . "-" . [:pick [/system clock get time] 3 5] . "-" . [:pick [/system clock get time] 6 8]); - :log info "$SMP Attempting to send email to `$emailTo`" +:local currentDateTime ("-" . $currentTime); - # 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]) +# Detect old date format, Example: `nov/11/2023` +:if ([:len [:tonum [:pick [/system clock get date] 0 1]]] = 0) do={ + :set currentDateTime ([:pick [/system clock get date] 7 11] . [:pick [/system clock get date] 0 3] . [:pick [/system clock get date] 4 6] . "-" . $currentTime); } 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; + # New date format, Example: `2023-11-11` + :set currentDateTime ([/system clock get date] . "-" . $currentTime); }; +:local isSoftBased false; +:if ([:pick [/system resource get board-name] 0 3] = "CHR" or [:pick [/system resource get board-name] 0 3] = "x86") do={ + :set isSoftBased true; +}; + +:local deviceOsVerInst [/system package update get installed-version]; +:local deviceOsVerInstNum [$buGlobalFuncGetOsVerNum paramOsVer=$deviceOsVerInst]; +:local deviceOsVerAvail ""; +:local deviceOsVerAvailNum 0; :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 "--" +: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] +: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]; }; -:local runningOsChannel [$FuncGetRunningOsChannel] -:local runningOsVersion [$FuncGetRunningOsVersion] -:local deviceOsVerAndChannelRunning [/system resource get version] +:local isOsUpdateAvailable false; +:local isOsNeedsToBeUpdated false; -:local backupNameTemplate "backup_v$runningOsVersion_$runningOsChannel_$currentDateTime" -:local backupNameBeforeUpdate "backup_before_update_$backupNameTemplate" -:local backupNameAfterUpdate "backup_after_update_$backupNameTemplate" +:local isSendEmailRequired true; -## Email body template +:local mailSubject "$SMP Device - $deviceIdentityNameShort."; +:local mailBody ""; -:local mailSubjectPrefix "$SMP Device - `$deviceIdentityNameShort`" +:local mailBodyDeviceInfo "\r\n\r\nDevice information: \r\nIdentity: $deviceIdentityName \r\nModel: $deviceRbModel \r\nSerial number: $deviceRbSerialNumber \r\nCurrent RouterOS: $deviceOsVerInst ($[/system package update get channel]) $[/system resource get build-time] \r\nCurrent routerboard FW: $deviceRbCurrentFw \r\nDevice uptime: $[/system resource get uptime]"; +:local mailBodyCopyright "\r\n\r\nMikrotik RouterOS automatic backup & update (ver. $scriptVersion) \r\nhttps://github.com/beeyev/Mikrotik-RouterOS-automatic-backup-and-update"; +:local changelogUrl ("Check RouterOS changelog: https://mikrotik.com/download/changelogs/" . $updateChannel . "-release-tree"); -: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 backupName "v$deviceOsVerInst_$deviceUpdateChannel_$currentDateTime"; +:local backupNameBeforeUpd "backup_before_update_$backupName"; +:local backupNameAfterUpd "backup_after_update_$backupName"; -: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 backupNameFinal $backupName; +:local mailAttachments [:toarray ""]; -:local mailAttachments [:toarray ""] + +:local ipAddressDetectServiceDefault "https://ipv4.mikrotik.ovh/" +:local ipAddressDetectServiceFallback "https://api.ipify.org/" +: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 ($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 +: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; } - # 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) + :set telemetryDataQuery ("\?mode=" . $scriptMode . "&osver=" . $deviceOsVerInst . "&model=" . $deviceRbModel); } - :do {:set publicIpAddress ([/tool fetch http-method="get" url=($ipAddressDetectServiceDefault . $telemetryDataQuery) output=user as-value]->"data")} on-error={ + :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`" + :log warning "$SMP Could not detect public IP address using default detection service." + :log warning "$SMP Trying to detect public ip using fallback detection service." + + :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." } } } - :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`" + # Always truncate the string for safety measures + :set publicIpAddress ([:pick $publicIpAddress 0 15]) + :set mailBodyDeviceInfo ($mailBodyDeviceInfo . "\r\nPublic IP address: " . $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 "" +:if ($updateStep = 1) do={ + :log info ("$SMP Performing the first step."); # 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 + if ($scriptMode = "osupdate" or $scriptMode = "osnotify") do={ + log info ("$SMP Checking for new RouterOS version. Current version is: $deviceOsVerInst"); + /system package update set channel=$updateChannel; + /system package update check-for-updates; :delay 5s; + :set deviceOsVerAvail [/system package update get latest-version]; - :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" + # If there is a problem getting information about available RouterOS versions from server + :if ([:len $deviceOsVerAvail] = 0) do={ + :log warning ("$SMP There is a problem getting information about new RouterOS from server."); + :set mailSubject ($mailSubject . " Error: No data about new RouterOS!") + :set mailBody ($mailBody . "Error occured! \r\nMikrotik couldn't get any information about new RouterOS from server! \r\nWatch additional information in device logs.") } 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 + #Get numeric version of OS + :set deviceOsVerAvailNum [$buGlobalFuncGetOsVerNum paramOsVer=$deviceOsVerAvail]; - :set mailSubjectPartAction "No os update available" - :set mailPtBodyAction "No new RouterOS version is available, the latest version is already installed: `v$runningOsVersion`" + # Checking if OS on server is greater than installed one. + :if ($deviceOsVerAvailNum > $deviceOsVerInstNum) do={ + :set isOsUpdateAvailable true; + :log info ("$SMP New RouterOS is available! $deviceOsVerAvail"); } 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." + :set isSendEmailRequired false; + :log info ("$SMP System is already up to date."); + :set mailSubject ($mailSubject . " No new OS updates."); + :set mailBody ($mailBody . "Your system is up to date."); } - } + }; + } else={ + :set scriptMode "backup"; + }; + + if ($forceBackup = true) do={ + # In this case the script will always send an email, because it has to create backups + :set isSendEmailRequired true; } - # 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.") + # If a new OS version is available to install + if ($isOsUpdateAvailable = true and $isSendEmailRequired = true) do={ + # If we only need to notify about a new available version + if ($scriptMode = "osnotify") do={ + :set mailSubject ($mailSubject . " New RouterOS is available! v.$deviceOsVerAvail.") + :set mailBody ($mailBody . "New RouterOS version is available to install: v.$deviceOsVerAvail ($updateChannel) \r\n$changelogUrl") } - :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 + # If we need to initiate RouterOS update process + if ($scriptMode = "osupdate") do={ + :set isOsNeedsToBeUpdated true; + # If we need to install only patch updates + :if ($installOnlyPatchUpdates = true) do={ + #Check if Major and Minor builds are the same. + :if ([:pick $deviceOsVerInstNum 0 ([:len $deviceOsVerInstNum]-2)] = [:pick $deviceOsVerAvailNum 0 ([:len $deviceOsVerAvailNum]-2)]) do={ + :log info ("$SMP New patch version of RouterOS firmware is available."); + } else={ + :log info ("$SMP New major or minor version of RouterOS firmware is available. You need to update it manually."); + :set mailSubject ($mailSubject . " New RouterOS: v.$deviceOsVerAvail needs to be installed manually."); + :set mailBody ($mailBody . "New major or minor RouterOS version is available to install: v.$deviceOsVerAvail ($updateChannel). \r\nYou chose to automatically install only patch updates, so this major update you need to install manually. \r\n$changelogUrl"); + :set isOsNeedsToBeUpdated false; + } } - :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 + #Check again, because this variable could be changed during checking for installing only patch updats + if ($isOsNeedsToBeUpdated = true) do={ + :log info ("$SMP New RouterOS is going to be installed! v.$deviceOsVerInst -> v.$deviceOsVerAvail"); + :set mailSubject ($mailSubject . " New RouterOS is going to be installed! v.$deviceOsVerInst -> v.$deviceOsVerAvail."); + :set mailBody ($mailBody . "Your Mikrotik will be updated to the new RouterOS version from v.$deviceOsVerInst to v.$deviceOsVerAvail (Update channel: $updateChannel) \r\nA final report with detailed information will be sent once the update process is completed. \r\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."); + #!! There is more code connected to this part and first step at the end of the script. + } - /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 } } + + ## Checking If the script needs to create a backup + :log info ("$SMP Checking If the script needs to create a backup."); + if ($forceBackup = true or $scriptMode = "backup" or $isOsNeedsToBeUpdated = true) do={ + :log info ("$SMP Creating system backups."); + if ($isOsNeedsToBeUpdated = true) do={ + :set backupNameFinal $backupNameBeforeUpd; + }; + if ($scriptMode != "backup") do={ + :set mailBody ($mailBody . "\r\n\r\n"); + }; + + :set mailSubject ($mailSubject . " Backup was created."); + :set mailBody ($mailBody . "System backups were created and attached to this email."); + + :set mailAttachments [$buGlobalFuncCreateBackups backupName=$backupNameFinal backupPassword=$backupPassword sensitiveDataInConfig=$sensitiveDataInConfig]; + } else={ + :log info ("$SMP Creating a backup is not necessary."); + } + + # Combine first step email + :set mailBody ($mailBody . $mailBodyDeviceInfo . $mailBodyCopyright); } ## 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; +:if ($updateStep = 2) do={ + :log info ("$SMP Performing the second step."); + ## RouterOS is the latest, let's check for upgraded routerboard firmware + if ($deviceRbCurrentFw != $deviceRbUpgradeFw) do={ + :set isSendEmailRequired false; + :delay 10s; + :log info "$SMP Upgrading routerboard firmware from v.$deviceRbCurrentFw to v.$deviceRbUpgradeFw"; + ## Start the upgrading process + /system routerboard upgrade; + ## Wait until the upgrade is completed + :delay 5s; + :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-FINAL-REPORT-ON-NEXT-BOOT on-event=":delay 5s; /system scheduler remove BKPUPD-FINAL-REPORT-ON-NEXT-BOOT; :global buGlobalVarUpdateStep 3; :delay 10s; /system script run BackupAndUpdate;" start-time=startup interval=0; + ## Reboot system to boot with new firmware + /system reboot; + } else={ + :log info "$SMP It appears that your routerboard is already up to date, skipping this step."; + :set updateStep 3; + }; } ## 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.") +:if ($updateStep = 3) do={ + :log info ("$SMP Performing the third step."); + :log info "Bkp&Upd: RouterOS and routerboard upgrade process was completed. New RouterOS version: v.$deviceOsVerInst, routerboard firmware: v.$deviceRbCurrentFw."; + ## Small delay in case mikrotik needs some time to initialize connections + :log info "$SMP Sending the final email with report and backups."; + :set mailSubject ($mailSubject . " RouterOS Upgrade is completed, new version: v.$deviceOsVerInst!"); + :set mailBody "RouterOS and routerboard upgrade process was completed. \r\nNew RouterOS version: v.$deviceOsVerInst, routerboard firmware: v.$deviceRbCurrentFw. \r\n$changelogUrl \r\n\r\nBackups of the upgraded system are in the attachment of this email. $mailBodyDeviceInfo $mailBodyCopyright"; + :set mailAttachments [$buGlobalFuncCreateBackups backupName=$backupNameAfterUpd backupPassword=$backupPassword sensitiveDataInConfig=$sensitiveDataInConfig]; +} - :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." +# 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={} + +## +## SENDING EMAIL +## +# Trying to send email with backups as attachments. + +:if ($isSendEmailRequired = true) do={ + :log info "$SMP Dispatching email message; estimated completion within 30 seconds."; + :do {/tool e-mail send to=$emailAddress subject=$mailSubject body=$mailBody file=$mailAttachments;} on-error={ + :delay 5s; + :log error "$SMP could not send email message ($[/tool e-mail get last-status]). Will attempt redelivery shortly." + + :delay 5m; + + :do {/tool e-mail send to=$emailAddress subject=$mailSubject body=$mailBody file=$mailAttachments;} on-error={ + :delay 5s; + :log error "$SMP failed to send email message ($[/tool e-mail get last-status]) for the second time." + + if ($isOsNeedsToBeUpdated = true) do={ + :set isOsNeedsToBeUpdated false; + :log warning "$SMP script is not going to initialise update process due to inability to send backups to email." + } + } } - :local mailStep3Subject $mailSubjectPrefix - :local mailStep3Body "" + :delay 30s; - :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..." + :if ([:len $mailAttachments] > 0 and [/tool e-mail get last-status] = "succeeded") do={ + :log info "$SMP File system cleanup." /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" + +# Fire RouterOS update process +if ($isOsNeedsToBeUpdated = true) do={ + + :if ($isSoftBased = false) do={ + ## Set scheduled task to upgrade routerboard firmware on the next boot, task will be deleted when upgrade is done. (That is why you should keep original script name) + /system scheduler add name=BKPUPD-UPGRADE-ON-NEXT-BOOT on-event=":delay 5s; /system scheduler remove BKPUPD-UPGRADE-ON-NEXT-BOOT; :global buGlobalVarUpdateStep 2; :delay 10s; /system script run BackupAndUpdate;" start-time=startup interval=0; + } else= { + ## If the script is executed on CHR, step 2 will be skipped + /system scheduler add name=BKPUPD-UPGRADE-ON-NEXT-BOOT on-event=":delay 5s; /system scheduler remove BKPUPD-UPGRADE-ON-NEXT-BOOT; :global buGlobalVarUpdateStep 3; :delay 10s; /system script run BackupAndUpdate;" start-time=startup interval=0; + }; + + + :log info "$SMP everything is ready to install new RouterOS, going to reboot in a moment!" + ## Command is reincarnation of the "upgrade" command - doing exactly the same but under a different name + /system package update install; +} + +:log info "$SMP script \"Mikrotik RouterOS automatic backup & update\" completed it's job.\r\n";