gns3-server/gns3server/utils/application_id.py
YueGuobin 0119373690
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.
2026-09-05 21:54:52 +08:00

84 lines
3.1 KiB
Python

#
# Copyright (C) 2017 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from gns3server.controller.controller_error import ControllerError
import logging
log = logging.getLogger(__name__)
# IOU draws from the lower half: uBridge's iol_bridge uses application_id + 512
# for its netio peer endpoint. IOL Docker nodes (iol-runner images) draw from
# the upper half: their netiomux peer is the fixed id 1023. Both node types
# derive interface MACs from the id (aabb.cc{app}{iface}), so the two pools
# must stay disjoint — a shared id silently blackholes traffic between the
# nodes as a MAC loop.
IOU_APPLICATION_ID_POOL = range(1, 512)
IOL_DOCKER_APPLICATION_ID_POOL = range(512, 1023)
def is_iol_runner_environment(environment) -> bool:
"""
Whether a Docker node environment carries the GNS3_IOL_RUNNER marker —
the same signal the compute uses to select the IOLDockerVM class.
"""
return "GNS3_IOL_RUNNER=" in (environment or "")
def get_next_application_id(projects, computes, iol_docker=False):
"""
Calculates free application_id from given nodes
:param projects: all projects managed by controller
:param computes: all computes used by the project
:param iol_docker: allocate for an IOL Docker (iol-runner) node instead of IOU
:raises HTTPConflict when exceeds number
:return: integer first free id
"""
nodes = []
# look for application id for in all nodes across all opened projects that share the same computes
for project in projects.values():
if project.status == "opened":
nodes.extend(list(project.nodes.values()))
if iol_docker:
used = {
n.properties["application_id"]
for n in nodes
if n.node_type == "docker"
and n.compute.id in computes
and "application_id" in n.properties
and is_iol_runner_environment(n.properties.get("environment"))
}
pool = set(IOL_DOCKER_APPLICATION_ID_POOL)
limit = "511 IOL Docker nodes"
else:
used = {n.properties["application_id"] for n in nodes if n.node_type == "iou" and n.compute.id in computes}
pool = set(IOU_APPLICATION_ID_POOL)
limit = "512 nodes"
try:
application_id = (pool - used).pop()
return application_id
except KeyError:
raise ControllerError(
f"Cannot create a new {'IOL Docker' if iol_docker else 'IOU'} node "
f"(limit of {limit} across all opened projects using the same computes)"
)