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
This commit is contained in:
parent
1884cda271
commit
8eb4df7b1d
10 changed files with 162 additions and 55 deletions
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue