Skip to content

RouterOS script

mikroscope plan --rsc writes the whole install as one RouterOS script. Run it on the router, from a terminal or with /import: no ssh from your computer, and the script checks the router before it writes anything. To fill in the options in a form instead, use the Script generator; it writes the same script.

Terminal window
mikroscope plan --rsc --remote-image jmrplens/mikroscope-agent:1.6.1 --out install.rsc

It connects to nothing and prints install.rsc: 39 lines; review it, then paste it into the router's terminal or /import it. Without --out the script goes to standard output. It takes the same flags as install: --name, --veth, --subnet, --iface-list, --addr-list, --rate, --disk and the rest (CLI).

  • Registry pull, with --remote-image: the router pulls the image itself.
  • Image tar, without it: the script expects the tar on the router as mikroscope.tar (<name>.tar with --name), and its header says so (BEFORE RUNNING: put the agent image tar on the device as mikroscope.tar). Upload it first: Offline install.

With the default options the script is:

install.rsc
# mikroscope 1.6.1: install script for RouterOS 7.24 or later. Container name: mikroscope
# Every object it creates carries the comment "mikroscope:mikroscope (managed by mikroscope)", which is how
# `mikroscope status` and `uninstall` recognize them later. It lists them in
# mikroscope/mikroscope.manifest.txt on the router, which `mikroscope uninstall` reads and deletes last.
#
# The router pulls registry-1.docker.io/jmrplens/mikroscope-agent:1.6.1 itself.
# The registry host is part of remote-image= (RouterOS 7.18 and later take it
# there), so this script neither reads nor changes the device-wide registry-url.
# A registry username set on the device for a registry other than registry-1.docker.io
# can make the pull end in `auth error`; `mikroscope doctor` warns about it.
#
# It runs as one block: a check that fails stops it before anything is written,
# and a step that fails stops the steps after it.
{
:if (!([/system/resource/get version] ~ "^(7[.](2[4-9]|[3-9][0-9]|[1-9][0-9][0-9])|([89]|[1-9][0-9]+)[.])")) do={ :error "mikroscope: needs RouterOS 7.24 or later" }
:if ([:len [/system/package/find name="container" disabled=no]] = 0) do={ :error "mikroscope: the container package is not installed" }
:local dm [:tostr [/system/device-mode/get container]]; :if ($dm != "yes" && $dm != "true") do={ :error "mikroscope: device-mode container is not enabled" }
:if ([:len [/interface/veth/find name="veth-mikroscope"]] > 0 && [:len [/interface/veth/find name="veth-mikroscope" comment="mikroscope:mikroscope (managed by mikroscope)"]] = 0) do={ :error "mikroscope: veth veth-mikroscope exists and is not mikroscope's" }
:if ([:len [/container/envs/find list="mikroscope-env"]] > 0 && [:len [/container/envs/find list="mikroscope-env" key="MIKROSCOPE_TAG" value="mikroscope:mikroscope (managed by mikroscope)"]] = 0) do={ :error "mikroscope: envlist mikroscope-env exists and is not mikroscope's" }
:if ([:len [/interface/list/find name="LAN"]] = 0) do={ :error "mikroscope: interface list LAN does not exist" }
:if ([:len [/file/find name="mikroscope/mikroscope.manifest.txt"]] > 0) do={ :if (!([:typeof [:find [/file/get [find name="mikroscope/mikroscope.manifest.txt"] contents] "\ntag=mikroscope:mikroscope (managed by mikroscope)\n"]] = "num")) do={ :error "mikroscope: mikroscope/mikroscope.manifest.txt exists and is not this install's manifest" } }
# install manifest mikroscope/mikroscope.manifest.txt
:local m "mikroscope-manifest=1\nname=mikroscope\ntag=mikroscope:mikroscope (managed by mikroscope)\ndisk=\nveth=veth-mikroscope\nsubnet=172.30.10.0/30\nport=9123\niface-list=LAN\naddr-list=LANs\nexpose=\ncontainer-name=\nremote-image=registry-1.docker.io/jmrplens/mikroscope-agent:1.6.1\ntoken=no\ndir=mikroscope\nfile=mikroscope/mikroscope.manifest.txt\nobject=/interface/veth name=veth-mikroscope\nobject=/ip/address interface=veth-mikroscope\nobject=/interface/list/member interface=veth-mikroscope list=LAN\nobject=/ip/firewall/address-list list=LANs address=172.30.10.0/30\nobject=/container/envs list=mikroscope-env\nobject=/container interface=veth-mikroscope\ndir=mikroscope/mikroscope\n"; :if ([:len [/file/find name="mikroscope/mikroscope.manifest.txt"]] > 0) do={ /file/set [find name="mikroscope/mikroscope.manifest.txt"] contents=$m } else={ /file/add name="mikroscope/mikroscope.manifest.txt" contents=$m }
# veth interface veth-mikroscope
/interface/veth/add name="veth-mikroscope" address=172.30.10.2/30 gateway=172.30.10.1 comment="mikroscope:mikroscope (managed by mikroscope)"
# router address 172.30.10.1
/ip/address/add address=172.30.10.1/30 interface="veth-mikroscope" comment="mikroscope:mikroscope (managed by mikroscope)"
# interface-list membership LAN
/interface/list/member/add list="LAN" interface="veth-mikroscope" comment="mikroscope:mikroscope (managed by mikroscope)"
# address-list membership LANs
/ip/firewall/address-list/add list="LANs" address=172.30.10.0/30 comment="mikroscope:mikroscope (managed by mikroscope)"
# container mikroscope
:if ([:len [/container/envs/find list="mikroscope-env" key="MIKROSCOPE_TAG" value="mikroscope:mikroscope (managed by mikroscope)"]] > 0) do={ /container/envs/remove [find list="mikroscope-env"] }; /container/envs/add list="mikroscope-env" key=MIKROSCOPE_TAG value="mikroscope:mikroscope (managed by mikroscope)"; /container/envs/add list="mikroscope-env" key=RATE_HZ value="10"; /container/envs/add list="mikroscope-env" key=BUFFER_S value="60"; /container/envs/add list="mikroscope-env" key=PORT value="9123"; /container/envs/add list="mikroscope-env" key=ADDR value="172.30.10.2"; /container/envs/add list="mikroscope-env" key=MEM_LIMIT_MB value="16"; /container/envs/add list="mikroscope-env" key=CAPTURE_MB value="4"; /container/add remote-image="registry-1.docker.io/jmrplens/mikroscope-agent:1.6.1" interface="veth-mikroscope" root-dir=mikroscope/mikroscope envlist="mikroscope-env" logging=yes start-on-boot=yes restart-policy=on-failure restart-max-count=5 restart-interval=10s memory-max=64M privileged=yes ignore-remote-image-change=yes comment="mikroscope:mikroscope (managed by mikroscope)"; /container/start [find comment="mikroscope:mikroscope (managed by mikroscope)"]
:local k 0; :while ([:len [/container/find comment="mikroscope:mikroscope (managed by mikroscope)" running]] = 0 && $k < 120) do={ :delay 1s; :set k ($k + 1) }
:if ([:len [/container/find comment="mikroscope:mikroscope (managed by mikroscope)" running]] > 0) do={ :put "mikroscope: agent running, http://172.30.10.2:9123/healthz" } else={ :put "mikroscope: not running yet; see /log/print where topics~\"container\"" }
}
# When it is done: /container/print where comment="mikroscope:mikroscope (managed by mikroscope)"
# The agent answers on http://172.30.10.2:9123/healthz from the router's LAN.

