Merge branch 'coretk' into coretk-config

This commit is contained in:
Huy Pham 2019-11-20 16:53:05 -08:00
commit 37c032afc5
9 changed files with 181 additions and 73 deletions

View file

@ -5,6 +5,7 @@ from core.api.grpc import core_pb2
from coretk.dialogs.emaneconfig import EmaneConfiguration from coretk.dialogs.emaneconfig import EmaneConfiguration
from coretk.dialogs.nodeconfig import NodeConfigDialog from coretk.dialogs.nodeconfig import NodeConfigDialog
from coretk.dialogs.wlanconfig import WlanConfigDialog from coretk.dialogs.wlanconfig import WlanConfigDialog
from coretk.nodeutils import NodeUtils
class CanvasAction: class CanvasAction:
@ -16,7 +17,7 @@ class CanvasAction:
def display_configuration(self, canvas_node): def display_configuration(self, canvas_node):
node_type = canvas_node.core_node.type node_type = canvas_node.core_node.type
self.node_to_show_config = canvas_node self.node_to_show_config = canvas_node
if node_type == core_pb2.NodeType.DEFAULT: if NodeUtils.is_container_node(node_type):
self.display_node_configuration() self.display_node_configuration()
elif node_type == core_pb2.NodeType.WIRELESS_LAN: elif node_type == core_pb2.NodeType.WIRELESS_LAN:
self.display_wlan_configuration(canvas_node) self.display_wlan_configuration(canvas_node)
@ -30,7 +31,7 @@ class CanvasAction:
def display_wlan_configuration(self, canvas_node): def display_wlan_configuration(self, canvas_node):
wlan_config = self.master.core.wlanconfig_management.configurations[ wlan_config = self.master.core.wlanconfig_management.configurations[
canvas_node.core_id canvas_node.core_node.id
] ]
dialog = WlanConfigDialog( dialog = WlanConfigDialog(
self.master, self.master, self.node_to_show_config, wlan_config self.master, self.master, self.node_to_show_config, wlan_config

View file

@ -445,12 +445,16 @@ class CoreClient:
""" """
node_id = self.get_id() node_id = self.get_id()
position = core_pb2.Position(x=x, y=y) position = core_pb2.Position(x=x, y=y)
image = None
if NodeUtils.is_image_node(node_type):
image = "ubuntu:latest"
node = core_pb2.Node( node = core_pb2.Node(
id=node_id, id=node_id,
type=node_type, type=node_type,
name=f"n{node_id}", name=f"n{node_id}",
model=model, model=model,
position=position, position=position,
image=image,
) )
# set default configuration for wireless node # set default configuration for wireless node
@ -535,7 +539,7 @@ class CoreClient:
def create_interface(self, canvas_node): def create_interface(self, canvas_node):
interface = None interface = None
core_node = canvas_node.core_node core_node = canvas_node.core_node
if NodeUtils.is_interface_node(core_node.type): if NodeUtils.is_container_node(core_node.type):
ifid = len(canvas_node.interfaces) ifid = len(canvas_node.interfaces)
name = f"eth{ifid}" name = f"eth{ifid}"
interface = core_pb2.Interface( interface = core_pb2.Interface(

View file

@ -20,6 +20,7 @@ class EmaneConfiguration(Dialog):
super().__init__(master, app, "emane configuration", modal=False) super().__init__(master, app, "emane configuration", modal=False)
self.app = app self.app = app
self.canvas_node = canvas_node self.canvas_node = canvas_node
self.node = canvas_node.core_node
self.radiovar = tk.IntVar() self.radiovar = tk.IntVar()
self.radiovar.set(1) self.radiovar.set(1)
self.columnconfigure(0, weight=1) self.columnconfigure(0, weight=1)
@ -122,20 +123,15 @@ class EmaneConfiguration(Dialog):
# add string emane_ infront for grpc call # add string emane_ infront for grpc call
response = self.app.core.client.set_emane_model_config( response = self.app.core.client.set_emane_model_config(
self.app.core.session_id, self.app.core.session_id, self.node.id, f"emane_{model_name}", config
self.canvas_node.core_id,
"emane_" + model_name,
config,
) )
logging.info( logging.info(
"emaneconfig.py config emane model (%s), result: %s", "emaneconfig.py config emane model (%s), result: %s", self.node.id, response
self.canvas_node.core_id,
response,
) )
# store the change locally # store the change locally
self.app.core.emaneconfig_management.set_custom_emane_cloud_config( self.app.core.emaneconfig_management.set_custom_emane_cloud_config(
self.canvas_node.core_id, "emane_" + model_name self.node.id, f"emane_{model_name}"
) )
self.emane_model_dialog.destroy() self.emane_model_dialog.destroy()
@ -161,7 +157,7 @@ class EmaneConfiguration(Dialog):
session_id = self.app.core.session_id session_id = self.app.core.session_id
# add string emane_ before model name for grpc call # add string emane_ before model name for grpc call
response = self.app.core.client.get_emane_model_config( response = self.app.core.client.get_emane_model_config(
session_id, self.canvas_node.core_id, "emane_" + model_name session_id, self.node.id, f"emane_{model_name}"
) )
logging.info("emane model config %s", response) logging.info("emane model config %s", response)

View file

@ -19,9 +19,10 @@ class MobilityConfigDialog(Dialog):
""" """
super().__init__(master, app, "ns2script configuration", modal=True) super().__init__(master, app, "ns2script configuration", modal=True)
self.canvas_node = canvas_node self.canvas_node = canvas_node
self.node = canvas_node.core_node
logging.info(app.canvas.core.mobilityconfig_management.configurations) logging.info(app.canvas.core.mobilityconfig_management.configurations)
self.node_config = app.canvas.core.mobilityconfig_management.configurations[ self.node_config = app.canvas.core.mobilityconfig_management.configurations[
canvas_node.core_id self.node.id
] ]
self.mobility_script_parameters() self.mobility_script_parameters()
@ -208,7 +209,7 @@ class MobilityConfigDialog(Dialog):
else: else:
loop = "0" loop = "0"
self.app.canvas.core.mobilityconfig_management.set_custom_configuration( self.app.canvas.core.mobilityconfig_management.set_custom_configuration(
node_id=self.canvas_node.core_id, node_id=self.node.id,
file=file, file=file,
refresh_ms=refresh_time, refresh_ms=refresh_time,
loop=loop, loop=loop,

View file

@ -1,14 +1,36 @@
import logging
import tkinter as tk import tkinter as tk
from functools import partial
from tkinter import ttk from tkinter import ttk
from coretk.dialogs.dialog import Dialog from coretk.dialogs.dialog import Dialog
from coretk.dialogs.icondialog import IconDialog from coretk.dialogs.icondialog import IconDialog
from coretk.dialogs.nodeservice import NodeService from coretk.dialogs.nodeservice import NodeService
from coretk.nodeutils import NodeUtils
from coretk.widgets import FrameScroll
DEFAULT_NODES = {"router", "host", "PC", "mdr", "prouter"}
PAD = 5 PAD = 5
def mac_auto(is_auto, entry):
logging.info("mac auto clicked")
if is_auto.get():
logging.info("disabling mac")
entry.var.set("")
entry.config(state=tk.DISABLED)
else:
entry.var.set("00:00:00:00:00:00")
entry.config(state=tk.NORMAL)
class InterfaceData:
def __init__(self, is_auto, mac, ip4, ip6):
self.is_auto = is_auto
self.mac = mac
self.ip4 = ip4
self.ip6 = ip6
class NodeConfigDialog(Dialog): class NodeConfigDialog(Dialog):
def __init__(self, master, app, canvas_node): def __init__(self, master, app, canvas_node):
""" """
@ -18,13 +40,21 @@ class NodeConfigDialog(Dialog):
:param coretk.app.Application: main app :param coretk.app.Application: main app
:param coretk.graph.CanvasNode canvas_node: canvas node object :param coretk.graph.CanvasNode canvas_node: canvas node object
""" """
super().__init__(master, app, f"{canvas_node.name} Configuration", modal=True) super().__init__(
master, app, f"{canvas_node.core_node.name} Configuration", modal=True
)
self.canvas_node = canvas_node self.canvas_node = canvas_node
self.node = canvas_node.core_node
self.image = canvas_node.image self.image = canvas_node.image
self.image_button = None self.image_button = None
self.name = tk.StringVar(value=canvas_node.name) self.name = tk.StringVar(value=self.node.name)
self.type = tk.StringVar(value=canvas_node.core_node.model) self.type = tk.StringVar(value=self.node.model)
self.server = tk.StringVar() self.container_image = tk.StringVar(value=self.node.image)
server = "localhost"
if self.node.server:
server = self.node.server
self.server = tk.StringVar(value=server)
self.interfaces = {}
self.draw() self.draw()
def draw(self): def draw(self):
@ -57,33 +87,88 @@ class NodeConfigDialog(Dialog):
row += 1 row += 1
# node type field # node type field
label = ttk.Label(frame, text="Type") if NodeUtils.is_model_node(self.node.type):
label.grid(row=row, column=0, sticky="ew", padx=PAD, pady=PAD) label = ttk.Label(frame, text="Type")
combobox = ttk.Combobox( label.grid(row=row, column=0, sticky="ew", padx=PAD, pady=PAD)
frame, textvariable=self.type, values=list(DEFAULT_NODES), state="readonly" combobox = ttk.Combobox(
) frame,
combobox.grid(row=row, column=1, sticky="ew") textvariable=self.type,
row += 1 values=list(NodeUtils.NODE_MODELS),
state="readonly",
)
combobox.grid(row=row, column=1, sticky="ew")
row += 1
# container image field
if NodeUtils.is_image_node(self.node.type):
label = ttk.Label(frame, text="Image")
label.grid(row=row, column=0, sticky="ew", padx=PAD, pady=PAD)
entry = ttk.Entry(frame, textvariable=self.container_image)
entry.grid(row=row, column=1, sticky="ew")
row += 1
# server # server
frame.grid(sticky="ew") if NodeUtils.is_container_node(self.node.type):
frame.columnconfigure(1, weight=1) frame.grid(sticky="ew")
label = ttk.Label(frame, text="Server") frame.columnconfigure(1, weight=1)
label.grid(row=row, column=0, sticky="ew", padx=PAD, pady=PAD) label = ttk.Label(frame, text="Server")
servers = ["localhost"] label.grid(row=row, column=0, sticky="ew", padx=PAD, pady=PAD)
servers.extend(list(sorted(self.app.core.servers.keys()))) servers = ["localhost"]
combobox = ttk.Combobox( servers.extend(list(sorted(self.app.core.servers.keys())))
frame, textvariable=self.server, values=servers, state="readonly" combobox = ttk.Combobox(
) frame, textvariable=self.server, values=servers, state="readonly"
combobox.grid(row=row, column=1, sticky="ew") )
row += 1 combobox.grid(row=row, column=1, sticky="ew")
row += 1
# services # services
button = ttk.Button(self.top, text="Services", command=self.click_services) button = ttk.Button(self.top, text="Services", command=self.click_services)
button.grid(sticky="ew", pady=PAD) button.grid(sticky="ew", pady=PAD)
# interfaces
if self.canvas_node.interfaces:
self.draw_interfaces()
self.draw_buttons() self.draw_buttons()
def draw_interfaces(self):
scroll = FrameScroll(self.top, self.app, text="Interfaces")
scroll.grid(sticky="nsew")
scroll.frame.columnconfigure(0, weight=1)
scroll.frame.rowconfigure(0, weight=1)
for interface in self.canvas_node.interfaces:
logging.info("interface: %s", interface)
frame = ttk.LabelFrame(scroll.frame, text=interface.name, padding=PAD)
frame.grid(sticky="ew", pady=PAD)
frame.columnconfigure(1, weight=1)
frame.columnconfigure(2, weight=1)
label = ttk.Label(frame, text="MAC")
label.grid(row=0, column=0, padx=PAD, pady=PAD)
is_auto = tk.BooleanVar(value=True)
checkbutton = ttk.Checkbutton(frame, text="Auto?", variable=is_auto)
checkbutton.var = is_auto
checkbutton.grid(row=0, column=1, padx=PAD)
mac = tk.StringVar(value=interface.mac)
entry = ttk.Entry(frame, textvariable=mac, state=tk.DISABLED)
entry.grid(row=0, column=2, sticky="ew")
func = partial(mac_auto, is_auto, entry)
checkbutton.config(command=func)
label = ttk.Label(frame, text="IPv4")
label.grid(row=1, column=0, padx=PAD, pady=PAD)
ip4 = tk.StringVar(value=f"{interface.ip4}/{interface.ip4mask}")
entry = ttk.Entry(frame, textvariable=ip4)
entry.grid(row=1, column=1, columnspan=2, sticky="ew")
label = ttk.Label(frame, text="IPv6")
label.grid(row=2, column=0, padx=PAD, pady=PAD)
ip6 = tk.StringVar(value=f"{interface.ip6}/{interface.ip6mask}")
entry = ttk.Entry(frame, textvariable=ip6)
entry.grid(row=2, column=1, columnspan=2, sticky="ew")
self.interfaces[interface.id] = InterfaceData(is_auto, mac, ip4, ip6)
def draw_buttons(self): def draw_buttons(self):
frame = ttk.Frame(self.top) frame = ttk.Frame(self.top)
frame.grid(sticky="ew") frame.grid(sticky="ew")
@ -101,16 +186,24 @@ class NodeConfigDialog(Dialog):
dialog.show() dialog.show()
def click_icon(self): def click_icon(self):
dialog = IconDialog( dialog = IconDialog(self, self.app, self.node.name, self.canvas_node.image)
self, self.app, self.canvas_node.name, self.canvas_node.image
)
dialog.show() dialog.show()
if dialog.image: if dialog.image:
self.image = dialog.image self.image = dialog.image
self.image_button.config(image=self.image) self.image_button.config(image=self.image)
def config_apply(self): def config_apply(self):
self.canvas_node.name = self.name.get() # update core node
self.node.name = self.name.get()
if NodeUtils.is_image_node(self.node.type):
self.node.image = self.container_image.get()
if NodeUtils.is_container_node(self.node.type):
self.node.server = self.server.get()
# update canvas node
self.canvas_node.image = self.image self.canvas_node.image = self.image
self.canvas_node.canvas.itemconfig(self.canvas_node.id, image=self.image)
# redraw
self.canvas_node.redraw()
self.destroy() self.destroy()

View file

@ -13,13 +13,14 @@ from coretk.dialogs.mobilityconfig import MobilityConfigDialog
class WlanConfigDialog(Dialog): class WlanConfigDialog(Dialog):
def __init__(self, master, app, canvas_node, config): def __init__(self, master, app, canvas_node, config):
super().__init__( super().__init__(
master, app, f"{canvas_node.name} Wlan Configuration", modal=True master, app, f"{canvas_node.core_node.name} Wlan Configuration", modal=True
) )
self.image = canvas_node.image self.image = canvas_node.image
self.canvas_node = canvas_node self.canvas_node = canvas_node
self.node = canvas_node.core_node
self.config = config self.config = config
self.name = tk.StringVar(value=canvas_node.name) self.name = tk.StringVar(value=self.node.name)
self.range_var = tk.StringVar(value=config["range"]) self.range_var = tk.StringVar(value=config["range"])
self.bandwidth_var = tk.StringVar(value=config["bandwidth"]) self.bandwidth_var = tk.StringVar(value=config["bandwidth"])
self.delay_var = tk.StringVar(value=config["delay"]) self.delay_var = tk.StringVar(value=config["delay"])
@ -169,9 +170,7 @@ class WlanConfigDialog(Dialog):
dialog.show() dialog.show()
def click_icon(self): def click_icon(self):
dialog = IconDialog( dialog = IconDialog(self, self.app, self.node.name, self.canvas_node.image)
self, self.app, self.canvas_node.name, self.canvas_node.image
)
dialog.show() dialog.show()
if dialog.image: if dialog.image:
self.image = dialog.image self.image = dialog.image
@ -192,7 +191,7 @@ class WlanConfigDialog(Dialog):
# set wireless node configuration here # set wireless node configuration here
wlanconfig_manager = self.app.core.wlanconfig_management wlanconfig_manager = self.app.core.wlanconfig_management
wlanconfig_manager.set_custom_config( wlanconfig_manager.set_custom_config(
node_id=self.canvas_node.core_id, node_id=self.node.id,
range=basic_range, range=basic_range,
bandwidth=bandwidth, bandwidth=bandwidth,
jitter=jitter, jitter=jitter,

View file

@ -155,21 +155,22 @@ class CanvasGraph(tk.Canvas):
# draw nodes on the canvas # draw nodes on the canvas
image = NodeUtils.node_icon(core_node.type, core_node.model) image = NodeUtils.node_icon(core_node.type, core_node.model)
position = core_node.position node = CanvasNode(self.master, core_node, image)
node = CanvasNode(position.x, position.y, image, self.master, core_node)
self.nodes[node.id] = node self.nodes[node.id] = node
self.core.canvas_nodes[core_node.id] = node self.core.canvas_nodes[core_node.id] = node
# draw existing links # draw existing links
for link in session.links: for link in session.links:
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
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
is_wired = link.type == core_pb2.LinkType.WIRED is_wired = link.type == core_pb2.LinkType.WIRED
edge = CanvasEdge( edge = CanvasEdge(
canvas_node_one.x_coord, node_one.position.x,
canvas_node_one.y_coord, node_one.position.y,
canvas_node_two.x_coord, node_two.position.x,
canvas_node_two.y_coord, node_two.position.y,
canvas_node_one.id, canvas_node_one.id,
self, self,
is_wired=is_wired, is_wired=is_wired,
@ -402,7 +403,7 @@ class CanvasGraph(tk.Canvas):
core_node = self.core.create_node( core_node = self.core.create_node(
int(x), int(y), self.node_draw.node_type, self.node_draw.model int(x), int(y), self.node_draw.node_type, self.node_draw.model
) )
node = CanvasNode(x, y, self.node_draw.image, self.master, core_node) node = CanvasNode(self.master, core_node, self.node_draw.image)
self.core.canvas_nodes[core_node.id] = node self.core.canvas_nodes[core_node.id] = node
self.nodes[node.id] = node self.nodes[node.id] = node
return node return node
@ -590,19 +591,18 @@ class CanvasEdge:
class CanvasNode: class CanvasNode:
def __init__(self, x, y, image, app, core_node): def __init__(self, app, core_node, image):
self.image = image
self.app = app self.app = app
self.canvas = app.canvas self.canvas = app.canvas
self.image = image
self.core_node = core_node
x = self.core_node.position.x
y = self.core_node.position.y
self.id = self.canvas.create_image( self.id = self.canvas.create_image(
x, y, anchor=tk.CENTER, image=self.image, tags="node" x, y, anchor=tk.CENTER, image=self.image, tags="node"
) )
self.core_node = core_node
self.name = core_node.name
self.x_coord = x
self.y_coord = y
self.text_id = self.canvas.create_text( self.text_id = self.canvas.create_text(
x, y + 20, text=self.name, tags="nodename" x, y + 20, text=self.core_node.name, tags="nodename"
) )
self.antenna_draw = WlanAntennaManager(self.canvas, self.id) self.antenna_draw = WlanAntennaManager(self.canvas, self.id)
self.tooltip = CanvasTooltip(self.canvas) self.tooltip = CanvasTooltip(self.canvas)
@ -620,6 +620,10 @@ class CanvasNode:
self.wlans = [] self.wlans = []
self.moving = None self.moving = None
def redraw(self):
self.canvas.itemconfig(self.id, image=self.image)
self.canvas.itemconfig(self.text_id, text=self.core_node.name)
def on_enter(self, event): def on_enter(self, event):
if self.app.core.is_runtime() and self.app.core.observer: if self.app.core.is_runtime() and self.app.core.observer:
self.tooltip.text.set("waiting...") self.tooltip.text.set("waiting...")
@ -640,18 +644,18 @@ class CanvasNode:
self.canvas.canvas_action.display_configuration(self) self.canvas.canvas_action.display_configuration(self)
def update_coords(self): def update_coords(self):
self.x_coord, self.y_coord = self.canvas.coords(self.id) x, y = self.canvas.coords(self.id)
self.core_node.position.x = int(self.x_coord) self.core_node.position.x = int(x)
self.core_node.position.y = int(self.y_coord) self.core_node.position.y = int(y)
def click_press(self, event): def click_press(self, event):
logging.debug(f"node click press {self.name}: {event}") logging.debug(f"node click press {self.core_node.name}: {event}")
self.moving = self.canvas.canvas_xy(event) self.moving = self.canvas.canvas_xy(event)
self.canvas.canvas_management.node_select(self) self.canvas.canvas_management.node_select(self)
def click_release(self, event): def click_release(self, event):
logging.debug(f"node click release {self.name}: {event}") logging.debug(f"node click release {self.core_node.name}: {event}")
self.update_coords() self.update_coords()
self.moving = None self.moving = None
@ -681,7 +685,6 @@ class CanvasNode:
else: else:
self.canvas.coords(edge.id, x1, y1, new_x, new_y) self.canvas.coords(edge.id, x1, y1, new_x, new_y)
edge.link_info.recalculate_info() edge.link_info.recalculate_info()
# self.canvas.core_grpc.throughput_draw.update_throughtput_location(edge)
self.canvas.helper.update_wlan_connection( self.canvas.helper.update_wlan_connection(
old_x, old_y, new_x, new_y, self.wlans old_x, old_y, new_x, new_y, self.wlans
@ -691,4 +694,4 @@ class CanvasNode:
self.canvas.canvas_management.node_select(self, True) self.canvas.canvas_management.node_select(self, True)
def context(self, event): def context(self, event):
logging.debug(f"context click {self.name}: {event}") logging.debug(f"context click {self.core_node.name}: {event}")

View file

@ -44,11 +44,21 @@ class NodeUtils:
NODES = [] NODES = []
NETWORK_NODES = [] NETWORK_NODES = []
NODE_ICONS = {} NODE_ICONS = {}
INTERFACE_NODE = {NodeType.DEFAULT, NodeType.DOCKER, NodeType.LXC} CONTAINER_NODES = {NodeType.DEFAULT, NodeType.DOCKER, NodeType.LXC}
IMAGE_NODES = {NodeType.DOCKER, NodeType.LXC}
NODE_MODELS = {"router", "host", "PC", "mdr", "prouter"}
@classmethod @classmethod
def is_interface_node(cls, node_type): def is_container_node(cls, node_type):
return node_type in cls.INTERFACE_NODE return node_type in cls.CONTAINER_NODES
@classmethod
def is_model_node(cls, node_type):
return node_type == NodeType.DEFAULT
@classmethod
def is_image_node(cls, node_type):
return node_type in cls.IMAGE_NODES
@classmethod @classmethod
def node_icon(cls, node_type, model): def node_icon(cls, node_type, model):

View file

@ -85,7 +85,8 @@ def load(style):
"fieldbackground": Colors.white, "fieldbackground": Colors.white,
"foreground": Colors.black, "foreground": Colors.black,
"padding": (2, 0), "padding": (2, 0),
} },
"map": {"fieldbackground": [("disabled", Colors.frame)]},
}, },
"TCombobox": { "TCombobox": {
"configure": { "configure": {