Add topology-canvas placeholder NE create (source=topology).

Toolbar and pane context menu create placeholders that follow the same setup path as LLDP in NE management and WebCRT.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-05 17:42:51 +08:00
parent 71c5193ecd
commit 241093343e
21 changed files with 460 additions and 31 deletions

View file

@ -573,6 +573,46 @@ class WebcrtUpsertAndTargetsTests(unittest.TestCase):
self.assertEqual(hit["ne_source"], WEBCRT_NE_SOURCE)
self.assertEqual(hit["ip_address"], "10.9.9.9")
def test_claim_topology_placeholder_promotes_to_webcrt(self):
from netx_api.device_types import TOPOLOGY_NE_SOURCE
from netx_api.ne_service_common import _now
now = _now()
row = ManagedNE(
name="TOPO-PH-01",
vendor="Other",
device_type="generic",
ip_address="",
port=22,
protocol="ssh",
username="",
password_enc="",
enable_secret_enc="",
connect_status="unknown",
tags="",
remark="",
source=TOPOLOGY_NE_SOURCE,
source_ref="",
created_at=now,
updated_at=now,
)
self.db.add(row)
self.db.commit()
self.db.refresh(row)
out, action = upsert_webcrt_session_host(
self.db,
ne_id=row.id,
name="TOPO-PH-01",
ip_address="10.9.9.10",
protocol="ssh",
username="admin",
password="secret",
save_password=True,
)
self.assertEqual(action, "updated")
self.assertEqual(out.id, row.id)
self.assertEqual(out.source, WEBCRT_NE_SOURCE)
if __name__ == "__main__":
unittest.main()

View file

@ -13,7 +13,7 @@ from sqlalchemy.exc import OperationalError
from netx_api import topology_lldp as lldp
from netx_api import topology_service as svc
from netx_api.db import Base, SessionLocal, engine
from netx_api.device_types import LLDP_DISCOVERED_NE_SOURCE, WEBCRT_NE_SOURCE
from netx_api.device_types import LLDP_DISCOVERED_NE_SOURCE, TOPOLOGY_NE_SOURCE, WEBCRT_NE_SOURCE
from netx_api.models import (
ManagedNE,
TopoDiscoverJob,
@ -28,6 +28,7 @@ from netx_api.models import (
from netx_api.topology_schemas import (
FabricDiscoverRequest,
TopologyFolderCreate,
TopologyPlaceholderCreate,
TopologyViewCreate,
ViewNodesAdd,
ViewPopulateRequest,
@ -357,6 +358,30 @@ class FabricTopologyTests(unittest.TestCase):
self.db.delete(ne)
self.db.commit()
def test_create_topology_placeholder_on_view(self) -> None:
suffix = uuid4().hex[:8]
view = svc.create_view(
self.db,
TopologyViewCreate(name=f"Vph-{suffix}", folder_id=self._region(f"Rph-{suffix}")),
)
graph = svc.create_topology_placeholder_on_view(
self.db,
view.id,
TopologyPlaceholderCreate(name=f"SW-{suffix}", ip_address="", x=42, y=77),
)
self.assertEqual(len(graph.nodes), 1)
node = graph.nodes[0]
self.assertEqual(node.name, f"SW-{suffix}")
self.assertEqual(node.x, 42)
self.assertEqual(node.y, 77)
self.assertTrue(node.managed_ne_id)
ne = self.db.get(ManagedNE, node.managed_ne_id)
self.assertIsNotNone(ne)
assert ne is not None
self.assertEqual(ne.source, TOPOLOGY_NE_SOURCE)
self.assertEqual(ne.ip_address, "")
self.assertEqual(ne.device_type, "generic")
def test_topology_tree_root_region_and_leaf(self) -> None:
tree = svc.get_topology_tree(self.db)
self.assertIsNotNone(tree.root)