It runs as one { … } block, in this order:

  1. Guards. It stops with :error "mikroscope: …", before anything is written, when the RouterOS release is before 7.24, the container package is missing, device mode is off, a veth, an envlist or a --container-name container of that name belongs to something else, the interface list does not exist, the --disk is missing, the tar is not uploaded, or a file at the manifest’s path is not this install’s manifest.
  2. The install manifest, then each object, with the same tag install writes. A step that fails stops the steps after it.
  3. A wait of up to 120 s for the container to run, then one line: mikroscope: agent running, http://172.30.10.2:9123/healthz or mikroscope: not running yet; see /log/print where topics~"container".
  1. Open a terminal on the router: WebFig › Terminal, Winbox › New Terminal, or ssh.
  2. Wait for the ] > prompt.
  3. Paste the whole file.

A successful /import prints:

mikroscope: agent running, http://172.30.10.2:9123/healthz
Script file loaded and executed successfully

A guard that fails names the problem and writes nothing, for instance with the tar route and no tar uploaded:

Script Error: mikroscope: upload mikroscope.tar first (:error; line 20) (:import; line 1)

On the router:

/container/print where comment="mikroscope:mikroscope (managed by mikroscope)"
:put ([/tool/fetch url="http://172.30.10.2:9123/healthz" output=user as-value]->"data")

The first shows the container with the R (running) flag; the second prints the agent’s /healthz answer, {"ok":true,…}. From a computer with the CLI, mikroscope status --router … recognises the install and probes the agent.

The script writes the install manifest first, mikroscope/<name>.manifest.txt on the install’s disk. Read it on the router:

:put [/file/get [find name="mikroscope/mikroscope.manifest.txt"] contents]
mikroscope-manifest=1
name=mikroscope
tag=mikroscope:mikroscope (managed by mikroscope)
disk=
veth=veth-mikroscope
subnet=172.30.10.0/30
port=9123
iface-list=LAN
addr-list=LANs
expose=
container-name=
remote-image=registry-1.docker.io/jmrplens/mikroscope-agent:1.6.1
token=no
dir=mikroscope
file=mikroscope/mikroscope.manifest.txt
object=/interface/veth name=veth-mikroscope
object=/ip/address interface=veth-mikroscope
object=/interface/list/member interface=veth-mikroscope list=LAN
object=/ip/firewall/address-list list=LANs address=172.30.10.0/30
object=/container/envs list=mikroscope-env
object=/container interface=veth-mikroscope
dir=mikroscope/mikroscope

The first lines are the options the install was made with, under the CLI’s flag names; token= says only yes or no, never the value. Each object=, file= and dir= line is something the install created. mikroscope uninstall reads the manifest, removes everything it lists and the manifest last. The tar route adds file=mikroscope.tar.

  • It cannot upload anything. Pair it with --remote-image, or upload the tar before you run it.
  • With a token, the file is a credential. The envlist line carries the token in clear, because the router needs it in clear. The CLI writes the file 0600; delete it from the router after /import.
  • It runs no doctor. The guards cover the release, the package, device mode, name collisions, the interface list, the disk, the tar and the manifest path. Free memory and storage, a route that overlaps the /30 and a firewall rule that drops the agent’s replies are not checked: run mikroscope doctor from a computer that can reach the router, or check the Requirements by hand.

What has been run, and what has not, is on Tested on.

From a computer with the CLI:

Terminal window
mikroscope uninstall --router admin@192.168.88.1 --yes

It reads the manifest, so it needs no other flag. Without the CLI, run the removal commands on Manual install: terminal. Upgrade and uninstall has what removal never touches.