Page 1 of 1
Comparing config files
Posted: Fri Oct 07, 2022 4:05 am
by kiler129
Normally I have my configs stored on GIT with comments etc. Normally my workflow is to make some on-the-spot changes via winbox, then when everything is working I populate the changes into the GIT version. The issue is life isn't perfect: it happened to me many times I or someone else forgot to populate the change in the GIT copy... which is less than ideal.
Later on I discover a stray firewall rule and I wonder what else is missing
The only idea of comparing configs I had is exporting a file from the device and then emulating it on CHR, loading GIT version and exporting the config. Then it becomes a simple matter of diffing both configs. However, this isn't practical as e.g. QEmu on my macOS cannot emulate >10 interfaces nor can emulate sfpplus1 port...
Is there any way to compare two different config files? How people do it normally?
Re: Comparing config files
Posted: Fri Oct 07, 2022 5:25 am
by Sob
Why not automate the export->git part? For example, I have list of routers, script connects to each one using ssh, runs /export, saves output to file (after stripping some comments to avoid unnecessary changes), and finally commits everything. It's scheduled to run every day, or for some routers even more often. The most I can lose is few hours of changes. And I have full history, so I know exactly when something changed. It's not perfect. For start, it doesn't tell who changed it (if more people have access). Even more annoying is that export doesn't export everything (certificates, etc). But otherwise it's pretty good.
Re: Comparing config files
Posted: Fri Oct 07, 2022 6:10 am
by kiler129
That was the initial idea but the automatic export is a vomit which usually doesn't even import (e.g. it has no delays to wait for interfaces). It's not really editable as sections nor options aren't organized in a way to make manual editing easy. In addition I tend to e.g. indent chains in the firewall like below. My configs are usually more akin to how you organize code
Implementing automatic daily export isn't a bad idea as a backup thou.
I guess I would need some tool to normalize the config and then compare but I'm not sure if it's possible without execution.
Re: Comparing config files
Posted: Fri Oct 07, 2022 2:26 pm
by Znevna
I don't get why do you need to import the config in order to compare it with another config. What am I missing?
Nevermind, looks like you're not keeping versions of the actual config in git, but only config changes written in your own personal style.
Re: Comparing config files
Posted: Fri Oct 07, 2022 2:41 pm
by BartoszP
I use this:
https://sourceforge.net/projects/winmerge/ to visualy compare .rsc files
Re: Comparing config files
Posted: Fri Oct 07, 2022 2:47 pm
by Sob
@kiler129: That's it, I'm mainly after readable backup, I don't need it to be pretty or even importable as whole, so it works for me.
Re: Comparing config files
Posted: Fri Oct 07, 2022 2:58 pm
by olivier2831
Coincidence, I was about to open a new thread on a very similar topic.
Lately I needed to compare two configs and spot differences using Meld (winmerge-like tool).
Current and default export behaviour, IMHO, should be changed to get a more deterministic output, maybe using some sorting or filtering.
I thought of post-processing those files myself but these file content don't look easy to parse.
Re: Comparing config files
Posted: Fri Oct 07, 2022 2:59 pm
by rextended
You use windows?
Re: Comparing config files
Posted: Fri Oct 07, 2022 3:05 pm
by tangent
My backup tool produces diffs as part of its regular behavior. The docs explain why you’re better off not using git for this, but if you must, it wouldn’t be difficult to convert it.
Re: Comparing config files
Posted: Fri Oct 07, 2022 3:09 pm
by rextended
For have more readability, and do not have useless warning,
better replace something inside the export...
Re: Comparing config files
Posted: Fri Oct 07, 2022 3:34 pm
by rextended
"\_" => " "
literally \ and \r\n + 4 spaces => ""
"# " followed by date and time and " by RouterOS" => "# RouterOS"
Re: Comparing config files
Posted: Fri Oct 07, 2022 4:02 pm
by 611
I've tried similar approach with handcrafted config files and comparison, and crafted a Python script to compare actual config to one I'd want:
# RouterOS config file parser/sorter/comparer ver.0
# by 611
import sys
import re
# My preferred order of parameter sorting
# Firtsparams will go first in the listed order, then unlisted params in alphabetical order, then lastparams in the listed order
firstparams = "name", "peer", "list", "time-zone-name", \
"chain", "action", "address-list", "address-list-timeout", "connection-state", "protocol", \
"src-address", "src-address-list", "src-port", "dst-address", "dst-address-list", "dst-port", \
"local-address", "remote-address", \
"master-interface", "mode", "ssid", "security-profile", "wireless-protocol", "band", "country", "frequency", "channel-width", \
"server", "address", "mac-address", "network", "target", \
"leds", "gateway", "distance", "type", \
"bridge", "interface", "user", "password", \
"servers", "authentication-types", "wpa2-pre-shared-key"
lastparams = "disabled", "log", "log-prefix", "comment", "source"
# Sorting switch
dosort = False
doimport = True
includenoncommands = False
# Sort parameters with first list and last list
# Args: dict params; Returns: list of (arg, val) tuples sortedparams
def SortParams(params):
sortedparams = []
# First, add all present params on the "first" list in their sort order.
for p in firstparams:
if p in params:
sortedparams.append((p, params.pop(p)))
# Second, add all params NOT on the "last" list in alphabetical order.
for p in sorted(params):
if p not in lastparams:
sortedparams.append((p, params.pop(p)))
# Third, add all params on the "last" list in their sort order.
for p in lastparams:
if p in params:
sortedparams.append((p, params.pop(p)))
# Here we may have checked if there's anything left in params, but it's impossible :)
# Return sorted list
return sortedparams
# Make a set out of config list
# Args: list of tuples (cmd, params); Returns: same as a set
def MakeSet(config):
configset = set()
for line in config:
(cmd, params) = line
if (len(params) != 0) | includenoncommands:
configset.add((cmd, tuple(params)))
return configset
# Parse a Mikrotik-formatted config line
# Args: string line; Returns: tuple (cmd, params)
def ParseMTrscLine(line):
# Split the line into command (starting with "/", with optional condition in "[]")
# and parameters (starting with "arg=" or "!arg").
# Suggestions are welcome: this regex that may misbehave on cases like `/cmd [cond="]"] arg=...` -
# in-string closing square bracket would be caught.
# Command starts with "/", lazy captures anything to the next group, optionally includes "[" lazy capture of condition "]"
# Parameters are starting with some space and either non-space ending with "=" or non-space starting with "!"
parsedline = re.match(r"(?P<cmd>/.+?(?:\[.+?\])?)" + r"(?P<params>\s+(?:\S+?(?==)|\!\S+).*)", line)
# Return None if the line failed to parse (doesn't look like command)
if parsedline == None:
return None
# Split parameters into arument-value pairs (no value for inverted argument starting with "!"),
# strip extra spaces between parameters.
# Suggestions are welcome: this regex that may misbehave on cases like `arg="val\\"` -
# quotes that _look_ escaped are skipped when looking for the end of quoted value,
# even though they might be not actually escaped.
# Parameters are separated (started) with some space,
# and are either (non-space lazy) arg ending with "=" or just a (greedy!) arg starting with "!".
# Values are optional and starting with with "=", either quoted with any symbols inside (lazy),
# or with end-of-line instead of closing quote, or not quoted non-space.
# Quotes that _look_ escaped are skipped using negative lookbeihing group.
# Note that we have to stick to two capruring groups (or do extra processing outside regxp),
# so the first group must catch both "arg=" and "!arg" cases.
params = re.findall(r"\s*" + r"(?P<arg>\S+?(?==)|!\S+)" + r"(?:=(?P<val>\".*?(?:(?<!\\)\"|\\$)|\S+))?", parsedline.group('params'))
# Return tuple of command and list of arument-value pairs
return (parsedline.group('cmd'), params)
# Read a Mikrotik-formatted config line
# Args: string filename; Returns: list of tuples (cmd, params)
def ReadMTrscFile(filename):
# Init an empty list
config = []
# Open the file and iterate over it
infile = open(filename, "r")
for line in infile:
# Parse the line
parsedline = ParseMTrscLine(line)
# If the line failed to parse, just add it to the list as-is with empty params list
if parsedline == None:
config.append((line.rstrip("\n"), []))
continue
# Extract command and parameters form parsed line
(cmd, params) = parsedline
# Chech if we've got an include and if we'll honor it
if (cmd == "/import") & doimport:
config += ReadMTrscFile(dict(params)["file"])
# Add command with sorted parameters if required
elif dosort:
config.append((cmd, SortParams(dict(params))))
# Add tuple as-is if we don't sort
else:
config.append(parsedline)
# Finished with file
infile.close()
# Return the list containing the configuration
return config
# Write a Mikrotik-formatted config line
# Args: string filename, list of tuples (cmd, params)
def WriteMTrscFile(filename, config):
# Open the file and iterate over config
outfile = open(filename, "w")
for line in config:
# Extract parts from tuple and compose new line (staring with command and continuing with space separated argument[=value] blocks)
(cmd, params) = line
newline = cmd
for p in params:
newline += " " + p[0]
if p[1] != '':
newline += "=" + p[1]
# Parse the line
outfile.write(newline + "\n")
# Finished with file
outfile.close()
# Return nothing
return
command = sys.argv[1]
if command == "parse":
dosort = False
infilename = sys.argv[2]
outfilename = sys.argv[3]
WriteMTrscFile(outfilename, ReadMTrscFile(infilename))
elif command == "sort":
dosort = True
infilename = sys.argv[2]
outfilename = sys.argv[3]
WriteMTrscFile(outfilename, ReadMTrscFile(infilename))
elif command == "compare":
dosort = True
in1filename = sys.argv[2]
in2filename = sys.argv[3]
diff12filename = sys.argv[4]
diff21filename = sys.argv[5]
config1 = ReadMTrscFile(in1filename)
configset1 = MakeSet(config1)
config2 = ReadMTrscFile(in2filename)
configset2 = MakeSet(config2)
configset12 = configset1.difference(configset2)
configset21 = configset2.difference(configset1)
WriteMTrscFile(diff12filename, configset12)
WriteMTrscFile(diff21filename, configset21)
else:
print("Usage: MTrscTools <parse|sort|compare>")
exit
#WriteMTrscFile(outfilename, config)
# That's all folks!
The thing is unfinished as I never had a time to do it. Use at your discretion.
Re: Comparing config files
Posted: Fri Oct 07, 2022 6:09 pm
by holvoetn
Notepad++ and Compare plugin here.
Re: Comparing config files
Posted: Fri Oct 07, 2022 6:41 pm
by rextended
«diff -r -I '#*by RouterOS*'» powershell and windiff
Re: Comparing config files
Posted: Fri Oct 07, 2022 6:58 pm
by pe1chl
When you want an export that is easier to "diff" and less prone to showing lots of differences for only a single keyword change, use /export terse
Of course in v7 use /export show-sensitive terse
Re: Comparing config files
Posted: Fri Oct 07, 2022 7:11 pm
by rextended
@pe1chl thank you, no one wrote it and I hadn't even thought about writing the right command to use to perform the export...
As @pe1chl wrote:
for v6
# export all except users parts (and no certificates, dude database, etc.)
/export terse file=export.rsc
# export all about users and groups except passwords
/user export terse file=user_export.rsc
for v7
# export all except users parts (and no certificates, dude database, etc.)
/export show-sensitive terse file=export.rsc
# export all about users and groups except passwords
/user export show-sensitive terse file=user_export.rsc
Re: Comparing config files
Posted: Fri Oct 07, 2022 7:58 pm
by Znevna
All these years, never knew what "terse" does...
Re: Comparing config files
Posted: Fri Oct 07, 2022 9:01 pm
by Sob
I hate to admit it, but you're not alone.
Re: Comparing config files
Posted: Fri Oct 07, 2022 10:22 pm
by pe1chl
The difference is that "terse" outputs complete config lines and does not break them at around 80 characters.
So instead of:
/ip firewall mangle
add action=mark-packet chain=priority comment="Priority 0" new-packet-mark=\
prio0 passthrough=no priority=0
add action=mark-packet chain=priority comment="Priority 1" new-packet-mark=\
prio1 passthrough=no priority=1
add action=mark-packet chain=priority comment="Priority 2" new-packet-mark=\
prio2 passthrough=no priority=2
add action=mark-packet chain=priority comment="Priority 3" new-packet-mark=\
prio3 passthrough=no priority=3
add action=mark-packet chain=priority comment="Priority 4" new-packet-mark=\
prio4 passthrough=no priority=4
add action=mark-packet chain=priority comment="Priority 5" new-packet-mark=\
prio5 passthrough=no priority=5
add action=mark-packet chain=priority comment="Priority 6" new-packet-mark=\
prio6 passthrough=no priority=6
add action=mark-packet chain=priority comment="Priority 7" new-packet-mark=\
prio7 passthrough=no priority=7
it outputs:
/ip firewall mangle add action=mark-packet chain=priority comment="Priority 0" new-packet-mark=prio0 passthrough=no priority=0
/ip firewall mangle add action=mark-packet chain=priority comment="Priority 1" new-packet-mark=prio1 passthrough=no priority=1
/ip firewall mangle add action=mark-packet chain=priority comment="Priority 2" new-packet-mark=prio2 passthrough=no priority=2
/ip firewall mangle add action=mark-packet chain=priority comment="Priority 3" new-packet-mark=prio3 passthrough=no priority=3
/ip firewall mangle add action=mark-packet chain=priority comment="Priority 4" new-packet-mark=prio4 passthrough=no priority=4
/ip firewall mangle add action=mark-packet chain=priority comment="Priority 5" new-packet-mark=prio5 passthrough=no priority=5
/ip firewall mangle add action=mark-packet chain=priority comment="Priority 6" new-packet-mark=prio6 passthrough=no priority=6
/ip firewall mangle add action=mark-packet chain=priority comment="Priority 7" new-packet-mark=prio7 passthrough=no priority=7
That means that when you insert or delete a line, you always know its section, and when you change some option in the middle it does not change the position of a line break.
This the diffs are much easier to identify, when you use some diff tool that colorizes the parts that changed.
Re: Comparing config files
Posted: Fri Oct 07, 2022 11:48 pm
by anav
I tried using 'tense' but that didnt work...........
But yes, thanks but why have you been holding out on us for so long with this gem
In terms of comparing configs I put two configs side by side in notepadd ++ and use compare command or something similar Works well!!
Re: Comparing config files
Posted: Sat Oct 08, 2022 10:30 pm
by rextended
Probably because, like me, using it he assumed that others did too...
In fact we were just talking about methods to compare, not talking about how to get the file...
Re: Comparing config files
Posted: Sat Oct 08, 2022 11:22 pm
by Sob
Well, getting the file...
It's more difficult for OP if there's requirement for custom formatting, it means either manual editing or some clever reformatting tool (I don't know any). It could still be linked to automatic exports, at least in a way that it could check when was something changed in main (edited) repository and in automatic one, and send notification if the latter has newer changes. It would help to prevent unnoticed changes.
Other than that, actually getting the file means that either something downloads export from router, or router uploads export to some server. I currently use downloads, but I'm thinking about uploads. Advantage is that it doesn't require incoming connections to router (even though it wasn't big problem so far). I originally decided against uploading, because AFAIK it requires to first save export to disk/flash and I didn't want to wear it out (it wouldn't be that many writes, but still). It's no longer a concern for devices with ramdisk (and yes, those that don't use it should get it too).
As for comparing different files, it's not ideal. Some parts are fine, order of sections stays the same, ordered items (firewall rules, ...) are ok, but unordered ones (address list, ...) are not. But it wouldn't be so hard to do a little preprocessing before saving the file and sorting selected sections. I mean on server, in some normal programming language, not using RouterOS scripting.
Re: Comparing config files
Posted: Sat Oct 08, 2022 11:33 pm
by rextended
When I have working v7 for do some test, I try with REST API to downoad export without save locally the file at all:
https://usr:pwd@192.168.88.1/rest/export?terse
Actually all devices except "regular" CPE send me the backup on one internal ftp server at the night, on pseudo-random time, based on MAC address.
Subsequently the server use "diff" to compare, for each devices, the "expected" configuration with actual configuration, if something is different, send me one email with the differencies found.
Re: Comparing config files
Posted: Sat Oct 08, 2022 11:48 pm
by Sob
It's probably slightly simpler to connect to HTTPS than SSH. But you need valid certificate and that can be annoying. And you still need incoming connections to router. Not much difference in the end.
Re: Comparing config files
Posted: Sun Oct 09, 2022 2:34 am
by tangent
Of course in v7 use /export show-sensitive terse
I've modified
my backup tool to add both of these things. Thanks.
Re: Comparing config files
Posted: Sun Oct 09, 2022 12:19 pm
by pe1chl
order of sections stays the same, ordered items (firewall rules, ...) are ok, but unordered ones (address list, ...) are not.
It really irritates me that unordered lists are not exported in a sorted order so they would not depend on the sequence the items were inserted in the configuration!
Does not really matter which key they are sorted to, but at least sort them!
Re: Comparing config files
Posted: Sun Oct 09, 2022 1:26 pm
by anav
I hate it when functions automatically change the order of my lists without asking me ;-PP
If your list are not ordered prior to export, thats a personal problem........
Concur that it should be an option available though.
Re: Comparing config files
Posted: Sun Oct 09, 2022 2:29 pm
by BartoszP
I wonder: "What order key should be used during export?"
Re: Comparing config files
Posted: Sun Oct 09, 2022 2:32 pm
by holvoetn
Address list ?
Most logical would be ip address, no ?
Re: Comparing config files
Posted: Sun Oct 09, 2022 3:01 pm
by Jotne
I would love to have a function on the router to compare files.
This way I can compare previous day rcs/backup and see if its different.
If its not changed, do not send backup to external server.
Re: Comparing config files
Posted: Sun Oct 09, 2022 4:18 pm
by rextended
Address list ?
Most logical would be ip address, no ?
First address list name, then IP address...
Re: Comparing config files
Posted: Sun Oct 09, 2022 4:35 pm
by anav
Make sense, Op has control over list names.
One could also list based on comment entry (numbers/alphabetical like list names).
Re: Comparing config files
Posted: Sun Oct 09, 2022 4:52 pm
by BartoszP
Most logical would be ip address, no ?
A. Chronological order as items were added.
B. Listname + IP
C. IPs first then subnets or subnets then IPs?
D. Subnets with higher or lower mask first?
E. Lowest timeout first or last?
F. maybe the order which is used internally by search algorithm to make search tree the fastest one?
E. export sortkey=..... ?
Re: Comparing config files
Posted: Sun Oct 09, 2022 6:39 pm
by anav
Nice, in summary, MT needs to add a generic search sorting mechanisms with lots of options.......that can be applied to any list of things.
firewall rules, address lists etc..... BUT only after they allow firewall address lists in routing rules AND a client wireguard re-connect schema when the server becomes unavailable.
Re: Comparing config files
Posted: Sun Oct 09, 2022 7:59 pm
by BartoszP
Nothing fancy, just sortkey="script-like expression" which should evaluated for each record and used to sort.
Maybe the 'optimize-search' switch for the best sorting-tree order and simple 'asc'/'desc' ones.
Re: Comparing config files
Posted: Sun Oct 09, 2022 8:18 pm
by pe1chl
I see no need to have customized sort keys, when people want fancy sorting they can do that themselves.
But what I want is some sorting so that the output is always the same for the same configuration (i.e. addresses in a list, addresses on interfaces, etc) no matter how that configuration was constructed.
Indeed for address lists one would like first sorted on listname, then on address. For other config items one would probably want to sort on interface name then on address (when present in the config).
Interestingly, VLAN interface config is sorted (by VLAN ID).
Re: Comparing config files
Posted: Mon Oct 10, 2022 2:20 am
by Sob
Right, don't overthink it, the goal is to have consistent output, any sorting would do. If I have two routers where I enter same config, only in different order, but functionally it's exactly the same, it would be nice to get same output from both that could be easily compared. On the other hand, it's not major problem, at least all data is there, I can deal with that. Unlike with the missing parts (certificates, ...).
This way I can compare previous day rcs/backup and see if its different.
If its not changed, do not send backup to external server.
Isn't it better to do it on server? Unless it's a special case where even little traffic matters. At least you'd know that it's still alive and whole backup process didn't get stuck. Well, yes, it's option only for text export.
But you know what? I think it would be best to have only text export, get rid of unreadable impractical binary backup completely. What advantage does that thing have anyway? None I can see. Sure, it does include things that text export doesn't, but it's not advantage of binary backup, it's bug/shortcoming in text export. It can also be encrypted, but so could be text export.
Re: Comparing config files
Posted: Mon Oct 10, 2022 9:54 am
by BartoszP
Just "dreaming" ... no need to implement anything else than predictible known order.
Re: Comparing config files
Posted: Mon Oct 10, 2022 11:33 am
by pe1chl
Just "dreaming" ... no need to implement anything else than predictible known order.
That is what I mean, and what Sob also understands. At the moment, the "predictable" order usually is "order of configuring the items", but that is often incovenient e.g. when comparing config of two routers using diff.
So what I propose is do
some sorting so that the order is always the same when the same configuration items are present. What the order exactly is does not really matter.
Re: Comparing config files
Posted: Mon Oct 10, 2022 11:37 am
by olivier2831
I see no need to have customized sort keys, when people want fancy sorting they can do that themselves.
But what I want is some sorting so that the output is always the same for the same configuration (i.e. addresses in a list, addresses on interfaces, etc) no matter how that configuration was constructed.
+1