This way, I have tested it and it works OK. I hope it helps.
The $datetime2epoch and $date2ymd functions should already be in memory.
/system package update; check-for-updates without-paging as-value; :delay 3s
:global datetime2epoch
:global date2ymd
:local LatestVer [get latest-version]
:local Status [get status]
:local currDate [/system clock get date]
:local DaysToWait 30
:local DSR
:if ($Status = "New version is available") do={
# :if ($Status = "System is already up to date") do={
:local VerChangeLog ([/tool fetch "http://upgrade.mikrotik.com/routeros/$LatestVer/CHANGELOG" output=user as-value] -> "data")
:local dateEx [$date2ymd [:pick $VerChangeLog ([:find $VerChangeLog "("]+1) ([:find $VerChangeLog "):"]) ]]
:local EPOCHdateEx [$datetime2epoch $dateEx]
:local EPOCHcurrDate [$datetime2epoch $currDate]
:set DSR ((EPOCHcurrDate - $EPOCHdateEx)/86400)
/file
:if ($DSR < $DaysToWait) do={
:put "Only $DSR days of the $DaysToWait have passed"
:local filename "DaysSinceRelease.txt"
:local output "$DSR/$DaysToWait days since $dateEx"
print file=$filename; :delay 5s; set $filename contents=$output
} else={
:put "It's time to upgrade RouterOS, it's been $DSR of $DaysToWait days of waiting."
}
}
Some of the old /file statements should have been deleted. Not needed anymore. Just left over from V1 of the script.
Should not need any file operations anymore.
DSR was define, just not at top. I moved it.
Script updated.
Why without logic check CHANGELOG instead of simply use unix epoch already present on LATEST.7 NEWESTa7???
With one "data" load you have at the same time the latest version and the build time without so many frills...
I’ve had a script to check and install updates but I added functionality to update only after the release has been out for a period of time.
I do this to avoid installing a release that has bugs and has to be released again or pulled.
I run this script daily so it can count the days from the last update as well as the fw update script in case the RouterOS came with a FW update (usually does). I run RouterOSupdate then FirmwareInstall 15min later
I call this script RouterOSupdate
#This s…
search tag # rextended convert time format (like 45w10d14:32:45.37513571) to seconds
:global timetoseconds do={
:local inTime [:totime $1]
:local wPos [:find $inTime "w" -1]
:local dPos [:find $inTime "d" -1]
:local itLen [:find $inTime "." -1] ; :if ([:typeof $itLen] = "nil") do={:set itLen [:len $inTime]}
:local itSec [:pick $inTime ($itLen - 2) $itLen]
:local itMin [:pick $inTime ($itLen - 5) ($itLen - 3)]
:local itHou [:pick $inTime ($itLen - 8) ($itLen -…
:global timetoseconds do={
:local inTime $1
:local wPos [:find $inTime "w" -1]
:local dPos [:find $inTime "d" -1]
:local itLen [:find $inTime "." -1] ; :if ([:typeof $itLen] = "nil") do={:set itLen [:len $inTime]}
:local itSec [:pick $inTime ($itLen - 2) $itLen]
:local itMin [:pick $inTime ($itLen - 5) ($itLen - 3)]
:local itHou [:pick $inTime ($itLen - 8) ($itLen - 6)]
:local itDay 0
:local itWee 0
:if (([:typeof $wPos] = "nil") and ([:typeof $dPos] = "num")) do={:set itDay [:pick $inTime 0 $dPos] }
:if (([:typeof $wPos] = "num") and ([:typeof $dPos] = "num")) do={:set itDay [:pick $inTime ($wPos + 1) $dPos] }
:if ([:typeof $wPos] = "num") do={:set itWee [:pick $inTime 0 $wPos] }
:local totitSec ($itSec + (60 * $itMin) + (3600 * $itHou) + (86400 * $itDay) + (604800 * $itWee))
:return $totitSec
}
{
Actually the returned data end with \n. If for some reason on future \n is correctly removed, this prevent the script fail.
:local vchannel "stable" ; # can be: [long-term | stable | testing | development]
:local lastv7 ("$([/tool fetch url="https://upgrade.mikrotik.com//routeros/NEWESTa7.$vchannel " as-value output=user]->"data")\n")
:local lastv7ver [:pick $lastv7 0 [:find $lastv7 "_" -1]]
:local currver [/system/package get [find where name="routeros"] version]
:if ($lastv7ver != $currver) do={
:local lastv7build [:pick $lastv7 ([:find $lastv7 "_" -1] + 1) [:find $lastv7 "\n" -1]]
:local currtime [$timetoseconds [:timestamp]]
:local daysdiff (($currtime - $lastv7build) / 86400)
:local vdays 30
:put "The Installed version is $currver and the Latest version $lastv7ver is old $daysdiff days"
:if ($daysdiff > $vdays) do={
:put "Performing update: Latest is older than $vdays days"
# /sys pack up; check; in
} else={
:put "Not updated: Waiting Latest for be older than $vdays days"
}
} else={
:put "The Installed and Latest version are the same $currver"
}
}
If you want just shorter code, without "nice" formatted days values:
{
Actually the returned data end with \n. If for some reason on future \n is correctly removed, this prevent the script fail.
:local vchannel "stable" ; # can be: [long-term | stable | testing | development]
:local lastv7 ("$([/tool fetch url="https://upgrade.mikrotik.com//routeros/NEWESTa7.$vchannel " as-value output=user]->"data")\n")
:local lastv7ver [:pick $lastv7 0 [:find $lastv7 "_" -1]]
:local currver [/system/package get [find where name="routeros"] version]
:if ($lastv7ver != $currver) do={
:local lastv7build [:totime [:pick $lastv7 ([:find $lastv7 "_" -1] + 1) [:find $lastv7 "\n" -1]]]
:local currtime [:timestamp]
:local daysdiff ($currtime - $lastv7build)
:local vdays 30d
:put "The Installed version is $currver and the Latest version $lastv7ver is old $daysdiff days"
:if ($daysdiff > $vdays) do={
:put "Performing update: Latest is older than $vdays days"
# /sys pack up; check; in
} else={
:put "Not updated: Waiting Latest for be older than $vdays days"
}
} else={
:put "The Installed and Latest version are the same $currver"
}
}
EDIT 1: Updated the URLS, see next posts.
EDIT 2: Added channel selection.
EDIT 3: Changed post 1st line.
Much better this way. Nice!
One question: Is it only applicable to the “stable” version?
https://upgrade.mikrotik.com/routeros/LATEST.7
I have another script to control the “testing” version (I use the extraction of the build date from the changelog), do you know if it is possible to get the EPOCH date in the same way?
EDIT (I answer myself)
I have tried several options and it has only worked:
[admin@MikroTik] > :put ([/tool fetch url=“https://upgrade.mikrotik.com/routeros/LATEST.7 ” as-value output=user]->“data”)
7.11.2 1693490147
[admin@MikroTik] > :put ([/tool fetch url=“https://upgrade.mikrotik.com/routeros/LATEST.7rc ” as-value output=user]->“data”)
7.12rc1 1696484807
Use httpS
MikroTik is changing the urls for the new v7.10+ versions and also for latest v6:
Long term:
https://upgrade.mikrotik.com//routeros/NEWESTa7.long-term
(at time I write: “0.00\n” no long-term version)
Stable:
https://upgrade.mikrotik.com//routeros/NEWESTa7.stable
(at time I write: “7.13 1702545848\n”)
Testing:
https://upgrade.mikrotik.com//routeros/NEWESTa7.testing
(at time I write: “7.14beta3 1702985489\n”)
Development:
https://upgrade.mikrotik.com//routeros/NEWESTa7.development
(at time I write: “7.14beta3 1702985489\n”)
I update previous scripts for reflect the change.
Rather than some “script” based in RouterOS itself, something like this calls for real network automation. Use Ansible or something similar, or custom python code that runs on a seperate box.
Hi @rextended , I have detected a small discrepancy: the EPOCH calculations performed by MikroTik are different from the calculations performed by your $datetime2epoch function, what do you think?
:global datetime2epoch do={
:local dtime [:tostr $1]
/system clock
:local cyear [get date] ; :if ($cyear ~ "....-..-..") do={:set cyear [:pick $cyear 0 4]} else={:set cyear [:pick $cyear 7 11]}
:if (([:len $dtime] = 10) or ([:len $dtime] = 11)) do={:set dtime "$dtime 00:00:00"}
:if ([:len $dtime] = 15) do={:set dtime "$[:pick $dtime 0 6]/$cyear $[:pick $dtime 7 15]"}
:if ([:len $dtime] = 14) do={:set dtime "$cyear-$[:pick $dtime 0 5] $[:pick $dtime 6 14]"}
:if ([:len $dtime] = do={:set dtime "$[get date] $dtime"}
:if ([:tostr $1] = "") do={:set dtime ("$[get date] $[get time]")}
:local vdate [:pick $dtime 0 [:find $dtime " " -1]]
:local vtime [:pick $dtime ([:find $dtime " " -1] + 1) [:len $dtime]]
:local vgmt [get gmt-offset]; :if ($vgmt > 0x7FFFFFFF) do={:set vgmt ($vgmt - 0x100000000)}
:if ($vgmt < 0) do={:set vgmt ($vgmt * -1)}
:local arrm [:toarray "0,0,31,59,90,120,151,181,212,243,273,304,334"]
:local vdoff [:toarray "0,4,5,7,8,10"]
:local MM [:pick $vdate ($vdoff->2) ($vdoff->3)]
:local M [:tonum $MM]
:if ($vdate ~ ".../../....") do={
:set vdoff [:toarray "7,11,1,3,4,6"]
:set M ([:find "xxanebarprayunulugepctovecANEBARPRAYUNULUGEPCTOVEC" [:pick $vdate ($vdoff->2) ($vdoff->3)] -1] / 2)
:if ($M>12) do={:set M ($M - 12)}
}
:local yyyy [:pick $vdate ($vdoff->0) ($vdoff->1)] ; :if ((($yyyy - 1968) % 4) = 0) do={:set ($arrm->1) -1; :set ($arrm->2) 30}
:local totd ((($yyyy - 1970) * 365) + (($yyyy - 1968) / 4) + ($arrm->$M) + ([:pick $vdate ($vdoff->4) ($vdoff->5)] - 1))
:return (((((($totd * 24) + [:pick $vtime 0 2]) * 60) + [:pick $vtime 3 5]) * 60) + [:pick $vtime 6 8] - $vgmt)
}
{
:global datetime2epoch
:local vchannel "stable" ; # can be: [long-term | stable | testing | development]
:local latestData ("$([/tool fetch url="https://upgrade.mikrotik.com//routeros/NEWEST7.$vchannel " as-value output=user]->"data")\n"); # 7.11.2 1693490147\n\n
:local latestVer [:pick $latestData 0 [:find $latestData "_" -1]]; # 7.11.2
:local latestEpoch [:pick $latestData ([:find $latestData "_" -1] + 1) [:find $latestData "\n" -1]]; # 1693490147
:local currEpoch [$datetime2epoch [/system/package get [find where name="routeros"] build-time]]; # 1693482947 (2023-08-31 13:55:47)
:put $latestData
:put $latestEpoch
:put $latestVer
:put $currEpoch
:put ($latestEpoch = $currEpoch)
}
output:
7.11.2 1693490147
1693490147 7.11.2
1693482947 false <== It doesn't match
Makes since to me. Remember EPOCH is in seconds
:put [$unixtodatetime 1693490147] → 31/08/2023 15:55:47
:put [$unixtodatetime 1693482947] → 31/08/2023 13:55:47
1693490147-1693482947 is 7200 which is exactly 2 hours.
Now I understand, it’s because of the time difference in my country: GMT +2 (summer time). My router is set like this. Thanks.
Possible fix?:
:local currEpoch
:if ([/system clock get gmt-offset] = 7200) do={
# Summer time. GMT Offset +02:00
:set currEpoch ([$datetime2epoch [/system/package get [find where name="routeros"] build-time]] +7200)
} else={
# Winter time. GMT Offset +01:00
:set currEpoch ([$datetime2epoch [/system/package get [find where name="routeros"] build-time]] +3600)
}
Simply: I have considered timezone, MirkoTik not.
(I could be wrong, but I assume if NOT written, GMT is always meant… but who cares if the build has more or less up to 14 hours difference…)
Is why my above script do not uselessly contain $datetime2epoch…
And about [/system clock get gmt-offset], the correct way to use it is how is used inside my script… (until mikrotik do not fix something…)
OK, very helpful
{
:local vgmt [/system clock get gmt-offset]
:if ($vgmt > 0x7FFFFFFF) do={:set vgmt ($vgmt - 0x100000000)}
:if ($vgmt < 0) do={:set vgmt ($vgmt * -1)}
:put $vgmt
}
Thanks.
Since ROS 7.12 it is much easier to get a unix time.
:local currentTime [:pick [:tonsec [:timestamp]] 0 10];
:local changelog ([/tool fetch "https://upgrade.mikrotik.com//routeros/NEWEST7.stable" as-value output=user] -> "data");
:local changelogTime [:pick $changelog ([:find $changelog " "]+1) ([:find $changelog " "]+11)];
:log info "$changelogTime";
:log info "$currentTime";
Output:
1699515906
1699977978
Hope it would be helpful
Why all that?
Suffice just
:put [:tonum [:timestamp]]
:put [:tonum [:timestamp]] does not work for me. Returns nothing. that is on V7.11.2
Oops, overengineered by my stupidity, thanks for simplification.
Here is my implementation of update script
P.S. If somebody want to see code of functions and telegram script used in this script - reply and I'll show you.
:local changelog ([/tool fetch "https://upgrade.mikrotik.com//routeros/NEWEST7.stable" as-value output=user] -> "data");
:local changelogTime [:pick $changelog ([:find $changelog " "]+1) ([:find $changelog " "]+11)];
:local changelogVersion [:pick $changelog 0 [:find $changelog " "]];
:local installedBuildTime [:tonum [:totime [system package get [find where name="routeros"] build-time]]];
:local currentTime [:tonum [:timestamp]];
if ($installedBuildTime != $changelogTime) do={
/system script run FuncDeviceStatus;
:global FuncStatus;
:local daysDifference (($currentTime - $changelogTime) / 86400);
:local daysTimeout 7;
:local daysBeforeUpdate ($daysTimeout - $daysDifference);
:if ($daysDifference >= $daysTimeout) do={
:log warning "Updating to ROS $changelogVersion";
:global textSent "$[$FuncStatus]%0A\F0\9F\94\80\F0\9F\86\99 <b>Updating to ROS $changelogVersion</b>";
/system script run SendToTelegram;
/system package update; check-for-updates; install;
} else={
:log warning "Update to ROS $changelogVersion will be installed in $daysBeforeUpdate days";
:global textSent "$[$FuncStatus]%0A\E2\8C\9B\F0\9F\86\99 <b>Update to ROS $changelogVersion will be installed in $daysBeforeUpdate days</b>";
/system script run SendToTelegram;
}
}
Very useful your script, thanks.
Could you share your $FuncStatus function?
A tip: It is not necessary to end each line of code with “; ” unless another command follows.
Very useful your script, thanks.
Could you share your $FuncStatus function?
A tip: It is not necessary to end each line of code with “; ” unless another command follows.
Here is FuncStatus code, I use it in multiple scripts to get time info from Mikrotik into messages sent to Telegram.
:global FuncStatus do={
:local deviceDate [/system clock get date];
:local deviceTime [/system clock get time];
:local uptime [/system resource get uptime];
:return ($deviceDate." ".$deviceTime." uptime: ".$uptime);
}
:put [$FuncStatus];
Here is Telegram output:
I also use it for another Telegram informer scripts: