Commit Graph
2 Commits
Author SHA1 Message Date
Christian Manivong 5d193ba7e8 fix(ping): post the settings where the API expects them, not one node above
CI / test (3.11) (push) Failing after 7s
CI / test (3.10) (push) Failing after 12s
CI / test (3.9) (push) Failing after 6s
CI / test (3.12) (push) Failing after 18s
Every ping against a live OPNsense failed:

    ping job creation failed for 10.30.0.1: {'result': 'failed',
    'validations': {'ping.settings.hostname': 'A value is required.'}}

_ping_model_node read GET /api/diagnostics/ping/get and took the single
dict-valued key as the node to post under. The real model nests two levels:

    {"ping": {"settings": {"hostname": "", "fam": {"ip": {...}, "ip6": {...}},
                           "source_address": "", "packetsize": "", ...}}}

so the helper answered "ping" and the job was created with the fields sitting
where the settings node belongs. The hostname never arrived, and the firewall
said so on every single call.

_ping_model_path walks the whole chain of single-dict wrappers and stops at the
first level holding more than one key — the field level, where fam is a dict
too and one more step would land inside a form field. _wrap_in_model nests the
settings accordingly, so a one-level model keeps working and the default, for
when /get cannot be read, is what current firmware ships.

The tests missed this because FakePingAPI answered /get with a one-level model
and read the posted payload back through the same assumption: the fake agreed
with the code about a shape neither of them shares with a device. It now speaks
what an OPNsense speaks, reads the payload through the model path, and a second
test keeps the one-level case covered.

Verified against a live firewall: the job is accepted ({"result": "ok"}) and
10.30.0.1 answers 3 of 3 at 0.116 ms.

Closes #3
2026-08-22 15:44:56 +07:00
Christian Manivong f934cc0cfa feat(ping): implement ping and a batched ping_sweep over the diagnostics API
CI / test (3.10) (push) Failing after 19s
CI / test (3.11) (push) Failing after 8s
CI / test (3.12) (push) Failing after 11s
CI / test (3.9) (push) Failing after 8s
OPNsense has no synchronous ping endpoint. /api/diagnostics/ping is a job API —
create, start, read statistics, stop, remove — so a single ping costs five
requests and roughly a second of waiting, which makes the generic per-host
sweep from napalm-device-types unusable for a whole subnet.

The override exploits what the job API does offer instead: jobs are
independent and run on the firewall in parallel, and search_jobs reports all of
them in one response. A batch (32 by default) is created and started, waited
for once, harvested with a single request and then cleaned up, so waiting time
per batch is constant rather than linear in hosts. PING_SWEEP_MAX_TARGETS
bounds the sweep as a whole — this runs on production firewalls.

Two details the API forces: results are polled, because search_jobs signals the
running ping with SIGINFO and then parses whatever it has written so far, so
the first read of a healthy host can still show zero probes; and the model's
root node is read from /get rather than hardcoded, so a rename in a future
OPNsense release cannot silently break job creation.

Tested against mocked API responses only — no live device is reachable at the
moment, so the endpoint shapes come from the OPNsense sources (PingController,
scripts/interfaces/ping.py).
2026-08-13 16:51:05 +07:00