2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-04 12:07:56 +00:00

27958 Commits

Author SHA1 Message Date
Daniel Hilst e08fe95959 Merge pull request #7799 from VersatusHPC/fix/ipmi-ipv4-literal-guardrail
fix(ipmi): reject ambiguous IPv4 literals
2026-09-03 20:27:14 -03:00
Daniel Hilst 9f4e53e380 Merge pull request #7800 from VersatusHPC/refactor/dhcp-omapi-command-runner
refactor(dhcp): share OMAPI command runner
2026-09-03 19:34:10 -03:00
Daniel Hilst 994601c4d6 Merge pull request #7815 from VersatusHPC/refactor/ubuntu-media-kernel-probe
refactor(debian): resolve the install kernel and initrd from a table
2026-09-03 19:32:59 -03:00
Vinícius Ferrão b97c6feb2d test(xCAT-test): cover the install kernel and initrd resolution
Build each Ubuntu media layout on disk and ask the resolver for its kernel and
initrd: the netboot trees, the flat netboot layout, both casper images, and the
POWER layouts where the kernel and the initrd sit in different directories.

Pin the precedence the installer depends on, a netboot tree over a live image
and a hardware-enablement kernel over the release one, and pin the three ways
media resolve to nothing.
2026-09-03 18:49:07 -03:00
Vinícius Ferrão 4312fe8337 refactor(debian): resolve the install kernel and initrd from a table
The probe spelled out every candidate path twice inside one nested condition,
once to test it and once to assign it, so adding an architecture meant adding
another branch of the same shape. Move the candidates into a table keyed by
architecture family and walk it in order.

Same paths, same precedence, same failure behaviour: a media tree that matches
nothing leaves the caller on the "install image not found" path as before.
2026-09-03 18:49:07 -03:00
Vinícius Ferrão 95defee20b test(dhcp): exercise source-owned OMAPI behavior
Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
2026-09-03 18:14:00 -03:00
Vinícius Ferrão 1a2bbda692 test(dhcp): verify OMAPI writer flush ordering
Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
2026-09-03 18:14:00 -03:00
Vinícius Ferrão 3836ae937e test(dhcp): cover OMAPI inherited handle safety
Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
2026-09-03 18:13:59 -03:00
Vinícius Ferrão b2b188302a fix(dhcp): protect inherited handles in OMAPI child
Use xCAT::Utils->xfork so child-side setup or exec failures do not destroy database handles inherited from the caller.

Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
2026-09-03 18:13:59 -03:00
Vinícius Ferrão 9910f4b92f test(dhcp): cover OMAPI command runner 2026-09-03 18:13:59 -03:00
Vinícius Ferrão d1bd0fe576 refactor(dhcp): share OMAPI command runner 2026-09-03 18:13:59 -03:00
Daniel Hilst 362bf5eb9f Merge pull request #7760 from VersatusHPC/fix/ubuntu-mn-ntp-daemon
fix(xcat-core): makentp fails on a stock Ubuntu MN (timesyncd cannot serve time)
2026-09-03 17:54:02 -03:00
Daniel Hilst 7003e0c0b6 Merge pull request #7761 from VersatusHPC/fix/ubuntu-subiquity-diskful-install
fix(xcat-core): the Ubuntu Subiquity diskful install never completes
2026-09-03 17:51:50 -03:00
Daniel Hilst a3e16d83f9 fix(networkutils): OnlyV4 is answered from the cache whatever family it holds
getipaddr returns a cached address before it resolves, and the bypass tests
OnlyV6 and GetAllAddresses only. A caller asking OnlyV4 therefore receives
whatever the first lookup stored. An unrestricted lookup asks for AF_UNSPEC, so
on a dual-stack management node it stores the AAAA record, and %::hostiphash is
a global in a long-lived xcatd.

