@tikoci/centrs 0.1.9-next.0 → 0.1.9-next.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +177 -40
- package/GLOSSARY.txt +32 -0
- package/README.md +12 -8
- package/SECURITY.md +11 -3
- package/commands/api/README.md +39 -15
- package/commands/api/examples.md +44 -6
- package/commands/execute/README.md +108 -14
- package/commands/execute/examples.md +137 -3
- package/commands/explain/README.md +430 -129
- package/commands/explain/examples.md +260 -1
- package/commands/mcp/README.md +11 -0
- package/commands/mcp/examples.md +6 -0
- package/commands/retrieve/README.md +30 -13
- package/commands/retrieve/examples.md +10 -6
- package/commands/transfer/examples.md +9 -0
- package/docs/CLI.md +8 -8
- package/docs/CONSTITUTION.md +29 -2
- package/docs/MATRIX.md +26 -13
- package/docs/errors/cdb/empty-group.md +19 -3
- package/docs/errors/cdb/empty-selection.md +9 -3
- package/docs/errors/cmr/caller-policy-not-enforced.md +46 -0
- package/docs/errors/cmr/empty-selection.md +17 -3
- package/docs/errors/cmr/rule-selects-every-device.md +32 -0
- package/docs/errors/cmr/selection-includes-controller.md +27 -0
- package/docs/errors/usage/flag-after-separator.md +34 -0
- package/package.json +10 -5
- package/src/api-fanout.ts +11 -1
- package/src/api.ts +205 -65
- package/src/btest.ts +7 -2
- package/src/cli/api.ts +30 -16
- package/src/cli/common.ts +180 -1
- package/src/cli/devices.ts +0 -2
- package/src/cli/discover.ts +0 -1
- package/src/cli/execute.ts +44 -20
- package/src/cli/explain.ts +2 -0
- package/src/cli/mcp.ts +0 -1
- package/src/cli/retrieve.ts +40 -18
- package/src/cli/settings.ts +0 -3
- package/src/cli/terminal.ts +0 -2
- package/src/cli/transfer.ts +10 -8
- package/src/cli/version.ts +87 -0
- package/src/cli.ts +5 -3
- package/src/core/cmr-fleet.ts +486 -0
- package/src/core/cmr.ts +78 -0
- package/src/core/envelope.ts +32 -3
- package/src/core/error-catalog.ts +25 -3
- package/src/core/execute-command.ts +144 -19
- package/src/core/fanout.ts +130 -6
- package/src/errors.ts +2 -1
- package/src/execute-cmr.ts +95 -78
- package/src/execute-fanout.ts +11 -1
- package/src/execute.ts +105 -23
- package/src/explain/args.ts +69 -8
- package/src/explain/blocks.ts +3 -4
- package/src/explain/brace-slots.ts +5 -0
- package/src/explain/catalog.ts +13 -6
- package/src/explain/defects.ts +1 -0
- package/src/explain/group-faults.ts +927 -0
- package/src/explain/highlight-projection.ts +10 -8
- package/src/explain/operators.ts +20 -19
- package/src/explain/pathresolve.ts +102 -20
- package/src/explain/quoted-string.ts +41 -6
- package/src/explain/run-tokens.ts +223 -0
- package/src/explain/scope-brace.ts +103 -6
- package/src/explain/segment.ts +41 -3
- package/src/explain/spaced-args.ts +116 -0
- package/src/explain/symbols.ts +17 -1
- package/src/explain/transport.ts +13 -0
- package/src/explain/verbsplit.ts +14 -188
- package/src/explain/write.ts +8 -1
- package/src/explain.ts +378 -18
- package/src/index.ts +18 -1
- package/src/mcp/safety.ts +11 -0
- package/src/mcp/tools.ts +4 -1
- package/src/offline-gate.ts +30 -9
- package/src/protocols/btest-session.ts +30 -10
- package/src/protocols/mac-telnet-console.ts +16 -0
- package/src/resolver/cdb.ts +13 -1
- package/src/resolver/index.ts +4 -0
- package/src/resolver/selection.ts +37 -1
- package/src/resolver/target.ts +96 -8
- package/src/retrieve-fanout.ts +4 -1
- package/src/retrieve-follow.ts +29 -33
- package/src/retrieve-sample.ts +14 -8
- package/src/retrieve-stream.ts +38 -6
- package/src/retrieve.ts +73 -59
- package/src/transfer-fanout.ts +8 -1
- package/src/transfer.ts +15 -19
|
@@ -47,12 +47,23 @@ selection**, examples F1–F5). `romon` and `winbox-terminal` remain
|
|
|
47
47
|
write-shaped operation. In script mode every command head in a statement
|
|
48
48
|
(the statement start and each `[…]` or `{…}` body) must be a read: its verb
|
|
49
49
|
is the first word past the known menu prefix, so `/ip address print` is a
|
|
50
|
-
read and `/user expire-password print` is not (examples W3).
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
read and `/user expire-password print` is not (examples W3). Script
|
|
51
|
+
directives that only output, branch or compute a value are reads, including
|
|
52
|
+
the pure functions `:serialize`, `:deserialize`, `:convert`, `:delay`,
|
|
53
|
+
`:find`, `:timestamp`, `:rndnum`/`:rndstr` and the `:to*` converters, and the
|
|
54
|
+
`{…}`-bodied `:retry` and `:time` (example W4, #458). The absolute
|
|
55
|
+
`/serialize` spelling counts too; a bare `serialize` keeps the menu-verb rule,
|
|
56
|
+
because after navigation it could name a menu command. RouterOS runs only a
|
|
57
|
+
`{…}` block in a script-typed argument, so every body is visible to the gate.
|
|
58
|
+
Root mutators such as `/import` and indirect
|
|
59
|
+
execution through `:execute` are write-shaped too, as are `:global`, `:set`
|
|
60
|
+
and `:parse`, and so is any command carrying `file=` or `file-name=`
|
|
61
|
+
(including argument prefixes such as `fi=` and `file-n=`, and the spaced
|
|
62
|
+
`file-name value` spelling under print/export/serialize, example W5):
|
|
63
|
+
RouterOS redirects output into a device file, so
|
|
64
|
+
`/ip/address/export file=address` creates `address.rsc`,
|
|
65
|
+
`/file/print file=test` creates `test.txt` and `:serialize … file-name=x`
|
|
66
|
+
creates `x`, even though `export`, `print` and `:serialize` are reads. The shared
|
|
56
67
|
`rosetta`/`lsp-routeros-ts`
|
|
57
68
|
canonicalizer is for canonicalization only — never the structured predicate
|
|
58
69
|
(widening `structured` is a product regression). Pinned by
|
|
@@ -180,7 +191,9 @@ duplicate that table. Behavior notes the generated reference cannot carry:
|
|
|
180
191
|
- `--` — use it when the command contains tokens that would otherwise be
|
|
181
192
|
claimed as centrs flags; otherwise the command must be quoted as a single
|
|
182
193
|
argument. In fan-out, positional targets come BEFORE `--` and the command
|
|
183
|
-
AFTER it (see **Target selection**).
|
|
194
|
+
AFTER it (see **Target selection**). centrs flags go before `--` too: one of
|
|
195
|
+
execute's own long flags after it (a trailing `--json`) is refused before
|
|
196
|
+
dialing as `usage/flag-after-separator`, with the corrected line (#459).
|
|
184
197
|
|
|
185
198
|
### Designed, not implemented
|
|
186
199
|
|
|
@@ -296,8 +309,12 @@ when the target is itself a CMR client.
|
|
|
296
309
|
selects the controller. CDB selectors (`--group`/`--where`/`--all`/
|
|
297
310
|
`--default`/geo), several targets, and `--via mac-telnet|ssh` are
|
|
298
311
|
`usage/conflicting-flags`. `--labels` without `--cmr` is refused the same way.
|
|
299
|
-
Labels that no device has are accepted silently by RouterOS
|
|
300
|
-
selection is `ok: true` with the warning `cmr/empty-selection
|
|
312
|
+
Labels that no device has are accepted silently by RouterOS. An empty
|
|
313
|
+
selection is `ok: true` with the warning `cmr/empty-selection` for a read,
|
|
314
|
+
and the outer error `cmr/empty-selection` for a write-shaped command, with
|
|
315
|
+
or without `--yes` (#460), as RouterOS refuses it (`no devices selected`). A
|
|
316
|
+
mistyped label then exits 1 instead of looking like a successful bulk write.
|
|
317
|
+
A device's
|
|
301
318
|
identity selects it only while the controller's `/cmr auto-labels` includes
|
|
302
319
|
`identity` (the default `all` does). On 7.26beta1, after `auto-labels=version`
|
|
303
320
|
a client's row still *showed* its identity label but no longer matched it.
|
|
@@ -336,16 +353,33 @@ when the target is itself a CMR client.
|
|
|
336
353
|
- **Write gate:** one `--yes`/TTY confirmation, naming how many clients will run
|
|
337
354
|
it, how many were excluded, and through which controller. Clients that failed
|
|
338
355
|
validation are never sent the command.
|
|
356
|
+
- **The controller login is not the limit (#456).** On 7.26beta1, RouterOS
|
|
357
|
+
does not check the caller's policy against a CMR script. `run-script`
|
|
358
|
+
needed only `read` + `api` on the controller login, and
|
|
359
|
+
`/cmr/device/reboot` only `read` + `test` + `api`. A login refused a direct
|
|
360
|
+
write (`not enough permissions (9)`) still wrote to clients and to the
|
|
361
|
+
controller's own row through `run-script`, and rebooted a client through
|
|
362
|
+
both. So a "reader" login on a 7.26beta1 controller is not read-only, and
|
|
363
|
+
centrs' write gate (and `mcp=ro` on MCP) is the only gate. MikroTik
|
|
364
|
+
[says](https://forum.mikrotik.com/t/7-26beta-development-is-released/273582/139) 7.26beta2 refuses this for a `read` group; that is not yet
|
|
365
|
+
checked on a beta2 build. For a write,
|
|
366
|
+
centrs reads the login's own `/user` row and `/user/group` once (both are
|
|
367
|
+
readable with just `read` + `api`). When the login lacks `write`, `test` or
|
|
368
|
+
`reboot`, the result and the confirmation error carry
|
|
369
|
+
`cmr/caller-policy-not-enforced` naming what it lacks. A failed read only
|
|
370
|
+
loses the warning.
|
|
339
371
|
- **Dispatch:** one call per validated member, sending the input unchanged with
|
|
340
372
|
`timeout=` set from `--timeout` (capped at 55 s, under CMR's ~60 s per-call
|
|
341
373
|
ceiling). Every call pins `strategy=parallel fail-policy=continue
|
|
342
374
|
freeze-frame-interval=20ms`. **Never retried.**
|
|
343
375
|
- **Outcomes** are a `FanoutData` envelope in inventory order, with exit codes
|
|
344
|
-
0/2/1.
|
|
345
|
-
|
|
376
|
+
0/2/1. The outer `meta.target` is the controller, and the settings,
|
|
377
|
+
request and login every member shares are written once on the outer
|
|
378
|
+
envelope ([constitution](../../docs/CONSTITUTION.md#result-envelope)). Each
|
|
379
|
+
member's `meta.target.cmr` carries `{id, deviceKey, address}`. `success` → `data.ret` holds the client's output; a clipped
|
|
346
380
|
output adds `cmr/output-truncated` and `meta.truncated`. `--max-results`
|
|
347
381
|
applies per member, as in plain execute fan-out: a member whose rendered
|
|
348
|
-
envelope exceeds it (UTF-8 bytes) becomes `input/max-results-exceeded`, and
|
|
382
|
+
envelope (with its full meta, as it would render alone) exceeds it (UTF-8 bytes) becomes `input/max-results-exceeded`, and
|
|
349
383
|
that error says the command already ran (`context.dispatched: true`). A `failed` runtime
|
|
350
384
|
error maps through the usual `routeros/*` table. `skipped` →
|
|
351
385
|
`cmr/client-disconnected`.
|
|
@@ -366,8 +400,15 @@ when the target is itself a CMR client.
|
|
|
366
400
|
- **Validation metadata** reports what ran: `source: ":put [:parse ...] via cmr
|
|
367
401
|
run-script"`, the same claim the console gate makes. There is no client
|
|
368
402
|
`/console/inspect` stage.
|
|
369
|
-
- **
|
|
370
|
-
|
|
403
|
+
- **No progress stream.** `run-script` returns a client's output only when its
|
|
404
|
+
script ends, and no `/cmr` menu records a running script. To watch a long
|
|
405
|
+
CMR job, follow what it changes on a client:
|
|
406
|
+
`centrs retrieve <client> /ip/address --follow` while
|
|
407
|
+
`execute <controller> --cmr` adds addresses. That only works when the client
|
|
408
|
+
can also be reached directly; otherwise re-run a read through `--cmr`.
|
|
409
|
+
- **Not on MCP** until delegated authorization is designed (#449). That design
|
|
410
|
+
must not assume RouterOS refuses a fleet write for a `!write` login (see
|
|
411
|
+
above). `retrieve --cmr` is deferred (#441).
|
|
371
412
|
|
|
372
413
|
`/cmr/device/dashboard` is an interactive console view, refused as
|
|
373
414
|
`cmr/console-only`. A plain path, abbreviated or not (`/cmr/dev/dash`), is
|
|
@@ -382,6 +423,59 @@ Calling `/cmr/device/run-script` yourself, through `api` or plain `execute`,
|
|
|
382
423
|
validates only the controller request. The envelope then carries the warning
|
|
383
424
|
`cmr/client-script-not-validated`.
|
|
384
425
|
|
|
426
|
+
### Fleet commands run directly (#453)
|
|
427
|
+
|
|
428
|
+
A controller's fleet commands are one request to one router that act on many
|
|
429
|
+
devices: `/cmr/device/{reboot,upgrade,pair,run-script}`,
|
|
430
|
+
`/cmr/upgrade/trigger`, `/cmr/upgrade/job/run-next`, and `add`, `set`,
|
|
431
|
+
`remove`, `enable` or `disable` on `/cmr/vlan`, `/cmr/wifi`,
|
|
432
|
+
`/cmr/wifi/radio` and `/cmr/upgrade`. They are write-shaped, so they need
|
|
433
|
+
`--yes`. Before that gate, plain `execute` (and `api`) reads the controller's
|
|
434
|
+
`/cmr/device` inventory, read-only, and resolves which rows the command
|
|
435
|
+
reaches, where it can (see "Not resolved" below). The command still runs
|
|
436
|
+
exactly as typed.
|
|
437
|
+
|
|
438
|
+
- **The gate names the reach.** The prompt and the `usage/confirmation-required`
|
|
439
|
+
summary say how many CMR devices the command reaches and whether the
|
|
440
|
+
controller's own row (`controller=true`) is one of them. `error.context.cmr`
|
|
441
|
+
has `devices`, `connected`, `controllerIncluded`, `identities` and
|
|
442
|
+
`inventory`.
|
|
443
|
+
- **How the selection is resolved.** `numbers` matches a row's `.id` or
|
|
444
|
+
identity. `labels` follows the manual's rules over the row's `labels` and
|
|
445
|
+
`auto-labels`: plain labels are OR, `+label` must match, `-label` excludes,
|
|
446
|
+
`all` selects every row, and an expression with only exclusions starts from
|
|
447
|
+
every row. On a 7.26beta1 lab this matched the device's own selection
|
|
448
|
+
(a `run-script` probe per row) for every expression tried; the lab test
|
|
449
|
+
re-checks it (CMR-D9). It is the controller's current view: a disconnected
|
|
450
|
+
device's `auto-labels` can be stale. `/cmr/upgrade/trigger` reaches the rows
|
|
451
|
+
whose `upgrade-rule` it names. A rule's `set`, `remove`, `enable` or
|
|
452
|
+
`disable` reads the rule's own `labels`; a `set` that changes `labels`
|
|
453
|
+
reaches the old and the new devices. WiFi band labels (`+2ghz`, `+5ghz`,
|
|
454
|
+
`+6ghz`) pick radios, not devices, so they are left out.
|
|
455
|
+
- **Warnings, with or without `--yes`:**
|
|
456
|
+
- `cmr/selection-includes-controller`: the selection includes the
|
|
457
|
+
controller's own row (`labels=all` does, per the manual).
|
|
458
|
+
- `cmr/rule-selects-every-device`: a rule command with no device `labels`
|
|
459
|
+
(for `/cmr/vlan`, no `port-labels` either). The manual warns that such a
|
|
460
|
+
VLAN rule provisions every port of every connected device, the controller
|
|
461
|
+
included, and cuts the fleet's management access.
|
|
462
|
+
- `cmr/empty-selection`: by the controller's inventory now, nothing is
|
|
463
|
+
selected. RouterOS refuses this for a `/cmr/device` command; it accepts
|
|
464
|
+
a rule whose labels match nothing.
|
|
465
|
+
- `cmr/caller-policy-not-enforced` for `run-script` and `reboot`, as for
|
|
466
|
+
`--cmr` above (#456).
|
|
467
|
+
- **Not resolved:** an abbreviated selector argument (`lab=all`),
|
|
468
|
+
`--via ssh` or `mac-telnet`, `/cmr/upgrade/job/run-next` (a job's devices
|
|
469
|
+
are not in the inventory), and a failed read. The gate still names the
|
|
470
|
+
fleet command and says why its reach is unknown, for example `would reach
|
|
471
|
+
the CMR devices it selects through controller ctl (not resolved: an upgrade
|
|
472
|
+
job's devices are not in the inventory)`, and `error.context.cmr` has
|
|
473
|
+
`unresolved` instead of the counts. A failed read never blocks the command,
|
|
474
|
+
and after a transport or login failure the policy read is skipped. Two
|
|
475
|
+
cases are not recognized as fleet commands at all and keep the plain
|
|
476
|
+
wording: script-shaped input (a positional id or `[find …]`) and CDB fan-out
|
|
477
|
+
to several controllers, which has its own gate.
|
|
478
|
+
|
|
385
479
|
## mac-telnet L2 validation
|
|
386
480
|
|
|
387
481
|
Decided (2026-06-06): the real-L2 integration path is `@tikoci/quickchr`'s
|
|
@@ -530,6 +530,25 @@ has no `result`, `syntax` or `semantic`. Its stages are `offline: passed`, then
|
|
|
530
530
|
`device: skipped` with `reason: "not reached: transport/auth-failed"`. RouterOS
|
|
531
531
|
answered 401 before `:parse` could give a verdict.
|
|
532
532
|
|
|
533
|
+
### V9. A stray `}` that RouterOS would silently discard is refused (#504)
|
|
534
|
+
|
|
535
|
+
A `}` or `]` that closes nothing at the top level makes RouterOS accept the
|
|
536
|
+
script and stop reading there: everything from that byte on is dropped without a
|
|
537
|
+
word. `/system identity set name=a}` sets the identity to `a`. `explain` reports
|
|
538
|
+
it as a `warning`, because nothing is rejected (`commands/explain/examples.md`
|
|
539
|
+
example 42). The gate refuses that one warning by code, because the script the
|
|
540
|
+
device would run is not the one that was written.
|
|
541
|
+
|
|
542
|
+
```bash
|
|
543
|
+
centrs execute $R ':put "first"};:put "second"' --via rest-api --username $U --password $P --json
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
Envelope: `ok: false`, `error.code=validation/syntax`,
|
|
547
|
+
`error.context.validationStage="offline"`, `error.context.span` covering the `}`
|
|
548
|
+
through the end of the input, and
|
|
549
|
+
`error.context.diagnostics[0].code="explain/canonicalizer/discarded-close"`. With
|
|
550
|
+
`--validate=false` the device runs it and prints only `first`.
|
|
551
|
+
|
|
533
552
|
## Write authorization and returned runtime faults
|
|
534
553
|
|
|
535
554
|
These regressions are transport-independent and run in
|
|
@@ -573,6 +592,47 @@ argument happens to be `print`, so it returns `usage/confirmation-required`.
|
|
|
573
592
|
Every command head in a statement is checked, including `[…]` and `do={…}`
|
|
574
593
|
bodies.
|
|
575
594
|
|
|
595
|
+
### W4. Pure script functions are reads; an output file is not (#458)
|
|
596
|
+
|
|
597
|
+
```bash
|
|
598
|
+
centrs execute $R ':put [:serialize to=json [/system/resource/print as-value]]' --via rest-api --username $U --password $P </dev/null
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
Envelope: `ok: true` without `--yes`; `data.ret` parses as JSON whose
|
|
602
|
+
`version` is the router's. `:delay`, `:convert`, `:deserialize`, `:find`,
|
|
603
|
+
`:timestamp`, `:rndnum`, `:rndstr`, the `:to*` converters, `:retry` and `:time` are reads
|
|
604
|
+
too; any command inside their arguments or `{…}` bodies is still checked.
|
|
605
|
+
RouterOS runs only a `{…}` block in a script-typed argument and refuses a quoted
|
|
606
|
+
string there, so no body hides in text. `:serialize … file-name=centrs-w4`
|
|
607
|
+
creates a device file, so it returns `usage/confirmation-required`, as `file=`
|
|
608
|
+
does under a read verb. `:global`, `:set`, `:execute` and `:parse` stay writes.
|
|
609
|
+
|
|
610
|
+
### W5. Abbreviated output-file arguments keep the write gate
|
|
611
|
+
|
|
612
|
+
```bash
|
|
613
|
+
centrs execute $R '/system identity export fi=centrs-w5' --via rest-api --username $U --password $P </dev/null
|
|
614
|
+
centrs execute $R ':serialize to=json value=1 file-n=centrs-w5.json' --via rest-api --username $U --password $P </dev/null
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
Both return `usage/confirmation-required` and create no file. The same
|
|
618
|
+
commands with `--yes` pass validation and create `centrs-w5.rsc` and
|
|
619
|
+
`centrs-w5.json`, respectively. Native API has the same result. Through
|
|
620
|
+
`centrs_execute`, an `mcp=ro` record returns `cdb/write-not-permitted` with or
|
|
621
|
+
without `confirm: true`, and creates no file.
|
|
622
|
+
|
|
623
|
+
RouterOS also accepts `fi "centrs-w5"` and `file-n "centrs-w5.json"`, without
|
|
624
|
+
`=`. They keep the same confirmation and MCP policy gates, including when
|
|
625
|
+
serialize's file argument follows a nested value expression.
|
|
626
|
+
|
|
627
|
+
`test/integration/execute-write-gate.test.ts` proves the refusal, absence and
|
|
628
|
+
confirmed creation. The export control runs on the selected channel; the
|
|
629
|
+
serialize control pins CHR 7.26beta1, where its file output was reproduced.
|
|
630
|
+
The export uses CLI script spelling: structured attribute validation requires
|
|
631
|
+
full argument names and rejects `fi` as `validation/unknown-attribute`.
|
|
632
|
+
This is a grounding version, not an introduction boundary. Unknown or
|
|
633
|
+
ambiguous output-file prefixes stay write-shaped until the device resolves
|
|
634
|
+
them.
|
|
635
|
+
|
|
576
636
|
### R1. A post-validation `/execute` rejection fails the overall envelope
|
|
577
637
|
|
|
578
638
|
```bash
|
|
@@ -622,6 +682,16 @@ centrs execute --group no-such-group --via rest-api --cdb-file $CDB --json -- /s
|
|
|
622
682
|
`ok: true`, `data.summary = { total: 0, ok: 0, failed: 0 }`,
|
|
623
683
|
`warnings` include `cdb/empty-group`. Exit `0`.
|
|
624
684
|
|
|
685
|
+
The same group with a write is an outer error, with or without `--yes`, so a
|
|
686
|
+
misspelled group cannot pass for a successful bulk write (#460):
|
|
687
|
+
|
|
688
|
+
```bash
|
|
689
|
+
centrs execute --group no-such-group --via rest-api --cdb-file $CDB --yes --json -- '/system/identity/set name=x'
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
`ok: false`, `error.code` `cdb/empty-group`, `error.context.write` `true`.
|
|
693
|
+
Exit `1`. Nothing is dialed.
|
|
694
|
+
|
|
625
695
|
### F3. `--where` device-class selector (subset)
|
|
626
696
|
|
|
627
697
|
```bash
|
|
@@ -763,7 +833,7 @@ transport.
|
|
|
763
833
|
### Q3. Repeated `--quickchr` fans out; an unknown machine is an inner failure
|
|
764
834
|
|
|
765
835
|
```bash
|
|
766
|
-
centrs execute --quickchr $NAME --quickchr no-such-machine -- ':put 1'
|
|
836
|
+
centrs execute --quickchr $NAME --quickchr no-such-machine --json -- ':put 1'
|
|
767
837
|
```
|
|
768
838
|
|
|
769
839
|
Envelope: outer `ok: true`, `data.summary = { total: 2, ok: 1, failed: 1 }`;
|
|
@@ -775,9 +845,9 @@ the unknown machine's inner error is `quickchr/machine-not-found`. Exit code 2
|
|
|
775
845
|
`test/integration/execute-cmr.test.ts` runs these on a two-CHR CMR lab
|
|
776
846
|
(`test/integration/cmr-lab.ts`): `$CTL` is a 7.26beta1 controller with the
|
|
777
847
|
`cmr` package, paired with one client over a socket link. Examples CMR1–CMR7
|
|
778
|
-
and CMR10–
|
|
848
|
+
and CMR10–CMR18 run over **both** `--via native-api` and `--via rest-api` to
|
|
779
849
|
the controller, with validation on. The same test first asserts the device
|
|
780
|
-
behaviour the design depends on (CMR-D1–
|
|
850
|
+
behaviour the design depends on (CMR-D1–D9 in the test file).
|
|
781
851
|
|
|
782
852
|
### CMR0. A router without CMR
|
|
783
853
|
|
|
@@ -856,6 +926,16 @@ centrs execute --quickchr $CTL --cmr --labels no-such-label -- ':put x'
|
|
|
856
926
|
`ok: true`, `data.summary.total` is 0, warning `cmr/empty-selection`, exit
|
|
857
927
|
code 0.
|
|
858
928
|
|
|
929
|
+
The same labels with a write are an outer error, with or without `--yes`, as
|
|
930
|
+
RouterOS itself refuses an empty selection (#460):
|
|
931
|
+
|
|
932
|
+
```bash
|
|
933
|
+
centrs execute --quickchr $CTL --cmr --labels no-such-label --yes -- '/system/note/set note="x"'
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
`ok: false`, `error.code` `cmr/empty-selection`, `error.context.write` `true`,
|
|
937
|
+
exit code 1. No note changes.
|
|
938
|
+
|
|
859
939
|
### CMR8. CLI exit code for a mixed result
|
|
860
940
|
|
|
861
941
|
CMR2 through `runCli` exits `2`.
|
|
@@ -920,6 +1000,21 @@ centrs execute --quickchr <client> --cmr --labels all -- ':put x'
|
|
|
920
1000
|
`cmr/not-a-controller`: the client runs 7.26beta1 but has no `cmr` package.
|
|
921
1001
|
`context.controllerIdentity` names the controller it is paired to.
|
|
922
1002
|
|
|
1003
|
+
### CMR15. A controller login without `write` or `reboot`
|
|
1004
|
+
|
|
1005
|
+
The controller has a group with `read,test,api,rest-api,!write,!reboot,!sensitive,!policy`
|
|
1006
|
+
and a user `$READER` in it. That login cannot write on the controller
|
|
1007
|
+
directly, but:
|
|
1008
|
+
|
|
1009
|
+
```bash
|
|
1010
|
+
centrs execute 127.0.0.1 --port $API --username $READER --password $PW --cmr --labels <client> --yes -- '/system/note/set note="<marker>"'
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
The target is `ok`, the client's note reads `<marker>`, and the envelope warns
|
|
1014
|
+
`cmr/caller-policy-not-enforced` with `context.missing` `["write", "reboot"]`:
|
|
1015
|
+
CMR ran the script with `read,write,test,reboot` (#456). The same write as the
|
|
1016
|
+
lab's admin login has no such warning.
|
|
1017
|
+
|
|
923
1018
|
### CMR13. The dashboard behind a relative menu change
|
|
924
1019
|
|
|
925
1020
|
```bash
|
|
@@ -931,3 +1026,42 @@ The first is `cmr/console-only`: the controller's `:parse` resolves it to
|
|
|
931
1026
|
`/cmr/device/;(evl /cmr/device/dashboard)`, so it is refused before it runs.
|
|
932
1027
|
The second is `ok` with `data.ret` `CMR device dashboard`: a string that spells
|
|
933
1028
|
the words is not a command.
|
|
1029
|
+
|
|
1030
|
+
### CMR16. A fleet command names the devices it reaches (#453)
|
|
1031
|
+
|
|
1032
|
+
```bash
|
|
1033
|
+
centrs execute --quickchr $CTL -- '/cmr/device/reboot labels=all'
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
Without `--yes` (no TTY) this is `usage/confirmation-required`. Its summary
|
|
1037
|
+
says it would reach 2 CMR devices, the controller itself among them, and
|
|
1038
|
+
`error.context.cmr` is `{devices: 2, controllerIncluded: true}` with both
|
|
1039
|
+
identities. The envelope warns `cmr/selection-includes-controller`.
|
|
1040
|
+
Nothing reboots.
|
|
1041
|
+
|
|
1042
|
+
### CMR17. A rule with no labels
|
|
1043
|
+
|
|
1044
|
+
```bash
|
|
1045
|
+
centrs execute --quickchr $CTL --yes -- '/cmr/vlan/add vlan-ids=4001 disabled=yes comment=<marker>'
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
`ok: true` with the warning `cmr/rule-selects-every-device`: the rule has no
|
|
1049
|
+
`labels` and no `port-labels`. It is added disabled, so nothing is
|
|
1050
|
+
provisioned. Removing it by its `.id` warns the same way, read from the rule:
|
|
1051
|
+
|
|
1052
|
+
```bash
|
|
1053
|
+
centrs execute --quickchr $CTL --yes -- '/cmr/vlan/remove numbers=<id>'
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
### CMR18. `run-script` called directly by a login without `write` or `reboot`
|
|
1057
|
+
|
|
1058
|
+
With CMR15's restricted login:
|
|
1059
|
+
|
|
1060
|
+
```bash
|
|
1061
|
+
centrs execute 127.0.0.1 --port $API --username $READER --password $PW --yes -- '/cmr/device/run-script labels=<client> script=":put centrs-cmr18"'
|
|
1062
|
+
```
|
|
1063
|
+
|
|
1064
|
+
`ok: true`, with the warnings `cmr/caller-policy-not-enforced`
|
|
1065
|
+
(`context.missing` `["write", "reboot"]`) and
|
|
1066
|
+
`cmr/client-script-not-validated`. The same command as the lab's admin login
|
|
1067
|
+
has neither the policy warning nor `cmr/selection-includes-controller`.
|