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:
Blake Harnden 2019-12-18 22:09:00 -08:00
parent 1884cda271
commit 8eb4df7b1d
10 changed files with 162 additions and 55 deletions

View file

@ -452,6 +452,9 @@ class CoreClient:
hooks = list(self.hooks.values()) hooks = list(self.hooks.values())
service_configs = self.get_service_configs_proto() service_configs = self.get_service_configs_proto()
file_configs = self.get_service_file_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: if self.emane_config:
emane_config = {x: self.emane_config[x].value for x in self.emane_config} emane_config = {x: self.emane_config[x].value for x in self.emane_config}
else: else:
@ -471,6 +474,7 @@ class CoreClient:
mobility_configs, mobility_configs,
service_configs, service_configs,
file_configs, file_configs,
asymmetric_links,
) )
self.set_metadata() self.set_metadata()
process_time = time.perf_counter() - start process_time = time.perf_counter() - start

View file

@ -5,17 +5,34 @@ import logging
import tkinter as tk import tkinter as tk
from tkinter import ttk from tkinter import ttk
from core.api.grpc import core_pb2
from coretk.dialogs.colorpicker import ColorPicker from coretk.dialogs.colorpicker import ColorPicker
from coretk.dialogs.dialog import Dialog from coretk.dialogs.dialog import Dialog
from coretk.themes import PADX, PADY 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): class LinkConfiguration(Dialog):
def __init__(self, master, app, edge): def __init__(self, master, app, edge):
super().__init__(master, app, "Link Configuration", modal=True) super().__init__(master, app, "Link Configuration", modal=True)
self.app = app self.app = app
self.edge = edge self.edge = edge
self.is_symmetric = True self.is_symmetric = edge.link.options.unidirectional is False
if self.is_symmetric: if self.is_symmetric:
self.symmetry_var = tk.StringVar(value=">>") self.symmetry_var = tk.StringVar(value=">>")
else: else:
@ -26,16 +43,17 @@ class LinkConfiguration(Dialog):
self.jitter = tk.StringVar() self.jitter = tk.StringVar()
self.loss = tk.StringVar() self.loss = tk.StringVar()
self.duplicate = 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 = tk.StringVar(value="#000000")
self.color_button = None self.color_button = None
self.width = tk.DoubleVar() 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.load_link_config()
self.symmetric_frame = None self.symmetric_frame = None
self.asymmetric_frame = None self.asymmetric_frame = None
@ -113,7 +131,7 @@ class LinkConfiguration(Dialog):
validate="key", validate="key",
validatecommand=(self.app.validation.positive_int, "%P"), 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 row = row + 1
label = ttk.Label(frame, text="Delay (us)") label = ttk.Label(frame, text="Delay (us)")
@ -132,7 +150,7 @@ class LinkConfiguration(Dialog):
validate="key", validate="key",
validatecommand=(self.app.validation.positive_int, "%P"), 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 row = row + 1
label = ttk.Label(frame, text="Jitter (us)") label = ttk.Label(frame, text="Jitter (us)")
@ -151,7 +169,7 @@ class LinkConfiguration(Dialog):
validate="key", validate="key",
validatecommand=(self.app.validation.positive_int, "%P"), 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 row = row + 1
label = ttk.Label(frame, text="Loss (%)") label = ttk.Label(frame, text="Loss (%)")
@ -170,7 +188,7 @@ class LinkConfiguration(Dialog):
validate="key", validate="key",
validatecommand=(self.app.validation.positive_float, "%P"), 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 row = row + 1
label = ttk.Label(frame, text="Duplicate (%)") label = ttk.Label(frame, text="Duplicate (%)")
@ -189,7 +207,7 @@ class LinkConfiguration(Dialog):
validate="key", validate="key",
validatecommand=(self.app.validation.positive_int, "%P"), 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 row = row + 1
label = ttk.Label(frame, text="Color") 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, width=self.width.get())
self.app.canvas.itemconfigure(self.edge.id, fill=self.color.get()) self.app.canvas.itemconfigure(self.edge.id, fill=self.color.get())
link = self.edge.link link = self.edge.link
bandwidth = self.bandwidth.get() bandwidth = get_int(self.bandwidth)
if bandwidth != "": jitter = get_int(self.jitter)
link.options.bandwidth = int(bandwidth) delay = get_int(self.delay)
jitter = self.jitter.get() duplicate = get_int(self.duplicate)
if jitter != "": loss = get_float(self.loss)
link.options.jitter = int(jitter) options = core_pb2.LinkOptions(
delay = self.delay.get() bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, per=loss
if delay != "": )
link.options.delay = int(delay) link.options.CopyFrom(options)
duplicate = self.duplicate.get()
if duplicate != "": interface_one = None
link.options.dup = int(duplicate) if link.HasField("interface_one"):
loss = self.loss.get() interface_one = link.interface_one.id
if loss != "": interface_two = None
link.options.per = float(loss) 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"): 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 session_id = self.app.core.session_id
self.app.core.client.edit_link( self.app.core.client.edit_link(
session_id, session_id,
@ -261,6 +306,15 @@ class LinkConfiguration(Dialog):
interface_one, interface_one,
interface_two, 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() self.destroy()
@ -299,3 +353,10 @@ class LinkConfiguration(Dialog):
self.duplicate.set(str(link.options.dup)) self.duplicate.set(str(link.options.dup))
self.loss.set(str(link.options.per)) self.loss.set(str(link.options.per))
self.delay.set(str(link.options.delay)) 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))

