From 9a42368221e68abf271ef9d4733b9410abcc27cc Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 1 May 2020 13:39:27 -0700 Subject: [PATCH 1/2] initial changes to mimic prior address creation --- daemon/core/gui/coreclient.py | 8 +++- daemon/core/gui/interface.py | 87 +++++++++++++++++++++++------------ daemon/core/gui/nodeutils.py | 7 ++- 3 files changed, 70 insertions(+), 32 deletions(-) diff --git a/daemon/core/gui/coreclient.py b/daemon/core/gui/coreclient.py index b6b13c58..523c8bb8 100644 --- a/daemon/core/gui/coreclient.py +++ b/daemon/core/gui/coreclient.py @@ -830,6 +830,7 @@ class CoreClient: such as link, configurations, interfaces """ edges = set() + removed_links = [] for canvas_node in canvas_nodes: node_id = canvas_node.core_node.id if node_id not in self.canvas_nodes: @@ -841,11 +842,14 @@ class CoreClient: if edge in edges: continue edges.add(edge) - self.links.pop(edge.token, None) + edge = self.links.pop(edge.token, None) + if edge is not None: + removed_links.append(edge.link) + self.interfaces_manager.removed(removed_links) def create_interface(self, canvas_node: CanvasNode) -> core_pb2.Interface: node = canvas_node.core_node - ip4, ip6 = self.interfaces_manager.get_ips(node.id) + ip4, ip6 = self.interfaces_manager.get_ips(node) ip4_mask = self.interfaces_manager.ip4_mask ip6_mask = self.interfaces_manager.ip6_mask interface_id = len(canvas_node.interfaces) diff --git a/daemon/core/gui/interface.py b/daemon/core/gui/interface.py index 359dba8e..3310da90 100644 --- a/daemon/core/gui/interface.py +++ b/daemon/core/gui/interface.py @@ -1,6 +1,5 @@ import logging -import random -from typing import TYPE_CHECKING, Set, Union +from typing import TYPE_CHECKING, List, Set, Tuple, Union import netaddr from netaddr import EUI, IPNetwork @@ -14,20 +13,20 @@ if TYPE_CHECKING: from core.gui.graph.node import CanvasNode -def random_mac(): - return ("{:02x}" * 6).format(*[random.randrange(256) for _ in range(6)]) - - class Subnets: def __init__(self, ip4: IPNetwork, ip6: IPNetwork) -> None: self.ip4 = ip4 self.ip6 = ip6 + self.used_indexes = set() def __eq__(self, other: "Subnets") -> bool: - return (self.ip4, self.ip6) == (other.ip4, other.ip6) + return self.key() == other.key() def __hash__(self) -> int: - return hash((self.ip4, self.ip6)) + return hash(self.key()) + + def key(self) -> Tuple[IPNetwork, IPNetwork]: + return self.ip4, self.ip6 def next(self) -> "Subnets": return Subnets(self.ip4.next(), self.ip6.next()) @@ -47,6 +46,7 @@ class InterfaceManager: self.mac = EUI(mac) self.current_mac = None self.current_subnets = None + self.used_subnets = {} def update_ips(self, ip4: str, ip6: str) -> None: self.reset() @@ -65,37 +65,66 @@ class InterfaceManager: return mac def next_subnets(self) -> Subnets: - # define currently used subnets - used_subnets = set() - for edge in self.app.core.links.values(): - link = edge.link - subnets = None - if link.HasField("interface_one"): - subnets = self.get_subnets(link.interface_one) - if link.HasField("interface_two"): - subnets = self.get_subnets(link.interface_two) - if subnets: - used_subnets.add(subnets) - - # find next available subnets - subnets = Subnets(self.ip4_subnets, self.ip6_subnets) - while subnets in used_subnets: + subnets = self.current_subnets + if subnets is None: + subnets = Subnets(self.ip4_subnets, self.ip6_subnets) + while subnets.key() in self.used_subnets: subnets = subnets.next() + self.used_subnets[subnets.key()] = subnets return subnets def reset(self): self.current_subnets = None + self.used_subnets.clear() - def get_ips(self, node_id: int) -> [str, str]: - ip4 = self.current_subnets.ip4[node_id] - ip6 = self.current_subnets.ip6[node_id] + def removed(self, links: List["core_pb2.Link"]): + # get remaining subnets + remaining_subnets = set() + + for link in links: + if link.HasField("interface_one"): + subnets = self.get_subnets(link.interface_one) + if subnets not in remaining_subnets: + self.used_subnets.pop(subnets.key(), None) + if link.HasField("interface_two"): + subnets = self.get_subnets(link.interface_two) + if subnets not in remaining_subnets: + self.used_subnets.pop(subnets.key(), None) + + def initialize_links(self, links: List["core_pb2.Link"]): + for link in links: + if link.HasField("interface_one"): + subnets = self.get_subnets(link.interface_one) + if subnets.key() not in self.used_subnets: + self.used_subnets[subnets.key()] = subnets + if link.HasField("interface_two"): + subnets = self.get_subnets(link.interface_two) + if subnets.key() not in self.used_subnets: + self.used_subnets[subnets.key()] = subnets + + def next_index(self, node: "core_pb2.Node") -> int: + if NodeUtils.is_router_node(node): + index = 1 + else: + index = 20 + while True: + if index not in self.current_subnets.used_indexes: + self.current_subnets.used_indexes.add(index) + break + index += 1 + return index + + def get_ips(self, node: "core_pb2.Node") -> [str, str]: + index = self.next_index(node) + ip4 = self.current_subnets.ip4[index] + ip6 = self.current_subnets.ip6[index] return str(ip4), str(ip6) - @classmethod - def get_subnets(cls, interface: "core_pb2.Interface") -> Subnets: + def get_subnets(self, interface: "core_pb2.Interface") -> Subnets: ip4_subnet = IPNetwork(f"{interface.ip4}/{interface.ip4mask}").cidr ip6_subnet = IPNetwork(f"{interface.ip6}/{interface.ip6mask}").cidr - return Subnets(ip4_subnet, ip6_subnet) + subnets = Subnets(ip4_subnet, ip6_subnet) + return self.used_subnets.get(subnets.key(), subnets) def determine_subnets( self, canvas_src_node: "CanvasNode", canvas_dst_node: "CanvasNode" diff --git a/daemon/core/gui/nodeutils.py b/daemon/core/gui/nodeutils.py index 7ccb7ca3..24c01f06 100644 --- a/daemon/core/gui/nodeutils.py +++ b/daemon/core/gui/nodeutils.py @@ -1,7 +1,7 @@ import logging from typing import TYPE_CHECKING, Dict, List, Optional, Set, Union -from core.api.grpc.core_pb2 import NodeType +from core.api.grpc.core_pb2 import Node, NodeType from core.gui.images import ImageEnum, Images, TypeToImage if TYPE_CHECKING: @@ -64,8 +64,13 @@ class NodeUtils: RJ45_NODES = {NodeType.RJ45} IGNORE_NODES = {NodeType.CONTROL_NET, NodeType.PEER_TO_PEER} NODE_MODELS = {"router", "host", "PC", "mdr", "prouter"} + ROUTER_NODES = {"router", "mdr"} ANTENNA_ICON = None + @classmethod + def is_router_node(cls, node: Node) -> bool: + return cls.is_model_node(node.type) and node.model in cls.ROUTER_NODES + @classmethod def is_ignore_node(cls, node_type: NodeType) -> bool: return node_type in cls.IGNORE_NODES From 2e9968c306bd36a27ed669c74a8d50ba7b611063 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 1 May 2020 15:35:53 -0700 Subject: [PATCH 2/2] pygui further changes to mimic old gui behavior, parsing link data when joining and removing link data when they are removed --- daemon/core/gui/coreclient.py | 3 ++ daemon/core/gui/interface.py | 68 +++++++++++++++++++++++++---------- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/daemon/core/gui/coreclient.py b/daemon/core/gui/coreclient.py index ef50ace2..8c05a30f 100644 --- a/daemon/core/gui/coreclient.py +++ b/daemon/core/gui/coreclient.py @@ -283,6 +283,9 @@ class CoreClient: response = self.client.get_emane_config(self.session_id) self.emane_config = response.config + # update interface manager + self.interfaces_manager.joined(session.links) + # draw session self.app.canvas.reset_and_redraw(session) diff --git a/daemon/core/gui/interface.py b/daemon/core/gui/interface.py index 3310da90..8e2f4aa6 100644 --- a/daemon/core/gui/interface.py +++ b/daemon/core/gui/interface.py @@ -1,5 +1,5 @@ import logging -from typing import TYPE_CHECKING, List, Set, Tuple, Union +from typing import TYPE_CHECKING, Any, List, Optional, Set, Tuple import netaddr from netaddr import EUI, IPNetwork @@ -13,13 +13,22 @@ if TYPE_CHECKING: from core.gui.graph.node import CanvasNode +def get_index(interface: "core_pb2.Interface") -> int: + net = netaddr.IPNetwork(f"{interface.ip4}/{interface.ip4mask}") + ip_value = net.value + cidr_value = net.cidr.value + return ip_value - cidr_value + + class Subnets: def __init__(self, ip4: IPNetwork, ip6: IPNetwork) -> None: self.ip4 = ip4 self.ip6 = ip6 self.used_indexes = set() - def __eq__(self, other: "Subnets") -> bool: + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Subnets): + return False return self.key() == other.key() def __hash__(self) -> int: @@ -73,34 +82,55 @@ class InterfaceManager: self.used_subnets[subnets.key()] = subnets return subnets - def reset(self): + def reset(self) -> None: self.current_subnets = None self.used_subnets.clear() - def removed(self, links: List["core_pb2.Link"]): + def removed(self, links: List["core_pb2.Link"]) -> None: # get remaining subnets remaining_subnets = set() - - for link in links: + for edge in self.app.core.links.values(): + link = edge.link if link.HasField("interface_one"): subnets = self.get_subnets(link.interface_one) - if subnets not in remaining_subnets: - self.used_subnets.pop(subnets.key(), None) + remaining_subnets.add(subnets) if link.HasField("interface_two"): subnets = self.get_subnets(link.interface_two) - if subnets not in remaining_subnets: - self.used_subnets.pop(subnets.key(), None) + remaining_subnets.add(subnets) - def initialize_links(self, links: List["core_pb2.Link"]): + # remove all subnets from used subnets when no longer present + # or remove used indexes from subnet + interfaces = [] for link in links: if link.HasField("interface_one"): - subnets = self.get_subnets(link.interface_one) - if subnets.key() not in self.used_subnets: - self.used_subnets[subnets.key()] = subnets + interfaces.append(link.interface_one) if link.HasField("interface_two"): - subnets = self.get_subnets(link.interface_two) - if subnets.key() not in self.used_subnets: - self.used_subnets[subnets.key()] = subnets + interfaces.append(link.interface_two) + for interface in interfaces: + subnets = self.get_subnets(interface) + if subnets not in remaining_subnets: + if self.current_subnets == subnets: + self.current_subnets = None + self.used_subnets.pop(subnets.key(), None) + else: + index = get_index(interface) + subnets.used_indexes.discard(index) + + def joined(self, links: List["core_pb2.Link"]) -> None: + interfaces = [] + for link in links: + if link.HasField("interface_one"): + interfaces.append(link.interface_one) + if link.HasField("interface_two"): + interfaces.append(link.interface_two) + + # add to used subnets and mark used indexes + for interface in interfaces: + subnets = self.get_subnets(interface) + index = get_index(interface) + subnets.used_indexes.add(index) + if subnets.key() not in self.used_subnets: + self.used_subnets[subnets.key()] = subnets def next_index(self, node: "core_pb2.Node") -> int: if NodeUtils.is_router_node(node): @@ -128,7 +158,7 @@ class InterfaceManager: def determine_subnets( self, canvas_src_node: "CanvasNode", canvas_dst_node: "CanvasNode" - ): + ) -> None: src_node = canvas_src_node.core_node dst_node = canvas_dst_node.core_node is_src_container = NodeUtils.is_container_node(src_node.type) @@ -152,7 +182,7 @@ class InterfaceManager: def find_subnets( self, canvas_node: "CanvasNode", visited: Set[int] = None - ) -> Union[IPNetwork, None]: + ) -> Optional[IPNetwork]: logging.info("finding subnet for node: %s", canvas_node.core_node.name) canvas = self.app.canvas subnets = None