Merge pull request #525 from coreemu/feature/pygui-multiple-links
Feature/pygui multiple links
This commit is contained in:
commit
fc846272fc
7 changed files with 140 additions and 126 deletions
|
@ -87,14 +87,14 @@ class CoreClient:
|
||||||
self.read_config()
|
self.read_config()
|
||||||
|
|
||||||
# helpers
|
# helpers
|
||||||
self.iface_to_edge: Dict[Tuple[int, ...], Tuple[int, ...]] = {}
|
self.iface_to_edge: Dict[Tuple[int, ...], CanvasEdge] = {}
|
||||||
self.ifaces_manager: InterfaceManager = InterfaceManager(self.app)
|
self.ifaces_manager: InterfaceManager = InterfaceManager(self.app)
|
||||||
self.observer: Optional[str] = None
|
self.observer: Optional[str] = None
|
||||||
|
|
||||||
# session data
|
# session data
|
||||||
self.mobility_players: Dict[int, MobilityPlayer] = {}
|
self.mobility_players: Dict[int, MobilityPlayer] = {}
|
||||||
self.canvas_nodes: Dict[int, CanvasNode] = {}
|
self.canvas_nodes: Dict[int, CanvasNode] = {}
|
||||||
self.links: Dict[Tuple[int, int], CanvasEdge] = {}
|
self.links: Dict[str, CanvasEdge] = {}
|
||||||
self.handling_throughputs: Optional[grpc.Future] = None
|
self.handling_throughputs: Optional[grpc.Future] = None
|
||||||
self.handling_cpu_usage: Optional[grpc.Future] = None
|
self.handling_cpu_usage: Optional[grpc.Future] = None
|
||||||
self.handling_events: Optional[grpc.Future] = None
|
self.handling_events: Optional[grpc.Future] = None
|
||||||
|
@ -225,7 +225,9 @@ class CoreClient:
|
||||||
self.app.canvas.add_wired_edge(canvas_node1, canvas_node2, event.link)
|
self.app.canvas.add_wired_edge(canvas_node1, canvas_node2, event.link)
|
||||||
self.app.canvas.organize()
|
self.app.canvas.organize()
|
||||||
elif event.message_type == MessageType.DELETE:
|
elif event.message_type == MessageType.DELETE:
|
||||||
self.app.canvas.delete_wired_edge(canvas_node1, canvas_node2)
|
self.app.canvas.delete_wired_edge(
|
||||||
|
canvas_node1, canvas_node2, event.link
|
||||||
|
)
|
||||||
elif event.message_type == MessageType.NONE:
|
elif event.message_type == MessageType.NONE:
|
||||||
self.app.canvas.update_wired_edge(
|
self.app.canvas.update_wired_edge(
|
||||||
canvas_node1, canvas_node2, event.link
|
canvas_node1, canvas_node2, event.link
|
||||||
|
@ -877,7 +879,7 @@ class CoreClient:
|
||||||
|
|
||||||
def create_link(
|
def create_link(
|
||||||
self, edge: CanvasEdge, canvas_src_node: CanvasNode, canvas_dst_node: CanvasNode
|
self, edge: CanvasEdge, canvas_src_node: CanvasNode, canvas_dst_node: CanvasNode
|
||||||
) -> None:
|
) -> Link:
|
||||||
"""
|
"""
|
||||||
Create core link for a pair of canvas nodes, with token referencing
|
Create core link for a pair of canvas nodes, with token referencing
|
||||||
the canvas edge.
|
the canvas edge.
|
||||||
|
@ -888,15 +890,9 @@ class CoreClient:
|
||||||
src_iface = None
|
src_iface = None
|
||||||
if NodeUtils.is_container_node(src_node.type):
|
if NodeUtils.is_container_node(src_node.type):
|
||||||
src_iface = self.create_iface(canvas_src_node)
|
src_iface = self.create_iface(canvas_src_node)
|
||||||
self.iface_to_edge[(src_node.id, src_iface.id)] = edge.token
|
|
||||||
edge.src_iface = src_iface
|
|
||||||
canvas_src_node.ifaces[src_iface.id] = src_iface
|
|
||||||
dst_iface = None
|
dst_iface = None
|
||||||
if NodeUtils.is_container_node(dst_node.type):
|
if NodeUtils.is_container_node(dst_node.type):
|
||||||
dst_iface = self.create_iface(canvas_dst_node)
|
dst_iface = self.create_iface(canvas_dst_node)
|
||||||
self.iface_to_edge[(dst_node.id, dst_iface.id)] = edge.token
|
|
||||||
edge.dst_iface = dst_iface
|
|
||||||
canvas_dst_node.ifaces[dst_iface.id] = dst_iface
|
|
||||||
link = Link(
|
link = Link(
|
||||||
type=LinkType.WIRED,
|
type=LinkType.WIRED,
|
||||||
node1_id=src_node.id,
|
node1_id=src_node.id,
|
||||||
|
@ -904,9 +900,21 @@ class CoreClient:
|
||||||
iface1=src_iface,
|
iface1=src_iface,
|
||||||
iface2=dst_iface,
|
iface2=dst_iface,
|
||||||
)
|
)
|
||||||
edge.set_link(link)
|
|
||||||
self.links[edge.token] = edge
|
|
||||||
logging.info("added link between %s and %s", src_node.name, dst_node.name)
|
logging.info("added link between %s and %s", src_node.name, dst_node.name)
|
||||||
|
return link
|
||||||
|
|
||||||
|
def save_edge(
|
||||||
|
self, edge: CanvasEdge, canvas_src_node: CanvasNode, canvas_dst_node: CanvasNode
|
||||||
|
) -> None:
|
||||||
|
self.links[edge.token] = edge
|
||||||
|
src_node = canvas_src_node.core_node
|
||||||
|
dst_node = canvas_dst_node.core_node
|
||||||
|
if NodeUtils.is_container_node(src_node.type):
|
||||||
|
src_iface_id = edge.link.iface1.id
|
||||||
|
self.iface_to_edge[(src_node.id, src_iface_id)] = edge
|
||||||
|
if NodeUtils.is_container_node(dst_node.type):
|
||||||
|
dst_iface_id = edge.link.iface2.id
|
||||||
|
self.iface_to_edge[(dst_node.id, dst_iface_id)] = edge
|
||||||
|
|
||||||
def get_wlan_configs_proto(self) -> List[wlan_pb2.WlanConfig]:
|
def get_wlan_configs_proto(self) -> List[wlan_pb2.WlanConfig]:
|
||||||
configs = []
|
configs = []
|
||||||
|
|
|
@ -68,10 +68,14 @@ class LinkConfigurationDialog(Dialog):
|
||||||
|
|
||||||
def draw(self) -> None:
|
def draw(self) -> None:
|
||||||
self.top.columnconfigure(0, weight=1)
|
self.top.columnconfigure(0, weight=1)
|
||||||
source_name = self.app.canvas.nodes[self.edge.src].core_node.name
|
src_label = self.app.canvas.nodes[self.edge.src].core_node.name
|
||||||
dest_name = self.app.canvas.nodes[self.edge.dst].core_node.name
|
if self.edge.link.iface1:
|
||||||
|
src_label += f":{self.edge.link.iface1.name}"
|
||||||
|
dst_label = self.app.canvas.nodes[self.edge.dst].core_node.name
|
||||||
|
if self.edge.link.iface2:
|
||||||
|
dst_label += f":{self.edge.link.iface2.name}"
|
||||||
label = ttk.Label(
|
label = ttk.Label(
|
||||||
self.top, text=f"Link from {source_name} to {dest_name}", anchor=tk.CENTER
|
self.top, text=f"{src_label} to {dst_label}", anchor=tk.CENTER
|
||||||
)
|
)
|
||||||
label.grid(row=0, column=0, sticky=tk.EW, pady=PADY)
|
label.grid(row=0, column=0, sticky=tk.EW, pady=PADY)
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,14 @@ WIRELESS_COLOR: str = "#009933"
|
||||||
ARC_DISTANCE: int = 50
|
ARC_DISTANCE: int = 50
|
||||||
|
|
||||||
|
|
||||||
def create_edge_token(src: int, dst: int, network: int = None) -> Tuple[int, ...]:
|
def create_wireless_token(src: int, dst: int, network: int) -> str:
|
||||||
values = [src, dst]
|
return f"{src}-{dst}-{network}"
|
||||||
if network is not None:
|
|
||||||
values.append(network)
|
|
||||||
return tuple(sorted(values))
|
def create_edge_token(src: int, dst: int, link: Link) -> str:
|
||||||
|
iface1_id = link.iface1.id if link.iface1 else None
|
||||||
|
iface2_id = link.iface2.id if link.iface2 else None
|
||||||
|
return f"{src}-{iface1_id}-{dst}-{iface2_id}"
|
||||||
|
|
||||||
|
|
||||||
def arc_edges(edges) -> None:
|
def arc_edges(edges) -> None:
|
||||||
|
@ -67,17 +70,13 @@ class Edge:
|
||||||
self.src: int = src
|
self.src: int = src
|
||||||
self.dst: int = dst
|
self.dst: int = dst
|
||||||
self.arc: int = 0
|
self.arc: int = 0
|
||||||
self.token: Optional[Tuple[int, ...]] = None
|
self.token: Optional[str] = None
|
||||||
self.src_label: Optional[int] = None
|
self.src_label: Optional[int] = None
|
||||||
self.middle_label: Optional[int] = None
|
self.middle_label: Optional[int] = None
|
||||||
self.dst_label: Optional[int] = None
|
self.dst_label: Optional[int] = None
|
||||||
self.color: str = EDGE_COLOR
|
self.color: str = EDGE_COLOR
|
||||||
self.width: int = EDGE_WIDTH
|
self.width: int = EDGE_WIDTH
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_token(cls, src: int, dst: int) -> Tuple[int, ...]:
|
|
||||||
return tuple(sorted([src, dst]))
|
|
||||||
|
|
||||||
def scaled_width(self) -> float:
|
def scaled_width(self) -> float:
|
||||||
return self.width * self.canvas.app.app_scale
|
return self.width * self.canvas.app.app_scale
|
||||||
|
|
||||||
|
@ -242,15 +241,17 @@ class CanvasWirelessEdge(Edge):
|
||||||
canvas: "CanvasGraph",
|
canvas: "CanvasGraph",
|
||||||
src: int,
|
src: int,
|
||||||
dst: int,
|
dst: int,
|
||||||
|
network_id: int,
|
||||||
|
token: str,
|
||||||
src_pos: Tuple[float, float],
|
src_pos: Tuple[float, float],
|
||||||
dst_pos: Tuple[float, float],
|
dst_pos: Tuple[float, float],
|
||||||
token: Tuple[int, ...],
|
|
||||||
link: Link,
|
link: Link,
|
||||||
) -> None:
|
) -> None:
|
||||||
logging.debug("drawing wireless link from node %s to node %s", src, dst)
|
logging.debug("drawing wireless link from node %s to node %s", src, dst)
|
||||||
super().__init__(canvas, src, dst)
|
super().__init__(canvas, src, dst)
|
||||||
|
self.network_id: int = network_id
|
||||||
self.link: Link = link
|
self.link: Link = link
|
||||||
self.token: Tuple[int, ...] = token
|
self.token: str = token
|
||||||
self.width: float = WIRELESS_WIDTH
|
self.width: float = WIRELESS_WIDTH
|
||||||
color = link.color if link.color else WIRELESS_COLOR
|
color = link.color if link.color else WIRELESS_COLOR
|
||||||
self.color: str = color
|
self.color: str = color
|
||||||
|
@ -284,8 +285,6 @@ class CanvasEdge(Edge):
|
||||||
Create an instance of canvas edge object
|
Create an instance of canvas edge object
|
||||||
"""
|
"""
|
||||||
super().__init__(canvas, src)
|
super().__init__(canvas, src)
|
||||||
self.src_iface: Optional[Interface] = None
|
|
||||||
self.dst_iface: Optional[Interface] = None
|
|
||||||
self.text_src: Optional[int] = None
|
self.text_src: Optional[int] = None
|
||||||
self.text_dst: Optional[int] = None
|
self.text_dst: Optional[int] = None
|
||||||
self.link: Optional[Link] = None
|
self.link: Optional[Link] = None
|
||||||
|
@ -362,7 +361,6 @@ class CanvasEdge(Edge):
|
||||||
def complete(self, dst: int, linked_wireless: bool) -> None:
|
def complete(self, dst: int, linked_wireless: bool) -> None:
|
||||||
self.dst = dst
|
self.dst = dst
|
||||||
self.linked_wireless = linked_wireless
|
self.linked_wireless = linked_wireless
|
||||||
self.token = create_edge_token(self.src, self.dst)
|
|
||||||
dst_pos = self.canvas.coords(self.dst)
|
dst_pos = self.canvas.coords(self.dst)
|
||||||
self.move_dst(dst_pos)
|
self.move_dst(dst_pos)
|
||||||
self.check_wireless()
|
self.check_wireless()
|
||||||
|
|
|
@ -21,8 +21,10 @@ from core.gui.graph.edges import (
|
||||||
EDGE_WIDTH,
|
EDGE_WIDTH,
|
||||||
CanvasEdge,
|
CanvasEdge,
|
||||||
CanvasWirelessEdge,
|
CanvasWirelessEdge,
|
||||||
|
Edge,
|
||||||
arc_edges,
|
arc_edges,
|
||||||
create_edge_token,
|
create_edge_token,
|
||||||
|
create_wireless_token,
|
||||||
)
|
)
|
||||||
from core.gui.graph.enums import GraphMode, ScaleOption
|
from core.gui.graph.enums import GraphMode, ScaleOption
|
||||||
from core.gui.graph.node import CanvasNode
|
from core.gui.graph.node import CanvasNode
|
||||||
|
@ -69,9 +71,9 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.selected: Optional[int] = None
|
self.selected: Optional[int] = None
|
||||||
self.node_draw: Optional[NodeDraw] = None
|
self.node_draw: Optional[NodeDraw] = None
|
||||||
self.nodes: Dict[int, CanvasNode] = {}
|
self.nodes: Dict[int, CanvasNode] = {}
|
||||||
self.edges: Dict[int, CanvasEdge] = {}
|
self.edges: Dict[str, CanvasEdge] = {}
|
||||||
self.shapes: Dict[int, Shape] = {}
|
self.shapes: Dict[int, Shape] = {}
|
||||||
self.wireless_edges: Dict[Tuple[int, ...], CanvasWirelessEdge] = {}
|
self.wireless_edges: Dict[str, CanvasWirelessEdge] = {}
|
||||||
|
|
||||||
# map wireless/EMANE node to the set of MDRs connected to that node
|
# map wireless/EMANE node to the set of MDRs connected to that node
|
||||||
self.wireless_network: Dict[int, Set[int]] = {}
|
self.wireless_network: Dict[int, Set[int]] = {}
|
||||||
|
@ -206,14 +208,9 @@ class CanvasGraph(tk.Canvas):
|
||||||
iface_id = iface_throughput.iface_id
|
iface_id = iface_throughput.iface_id
|
||||||
throughput = iface_throughput.throughput
|
throughput = iface_throughput.throughput
|
||||||
iface_to_edge_id = (node_id, iface_id)
|
iface_to_edge_id = (node_id, iface_id)
|
||||||
token = self.core.iface_to_edge.get(iface_to_edge_id)
|
edge = self.core.iface_to_edge.get(iface_to_edge_id)
|
||||||
if not token:
|
|
||||||
continue
|
|
||||||
edge = self.edges.get(token)
|
|
||||||
if edge:
|
if edge:
|
||||||
edge.set_throughput(throughput)
|
edge.set_throughput(throughput)
|
||||||
else:
|
|
||||||
del self.core.iface_to_edge[iface_to_edge_id]
|
|
||||||
|
|
||||||
def draw_grid(self) -> None:
|
def draw_grid(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -230,7 +227,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.tag_lower(self.rect)
|
self.tag_lower(self.rect)
|
||||||
|
|
||||||
def add_wired_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
def add_wired_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
||||||
token = create_edge_token(src.id, dst.id)
|
token = create_edge_token(src.id, dst.id, link)
|
||||||
if token in self.edges and link.options.unidirectional:
|
if token in self.edges and link.options.unidirectional:
|
||||||
edge = self.edges[token]
|
edge = self.edges[token]
|
||||||
edge.asymmetric_link = link
|
edge.asymmetric_link = link
|
||||||
|
@ -240,72 +237,50 @@ class CanvasGraph(tk.Canvas):
|
||||||
src_pos = (node1.position.x, node1.position.y)
|
src_pos = (node1.position.x, node1.position.y)
|
||||||
dst_pos = (node2.position.x, node2.position.y)
|
dst_pos = (node2.position.x, node2.position.y)
|
||||||
edge = CanvasEdge(self, src.id, src_pos, dst_pos)
|
edge = CanvasEdge(self, src.id, src_pos, dst_pos)
|
||||||
edge.linked_wireless = self.is_linked_wireless(src.id, dst.id)
|
self.complete_edge(src, dst, edge, link)
|
||||||
edge.token = token
|
|
||||||
edge.dst = dst.id
|
|
||||||
edge.set_link(link)
|
|
||||||
edge.check_wireless()
|
|
||||||
src.edges.add(edge)
|
|
||||||
dst.edges.add(edge)
|
|
||||||
self.edges[edge.token] = edge
|
|
||||||
self.core.links[edge.token] = edge
|
|
||||||
if link.iface1:
|
|
||||||
iface1 = link.iface1
|
|
||||||
self.core.iface_to_edge[(node1.id, iface1.id)] = token
|
|
||||||
src.ifaces[iface1.id] = iface1
|
|
||||||
edge.src_iface = iface1
|
|
||||||
if link.iface2:
|
|
||||||
iface2 = link.iface2
|
|
||||||
self.core.iface_to_edge[(node2.id, iface2.id)] = edge.token
|
|
||||||
dst.ifaces[iface2.id] = iface2
|
|
||||||
edge.dst_iface = iface2
|
|
||||||
|
|
||||||
def delete_wired_edge(self, src: CanvasNode, dst: CanvasNode) -> None:
|
def delete_wired_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
||||||
token = create_edge_token(src.id, dst.id)
|
token = create_edge_token(src.id, dst.id, link)
|
||||||
edge = self.edges.get(token)
|
edge = self.edges.get(token)
|
||||||
if not edge:
|
if edge:
|
||||||
return
|
self.delete_edge(edge)
|
||||||
self.delete_edge(edge)
|
|
||||||
|
|
||||||
def update_wired_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
def update_wired_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
||||||
token = create_edge_token(src.id, dst.id)
|
token = create_edge_token(src.id, dst.id, link)
|
||||||
edge = self.edges.get(token)
|
edge = self.edges.get(token)
|
||||||
if not edge:
|
if edge:
|
||||||
return
|
edge.link.options = deepcopy(link.options)
|
||||||
edge.link.options = deepcopy(link.options)
|
|
||||||
|
|
||||||
def add_wireless_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
def add_wireless_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
||||||
network_id = link.network_id if link.network_id else None
|
network_id = link.network_id if link.network_id else None
|
||||||
token = create_edge_token(src.id, dst.id, network_id)
|
token = create_wireless_token(src.id, dst.id, network_id)
|
||||||
if token in self.wireless_edges:
|
if token in self.wireless_edges:
|
||||||
logging.warning("ignoring link that already exists: %s", link)
|
logging.warning("ignoring link that already exists: %s", link)
|
||||||
return
|
return
|
||||||
src_pos = self.coords(src.id)
|
src_pos = self.coords(src.id)
|
||||||
dst_pos = self.coords(dst.id)
|
dst_pos = self.coords(dst.id)
|
||||||
edge = CanvasWirelessEdge(self, src.id, dst.id, src_pos, dst_pos, token, link)
|
edge = CanvasWirelessEdge(
|
||||||
|
self, src.id, dst.id, network_id, token, src_pos, dst_pos, link
|
||||||
|
)
|
||||||
self.wireless_edges[token] = edge
|
self.wireless_edges[token] = edge
|
||||||
src.wireless_edges.add(edge)
|
src.wireless_edges.add(edge)
|
||||||
dst.wireless_edges.add(edge)
|
dst.wireless_edges.add(edge)
|
||||||
self.tag_raise(src.id)
|
self.tag_raise(src.id)
|
||||||
self.tag_raise(dst.id)
|
self.tag_raise(dst.id)
|
||||||
# update arcs when there are multiple links
|
self.arc_common_edges(edge)
|
||||||
common_edges = list(src.wireless_edges & dst.wireless_edges)
|
|
||||||
arc_edges(common_edges)
|
|
||||||
|
|
||||||
def delete_wireless_edge(
|
def delete_wireless_edge(
|
||||||
self, src: CanvasNode, dst: CanvasNode, link: Link
|
self, src: CanvasNode, dst: CanvasNode, link: Link
|
||||||
) -> None:
|
) -> None:
|
||||||
network_id = link.network_id if link.network_id else None
|
network_id = link.network_id if link.network_id else None
|
||||||
token = create_edge_token(src.id, dst.id, network_id)
|
token = create_wireless_token(src.id, dst.id, network_id)
|
||||||
if token not in self.wireless_edges:
|
if token not in self.wireless_edges:
|
||||||
return
|
return
|
||||||
edge = self.wireless_edges.pop(token)
|
edge = self.wireless_edges.pop(token)
|
||||||
edge.delete()
|
edge.delete()
|
||||||
src.wireless_edges.remove(edge)
|
src.wireless_edges.remove(edge)
|
||||||
dst.wireless_edges.remove(edge)
|
dst.wireless_edges.remove(edge)
|
||||||
# update arcs when there are multiple links
|
self.arc_common_edges(edge)
|
||||||
common_edges = list(src.wireless_edges & dst.wireless_edges)
|
|
||||||
arc_edges(common_edges)
|
|
||||||
|
|
||||||
def update_wireless_edge(
|
def update_wireless_edge(
|
||||||
self, src: CanvasNode, dst: CanvasNode, link: Link
|
self, src: CanvasNode, dst: CanvasNode, link: Link
|
||||||
|
@ -313,7 +288,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
if not link.label:
|
if not link.label:
|
||||||
return
|
return
|
||||||
network_id = link.network_id if link.network_id else None
|
network_id = link.network_id if link.network_id else None
|
||||||
token = create_edge_token(src.id, dst.id, network_id)
|
token = create_wireless_token(src.id, dst.id, network_id)
|
||||||
if token not in self.wireless_edges:
|
if token not in self.wireless_edges:
|
||||||
self.add_wireless_edge(src, dst, link)
|
self.add_wireless_edge(src, dst, link)
|
||||||
else:
|
else:
|
||||||
|
@ -454,12 +429,6 @@ class CanvasGraph(tk.Canvas):
|
||||||
edge.delete()
|
edge.delete()
|
||||||
return
|
return
|
||||||
|
|
||||||
# ignore repeated edges
|
|
||||||
token = create_edge_token(edge.src, self.selected)
|
|
||||||
if token in self.edges:
|
|
||||||
edge.delete()
|
|
||||||
return
|
|
||||||
|
|
||||||
# rj45 nodes can only support one link
|
# rj45 nodes can only support one link
|
||||||
if NodeUtils.is_rj45_node(src_node.core_node.type) and src_node.edges:
|
if NodeUtils.is_rj45_node(src_node.core_node.type) and src_node.edges:
|
||||||
edge.delete()
|
edge.delete()
|
||||||
|
@ -468,14 +437,23 @@ class CanvasGraph(tk.Canvas):
|
||||||
edge.delete()
|
edge.delete()
|
||||||
return
|
return
|
||||||
|
|
||||||
# set dst node and snap edge to center
|
# only 1 link between bridge based nodes
|
||||||
linked_wireless = self.is_linked_wireless(src_node.id, self.selected)
|
is_src_bridge = NodeUtils.is_bridge_node(src_node.core_node)
|
||||||
edge.complete(self.selected, linked_wireless)
|
is_dst_bridge = NodeUtils.is_bridge_node(dst_node.core_node)
|
||||||
|
common_links = src_node.edges & dst_node.edges
|
||||||
|
if all([is_src_bridge, is_dst_bridge, common_links]):
|
||||||
|
edge.delete()
|
||||||
|
return
|
||||||
|
|
||||||
self.edges[edge.token] = edge
|
# finalize edge creation
|
||||||
src_node.edges.add(edge)
|
self.complete_edge(src_node, dst_node, edge)
|
||||||
dst_node.edges.add(edge)
|
|
||||||
self.core.create_link(edge, src_node, dst_node)
|
def arc_common_edges(self, edge: Edge) -> None:
|
||||||
|
src_node = self.nodes[edge.src]
|
||||||
|
dst_node = self.nodes[edge.dst]
|
||||||
|
common_edges = list(src_node.edges & dst_node.edges)
|
||||||
|
common_edges += list(src_node.wireless_edges & dst_node.wireless_edges)
|
||||||
|
arc_edges(common_edges)
|
||||||
|
|
||||||
def select_object(self, object_id: int, choose_multiple: bool = False) -> None:
|
def select_object(self, object_id: int, choose_multiple: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -534,10 +512,10 @@ class CanvasGraph(tk.Canvas):
|
||||||
edge.delete()
|
edge.delete()
|
||||||
# update node connected to edge being deleted
|
# update node connected to edge being deleted
|
||||||
other_id = edge.src
|
other_id = edge.src
|
||||||
other_iface = edge.src_iface
|
other_iface = edge.link.iface1
|
||||||
if edge.src == object_id:
|
if edge.src == object_id:
|
||||||
other_id = edge.dst
|
other_id = edge.dst
|
||||||
other_iface = edge.dst_iface
|
other_iface = edge.link.iface2
|
||||||
other_node = self.nodes[other_id]
|
other_node = self.nodes[other_id]
|
||||||
other_node.edges.remove(edge)
|
other_node.edges.remove(edge)
|
||||||
if other_iface:
|
if other_iface:
|
||||||
|
@ -559,12 +537,12 @@ class CanvasGraph(tk.Canvas):
|
||||||
del self.edges[edge.token]
|
del self.edges[edge.token]
|
||||||
src_node = self.nodes[edge.src]
|
src_node = self.nodes[edge.src]
|
||||||
src_node.edges.discard(edge)
|
src_node.edges.discard(edge)
|
||||||
if edge.src_iface:
|
if edge.link.iface1:
|
||||||
del src_node.ifaces[edge.src_iface.id]
|
del src_node.ifaces[edge.link.iface1.id]
|
||||||
dst_node = self.nodes[edge.dst]
|
dst_node = self.nodes[edge.dst]
|
||||||
dst_node.edges.discard(edge)
|
dst_node.edges.discard(edge)
|
||||||
if edge.dst_iface:
|
if edge.link.iface2:
|
||||||
del dst_node.ifaces[edge.dst_iface.id]
|
del dst_node.ifaces[edge.link.iface2.id]
|
||||||
src_wireless = NodeUtils.is_wireless_node(src_node.core_node.type)
|
src_wireless = NodeUtils.is_wireless_node(src_node.core_node.type)
|
||||||
if src_wireless:
|
if src_wireless:
|
||||||
dst_node.delete_antenna()
|
dst_node.delete_antenna()
|
||||||
|
@ -572,6 +550,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
if dst_wireless:
|
if dst_wireless:
|
||||||
src_node.delete_antenna()
|
src_node.delete_antenna()
|
||||||
self.core.deleted_canvas_edges([edge])
|
self.core.deleted_canvas_edges([edge])
|
||||||
|
self.arc_common_edges(edge)
|
||||||
|
|
||||||
def zoom(self, event: tk.Event, factor: float = None) -> None:
|
def zoom(self, event: tk.Event, factor: float = None) -> None:
|
||||||
if not factor:
|
if not factor:
|
||||||
|
@ -903,20 +882,39 @@ class CanvasGraph(tk.Canvas):
|
||||||
def is_selection_mode(self) -> bool:
|
def is_selection_mode(self) -> bool:
|
||||||
return self.mode == GraphMode.SELECT
|
return self.mode == GraphMode.SELECT
|
||||||
|
|
||||||
def create_edge(self, source: CanvasNode, dest: CanvasNode) -> None:
|
def create_edge(self, src: CanvasNode, dst: CanvasNode) -> CanvasEdge:
|
||||||
"""
|
"""
|
||||||
create an edge between source node and destination node
|
create an edge between source node and destination node
|
||||||
"""
|
"""
|
||||||
token = create_edge_token(source.id, dest.id)
|
pos = (src.core_node.position.x, src.core_node.position.y)
|
||||||
if token not in self.edges:
|
edge = CanvasEdge(self, src.id, pos, pos)
|
||||||
pos = (source.core_node.position.x, source.core_node.position.y)
|
self.complete_edge(src, dst, edge)
|
||||||
edge = CanvasEdge(self, source.id, pos, pos)
|
return edge
|
||||||
linked_wireless = self.is_linked_wireless(source.id, dest.id)
|
|
||||||
edge.complete(dest.id, linked_wireless)
|
def complete_edge(
|
||||||
self.edges[edge.token] = edge
|
self,
|
||||||
self.nodes[source.id].edges.add(edge)
|
src: CanvasNode,
|
||||||
self.nodes[dest.id].edges.add(edge)
|
dst: CanvasNode,
|
||||||
self.core.create_link(edge, source, dest)
|
edge: CanvasEdge,
|
||||||
|
link: Optional[Link] = None,
|
||||||
|
) -> None:
|
||||||
|
linked_wireless = self.is_linked_wireless(src.id, dst.id)
|
||||||
|
edge.complete(dst.id, linked_wireless)
|
||||||
|
if link is None:
|
||||||
|
link = self.core.create_link(edge, src, dst)
|
||||||
|
if link.iface1:
|
||||||
|
iface1 = link.iface1
|
||||||
|
src.ifaces[iface1.id] = iface1
|
||||||
|
if link.iface2:
|
||||||
|
iface2 = link.iface2
|
||||||
|
dst.ifaces[iface2.id] = iface2
|
||||||
|
src.edges.add(edge)
|
||||||
|
dst.edges.add(edge)
|
||||||
|
edge.set_link(link)
|
||||||
|
edge.token = create_edge_token(src.id, dst.id, edge.link)
|
||||||
|
self.arc_common_edges(edge)
|
||||||
|
self.edges[edge.token] = edge
|
||||||
|
self.core.save_edge(edge, src, dst)
|
||||||
|
|
||||||
def copy(self) -> None:
|
def copy(self) -> None:
|
||||||
if self.core.is_runtime():
|
if self.core.is_runtime():
|
||||||
|
@ -970,13 +968,12 @@ class CanvasGraph(tk.Canvas):
|
||||||
if edge.src not in to_copy_ids or edge.dst not in to_copy_ids:
|
if edge.src not in to_copy_ids or edge.dst not in to_copy_ids:
|
||||||
if canvas_node.id == edge.src:
|
if canvas_node.id == edge.src:
|
||||||
dst_node = self.nodes[edge.dst]
|
dst_node = self.nodes[edge.dst]
|
||||||
self.create_edge(node, dst_node)
|
copy_edge = self.create_edge(node, dst_node)
|
||||||
token = create_edge_token(node.id, dst_node.id)
|
|
||||||
elif canvas_node.id == edge.dst:
|
elif canvas_node.id == edge.dst:
|
||||||
src_node = self.nodes[edge.src]
|
src_node = self.nodes[edge.src]
|
||||||
self.create_edge(src_node, node)
|
copy_edge = self.create_edge(src_node, node)
|
||||||
token = create_edge_token(src_node.id, node.id)
|
else:
|
||||||
copy_edge = self.edges[token]
|
continue
|
||||||
copy_link = copy_edge.link
|
copy_link = copy_edge.link
|
||||||
iface1_id = copy_link.iface1.id if copy_link.iface1 else None
|
iface1_id = copy_link.iface1.id if copy_link.iface1 else None
|
||||||
iface2_id = copy_link.iface2.id if copy_link.iface2 else None
|
iface2_id = copy_link.iface2.id if copy_link.iface2 else None
|
||||||
|
@ -1003,13 +1000,11 @@ class CanvasGraph(tk.Canvas):
|
||||||
|
|
||||||
# copy link and link config
|
# copy link and link config
|
||||||
for edge in to_copy_edges:
|
for edge in to_copy_edges:
|
||||||
src_node_id = copy_map[edge.token[0]]
|
src_node_id = copy_map[edge.src]
|
||||||
dst_node_id = copy_map[edge.token[1]]
|
dst_node_id = copy_map[edge.dst]
|
||||||
src_node_copy = self.nodes[src_node_id]
|
src_node_copy = self.nodes[src_node_id]
|
||||||
dst_node_copy = self.nodes[dst_node_id]
|
dst_node_copy = self.nodes[dst_node_id]
|
||||||
self.create_edge(src_node_copy, dst_node_copy)
|
copy_edge = self.create_edge(src_node_copy, dst_node_copy)
|
||||||
token = create_edge_token(src_node_copy.id, dst_node_copy.id)
|
|
||||||
copy_edge = self.edges[token]
|
|
||||||
copy_link = copy_edge.link
|
copy_link = copy_edge.link
|
||||||
iface1_id = copy_link.iface1.id if copy_link.iface1 else None
|
iface1_id = copy_link.iface1.id if copy_link.iface1 else None
|
||||||
iface2_id = copy_link.iface2.id if copy_link.iface2 else None
|
iface2_id = copy_link.iface2.id if copy_link.iface2 else None
|
||||||
|
|
|
@ -247,14 +247,18 @@ class CanvasNode:
|
||||||
)
|
)
|
||||||
unlink_menu = tk.Menu(self.context)
|
unlink_menu = tk.Menu(self.context)
|
||||||
for edge in self.edges:
|
for edge in self.edges:
|
||||||
other_id = edge.src
|
link = edge.link
|
||||||
if self.id == other_id:
|
if self.id == edge.src:
|
||||||
other_id = edge.dst
|
other_id = edge.dst
|
||||||
|
other_iface = link.iface2.name if link.iface2 else None
|
||||||
|
else:
|
||||||
|
other_id = edge.src
|
||||||
|
other_iface = link.iface1.name if link.iface1 else None
|
||||||
other_node = self.canvas.nodes[other_id]
|
other_node = self.canvas.nodes[other_id]
|
||||||
|
other_name = other_node.core_node.name
|
||||||
|
label = f"{other_name}:{other_iface}" if other_iface else other_name
|
||||||
func_unlink = functools.partial(self.click_unlink, edge)
|
func_unlink = functools.partial(self.click_unlink, edge)
|
||||||
unlink_menu.add_command(
|
unlink_menu.add_command(label=label, command=func_unlink)
|
||||||
label=other_node.core_node.name, command=func_unlink
|
|
||||||
)
|
|
||||||
themes.style_menu(unlink_menu)
|
themes.style_menu(unlink_menu)
|
||||||
self.context.add_cascade(label="Unlink", menu=unlink_menu)
|
self.context.add_cascade(label="Unlink", menu=unlink_menu)
|
||||||
edit_menu = tk.Menu(self.context)
|
edit_menu = tk.Menu(self.context)
|
||||||
|
@ -318,10 +322,10 @@ class CanvasNode:
|
||||||
for edge in self.edges:
|
for edge in self.edges:
|
||||||
if self.id == edge.src:
|
if self.id == edge.src:
|
||||||
other_id = edge.dst
|
other_id = edge.dst
|
||||||
edge_iface_id = edge.src_iface.id
|
edge_iface_id = edge.link.iface1.id
|
||||||
else:
|
else:
|
||||||
other_id = edge.src
|
other_id = edge.src
|
||||||
edge_iface_id = edge.dst_iface.id
|
edge_iface_id = edge.link.iface2.id
|
||||||
if edge_iface_id != iface_id:
|
if edge_iface_id != iface_id:
|
||||||
continue
|
continue
|
||||||
other_node = self.canvas.nodes[other_id]
|
other_node = self.canvas.nodes[other_id]
|
||||||
|
|
|
@ -196,10 +196,10 @@ class InterfaceManager:
|
||||||
for edge in canvas_node.edges:
|
for edge in canvas_node.edges:
|
||||||
src_node = canvas.nodes[edge.src]
|
src_node = canvas.nodes[edge.src]
|
||||||
dst_node = canvas.nodes[edge.dst]
|
dst_node = canvas.nodes[edge.dst]
|
||||||
iface = edge.src_iface
|
iface = edge.link.iface1
|
||||||
check_node = src_node
|
check_node = src_node
|
||||||
if src_node == canvas_node:
|
if src_node == canvas_node:
|
||||||
iface = edge.dst_iface
|
iface = edge.link.iface2
|
||||||
check_node = dst_node
|
check_node = dst_node
|
||||||
if check_node.core_node.id in visited:
|
if check_node.core_node.id in visited:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -62,12 +62,17 @@ class NodeUtils:
|
||||||
IMAGE_NODES: Set[NodeType] = {NodeType.DOCKER, NodeType.LXC}
|
IMAGE_NODES: Set[NodeType] = {NodeType.DOCKER, NodeType.LXC}
|
||||||
WIRELESS_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
|
WIRELESS_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
|
||||||
RJ45_NODES: Set[NodeType] = {NodeType.RJ45}
|
RJ45_NODES: Set[NodeType] = {NodeType.RJ45}
|
||||||
|
BRIDGE_NODES: Set[NodeType] = {NodeType.HUB, NodeType.SWITCH}
|
||||||
IGNORE_NODES: Set[NodeType] = {NodeType.CONTROL_NET}
|
IGNORE_NODES: Set[NodeType] = {NodeType.CONTROL_NET}
|
||||||
MOBILITY_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
|
MOBILITY_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
|
||||||
NODE_MODELS: Set[str] = {"router", "host", "PC", "mdr", "prouter"}
|
NODE_MODELS: Set[str] = {"router", "host", "PC", "mdr", "prouter"}
|
||||||
ROUTER_NODES: Set[str] = {"router", "mdr"}
|
ROUTER_NODES: Set[str] = {"router", "mdr"}
|
||||||
ANTENNA_ICON: PhotoImage = None
|
ANTENNA_ICON: PhotoImage = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_bridge_node(cls, node: Node) -> bool:
|
||||||
|
return node.type in cls.BRIDGE_NODES
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_mobility(cls, node: Node) -> bool:
|
def is_mobility(cls, node: Node) -> bool:
|
||||||
return node.type in cls.MOBILITY_NODES
|
return node.type in cls.MOBILITY_NODES
|
||||||
|
|
Loading…
Add table
Reference in a new issue