You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
changed the title [-]NethVoice: Review UDP Port Assignment and Limit Concurrent Calls[/-][+]NethVoice: Review UDP/TCP Port Assignment and Limit Concurrent Calls[/+]on Jun 10, 2025
Replaces the previous test cases (they targeted an older version of the PR).
This PR requests a 2003-port UDP block + 36 TCP ports from ns8-core and
derives every consumer from it. SIP UDP is distinct from SIP TCP.
UDP offset
Service
+0 … +999
Asterisk RTP
+1000 … +1999
Janus RTP
+2000
Satellite RTP
+2001
Asterisk IAX
+2002
Asterisk SIP UDP
Update baselines — a temporary fix (1.6.8, commit 4202d5ad) made Satellite
reuse SATELLITE_HTTP_PORT and unset the dedicated SATELLITE_RTP_PORT; this PR
re-introduces it at +2000. Updates must migrate correctly from both origins:
api-cli run module/<ID>/configure-module --data '{"nethvoice_host":"pbx.<domain>","nethcti_ui_host":"cti.<domain>","user_domain":"<domain>","timezone":"Europe/Rome","reports_international_prefix":"39","nethvoice_adm_username":"admin","nethvoice_adm_password":"<password>"}'
journalctl _UID=$(id -u <ID2>) | grep -i "address already in use"→ no matches (includes DUNDI 4520 and Janus demo 5002/5102).
B2 — Removing an instance frees its ports
remove-module <ID2>
sqlite3 /var/lib/nethserver/node/state/ports.sqlite "SELECT * FROM UDP_PORTS;"→ no rows left for <ID2>.
Update tests
Before each update, set a marker to check persistence: create a test extension in the PBX and note the admin password.
C1 — Update from 1.7.4 (current stable)
Install and configure a 1.7.4 instance.
Confirm the old state: runagent -m <ID> env | grep SATELLITE_RTP_PORT→ empty (unset).
Update:
api-cli run update-module --data '{"module_url":"<IMG>","instances":["<ID>"],"force":true}'
→ rc=0.
runagent -m <ID> env | grep SATELLITE_RTP_PORT→ now set (Satellite has its own port again).
runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports'→ UDP transport on the new ASTERISK_SIP_UDP_PORT, no manual restart needed.
Test extension still present, admin login still works → data preserved.
Run the same update command again → rc=0, nothing changes (idempotent).
C2 — Update from 1.6.7 (before the temporary fix)
Install and configure a 1.6.7 instance.
Confirm the old state: runagent -m <ID> env | grep SATELLITE_RTP_PORT→ set (old dedicated port).
Update to <IMG> (same command as C1 step 3). → rc=0.
runagent -m <ID> env | grep -E 'SATELLITE_RTP_PORT|UDP_PORTS_RANGE'→ Satellite port is inside the new UDP block; no stale old value.
Test extension still present, admin login still works → data preserved.
C3 — Update next to an existing instance (collision)
On one node: one PR instance + one legacy instance (1.7.4 or 1.6.7) with overlapping old ranges.
Update the legacy instance to <IMG>.
Compare UDP_PORTS_RANGE of the two instances → non-overlapping.
journalctl _UID=$(id -u <ID>) | grep -i "address already in use"→ no matches.
C4 — Update to a wrong image fails cleanly
api-cli run update-module with a non-existent module_url. → fails; the instance keeps working on the current image.
D1 — Clone to another node
Clone: api-cli run clone-module --data '{"module_id":"<ID>","node":2,"replace":false}'→ rc=0. Note <ID_DST> on node 2; configure it.
Source still works (check its UI/services).
On the destination: runagent -m <ID_DST> podman exec freepbx asterisk -rx 'pjsip show transports' → UDP transport on the destination ASTERISK_SIP_UDP_PORT, TCP transport on the destination ASTERISK_SIP_PORT — UDP ≠ TCP.
runagent -m <ID_DST> env | grep UDP_PORTS_RANGE→ different range from the source.
D2 — Clone with FreePBX offline (config rewrite)
Run the clone while the destination FreePBX is not running (offline path). → completes with no "sed not found" / exit 127.
E1 — Move to another node
Move: api-cli run clone-module --data '{"module_id":"<ID>","node":2,"replace":true}'→ rc=0.
runagent -m <ID_DST> env | grep MODULE_UUID and the source → no duplicate UUID; source removed.
Destination ports correct: runagent -m <ID_DST> podman exec freepbx asterisk -rx 'pjsip show transports'→ on the destination SIP UDP/TCP ports.
F1 — Backup and restore
Set a marker (test extension), then back up: api-cli run cluster/run-backup --data '{"id":<repo>}'→ rc=0.
Restore: api-cli run restore-module --data '{"repository":"<uuid>","path":"<path>","snapshot":"","node":<NODE>,"replace":false}'→ rc=0 (does not abort).
Marker present and admin login works → data restored.
runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports'→ SIP UDP/TCP on the restored instance's ports.
Satellite is running: runagent -m <ID> systemctl --user status satellite→ active (running) (Satellite PostgreSQL restored).
F2 — Restore in place (replace=true)
Restore over an existing instance with "replace":true. → rc=0; instance works; ports correct.
G1 — FreePBX restart keeps ports
runagent -m <ID> systemctl --user restart freepbx
runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports'→ SIP UDP still on ASTERISK_SIP_UDP_PORT.
G2 — Reconfigure doesn't drop calls
Run configure-module again changing a non-port setting (e.g. an integration).
runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports'→ ports unchanged; Asterisk not restarted unnecessarily.
H1 — Remove frees everything
remove-module <ID>
sqlite3 /var/lib/nethserver/node/state/ports.sqlite "SELECT * FROM UDP_PORTS;" and TCP_PORTS→ no rows for <ID>.
J1 — Real call (final gate)
Register two extensions on a fresh PR instance and place a call between them. → SIP registers on the allocated ports; two-way audio works.
Notes
Clone/move atomicity (no partial destination, no duplicate live UUID) and the
allocator capacity limit depend on ns8-core — verify at integration level.
The real call (J1) is the final gate before merge.
changed the title [-]NethVoice: Review UDP/TCP Port Assignment and Limit Concurrent Calls[/-][+]NethVoice: Review UDP/TCP Port Assignment[/+]on May 8, 2026
Activity
[-]NethVoice: Review UDP Port Assignment and Limit Concurrent Calls[/-][+]NethVoice: Review UDP/TCP Port Assignment and Limit Concurrent Calls[/+]tommaso-ascani commentedon May 8, 2026
Test Cases — Managed RTP/UDP ports (PR nethesis/ns8-nethvoice#817)
This PR requests a 2003-port UDP block + 36 TCP ports from ns8-core and
derives every consumer from it. SIP UDP is distinct from SIP TCP.
+0 … +999+1000 … +1999+2000+2001+2002Update baselines — a temporary fix (1.6.8, commit
4202d5ad) made Satellitereuse
SATELLITE_HTTP_PORTand unset the dedicatedSATELLITE_RTP_PORT; this PRre-introduces it at
+2000. Updates must migrate correctly from both origins:SATELLITE_RTP_PORT(old model)SATELLITE_HTTP_PORTreuse;SATELLITE_RTP_PORTunsetEnvironment: ns8-core cluster, 2 nodes for clone/move; one user domain +
one NethVoice Proxy per node; root SSH.
Images:
<IMG>= PR image; update baselines1.7.4and1.6.7.Placeholders:
<ID>= module id (e.g.nethvoice1) ·<NODE>= node id.Automated tests (Robot Framework —
tests/)Already covered by the automated suites; no manual run needed.
ASTERISK_SIP_UDP_PORT≠ASTERISK_SIP_PORTrc=0, no abortManual tests
Each test is self-contained: do the step, run the command, check the result.
A1 — Clean install
add-module <IMG> <NODE>→ note<ID>. →rc=0.rc=0.runagent -m <ID> systemctl --user --failed→ no failed units.A13 — Ports are correct after install
runagent -m <ID> env | grep -E 'ASTERISK_SIP_PORT|ASTERISK_SIP_UDP_PORT|ASTERISK_IAX_PORT|SATELLITE_RTP_PORT|ASTERISK_RTPSTART'→ all set;
ASTERISK_SIP_UDP_PORT≠ASTERISK_SIP_PORT.runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports'→ the UDP transport is bound to
ASTERISK_SIP_UDP_PORT.ss -lun | grep <ASTERISK_SIP_UDP_PORT>→ the SIP UDP port is listening.B1 — Two instances on the same node don't collide
add-module <IMG> <NODE>→<ID2>; configure it.journalctl _UID=$(id -u <ID2>) | grep -i "address already in use"→ no matches (includes DUNDI 4520 and Janus demo 5002/5102).B2 — Removing an instance frees its ports
remove-module <ID2>sqlite3 /var/lib/nethserver/node/state/ports.sqlite "SELECT * FROM UDP_PORTS;"→ no rows left for<ID2>.Update tests
Before each update, set a marker to check persistence: create a test extension in the PBX and note the admin password.
C1 — Update from 1.7.4 (current stable)
1.7.4instance.runagent -m <ID> env | grep SATELLITE_RTP_PORT→ empty (unset).api-cli run update-module --data '{"module_url":"<IMG>","instances":["<ID>"],"force":true}'rc=0.runagent -m <ID> env | grep SATELLITE_RTP_PORT→ now set (Satellite has its own port again).runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports'→ UDP transport on the newASTERISK_SIP_UDP_PORT, no manual restart needed.rc=0, nothing changes (idempotent).C2 — Update from 1.6.7 (before the temporary fix)
1.6.7instance.runagent -m <ID> env | grep SATELLITE_RTP_PORT→ set (old dedicated port).<IMG>(same command as C1 step 3). →rc=0.runagent -m <ID> env | grep -E 'SATELLITE_RTP_PORT|UDP_PORTS_RANGE'→ Satellite port is inside the new UDP block; no stale old value.C3 — Update next to an existing instance (collision)
1.7.4or1.6.7) with overlapping old ranges.<IMG>.UDP_PORTS_RANGEof the two instances → non-overlapping.journalctl _UID=$(id -u <ID>) | grep -i "address already in use"→ no matches.C4 — Update to a wrong image fails cleanly
api-cli run update-modulewith a non-existentmodule_url. → fails; the instance keeps working on the current image.D1 — Clone to another node
api-cli run clone-module --data '{"module_id":"<ID>","node":2,"replace":false}'→rc=0. Note<ID_DST>on node 2; configure it.runagent -m <ID_DST> podman exec freepbx asterisk -rx 'pjsip show transports'→ UDP transport on the destination
ASTERISK_SIP_UDP_PORT, TCP transport on the destinationASTERISK_SIP_PORT— UDP ≠ TCP.runagent -m <ID_DST> env | grep UDP_PORTS_RANGE→ different range from the source.D2 — Clone with FreePBX offline (config rewrite)
E1 — Move to another node
api-cli run clone-module --data '{"module_id":"<ID>","node":2,"replace":true}'→rc=0.runagent -m <ID_DST> env | grep MODULE_UUIDand the source → no duplicate UUID; source removed.runagent -m <ID_DST> podman exec freepbx asterisk -rx 'pjsip show transports'→ on the destination SIP UDP/TCP ports.F1 — Backup and restore
api-cli run cluster/run-backup --data '{"id":<repo>}'→rc=0.api-cli run restore-module --data '{"repository":"<uuid>","path":"<path>","snapshot":"","node":<NODE>,"replace":false}'→rc=0(does not abort).runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports'→ SIP UDP/TCP on the restored instance's ports.runagent -m <ID> systemctl --user status satellite→active (running)(Satellite PostgreSQL restored).F2 — Restore in place (replace=true)
"replace":true. →rc=0; instance works; ports correct.G1 — FreePBX restart keeps ports
runagent -m <ID> systemctl --user restart freepbxrunagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports'→ SIP UDP still onASTERISK_SIP_UDP_PORT.G2 — Reconfigure doesn't drop calls
configure-moduleagain changing a non-port setting (e.g. an integration).runagent -m <ID> podman exec freepbx asterisk -rx 'pjsip show transports'→ ports unchanged; Asterisk not restarted unnecessarily.H1 — Remove frees everything
remove-module <ID>sqlite3 /var/lib/nethserver/node/state/ports.sqlite "SELECT * FROM UDP_PORTS;"andTCP_PORTS→ no rows for<ID>.J1 — Real call (final gate)
Notes
allocator capacity limit depend on ns8-core — verify at integration level.
[-]NethVoice: Review UDP/TCP Port Assignment and Limit Concurrent Calls[/-][+]NethVoice: Review UDP/TCP Port Assignment[/+]22 remaining items
edospadoni commentedon Oct 12, 2026
Testing release:
Amygos commentedon Oct 12, 2026
Release
nethesis/ns8-nethvoice1.7.10