debian.pm resolves the install server with OnlyV4 and writes
nfsroot=<address>:/install. Given the cached IPv6 that renders
nfsroot=2001:db8::1:/install, which does not parse, so the Subiquity installer
never mounts and the node never completes. dhcp.pm and mknb.pm hold four more
OnlyV4 callers with the same exposure.

Skip the cache when OnlyV4 is asked for and the cached address is IPv6. The
lookup that follows requests AF_INET and replaces the entry with the IPv4
address, so the next caller of either kind gets a usable one. An IPv4 entry is
still served from the cache: the bypass is about the family of the cached
answer, not about disabling the cache for OnlyV4.

networkutils_getipaddr_onlyv4.t fails without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 17:17:04 -03:00
Daniel Hilst 2b6337d67d test(networkutils): capture OnlyV4 being answered with a cached IPv6 address
getipaddr answers from %::hostiphash before it resolves. The bypass tests
OnlyV6 and GetAllAddresses and does not test OnlyV4, so a caller that asks for
IPv4 is handed whatever the first lookup cached.

An unrestricted lookup passes AF_UNSPEC to getaddrinfo, so on a dual-stack
management node with an AAAA record it caches the IPv6 address. xcatd is
long-lived and the hash is a global, so one earlier caller poisons every OnlyV4
caller after it.

debian.pm then writes nfsroot=2001:db8::1:/install, which is not a parseable
nfsroot, and the Subiquity install never mounts. dhcp.pm and mknb.pm hold four
more OnlyV4 callers with the same exposure.

The test also pins what the fix must not break: an IPv4 cache entry is still
served to an OnlyV4 caller, and an unrestricted caller still gets its cache hit
whatever family it holds.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 17:14:28 -03:00
Daniel Hilst efc3f53dbc Merge pull request #7759 from VersatusHPC/fix/xcatd-respawn-install-monitor
fix(xcat-core): a dead xcatd install monitor never comes back
2026-09-03 16:58:01 -03:00
Daniel Hilst a82d77fbc4 Merge pull request #7758 from VersatusHPC/fix/makedhcp-ubuntu-backend-and-query
fix(dhcp): makedhcp fails on a stock Ubuntu MN, and host-block scans match the wrong node
2026-09-03 16:57:42 -03:00
Daniel Hilst 3290358e48 Merge pull request #7726 from VersatusHPC/fix/remoteshell-sshd-config-drop-in
fix(remoteshell): stop rewriting the administrator's sshd_config
2026-09-03 14:55:51 -03:00
Daniel Hilst 39eb6ce532 Merge pull request #7767 from VersatusHPC/refactor/commandutils-executable-finder
refactor(utils): centralize executable lookup
2026-09-03 14:55:14 -03:00
Daniel Hilst eff0399a7b Merge pull request #7794 from VersatusHPC/refactor/ipmi-ipv4-command-encoding
fix(ipmi): centralize IPv4 command encoding
2026-09-03 14:53:21 -03:00
Daniel Hilst 05fc81f7b5 fix(subiquity): three values the Ubuntu install path accepts and cannot use
The boot flip in compute.subiquity.tmpl addressed port 3002. xcatd's install
monitor listens on site.xcatiport, so a cluster that moves the port loses the
flip and every node PXE-loops back into the installer. The flip now reads
site.xcatiport and keeps 3002 as the default. TABLEBLANKOKAY, because the key is
optional and a plain TABLE lookup of an absent key fails the whole template.

The flip also counted any reply as an accepted request. It now requires the
monitor's "ready" greeting before it sends "next", and "done" afterwards, so a
different service on that port is not read as a flipped node.

subiquity_nfsroot_server in debian.pm called getipaddr without a family. A
dual-stack management node answers with its IPv6 address, and casper takes
everything after the first colon in nfsroot= as the path, so the live filesystem
never mounts. It now asks for IPv4, as dhcp.pm and mknb.pm do.

