From 8eb4df7b1d0c8af17684141f8aee74b5546978bb Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Wed, 18 Dec 2019 22:09:00 -0800 Subject: [PATCH] updated linkconfig to support asymmetric links, updated grpc start session to provide asymmetric links, since they currently depend on being processed as a link edit --- coretk/coretk/coreclient.py | 4 + coretk/coretk/dialogs/linkconfig.py | 127 +++++++++++++++++++------- coretk/coretk/graph/edges.py | 1 + coretk/coretk/graph/graph.py | 51 ++++++----- daemon/core/api/grpc/client.py | 3 + daemon/core/api/grpc/grpcutils.py | 25 ++++- daemon/core/api/grpc/server.py | 3 + daemon/core/nodes/base.py | 1 + daemon/core/nodes/network.py | 1 + daemon/proto/core/api/grpc/core.proto | 1 + 10 files changed, 162 insertions(+), 55 deletions(-) diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index 320c84e0..8f2d479d 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -452,6 +452,9 @@ class CoreClient: hooks = list(self.hooks.values()) service_configs = self.get_service_configs_proto() file_configs = self.get_service_file_configs_proto() + asymmetric_links = [ + x.asymmetric_link for x in self.links.values() if x.asymmetric_link + ] if self.emane_config: emane_config = {x: self.emane_config[x].value for x in self.emane_config} else: @@ -471,6 +474,7 @@ class CoreClient: mobility_configs, service_configs, file_configs, + asymmetric_links, ) self.set_metadata() process_time = time.perf_counter() - start diff --git a/coretk/coretk/dialogs/linkconfig.py b/coretk/coretk/dialogs/linkconfig.py index f96765dc..cf0daafc 100644 --- a/coretk/coretk/dialogs/linkconfig.py +++ b/coretk/coretk/dialogs/linkconfig.py @@ -5,17 +5,34 @@ import logging import tkinter as tk from tkinter import ttk +from core.api.grpc import core_pb2 from coretk.dialogs.colorpicker import ColorPicker from coretk.dialogs.dialog import Dialog from coretk.themes import PADX, PADY +def get_int(var): + value = var.get() + if value != "": + return int(value) + else: + return None + + +def get_float(var): + value = var.get() + if value != "": + return float(value) + else: + return None + + class LinkConfiguration(Dialog): def __init__(self, master, app, edge): super().__init__(master, app, "Link Configuration", modal=True) self.app = app self.edge = edge - self.is_symmetric = True + self.is_symmetric = edge.link.options.unidirectional is False if self.is_symmetric: self.symmetry_var = tk.StringVar(value=">>") else: @@ -26,16 +43,17 @@ class LinkConfiguration(Dialog): self.jitter = tk.StringVar() self.loss = tk.StringVar() self.duplicate = tk.StringVar() + + self.down_bandwidth = tk.StringVar() + self.down_delay = tk.StringVar() + self.down_jitter = tk.StringVar() + self.down_loss = tk.StringVar() + self.down_duplicate = tk.StringVar() + self.color = tk.StringVar(value="#000000") self.color_button = None self.width = tk.DoubleVar() - self.down_bandwidth = tk.IntVar(value="") - self.down_delay = tk.IntVar(value="") - self.down_jitter = tk.IntVar(value="") - self.down_loss = tk.DoubleVar(value="") - self.down_duplicate = tk.IntVar(value="") - self.load_link_config() self.symmetric_frame = None self.asymmetric_frame = None @@ -113,7 +131,7 @@ class LinkConfiguration(Dialog): validate="key", validatecommand=(self.app.validation.positive_int, "%P"), ) - entry.grid(row=row, column=2, sticky="nsew") + entry.grid(row=row, column=2, sticky="ew", pady=PADY) row = row + 1 label = ttk.Label(frame, text="Delay (us)") @@ -132,7 +150,7 @@ class LinkConfiguration(Dialog): validate="key", validatecommand=(self.app.validation.positive_int, "%P"), ) - entry.grid(row=row, column=2, sticky="ew") + entry.grid(row=row, column=2, sticky="ew", pady=PADY) row = row + 1 label = ttk.Label(frame, text="Jitter (us)") @@ -151,7 +169,7 @@ class LinkConfiguration(Dialog): validate="key", validatecommand=(self.app.validation.positive_int, "%P"), ) - entry.grid(row=row, column=2, sticky="ew") + entry.grid(row=row, column=2, sticky="ew", pady=PADY) row = row + 1 label = ttk.Label(frame, text="Loss (%)") @@ -170,7 +188,7 @@ class LinkConfiguration(Dialog): validate="key", validatecommand=(self.app.validation.positive_float, "%P"), ) - entry.grid(row=row, column=2, sticky="ew") + entry.grid(row=row, column=2, sticky="ew", pady=PADY) row = row + 1 label = ttk.Label(frame, text="Duplicate (%)") @@ -189,7 +207,7 @@ class LinkConfiguration(Dialog): validate="key", validatecommand=(self.app.validation.positive_int, "%P"), ) - entry.grid(row=row, column=2, sticky="ew") + entry.grid(row=row, column=2, sticky="ew", pady=PADY) row = row + 1 label = ttk.Label(frame, text="Color") @@ -229,29 +247,56 @@ class LinkConfiguration(Dialog): self.app.canvas.itemconfigure(self.edge.id, width=self.width.get()) self.app.canvas.itemconfigure(self.edge.id, fill=self.color.get()) link = self.edge.link - bandwidth = self.bandwidth.get() - if bandwidth != "": - link.options.bandwidth = int(bandwidth) - jitter = self.jitter.get() - if jitter != "": - link.options.jitter = int(jitter) - delay = self.delay.get() - if delay != "": - link.options.delay = int(delay) - duplicate = self.duplicate.get() - if duplicate != "": - link.options.dup = int(duplicate) - loss = self.loss.get() - if loss != "": - link.options.per = float(loss) + bandwidth = get_int(self.bandwidth) + jitter = get_int(self.jitter) + delay = get_int(self.delay) + duplicate = get_int(self.duplicate) + loss = get_float(self.loss) + options = core_pb2.LinkOptions( + bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, per=loss + ) + link.options.CopyFrom(options) + + interface_one = None + if link.HasField("interface_one"): + interface_one = link.interface_one.id + interface_two = None + if link.HasField("interface_two"): + interface_two = link.interface_two.id + + if not self.is_symmetric: + link.options.unidirectional = True + asym_interface_one = None + if interface_one: + asym_interface_one = core_pb2.Interface(id=interface_one) + asym_interface_two = None + if interface_two: + asym_interface_two = core_pb2.Interface(id=interface_two) + down_bandwidth = get_int(self.down_bandwidth) + down_jitter = get_int(self.down_jitter) + down_delay = get_int(self.down_delay) + down_duplicate = get_int(self.down_duplicate) + down_loss = get_float(self.down_loss) + options = core_pb2.LinkOptions( + bandwidth=down_bandwidth, + jitter=down_jitter, + delay=down_delay, + dup=down_duplicate, + per=down_loss, + unidirectional=True, + ) + self.edge.asymmetric_link = core_pb2.Link( + node_one_id=link.node_two_id, + node_two_id=link.node_one_id, + interface_one=asym_interface_one, + interface_two=asym_interface_two, + options=options, + ) + else: + link.options.unidirectional = False + self.edge.asymmetric_link = None if self.app.core.is_runtime() and link.HasField("options"): - interface_one = None - if link.HasField("interface_one"): - interface_one = link.interface_one.id - interface_two = None - if link.HasField("interface_two"): - interface_two = link.interface_two.id session_id = self.app.core.session_id self.app.core.client.edit_link( session_id, @@ -261,6 +306,15 @@ class LinkConfiguration(Dialog): interface_one, interface_two, ) + if self.edge.asymmetric_link: + self.app.core.client.edit_link( + session_id, + link.node_two_id, + link.node_one_id, + self.edge.asymmetric_link.options, + interface_one, + interface_two, + ) self.destroy() @@ -299,3 +353,10 @@ class LinkConfiguration(Dialog): self.duplicate.set(str(link.options.dup)) self.loss.set(str(link.options.per)) self.delay.set(str(link.options.delay)) + if not self.is_symmetric: + asym_link = self.edge.asymmetric_link + self.down_bandwidth.set(str(asym_link.options.bandwidth)) + self.down_jitter.set(str(asym_link.options.jitter)) + self.down_duplicate.set(str(asym_link.options.dup)) + self.down_loss.set(str(asym_link.options.per)) + self.down_delay.set(str(asym_link.options.delay)) diff --git a/coretk/coretk/graph/edges.py b/coretk/coretk/graph/edges.py index 80193cc8..e25a5305 100644 --- a/coretk/coretk/graph/edges.py +++ b/coretk/coretk/graph/edges.py @@ -54,6 +54,7 @@ class CanvasEdge: self.token = None self.font = Font(size=8) self.link = None + self.asymmetric_link = None self.throughput = None self.set_binding() diff --git a/coretk/coretk/graph/graph.py b/coretk/coretk/graph/graph.py index c448ecd1..1d05d7f5 100644 --- a/coretk/coretk/graph/graph.py +++ b/coretk/coretk/graph/graph.py @@ -201,12 +201,12 @@ class CanvasGraph(tk.Canvas): """ # draw existing nodes for core_node in session.nodes: - logging.info("drawing core node: %s", core_node) # peer to peer node is not drawn on the GUI if NodeUtils.is_ignore_node(core_node.type): continue # draw nodes on the canvas + logging.info("drawing core node: %s", core_node) image = NodeUtils.node_icon(core_node.type, core_node.model) if core_node.icon: try: @@ -222,33 +222,42 @@ class CanvasGraph(tk.Canvas): # draw existing links for link in session.links: + logging.info("drawing link: %s", link) canvas_node_one = self.core.canvas_nodes[link.node_one_id] node_one = canvas_node_one.core_node canvas_node_two = self.core.canvas_nodes[link.node_two_id] node_two = canvas_node_two.core_node + token = tuple(sorted((canvas_node_one.id, canvas_node_two.id))) + if link.type == core_pb2.LinkType.WIRELESS: self.add_wireless_edge(canvas_node_one, canvas_node_two) else: - edge = CanvasEdge( - node_one.position.x, - node_one.position.y, - node_two.position.x, - node_two.position.y, - canvas_node_one.id, - self, - ) - edge.set_link(link) - edge.token = tuple(sorted((canvas_node_one.id, canvas_node_two.id))) - edge.dst = canvas_node_two.id - edge.check_wireless() - canvas_node_one.edges.add(edge) - canvas_node_two.edges.add(edge) - self.edges[edge.token] = edge - self.core.links[edge.token] = edge - if link.HasField("interface_one"): - canvas_node_one.interfaces.append(link.interface_one) - if link.HasField("interface_two"): - canvas_node_two.interfaces.append(link.interface_two) + if token not in self.edges: + edge = CanvasEdge( + node_one.position.x, + node_one.position.y, + node_two.position.x, + node_two.position.y, + canvas_node_one.id, + self, + ) + edge.token = token + edge.dst = canvas_node_two.id + edge.set_link(link) + edge.check_wireless() + canvas_node_one.edges.add(edge) + canvas_node_two.edges.add(edge) + self.edges[edge.token] = edge + self.core.links[edge.token] = edge + if link.HasField("interface_one"): + canvas_node_one.interfaces.append(link.interface_one) + if link.HasField("interface_two"): + canvas_node_two.interfaces.append(link.interface_two) + elif link.options.unidirectional: + edge = self.edges[token] + edge.asymmetric_link = link + else: + logging.error("duplicate link received: %s", link) # raise the nodes so they on top of the links self.tag_raise(tags.NODE) diff --git a/daemon/core/api/grpc/client.py b/daemon/core/api/grpc/client.py index bc48c9ab..9aa99349 100644 --- a/daemon/core/api/grpc/client.py +++ b/daemon/core/api/grpc/client.py @@ -161,6 +161,7 @@ class CoreGrpcClient: mobility_configs=None, service_configs=None, service_file_configs=None, + asymmetric_links=None, ): """ Start a session. @@ -176,6 +177,7 @@ class CoreGrpcClient: :param list mobility_configs: node mobility configurations :param list service_configs: node service configurations :param list service_file_configs: node service file configurations + :param list asymmetric_links: asymmetric links to edit :return: start session response :rtype: core_pb2.StartSessionResponse """ @@ -191,6 +193,7 @@ class CoreGrpcClient: mobility_configs=mobility_configs, service_configs=service_configs, service_file_configs=service_file_configs, + asymmetric_links=asymmetric_links, ) return self.stub.StartSession(request) diff --git a/daemon/core/api/grpc/grpcutils.py b/daemon/core/api/grpc/grpcutils.py index 4ea752fd..a3b25541 100644 --- a/daemon/core/api/grpc/grpcutils.py +++ b/daemon/core/api/grpc/grpcutils.py @@ -128,7 +128,7 @@ def create_nodes(session, node_protos): def create_links(session, link_protos): """ - Create nodes using a thread pool and wait for completion. + Create links using a thread pool and wait for completion. :param core.emulator.session.Session session: session to create nodes in :param list[core_pb2.Link] link_protos: link proto messages @@ -149,6 +149,29 @@ def create_links(session, link_protos): return results, exceptions +def edit_links(session, link_protos): + """ + Edit links using a thread pool and wait for completion. + + :param core.emulator.session.Session session: session to create nodes in + :param list[core_pb2.Link] link_protos: link proto messages + :return: results and exceptions for created links + :rtype: tuple + """ + funcs = [] + for link_proto in link_protos: + node_one_id = link_proto.node_one_id + node_two_id = link_proto.node_two_id + interface_one, interface_two, options = add_link_data(link_proto) + args = (node_one_id, node_two_id, interface_one.id, interface_two.id, options) + funcs.append((session.update_link, args, {})) + start = time.monotonic() + results, exceptions = utils.threadpool(funcs) + total = time.monotonic() - start + logging.debug("grpc edit links time: %s", total) + return results, exceptions + + def convert_value(value): """ Convert value into string. diff --git a/daemon/core/api/grpc/server.py b/daemon/core/api/grpc/server.py index 068d3eeb..bf2a8ac8 100644 --- a/daemon/core/api/grpc/server.py +++ b/daemon/core/api/grpc/server.py @@ -158,6 +158,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): # create links grpcutils.create_links(session, request.links) + # asymmetric links + grpcutils.edit_links(session, request.asymmetric_links) + # set to instantiation and start session.set_state(EventTypes.INSTANTIATION_STATE) session.instantiate() diff --git a/daemon/core/nodes/base.py b/daemon/core/nodes/base.py index a663741e..3193d954 100644 --- a/daemon/core/nodes/base.py +++ b/daemon/core/nodes/base.py @@ -1053,6 +1053,7 @@ class CoreNetworkBase(NodeBase): message_type=0, node1_id=linked_node.id, node2_id=self.id, + link_type=self.linktype, unidirectional=1, delay=netif.getparam("delay"), bandwidth=netif.getparam("bw"), diff --git a/daemon/core/nodes/network.py b/daemon/core/nodes/network.py index f0639649..5342215b 100644 --- a/daemon/core/nodes/network.py +++ b/daemon/core/nodes/network.py @@ -965,6 +965,7 @@ class PtpNet(CoreNetwork): if unidirectional: link_data = LinkData( message_type=0, + link_type=self.linktype, node1_id=if2.node.id, node2_id=if1.node.id, delay=if2.getparam("delay"), diff --git a/daemon/proto/core/api/grpc/core.proto b/daemon/proto/core/api/grpc/core.proto index ec7bf49c..050d5e75 100644 --- a/daemon/proto/core/api/grpc/core.proto +++ b/daemon/proto/core/api/grpc/core.proto @@ -150,6 +150,7 @@ message StartSessionRequest { repeated MobilityConfig mobility_configs = 9; repeated ServiceConfig service_configs = 10; repeated ServiceFileConfig service_file_configs = 11; + repeated Link asymmetric_links = 12; } message StartSessionResponse {