feat: allocate IOL Docker application IDs from a pool disjoint from IOU

IOL interface MACs derive from the application ID (aabb.cc{app}{iface}),
so ids must be unique across opened projects sharing computes — the same
reason IOU has its allocator. IOL Docker nodes draw from the upper half
(512-1022, netiomux's fixed peer is 1023) so the two node types can
neither collide nor starve each other; IOU behavior is unchanged.

The controller sniffs the same GNS3_IOL_RUNNER environment marker the
compute uses to select IOLDockerVM (both the nested-properties and
template/top-level-kwarg shapes), stores the id in node properties like
IOU does, and passes it through the Docker create payload. Without an
allocation the compute falls back to a stable node-derived id in the
same upper range.
This commit is contained in:
YueGuobin committed 2026-09-05 21:54:52 +08:00
1 parent b17d021bf6
commit 0119373690
8 files changed
+177 -24

No files matched your search

+31 -7
View File
@@ -29,8 +29,10 @@ netiomux exposes per-interface AF_UNIX datagram sockets in the container's
``/tmp`` (``s%02d.sock`` receive, ``c%02d.sock`` send — raw Ethernet frames),
wired by the generic ``GNS3_UNIX_SOCKET_NIO`` capability of VendorDockerVM
(uBridge reaches them through a per-node runtime directory bound at /tmp —
see ``VendorDockerVM._unix_socket_host_dir``). The local application ID is
derived from the node ID so that linked nodes get distinct MACs.
see ``VendorDockerVM._unix_socket_host_dir``). The controller allocates the
IOL application ID (upper half of the id space, disjoint from IOU's) so that
linked nodes get distinct MACs; without an allocation a stable node-derived
fallback is used.
This class is selected by the ``GNS3_IOL_RUNNER=1`` environment marker.
"""
@@ -91,6 +93,27 @@ class IOLDockerVM(VendorDockerVM):
except ValueError:
pass
# Application ID allocated by the controller (upper half of the id
# space, disjoint from IOU's); None until the create payload carries it.
self._application_id = None
@property
def application_id(self) -> int:
"""
IOL application ID: drives interface MACs (aabb.cc{app}{iface}) and
the NVRAM file name. Falls back to a stable per-node value in the
IOL Docker half of the id space when no allocation is available
(raw compute API use, topologies created before allocation existed).
"""
if self._application_id is None:
return 512 + int(self.id.replace("-", ""), 16) % 511
return self._application_id
@application_id.setter
def application_id(self, value) -> None:
self._application_id = int(value)
@DockerVM.adapters.setter
def adapters(self, adapters):
"""
@@ -194,11 +217,12 @@ class IOLDockerVM(VendorDockerVM):
"num-eth": self.adapters * 4, # every adapter is a 4-port unit
"num-serial": 0, # GNS3 docker adapters are ethernet-only
# IOL derives interface MACs from the local application ID
# (aabb.cc00.0<app><iface>0); every node needs a distinct one or
# linked routers share MACs and drop each other's frames as loops.
# Derived from the node ID: stable across restarts, unique enough
# (CML allocates per-lab sequential IDs for the same reason).
"local-app": int(self.id.replace("-", ""), 16) % 1022 + 1,
# (aabb.cc{app}{iface}); every node needs a distinct one or
# linked routers share MACs and drop each other's frames as
# loops. Allocated by the controller per node (upper half of
# the id space, disjoint from IOU's — CML does the same with
# its per-deployment iol_app_id).
"local-app": self.application_id,
"remote-app": 1023, # netiomux's fake peer application ID
"user-id": os.getuid(),
"group-id": os.getgid(),