Skip to content

Add configuration management (Config/AsyncConfig)#383

Open
ggiesen wants to merge 1 commit into
luqasz:mainfrom
ggiesen:config-management
Open

Add configuration management (Config/AsyncConfig)#383
ggiesen wants to merge 1 commit into
luqasz:mainfrom
ggiesen:config-management

Conversation

@ggiesen

@ggiesen ggiesen commented Jul 12, 2026

Copy link
Copy Markdown

Summary

Adds a configuration management layer to librouteros, reachable via api.config(),
for RouterOS 7.x. It is implemented entirely over the binary API -- no SSH/terminal
access is required -- and adds no runtime dependencies (stdlib difflib only).

Config (sync) and AsyncConfig (async) provide:

  • export() -- read the running config. /export returns nothing when run directly
    over the API, so this exports to a temporary file and reads it back.
  • apply() / validate() -- /import a .rsc script, or dry-run it (verbose +
    dry-run, RouterOS 7.16+). Errors surface as TrapError with line and column.
  • compare() -- unified diff of running vs candidate (informational; the module-level
    compare() diffs two strings directly).
  • backup_save() / backup_load().
  • arm_rollback() / cancel_rollback() / rollback_pending() -- a device-side
    /system/scheduler + backup dead man's switch (commit-confirm style). Because the
    timer lives on the device it survives a lost session or a lock-out, with no safe-mode
    action limit.
  • replace() -- /system reset-configuration run-after-reset (wipe + reboot into a
    new config).

Why

Configuration management on RouterOS has historically been considered impractical over
the API (no config replace, no commit/rollback, safe mode is terminal-only). On
RouterOS 7.x the API is actually sufficient: /export file=... followed by reading the
/file contents retrieves a full export, /file/add + /import applies a script
(with errors reported), and a scheduler-based backup restore provides a rollback net
that survives a lock-out without needing safe mode.

Design notes

  • No diff engine. Applying a config needs no computed patch (/import runs the
    script's own actions). compare() is a plain textual diff for humans, not an
    executable patch, so it cannot silently misapply.
  • RouterOS has no native non-reboot commit/rollback, so the rollback timer lives on the
    device (a one-shot /system/scheduler that restores a backup), not on the client --
    it fires even if the client process dies.
  • replace() is destructive and has no automatic rollback (safe mode is ignored for
    resets, the run-after-reset script must finish within ~2 minutes); documented as such.

Testing

  • Unit tests (tests/unit/test_config.py): argument construction for every RouterOS
    command, diff/normalization logic, orchestration + temp-file cleanup (mocked helpers),
    and the query-builder helpers.
  • Integration tests (tests/integration/test_config.py): export round-trip, merge
    apply, dry-run validation, compare, and the arm/pending/cancel rollback lifecycle,
    both sync and async, gated to RouterOS 7.x.
  • Verified end to end against a CHR 7.21.5 instance, including the replace() reboot.
  • ruff check, ruff format --check and mypy are clean.

Sync/async parity is maintained throughout. Documentation added at docs/config.rst.

Adds a pure binary-API configuration management layer, reachable via
api.config(), for RouterOS 7.x. No SSH/terminal access is required.

Config and AsyncConfig provide:

- export(): read the running config (/export writes nothing directly over
  the API, so export to a temp file and read it back)
- apply()/validate(): import or dry-run a .rsc script; errors surface as
  TrapError with line and column
- compare(): unified diff of running vs candidate configuration
- backup_save()/backup_load()
- arm_rollback()/cancel_rollback()/rollback_pending(): a device-side
  /system/scheduler + backup dead man's switch (commit-confirm style) that
  survives a lost session or lock-out, with no safe-mode action limit
- replace(): /system reset-configuration run-after-reset (wipe + reboot)

RouterOS has no native non-reboot commit/rollback, so the rollback timer
lives on the device rather than the client.
@ggiesen
ggiesen force-pushed the config-management branch from 2641e29 to 7db58b6 Compare July 12, 2026 03:02
@luqasz

luqasz commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Hi

Thx for PR. I will review it in few days. Please be patient.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants