Are there plans (and is there sufficient community interest) for RouterOS to implement something equivalent to `configure replace` on Cisco/Arista or `load override` on Juniper?
I was able to find at least one reference to this from 2016 [1], but I'm hoping that there is more interest in this feature in the 7 years since.
The ability to do transactions to change the system configuration would also be welcome if the system got overhauled. The main use case for having partial or complete transactions (like 'configure replace') is to enable network automation workflows where the configuration is templated externally (via Ansible/Jinja2/some other method), copied to the device, and replaced while the device remains operational.
I appreciate the fact that this might be difficult to accomplish since unlike many of its competitors, RouterOS' configuration syntax isn't so much a declarative configuration as it is a scripting language. A given configuration file can have global side effects that produce different configuration outcomes under different circumstances.
Because of this, an approach to provide the same effect within the RouterOS framework might be to provide us with a new verb (along with `add`|`remove`|`get`|`set`) called `assert`. The purpose of this verb would be to have the effect of `add` if no such element exists or `set` for all of the fields whose values differ from the configured values.
Here is a concrete example. Right now, if I generate a configuration like `/interface/bridge add name="foo" protocol-mode="none"`, it will work correctly the first time. If I apply the configuration again, it would fail with the error that a bridge already exists with that name, even though the intent of the statement was to change nothing. Likewise, if I wanted to change the value of a field, I could instead do `/interface/bridge set [ find where name="foo" ] protocol-mode="rstp"`. This isn't too cumbersome, but if that was what my config was generated as originally, it would fail because there would be zero matching rows for `set` to operate on since the bridge doesn't exist.
This can of course be fixed with something like: `: if ([ /interface/bridge print count-only where name="foo"] > 0) do={ /interface/bridge set [ find where name="foo" ] protocol-mode="rstp" } else={ /interface/bridge add name="foo" protocol-mode="rstp" }`. But this starts to get unwieldy when the entire configuration has to be defended with all of this bespoke logic in order to be applied multiple times and produce idempotent results.
If we had a verb like `assert`, the configuration could simply be `/interface/bridge assert name="foo" protocol-mode="rstp"`. Any values that aren't provided after the `assert` will be set to their defaults (even if already set). Any values that are provided after the `assert` will be updated to match. I assume that RouterOS behind the scenes uses a hidden primary key in the operational datastructure to keep track of which entries are unique, so that can continue to be leveraged to figure out which of the provided values are supposed to indicate which item to operate on. So for example, two bridges are identical if their names are the same. There are some tricky cases like firewall filters which have an order within the chain but not one directly exposed in the configuration syntax (unlike, say, Cisco IOS sequence numbers). I'm sure a solution could be found.
The other major gap to close would be what to do with lines that were intended to be deleted. Today, the only way to accomplish this would be to `/bridge remove [find]` before adding everything back (disruptive therefore not an option) or to carefully track everything that needs to be removed (also problematic). In the second case the templated configuration would have to generate all of the conditional logic like above and then construct a complicated `where` clause to remove every other item. This is fine in theory except that adding a new `assert` to the templated configuration would require modification to the cleanup `remove [ find where <gigantic compound match clause> ]` statement. This quickly fouls up the complexity of the templating logic because the last line has to depend on the state of the previous lines. Very ugly and a big part of the reason to ask for this feature on the RouterOS side.
Fortunately, this too could be addressed by adding an `assert-only=true|false` (default: `false`) option to the `import` command. With `assert` and default `import` behavior, the device would behave somewhat like a configuration merge. In this case, the applied configuration using `assert` would only add and modify. With `assert-only=true` and a configuration full of `assert`, the parser would automatically keep track of items not evaluated by `assert` clauses during the `import` and at the end, automatically remove them. The result would be equivalent to `config replace` or `load override` in that the changes made would leave the device configured in a way exclusively described by the `assert`s.
There are probably more gotchas that would need to be addressed, but to me this seems like it would be a foundation on which Mikrotik could design this feature in a way that minimizes the amount of overhaul to the configuration/parsing mechanism and preserves backward compatibility.
Thanks for reading!
[1] viewtopic.php?t=110380