The DNS setup wrote the xcatmaster name as a nameserver when getent found no
address, which is the case the step exists to prevent. It now keeps the
resolv.conf DHCP gave the live installer.

ubuntu_subiquity_boot_flip.t, debian_subiquity_boot_params.t and
ubuntu_resolvconf_ip.t fail on the parent commit and pass here.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 14:52:49 -03:00
Daniel Hilst 912a633eef Merge pull request #7804 from VersatusHPC/refactor/getadapter-pci-scan
refactor(getadapter): share PCI adapter scan
2026-09-03 14:52:33 -03:00
Daniel Hilst 7876650d4c Merge pull request #7814 from VersatusHPC/refactor/confluent-first-row-flattening
refactor(confluent): share first-row attribute flattening
2026-09-03 14:50:59 -03:00
Daniel Hilst b60cc7feb6 Merge pull request #7785 from VersatusHPC/fix/remoteshell-kill-signal
fix(remoteshell): stop the ssh daemon before starting a new one
2026-09-03 14:47:21 -03:00
Daniel Hilst 403bf8ad7c Merge pull request #7812 from VersatusHPC/fix/zvm-command-continuation-redaction
fix(zvm): mask the whole continued COMMAND statement
2026-09-03 14:46:46 -03:00
Daniel Hilst 173cbea550 test(subiquity): three values the Ubuntu install path accepts and cannot use
The Subiquity diskful path builds three values from configuration, and nothing
checks that the value it builds is one the consumer can use.

The boot flip addresses port 3002. xcatd's install monitor listens on
site.xcatiport, so a cluster that moves the port loses the flip and every node
PXE-loops. The exchange also counts any reply as an accepted request, so a
different service on that port reads as a flipped node. xcatd greets with
"ready" and answers every request with "done".

subiquity_nfsroot_server resolves the install server without a family. A
dual-stack management node answers with its IPv6 address, and nfsroot=<v6>:<path>
is unparseable, because klibc nfsmount takes everything after the first colon as
the path.

The DNS setup writes the xcatmaster name as a nameserver when getent fails,
which is the case the step exists to prevent.

ubuntu_subiquity_boot_flip.t drives the template's own late-command against a
stand-in monitor. ubuntu_resolvconf_ip.t runs the DNS step and reads the file it
writes. debian_subiquity_boot_params.t drives the helpers with a dual-stack
resolver. All three fail on this commit.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 14:44:17 -03:00
Daniel Hilst 947c624b3c fix(xcat-core): makedhcp -q hides a dhcpd.conf read failure and loses InfiniBand addresses
`makedhcp -q <node>` on Ubuntu's ISC-limited releases answers "no DHCP reservation
found" when it cannot read dhcpd.conf. The operator reads that as a node without a
reservation. An InfiniBand node also gets an answer with no hardware address.

_query_isc_static_host in dhcp.pm read the file with an -r test and dropped a failed
open. It also matched only a "hardware ethernet" line, while _add_isc_static_host
writes "hardware infiniband" for an InfiniBand node and adds a twin declaration
between the same markers.

_read_isc_conf_lines now returns the read error, _query_isc_static_host returns it to
listnode, and listnode answers the caller with an error. The parser accepts any
hardware type and keeps the first declaration of the block. The path of dhcpd.conf and
the distribution name are package variables, so a test can drive the query and
listnode.

dhcp_isc_static_host_query.t covers the InfiniBand address, the twin declaration, the
unreadable file and the listnode answer. It fails without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 14:42:51 -03:00
Daniel Hilst 3249298cbd fix(xcat-core): site.ntpbackend is not documented anywhere
makentp reads site.ntpbackend to select the NTP daemon, and setupntp takes the same value as
--backend, but no help text names the attribute. An admin who needs ntpd on a host that has
chrony has no way to find out the attribute exists. site.dhcpbackend, which selects the DHCP
implementation the same way, is documented in the site table help.

The site table description in xCAT::Schema now carries ntpbackend beside ntpservers, with its
valid values and the auto default. The makentp man page lists it with the other site attributes
the command honors, and names the setupntp --backend option that carries the value to the nodes.

ntp_backend_selection.t reads the site help from the loaded schema and the makentp pod. Five
assertions fail without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 14:41:32 -03:00
Daniel Hilst 255c6a6b76 test(dhcp): makedhcp -q hides a dhcpd.conf read failure and loses InfiniBand addresses
The static host query reads dhcpd.conf when no configuration is in memory. When the
read fails the query returns nothing, and listnode reports "no DHCP reservation
found" -- the answer for a node that has no reservation. The operator cannot tell
the two apart.

The same query only reads a "hardware ethernet" line. An InfiniBand node declares
"hardware infiniband", so its query answer carries no hardware address. A twin
declaration inside the same markers must not replace the primary one either.

The new assertions drive the writer to build both InfiniBand shapes, set the path of
dhcpd.conf to a file that does not exist, and call listnode. A deletion that names a
hostname is asserted to keep the other declarations of the node.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 14:41:13 -03:00
Daniel Hilst 67c1059ad0 test(site): site.ntpbackend is not documented in the site table help
makentp reads site.ntpbackend to pick the NTP daemon, so an admin has to set it, but the
attribute appears in no help text. lsdef -t site -h and tabdump -d print the site table
description from xCAT::Schema, which documents site.dhcpbackend and says nothing about
ntpbackend. The makentp man page lists the site attributes the command honors and does not
list it either.

The selector test now reads the site help from the loaded schema and the makentp pod.

Five assertions fail: the attribute name, its three valid values, and the man page entry.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 14:41:03 -03:00
Daniel Hilst a6212e8384 fix(xcat-core): makentp and the NTP selector disagree on when chrony is usable
xCAT::NTP::Backend->available reported chrony as available on chronyd alone, while makentp
configured chrony only where systemctl was present too. On a host with chronyd and no systemctl
the selector returned chrony with no downgrade, makentp fell through to the ntpd path, and the
admin saw either a silent switch or "Please make sure ntpd is installed".

available now requires chronyd and systemctl for chrony, so the selector answers on the same
terms makentp acts on, and makentp branches on the name alone. choose therefore downgrades to
ntpd, or reports install, in the case it used to pass over. A commands argument injects the
command probe, in the same shape as the existing available argument.

ntp_backend_selection.t covers both commands. Six of its assertions fail without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 14:40:31 -03:00
Daniel Hilst b3d6e1f9aa test(ntp): the selector calls chrony available where makentp will not use it
xCAT::NTP::Backend->available reports chrony as available when chronyd exists. makentp
configures chrony only when systemctl exists as well, and setupntp hands over to ntpd without
it. On a host with chronyd and no systemctl the selector answers chrony, makentp takes the ntpd
path anyway, and the admin is told nothing.

ntp_backend_selection.t drives available and choose with the command probe injected, so a case
can hold chronyd present and systemctl absent without depending on what the test host has
installed.

Six assertions fail: available reports chrony on chronyd alone, and choose neither downgrades
nor asks for an install.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 14:40:07 -03:00
Daniel Hilst 670ea2df51 fix(setupntp): --backend ntpd configures a daemon that is not installed
setupntp falls back from chrony to ntpd when chronyd is absent, but not the other way. A node
told --backend ntpd, or --use-ntpd, that has only chrony execs setupntp.traditional, which
writes /etc/ntp.conf and calls startservice ntpserver for a daemon that is not there. The node
ends with no running time daemon and a warning in the log.

The ntpd branch of the backend selection now probes ntpd. When ntpd is absent and chronyd and
systemctl are both present, setupntp uses chrony and logs the change, the same way the chrony
branch already does.

makentp_ntp_deps.t covers both directions. The two ntpd cases fail without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 14:39:50 -03:00
Daniel Hilst 5d4dee34ae test(setupntp): the ntpd backend never falls back to chrony
setupntp treats the requested backend as a preference in one direction only. A node given
--backend chrony without chronyd uses ntpd, but a node given --backend ntpd without ntpd still
hands over to setupntp.traditional, which writes /etc/ntp.conf and starts a daemon that is not
installed.

The selection cases in makentp_ntp_deps.t now also make ntpd absent. The stub for check_executes
takes a list of absent commands, so a case can hide ntpd, or ntpd and chronyd together.

Two cases fail: --backend ntpd and --use-ntpd both keep ntpd where only chrony is present.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 14:39:34 -03:00
Daniel Hilst efa914c5af style(xcat-core): the respawn comments explain more than the code needs
The comments around the install monitor respawn retell the failure, defend the
design and repeat the same causal chain in three places. Reduce them to the
facts that are not visible at the site: the ordering rules, why there is no
attempt limit, and what each fork site inherits. The rest is in the commit
messages and the PR.

Comment only. RespawnUtils.pm loses 26 lines and no code changes; xcatd loses
comment lines only. Both unit test files still pass.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 09:22:26 -03:00
Daniel Hilst 424f297d4b fix(xcat-core): the respawned monitor holds client sockets open for good
The respawn is forked from the middle of the service loop, so the child
inherits @pendingconnections -- the client sockets the parent has accepted and
not yet handed to a worker. The monitor never serves one, and it outlives the
worker that does, so its copy keeps that client's socket open until the daemon
exits.

Close them in the child, next to the listener and the rescanplugins channel it
already drops.

xcatd_install_monitor.t runs the lifted respawn block against stand-in
descriptors and requires every pending connection to be closed. It fails
without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 09:09:22 -03:00
Daniel Hilst b7c1461f9b fix(xcat-core): a monitor death reaped at startup is never noticed
The install monitor is forked while generic_reaper is the SIGCHLD handler.
ssl_reaper is only installed once the main service loop starts, and
generic_reaper comes back whenever connections are throttled.

Only ssl_reaper cleared $pid_MON. A death reaped by generic_reaper left
$pid_MON holding a dead pid, and the service loop re-forks only when $pid_MON
is clear, so xcatiport stayed dead for the life of the daemon.

Move that accounting into reap_install_monitor and call it from both reapers.

xcatd_install_monitor.t runs both reapers over a dead child and requires each
to clear $pid_MON and fold the death into the pacing. The generic_reaper case
fails without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 09:09:22 -03:00
Daniel Hilst 4e44053f32 test(xcat-core): nothing covers what xcatd does when the monitor dies or is re-forked
The respawn tests cover the pacing in xCAT::RespawnUtils. Two things the daemon
itself has to do are untested.

A SIGCHLD handler that does not clear $pid_MON leaves xcatd holding a dead pid,
so the service loop never re-forks the monitor. Only ssl_reaper clears it, and
generic_reaper is the handler at startup and again while connections are
throttled.

The respawn block also runs from the middle of the service loop, so the child
inherits the client connections the parent has accepted but not yet dispatched.

xcatd cannot be loaded in a unit test, so the two reapers and the respawn fork
block are lifted out of the program text and run in a scratch package against
stand-in descriptors. generic_reaper fails, and so does the pending-connection
assertion; ssl_reaper passes and guards the path that already works.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 09:09:22 -03:00
Vinícius Ferrão ac77bec6fb test(getadapter): cover shared PCI adapter scan 2026-09-02 23:02:46 -03:00
Vinícius Ferrão 4aa1db0dce refactor(getadapter): share PCI adapter scan 2026-09-02 23:02:45 -03:00
Vinícius Ferrão 762e67d6cc test(utils): reuse repository file helpers 2026-09-02 22:58:22 -03:00
Vinícius Ferrão 0c0803d83a test(utils): reuse and cover executable lookup 2026-09-02 22:58:22 -03:00
Vinícius Ferrão 77c1694b03 refactor(utils): centralize executable lookup 2026-09-02 22:57:05 -03:00
Daniel Hilst a11bd9e43d fix(xcat-core): make makedhcp work on a stock Ubuntu management node
Fall back to an available DHCP backend on auto-selection. When the request is "auto"
and the backend chosen for this OS is not installed, use the other one if it is,
recording fallback_from so process_request can tell the operator which preferred
backend is missing. A backend the admin forced through site.dhcpbackend still fails
hard when absent, and "neither installed" still errors clearly. Fixes #7710.

Answer makedhcp -q from the static host block on Ubuntu's ISC-limited releases.
listnode now branches on _isc_static_host_fallback() before any omapi work and reads
the node's fixed-address and hardware ethernet straight out of dhcpd.conf, so the
query path never spawns the omshell its own write paths already avoid. A node with no
reservation is now reported rather than answered with silence.

Match the host-block markers exactly. _add_isc_static_host writes a fully determined
pair -- "#xCAT host declaration for <node> aka host <hostname> start" and the "}"
line carrying the matching end -- so both scans anchor on that whole shape through
shared _isc_host_start_re/_isc_host_end_re helpers. The previous /\Q$node\E\b.*/ also
matched at a hyphen, letting node "compute" act on "compute-01"'s block: the query
could return another node's address and the delete could remove another node's
reservation. _delete_isc_static_host also accepts an explicit line list now, so the
scan is unit testable without file-scoped state.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-02 21:29:33 -03:00
Daniel Hilst a5cc24c18f test(xcat-core): capture two makedhcp failures on Ubuntu
DHCP backend auto-selection fails hard when the backend it prefers is not installed.
The xcat metapackage's "isc-dhcp-server | kea" Depends guarantees isc-dhcp-server,
while auto-selection prefers kea on 22.04+, where kea arrives only through Recommends.
An apt run that skips recommends therefore leaves a node with isc only, auto picks the
absent kea, and makedhcp errors out on a freshly installed management node (#7710).

makedhcp -q re-enters omshell on the very releases whose write paths avoid it. xCAT
already records each reservation as a static host block in dhcpd.conf on Ubuntu's
ISC-limited releases because their ISC DHCP 4.4 omshell can wedge at 100% CPU and
never be reaped, but listnode still called _omshell_query_host unconditionally.

Both host-block scans also match the node name loosely: /\Q$node\E\b.* start$/ matches
at a hyphen, so "compute" matches the "compute-01" marker -- a query can answer with
another node's address and a delete can remove another node's reservation.

Cover all three: the auto-selection fallback, a query answered from dhcpd.conf without
omshell, and exact node-name matching for both the query and the delete.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-02 21:29:32 -03:00
Vinícius Ferrão 20ff3588d4 test(confluent): exercise explicit-node paths through plugin
Replace source-shape extraction with real preprocess and command execution. Model missing table rows with the same return shape as xCAT::Table.

Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
2026-09-02 20:06:00 -03:00
Vinícius Ferrão 17f5b06106 refactor(confluent): share first-row attribute flattening
Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
2026-09-02 19:32:29 -03:00
Daniel Hilst aa4f48f9f5 Merge pull request #7813 from VersatusHPC/test/confluent-drive-real-plugin
test(confluent): drive the switch topology export through the plugin
2026-09-02 19:22:20 -03:00
Daniel Hilst c72406cbd1 Merge pull request #7811 from VersatusHPC/fix/xml-parser-entity-expansion
fix(xcatd): refuse an XML request that carries a document type declaration
2026-09-02 19:10:48 -03:00
Daniel Hilst b4579ef459 Merge pull request #7802 from VersatusHPC/refactor/networkutils-ip-validation
refactor(networkutils): remove the legacy validate_ip helper
2026-09-02 15:04:20 -03:00