View file

@ -54,6 +54,7 @@ class CanvasEdge:
self.token = None self.token = None
self.font = Font(size=8) self.font = Font(size=8)
self.link = None self.link = None
self.asymmetric_link = None
self.throughput = None self.throughput = None
self.set_binding() self.set_binding()

View file

@ -201,12 +201,12 @@ class CanvasGraph(tk.Canvas):
""" """
# draw existing nodes # draw existing nodes
for core_node in session.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 # peer to peer node is not drawn on the GUI
if NodeUtils.is_ignore_node(core_node.type): if NodeUtils.is_ignore_node(core_node.type):
continue continue
# draw nodes on the canvas # draw nodes on the canvas
logging.info("drawing core node: %s", core_node)
image = NodeUtils.node_icon(core_node.type, core_node.model) image = NodeUtils.node_icon(core_node.type, core_node.model)
if core_node.icon: if core_node.icon:
try: try:
@ -222,33 +222,42 @@ class CanvasGraph(tk.Canvas):
# draw existing links # draw existing links
for link in session.links: for link in session.links:
logging.info("drawing link: %s", link)
canvas_node_one = self.core.canvas_nodes[link.node_one_id] canvas_node_one = self.core.canvas_nodes[link.node_one_id]
node_one = canvas_node_one.core_node node_one = canvas_node_one.core_node
canvas_node_two = self.core.canvas_nodes[link.node_two_id] canvas_node_two = self.core.canvas_nodes[link.node_two_id]
node_two = canvas_node_two.core_node 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: if link.type == core_pb2.LinkType.WIRELESS:
self.add_wireless_edge(canvas_node_one, canvas_node_two) self.add_wireless_edge(canvas_node_one, canvas_node_two)
else: else:
edge = CanvasEdge( if token not in self.edges:
node_one.position.x, edge = CanvasEdge(
node_one.position.y, node_one.position.x,
node_two.position.x, node_one.position.y,
node_two.position.y, node_two.position.x,
canvas_node_one.id, node_two.position.y,
self, canvas_node_one.id,
) self,
edge.set_link(link) )
edge.token = tuple(sorted((canvas_node_one.id, canvas_node_two.id))) edge.token = token
edge.dst = canvas_node_two.id edge.dst = canvas_node_two.id
edge.check_wireless() edge.set_link(link)
canvas_node_one.edges.add(edge) edge.check_wireless()
canvas_node_two.edges.add(edge) canvas_node_one.edges.add(edge)
self.edges[edge.token] = edge canvas_node_two.edges.add(edge)
self.core.links[edge.token] = edge self.edges[edge.token] = edge
if link.HasField("interface_one"): self.core.links[edge.token] = edge
canvas_node_one.interfaces.append(link.interface_one) if link.HasField("interface_one"):
if link.HasField("interface_two"): canvas_node_one.interfaces.append(link.interface_one)
canvas_node_two.interfaces.append(link.interface_two) 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 # raise the nodes so they on top of the links
self.tag_raise(tags.NODE) self.tag_raise(tags.NODE)

