mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 14:01:15 +02:00
Merge branch '3.1' into fix/duplicate-device-configs-merge
Resolve conflicts against the upstream wave (PRs #2952-#2965, incl. the typed-response/schema hardening and the drop of Python 3.10): - controller/node.py: keep upstream's _transitioning/_confirm_status lifecycle guard; the kernel-anchor link wiring (node_started) runs after the transition completes. - controller/link.py: keep upstream's add_node rollback (release_ports on create failure) and delete via release_ports; release_ports now resolves ports live (same staleness reason as _bind_members) and discards the node link unconditionally. - schemas/controller/links.py: extend LinkFilterType with the eight kernel-datapath filter types and LinkFilterParameter.type with "str" so the newly typed /available_filters response validates; Link.filters stays a free-form dict — the vocabulary is capability-gated per link and validated by the controller (409), not by a strict request schema. - schemas/controller/topology.py: drop the type: ignore made redundant by the dict filters field. - tests: route-level filter validation now expects the controller's 409 (unknown types, out-of-range values); packet_loss correlation is a valid kernel parameter (positive test added); OpenAPI assertions match the free-form filters property.
This commit is contained in:
60 files changed
+2654
-271
No files matched your search
@@ -399,9 +399,22 @@ class Link:
|
||||
|
||||
port = node.get_port(adapter_number, port_number)
|
||||
if port is None:
|
||||
raise ControllerNotFoundError(f"Port {adapter_number}/{port_number} for {node.name} not found")
|
||||
raise ControllerNotFoundError(
|
||||
f"Port {adapter_number}/{port_number} for {node.name} not found",
|
||||
code="port_not_found",
|
||||
details={"node_id": node.id, "adapter_number": adapter_number, "port_number": port_number},
|
||||
)
|
||||
if port.link is not None:
|
||||
raise ControllerError("Port is already used")
|
||||
raise ControllerError(
|
||||
"Port is already used",
|
||||
code="port_in_use",
|
||||
details={
|
||||
"node_id": node.id,
|
||||
"adapter_number": adapter_number,
|
||||
"port_number": port_number,
|
||||
"link_id": port.link.id,
|
||||
},
|
||||
)
|
||||
|
||||
self._link_type = port.link_type
|
||||
|
||||
@@ -438,8 +451,15 @@ class Link:
|
||||
{"node": node, "adapter_number": adapter_number, "port_number": port_number, "port": port, "label": label}
|
||||
)
|
||||
|
||||
if not batch:
|
||||
port.link = self
|
||||
|
||||
if len(self._nodes) == 2 and not batch:
|
||||
await self.create()
|
||||
try:
|
||||
await self.create()
|
||||
except BaseException:
|
||||
self.release_ports()
|
||||
raise
|
||||
self._bind_members()
|
||||
self._created = True
|
||||
self._project.emit_notification("link.created", self.asdict())
|
||||
@@ -447,6 +467,16 @@ class Link:
|
||||
if dump:
|
||||
self._project.dump()
|
||||
|
||||
def release_ports(self):
|
||||
for n in self._nodes:
|
||||
# The live port, for the same staleness reason as _bind_members;
|
||||
# the link could be different from self if we rollback an
|
||||
# already existing link
|
||||
port = n["node"].get_port(n["adapter_number"], n["port_number"]) or n["port"]
|
||||
if port.link == self:
|
||||
port.link = None
|
||||
n["node"].links.discard(self)
|
||||
|
||||
async def update_nodes(self, nodes):
|
||||
for node_data in nodes:
|
||||
node = self._project.get_node(node_data["node_id"])
|
||||
@@ -490,14 +520,7 @@ class Link:
|
||||
"""
|
||||
Delete the link
|
||||
"""
|
||||
for n in self._nodes:
|
||||
# The live port, for the same staleness reason as _bind_members;
|
||||
# the link could be different from self if we rollback an
|
||||
# already existing link
|
||||
port = n["node"].get_port(n["adapter_number"], n["port_number"]) or n["port"]
|
||||
if port.link == self:
|
||||
port.link = None
|
||||
n["node"].remove_link(self)
|
||||
self.release_ports()
|
||||
|
||||
async def reset(self):
|
||||
"""
|
||||
|
||||
Reference in new issue
Block a user