View file

@ -161,6 +161,7 @@ class CoreGrpcClient:
mobility_configs=None, mobility_configs=None,
service_configs=None, service_configs=None,
service_file_configs=None, service_file_configs=None,
asymmetric_links=None,
): ):
""" """
Start a session. Start a session.
@ -176,6 +177,7 @@ class CoreGrpcClient:
:param list mobility_configs: node mobility configurations :param list mobility_configs: node mobility configurations
:param list service_configs: node service configurations :param list service_configs: node service configurations
:param list service_file_configs: node service file configurations :param list service_file_configs: node service file configurations
:param list asymmetric_links: asymmetric links to edit
:return: start session response :return: start session response
:rtype: core_pb2.StartSessionResponse :rtype: core_pb2.StartSessionResponse
""" """
@ -191,6 +193,7 @@ class CoreGrpcClient:
mobility_configs=mobility_configs, mobility_configs=mobility_configs,
service_configs=service_configs, service_configs=service_configs,
service_file_configs=service_file_configs, service_file_configs=service_file_configs,
asymmetric_links=asymmetric_links,
) )
return self.stub.StartSession(request) return self.stub.StartSession(request)

View file

@ -128,7 +128,7 @@ def create_nodes(session, node_protos):
def create_links(session, link_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 core.emulator.session.Session session: session to create nodes in
:param list[core_pb2.Link] link_protos: link proto messages :param list[core_pb2.Link] link_protos: link proto messages
@ -149,6 +149,29 @@ def create_links(session, link_protos):
return results, exceptions 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): def convert_value(value):
""" """
Convert value into string. Convert value into string.

View file

@ -158,6 +158,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
# create links # create links
grpcutils.create_links(session, request.links) grpcutils.create_links(session, request.links)
# asymmetric links
grpcutils.edit_links(session, request.asymmetric_links)
# set to instantiation and start # set to instantiation and start
session.set_state(EventTypes.INSTANTIATION_STATE) session.set_state(EventTypes.INSTANTIATION_STATE)
session.instantiate() session.instantiate()

View file

@ -1053,6 +1053,7 @@ class CoreNetworkBase(NodeBase):
message_type=0, message_type=0,
node1_id=linked_node.id, node1_id=linked_node.id,
node2_id=self.id, node2_id=self.id,
link_type=self.linktype,
unidirectional=1, unidirectional=1,
delay=netif.getparam("delay"), delay=netif.getparam("delay"),
bandwidth=netif.getparam("bw"), bandwidth=netif.getparam("bw"),

View file

@ -965,6 +965,7 @@ class PtpNet(CoreNetwork):
if unidirectional: if unidirectional:
link_data = LinkData( link_data = LinkData(
message_type=0, message_type=0,
link_type=self.linktype,
node1_id=if2.node.id, node1_id=if2.node.id,
node2_id=if1.node.id, node2_id=if1.node.id,
delay=if2.getparam("delay"), delay=if2.getparam("delay"),

View file

@ -150,6 +150,7 @@ message StartSessionRequest {
repeated MobilityConfig mobility_configs = 9; repeated MobilityConfig mobility_configs = 9;
repeated ServiceConfig service_configs = 10; repeated ServiceConfig service_configs = 10;
repeated ServiceFileConfig service_file_configs = 11; repeated ServiceFileConfig service_file_configs = 11;
repeated Link asymmetric_links = 12;
} }
message StartSessionResponse { message StartSessionResponse {