Merge branch 'coretk-create-node' into coretk

This commit is contained in:
Huy Pham 2019-10-29 09:08:58 -07:00
commit f662846f9e
88 changed files with 3504 additions and 303 deletions

View file

@ -14,3 +14,4 @@ pre-commit = "*"
[packages]
coretk = {editable = true,path = "."}
core = {editable = true,path = "./../daemon"}

111
coretk/Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "52de2a0b7a80abe39564a3943879fe75305b0cb8243c0fce78ef43689bee77c0"
"sha256": "2cb6b23bfb8b9bebb5ece1016de468cc57fe46cf4df08a61b1861aee4bed1028"
},
"pipfile-spec": 6,
"requires": {},
@ -14,10 +14,91 @@
]
},
"default": {
"configparser": {
"hashes": [
"sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c",
"sha256:c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df"
],
"version": "==4.0.2"
},
"core": {
"editable": true,
"path": "./../daemon"
},
"coretk": {
"editable": true,
"path": "."
},
"future": {
"hashes": [
"sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"
],
"version": "==0.17.1"
},
"grpcio": {
"hashes": [
"sha256:0337debec20fe385bcd49048d6917270efbc17a5119857466559b4db91f8995b",
"sha256:164f82a99e08797ea786283b66b45ebe76772d321577d1674ba6fe0200155892",
"sha256:172dfba8d9621048c2cbc1d1cf7a02244e9a9a8cff5bb79bb30bcb0c13c7fd31",
"sha256:18f4b536d8a9cfa15b3214e0bb628071def94160699e91798f0a954c3b2db88d",
"sha256:2283b56bda49b068b0f08d006fffc7dd46eae72322f1a5dec87fc9c218f1dc2d",
"sha256:26b33f488a955bf49262d2ce3423d3a8174108506d8f819e8150aca21bdd3b99",
"sha256:31cc9b6f70bdd0d9ff53df2d563ea1fb278601d5c625932d8a82d03b08ff3de0",
"sha256:37dd8684fbc2bc00766ae6784bcbd7f874bc96527636a341411db811d04ff650",
"sha256:424c01189ef51a808669f020368b01204e0f1fa0bf2adab7c8d0d13166f92e9e",
"sha256:431c099f20a1f1d97def98f87bb74fa752e8819c2bab23d79089353aed1acc9b",
"sha256:4c2f1d0b27bcef301e5d5c1da05ffd7d174f807f61889c006b8e708b16bc978e",
"sha256:59b8d738867b59c5daaff5df242b5f3f9c58b47862f603c6ee530964b897b69b",
"sha256:8d4f1ee2a67cf8f792d4fc9b8c7bb2148174e838d935f175653aec234752828b",
"sha256:97ab9e35b47bda0441332204960f95c1169c55ec8e989381bedd32bdb9f78b05",
"sha256:9cf93e185507bfdaa7ed45a90049bd3f1ed3f6357ad3772b31e993ff723cf67d",
"sha256:a5a81472c0ca6181492b9291c316ff60c6c94dd3f21c1e8c481f21923d899af0",
"sha256:aaa1feb0fdd094af6db0a16cbd446ed94285a50e320aede5971152d9ea022df8",
"sha256:b36bf4408f8400ee9ab13ff129e71f2e4c72ce2d8886b744aeab77ce50a55cf6",
"sha256:bb345f7e98b38a2c1ef33ff1145687234f78dfeedf308b41b3e41f4b42eba099",
"sha256:c13ae15695e0eb4ba2db920d6a197171d2398c675afa4f27460b6381d20a6884",
"sha256:c4a233a00cc5b64543e97733902151bc6738396931b3c166aad03a3aaadbd479",
"sha256:c521c5f8a95baabba69c68dd0f5e34f37c8adf1a9691f9884dba3eab4ebadc29",
"sha256:c772edd094fe3e54d6d54fdecb90c51cb8d07b55e9c1cda2d33e9615e33d07e8",
"sha256:cebfba6542855403b29e4bc95bbcd5ab444f21137b440f2fb7c7925ca0e55bfd",
"sha256:d7490e013c4bad3e8db804fc6483b47125dc8df0ebcfd6e419bd25df35025301",
"sha256:dfb6063619f297cbd22c67530d7465d98348b35d0424bbc1756b36c5ef9f99d4",
"sha256:e80a15b48a66f35c7c33db2a7df4034a533b362269d0e60e0036e23f14bac7b5",
"sha256:ea444fa1c1ec4f8d2ce965bb01e06148ef9ceb398fb2f627511d50f137eac35b",
"sha256:ec986cbf8837a49f9612cc1cfc2a8ccb54875cfce5355a121279de35124ea1db",
"sha256:fb641df6de8c4a55c784c24d334d53096954d9b30679d3ce5eb6a4d25c1020a3",
"sha256:fb88bd791c8efbcb36de12f0aa519ceec0b7806d3decff16e412e097d4725d44",
"sha256:ffa1be3d566a9cbd21a5f2d95fd9262ec6c337c499291bfeb51547b8de18942e"
],
"version": "==1.24.0"
},
"lxml": {
"hashes": [
"sha256:02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4",
"sha256:096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc",
"sha256:0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1",
"sha256:17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046",
"sha256:17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36",
"sha256:1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5",
"sha256:2e8f77db25b0a96af679e64ff9bf9dddb27d379c9900c3272f3041c4d1327c9d",
"sha256:4dffd405390a45ecb95ab5ab1c1b847553c18b0ef8ed01e10c1c8b1a76452916",
"sha256:6b899931a5648862c7b88c795eddff7588fb585e81cecce20f8d9da16eff96e0",
"sha256:726c17f3e0d7a7200718c9a890ccfeab391c9133e363a577a44717c85c71db27",
"sha256:760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc",
"sha256:796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7",
"sha256:891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38",
"sha256:a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5",
"sha256:a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832",
"sha256:b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a",
"sha256:be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f",
"sha256:c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9",
"sha256:c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692",
"sha256:d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84",
"sha256:e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79",
"sha256:fe489d486cd00b739be826e8c1be188ddb74c7a1ca784d93d06fda882a6a1681"
],
"version": "==4.4.1"
},
"pillow": {
"hashes": [
"sha256:0804f77cb1e9b6dbd37601cee11283bba39a8d44b9ddb053400c58e0c0d7d9de",
@ -48,6 +129,34 @@
"sha256:f8dc19d92896558f9c4317ee365729ead9d7bbcf2052a9a19a3ef17abbb8ac5b"
],
"version": "==6.1.0"
},
"protobuf": {
"hashes": [
"sha256:26c0d756c7ad6823fccbc3b5f84c619b9cc7ac281496fe0a9d78e32023c45034",
"sha256:3200046e4d4f6c42ed66257dbe15e2e5dc76072c280e9b3d69dc8f3a4fa3fbbc",
"sha256:368f1bae6dd22d04fd2254d30cd301863408a96ff604422e3ddd8ab601f095a4",
"sha256:3902fa1920b4ef9f710797496b309efc5ccd0faeba44dc82ed6a711a244764a0",
"sha256:3a7a8925ba6481b9241cdb5d69cd0b0700f23efed6bb691dc9543faa4aa25d6f",
"sha256:4bc33d49f43c6e9916fb56b7377cb4478cbf25824b4d2bedfb8a4e3df31c12ca",
"sha256:568b434a36e31ed30d60d600b2227666ce150b8b5275948f50411481a4575d6d",
"sha256:5c393cd665d03ce6b29561edd6b0cc4bcb3fb8e2a7843e8f223d693f07f61b40",
"sha256:80072e9ba36c73cf89c01f669c7b123733fc2de1780b428082a850f53cc7865f",
"sha256:843f498e98ad1469ad54ecb4a7ccf48605a1c5d2bd26ae799c7a2cddab4a37ec",
"sha256:aa45443035651cbfae74c8deb53358ba660d8e7a5fbab3fc4beb33fb3e3ca4be",
"sha256:aaab817d9d038dd5f56a6fb2b2e8ae68caf1fd28cc6a963c755fa73268495c13",
"sha256:e6f68b9979dc8f75299293d682f67fecb72d78f98652da2eeb85c85edef1ca94",
"sha256:e7366cabddff3441d583fdc0176ab42eba4ee7090ef857d50c4dd59ad124003a",
"sha256:f0144ad97cd28bfdda0567b9278d25061ada5ad2b545b538cd3577697b32bda3",
"sha256:f655338491481f482042f19016647e50365ab41b75b486e0df56e0dcc425abf4"
],
"version": "==3.9.2"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
}
},
"develop": {

View file

@ -1,50 +1,72 @@
import logging
import tkinter as tk
import coretk.appcache as appcache
import coretk.images as images
from coretk.coregrpc import CoreGrpc
from coretk.coremenubar import CoreMenubar
from coretk.coretoolbar import CoreToolbar
from coretk.graph import CanvasGraph
from coretk.images import Images
from coretk.images import ImageEnum, Images
from coretk.menuaction import MenuAction
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
appcache.cache_variable(self)
print(self.is_open_xml)
self.load_images()
self.setup_app()
self.menubar = None
self.core_menu = None
self.canvas = None
self.core_editbar = None
self.core_grpc = None
self.create_menu()
self.create_widgets()
self.draw_canvas()
self.start_grpc()
# self.try_make_table()
def load_images(self):
Images.load("core", "core-icon.png")
"""
Load core images
:return:
"""
images.load_core_images(Images)
def setup_app(self):
self.master.title("CORE")
self.master.geometry("800x600")
image = Images.get("core")
self.master.geometry("1000x800")
image = Images.get(ImageEnum.CORE.value)
self.master.tk.call("wm", "iconphoto", self.master._w, image)
self.pack(fill=tk.BOTH, expand=True)
def create_menu(self):
self.master.option_add("*tearOff", tk.FALSE)
self.menubar = tk.Menu(self.master)
core_menu = CoreMenubar(self, self.master, self.menubar)
core_menu.create_core_menubar()
self.core_menu = CoreMenubar(self, self.master, self.menubar)
self.core_menu.create_core_menubar()
self.master.config(menu=self.menubar)
def create_widgets(self):
edit_frame = tk.Frame(self)
edit_frame.pack(side=tk.LEFT, fill=tk.Y, ipadx=2, ipady=2)
core_editbar = CoreToolbar(self.master, edit_frame, self.menubar)
core_editbar.create_toolbar()
self.core_editbar = CoreToolbar(self, edit_frame, self.menubar)
self.core_editbar.create_toolbar()
def draw_canvas(self):
self.canvas = CanvasGraph(
self, background="#cccccc", scrollregion=(0, 0, 1000, 1000)
master=self,
grpc=self.core_grpc,
background="#cccccc",
scrollregion=(0, 0, 1200, 1000),
)
self.canvas.pack(fill=tk.BOTH, expand=True)
# self.canvas.create_line(0, 0, 10, 10)
self.core_editbar.canvas = self.canvas
scroll_x = tk.Scrollbar(
self.canvas, orient=tk.HORIZONTAL, command=self.canvas.xview
@ -64,8 +86,35 @@ class Application(tk.Frame):
b = tk.Button(status_bar, text="Button 3")
b.pack(side=tk.LEFT, padx=1)
def start_grpc(self):
"""
Conect client to grpc, query sessions and prompt use to choose an existing session if there exist any
:return: nothing
"""
self.master.update()
self.core_grpc = CoreGrpc(self)
self.core_grpc.set_up()
self.canvas.core_grpc = self.core_grpc
self.canvas.grpc_manager.core_grpc = self.core_grpc
self.canvas.grpc_manager.update_preexisting_ids()
self.canvas.draw_existing_component()
def on_closing(self):
menu_action = MenuAction(self, self.master)
menu_action.on_quit()
# self.quit()
def try_make_table(self):
f = tk.Frame(self.master)
for i in range(3):
e = tk.Entry(f)
e.grid(row=0, column=1, stick="nsew")
f.pack(side=tk.TOP)
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
app = Application()
app.master.protocol("WM_DELETE_WINDOW", app.on_closing)
app.mainloop()

33
coretk/coretk/appcache.py Normal file
View file

@ -0,0 +1,33 @@
"""
stores some information helpful for setting starting values for some tables
like size and scale, set wallpaper, etc
"""
import tkinter as tk
def cache_variable(application):
# for menubar
application.is_open_xml = False
application.size_and_scale = None
application.set_wallpaper = None
# set wallpaper variables
# canvas id of the wallpaper
application.wallpaper_id = None
# current image for wallpaper
application.current_wallpaper = None
# wallpaper option
application.radiovar = tk.IntVar()
application.radiovar.set(1)
# show grid option
application.show_grid_var = tk.IntVar()
application.show_grid_var.set(1)
# adjust canvas to image dimension variable
application.adjust_to_dim_var = tk.IntVar()
application.adjust_to_dim_var.set(0)

291
coretk/coretk/coregrpc.py Normal file
View file

@ -0,0 +1,291 @@
"""
Incorporate grpc into python tkinter GUI
"""
import logging
import os
from collections import OrderedDict
from core.api.grpc import client, core_pb2
from coretk.linkinfo import Throughput
from coretk.querysessiondrawing import SessionTable
from coretk.wirelessconnection import WirelessConnection
class CoreGrpc:
def __init__(self, app, sid=None):
"""
Create a CoreGrpc instance
"""
self.core = client.CoreGrpcClient()
self.session_id = sid
self.node_ids = []
self.master = app.master
# self.set_up()
self.interface_helper = None
self.throughput_draw = Throughput(app.canvas, self)
self.wireless_draw = WirelessConnection(app.canvas, self)
def log_event(self, event):
logging.info("event: %s", event)
if event.link_event is not None:
self.wireless_draw.hangle_link_event(event.link_event)
def log_throughput(self, event):
interface_throughputs = event.interface_throughputs
for i in interface_throughputs:
print("")
return
throughputs_belong_to_session = []
for if_tp in interface_throughputs:
if if_tp.node_id in self.node_ids:
throughputs_belong_to_session.append(if_tp)
# bridge_throughputs = event.bridge_throughputs
self.throughput_draw.process_grpc_throughput_event(
throughputs_belong_to_session
)
def create_new_session(self):
"""
Create a new session
:return: nothing
"""
response = self.core.create_session()
logging.info("created session: %s", response)
# handle events session may broadcast
self.session_id = response.session_id
self.master.title("CORE Session ID " + str(self.session_id))
self.core.events(self.session_id, self.log_event)
# self.core.throughputs(self.log_throughput)
def query_existing_sessions(self, sessions):
"""
Query for existing sessions and prompt to join one
:param repeated core_pb2.SessionSummary sessions: summaries of all the existing sessions
:return: nothing
"""
SessionTable(self, self.master)
def delete_session(self, custom_sid=None):
if custom_sid is None:
sid = self.session_id
else:
sid = custom_sid
response = self.core.delete_session(sid)
logging.info("Deleted session result: %s", response)
def terminate_session(self, custom_sid=None):
if custom_sid is None:
sid = self.session_id
else:
sid = custom_sid
s = self.core.get_session(sid).session
# delete links and nodes from running session
if s.state == core_pb2.SessionState.RUNTIME:
self.set_session_state("datacollect", sid)
self.delete_links(sid)
self.delete_nodes(sid)
self.delete_session(sid)
def set_up(self):
"""
Query sessions, if there exist any, prompt whether to join one
:return: existing sessions
"""
self.core.connect()
response = self.core.get_sessions()
# logging.info("coregrpc.py: all sessions: %s", response)
# if there are no sessions, create a new session, else join a session
sessions = response.sessions
if len(sessions) == 0:
self.create_new_session()
else:
self.query_existing_sessions(sessions)
def get_session_state(self):
response = self.core.get_session(self.session_id)
# logging.info("get session: %s", response)
return response.session.state
def set_session_state(self, state, custom_session_id=None):
"""
Set session state
:param str state: session state to set
:return: nothing
"""
if custom_session_id is None:
sid = self.session_id
else:
sid = custom_session_id
if state == "configuration":
response = self.core.set_session_state(
sid, core_pb2.SessionState.CONFIGURATION
)
elif state == "instantiation":
response = self.core.set_session_state(
sid, core_pb2.SessionState.INSTANTIATION
)
elif state == "datacollect":
response = self.core.set_session_state(
sid, core_pb2.SessionState.DATACOLLECT
)
elif state == "shutdown":
response = self.core.set_session_state(sid, core_pb2.SessionState.SHUTDOWN)
elif state == "runtime":
response = self.core.set_session_state(sid, core_pb2.SessionState.RUNTIME)
elif state == "definition":
response = self.core.set_session_state(
sid, core_pb2.SessionState.DEFINITION
)
elif state == "none":
response = self.core.set_session_state(sid, core_pb2.SessionState.NONE)
else:
logging.error("coregrpc.py: set_session_state: INVALID STATE")
logging.info("set session state: %s", response)
def add_node(self, node_type, model, x, y, name, node_id):
position = core_pb2.Position(x=x, y=y)
node = core_pb2.Node(id=node_id, type=node_type, position=position, model=model)
self.node_ids.append(node_id)
response = self.core.add_node(self.session_id, node)
logging.info("created node: %s", response)
if node_type == core_pb2.NodeType.WIRELESS_LAN:
d = OrderedDict()
d["basic_range"] = "275"
d["bandwidth"] = "54000000"
d["jitter"] = "0"
d["delay"] = "20000"
d["error"] = "0"
r = self.core.set_wlan_config(self.session_id, node_id, d)
logging.debug("set wlan config %s", r)
return response.node_id
def edit_node(self, node_id, x, y):
position = core_pb2.Position(x=x, y=y)
response = self.core.edit_node(self.session_id, node_id, position)
logging.info("updated node id %s: %s", node_id, response)
# self.core.events(self.session_id, self.log_event)
def delete_nodes(self, delete_session=None):
if delete_session is None:
sid = self.session_id
else:
sid = delete_session
for node in self.core.get_session(sid).session.nodes:
response = self.core.delete_node(self.session_id, node.id)
logging.info("delete nodes %s", response)
def delete_links(self, delete_session=None):
# sid = None
if delete_session is None:
sid = self.session_id
else:
sid = delete_session
for link in self.core.get_session(sid).session.links:
response = self.core.delete_link(
self.session_id,
link.node_one_id,
link.node_two_id,
link.interface_one.id,
link.interface_two.id,
)
logging.info("delete links %s", response)
def add_link(self, id1, id2, type1, type2, edge):
"""
Grpc client request add link
:param int session_id: session id
:param int id1: node 1 core id
:param core_pb2.NodeType type1: node 1 core node type
:param int id2: node 2 core id
:param core_pb2.NodeType type2: node 2 core node type
:return: nothing
"""
if1 = None
if2 = None
if type1 == core_pb2.NodeType.DEFAULT:
interface = edge.interface_1
if1 = core_pb2.Interface(
id=interface.id,
name=interface.name,
mac=interface.mac,
ip4=interface.ipv4,
ip4mask=interface.ip4prefix,
)
logging.debug("create interface 1 %s", if1)
# interface1 = self.interface_helper.create_interface(id1, 0)
if type2 == core_pb2.NodeType.DEFAULT:
interface = edge.interface_2
if2 = core_pb2.Interface(
id=interface.id,
name=interface.name,
mac=interface.mac,
ip4=interface.ipv4,
ip4mask=interface.ip4prefix,
)
logging.debug("create interface 2: %s", if2)
response = self.core.add_link(self.session_id, id1, id2, if1, if2)
logging.info("created link: %s", response)
# self.core.get_node_links(self.session_id, id1)
# def get_session(self):
# response = self.core.get_session(self.session_id)
# nodes = response.session.nodes
# for node in nodes:
# r = self.core.get_node_links(self.session_id, node.id)
# logging.info(r)
def launch_terminal(self, node_id):
response = self.core.get_node_terminal(self.session_id, node_id)
logging.info("get terminal %s", response.terminal)
os.system("xterm -e %s &" % response.terminal)
def save_xml(self, file_path):
"""
Save core session as to an xml file
:param str file_path: file path that user pick
:return: nothing
"""
response = self.core.save_xml(self.session_id, file_path)
logging.info("coregrpc.py save xml %s", response)
self.core.events(self.session_id, self.log_event)
def open_xml(self, file_path):
"""
Open core xml
:param str file_path: file to open
:return: session id
"""
response = self.core.open_xml(file_path)
self.session_id = response.session_id
logging.debug("coreprgc.py open_xml(): %s", response.result)
def close(self):
"""
Clean ups when done using grpc
:return: nothing
"""
logging.debug("Close grpc")
self.core.close()

View file

@ -1,6 +1,7 @@
import tkinter as tk
import coretk.menuaction as action
from coretk.menuaction import MenuAction
class CoreMenubar(object):
@ -19,6 +20,33 @@ class CoreMenubar(object):
self.menubar = menubar
self.master = master
self.application = application
self.menuaction = action.MenuAction(application, master)
self.menu_action = MenuAction(self.application, self.master)
# def on_quit(self):
# """
# Prompt use to stop running session before application is closed
#
# :return: nothing
# """
# state = self.application.core_grpc.get_session_state()
#
# if state == core_pb2.SessionState.SHUTDOWN or state == core_pb2.SessionState.DEFINITION:
# self.application.core_grpc.delete_session()
# self.application.core_grpc.core.close()
# # self.application.quit()
# else:
# msgbox = tk.messagebox.askyesnocancel("stop", "Stop the running session?")
#
# if msgbox or msgbox == False:
# if msgbox:
# self.application.core_grpc.set_session_state("datacollect")
# self.application.core_grpc.delete_links()
# self.application.core_grpc.delete_nodes()
# self.application.core_grpc.delete_session()
#
# self.application.core_grpc.core.close()
# # self.application.quit()
def create_file_menu(self):
"""
@ -27,18 +55,22 @@ class CoreMenubar(object):
:return: nothing
"""
file_menu = tk.Menu(self.menubar)
# menu_action = MenuAction(self.application, self.master)
file_menu.add_command(
label="New", command=action.file_new, accelerator="Ctrl+N", underline=0
)
file_menu.add_command(
label="Open...", command=action.file_open, accelerator="Ctrl+O", underline=0
label="Open...",
command=self.menu_action.file_open_xml,
accelerator="Ctrl+O",
underline=0,
)
file_menu.add_command(label="Reload", command=action.file_reload, underline=0)
file_menu.add_command(
label="Save", command=action.file_save, accelerator="Ctrl+S", underline=0
)
file_menu.add_command(label="Save As XML...", command=action.file_save_as_xml)
file_menu.add_command(label="Save As imn...", command=action.file_save_as_imn)
# file_menu.add_command(
# label="Save", command=action.file_save, accelerator="Ctrl+S", underline=0
# )
# file_menu.add_command(label="Save As XML...", command=action.file_save_as_xml)
file_menu.add_command(label="Save", command=self.menu_action.file_save_as_xml)
file_menu.add_separator()
@ -68,13 +100,8 @@ class CoreMenubar(object):
file_menu.add_separator()
file_menu.add_command(
label="/home/ncs/.core/configs/sample1.imn",
command=action.file_example_link,
label="Quit", command=self.menuaction.on_quit, underline=0
)
file_menu.add_separator()
file_menu.add_command(label="Quit", command=self.master.quit, underline=0)
self.menubar.add_cascade(label="File", menu=file_menu, underline=0)
def create_edit_menu(self):
@ -137,8 +164,12 @@ class CoreMenubar(object):
canvas_menu.add_separator()
canvas_menu.add_command(label="Size/scale...", command=action.canvas_size_scale)
canvas_menu.add_command(label="Wallpaper...", command=action.canvas_wallpaper)
canvas_menu.add_command(
label="Size/scale...", command=self.menu_action.canvas_size_and_scale
)
canvas_menu.add_command(
label="Wallpaper...", command=self.menu_action.canvas_set_wallpaper
)
canvas_menu.add_separator()
@ -560,9 +591,7 @@ class CoreMenubar(object):
:return: nothing
"""
session_menu = tk.Menu(self.menubar)
session_menu.add_command(
label="Start", command=action.session_start, underline=0
)
session_menu.add_command(
label="Change sessions...",
command=action.session_change_sessions,
@ -604,10 +633,11 @@ class CoreMenubar(object):
"""
help_menu = tk.Menu(self.menubar)
help_menu.add_command(
label="Core Github (www)", command=action.help_core_github
label="Core Github (www)", command=self.menu_action.help_core_github
)
help_menu.add_command(
label="Core Documentation (www)", command=action.help_core_documentation
label="Core Documentation (www)",
command=self.menu_action.help_core_documentation,
)
help_menu.add_command(label="About", command=action.help_about)

View file

@ -0,0 +1,45 @@
"""
provide mapping from core to canvas
"""
import logging
class CoreToCanvasMapping:
def __init__(self):
self.core_id_to_canvas_id = {}
self.core_node_and_interface_to_canvas_edge = {}
# self.edge_id_to_canvas_token = {}
def map_node_and_interface_to_canvas_edge(self, nid, iid, edge_token):
self.core_node_and_interface_to_canvas_edge[tuple([nid, iid])] = edge_token
def get_token_from_node_and_interface(self, nid, iid):
key = tuple([nid, iid])
if key in self.core_node_and_interface_to_canvas_edge:
return self.core_node_and_interface_to_canvas_edge[key]
else:
logging.error("invalid key")
return None
def map_core_id_to_canvas_id(self, core_nid, canvas_nid):
if core_nid not in self.core_id_to_canvas_id:
self.core_id_to_canvas_id[core_nid] = canvas_nid
else:
logging.debug("key already existed")
def get_canvas_id_from_core_id(self, core_id):
if core_id in self.core_id_to_canvas_id:
return self.core_id_to_canvas_id[core_id]
else:
logging.debug("invalid key")
return None
# def add_mapping(self, core_id, canvas_id):
# if core_id not in self.core_id_to_canvas_id:
# self.core_id_to_canvas_id[core_id] = canvas_id
# else:
# logging.error("key already mapped")
#
# def delete_mapping(self, core_id):
# result = self.core_id_to_canvas_id.pop(core_id, None)
# return result

View file

@ -1,22 +1,37 @@
import logging
import tkinter as tk
from enum import Enum
from coretk.images import Images
from core.api.grpc import core_pb2
from coretk.coretoolbarhelp import CoreToolbarHelp
from coretk.graph import GraphMode
from coretk.images import ImageEnum, Images
from coretk.tooltip import CreateToolTip
class SessionStateEnum(Enum):
NONE = "none"
DEFINITION = "definition"
CONFIGURATION = "configuration"
RUNTIME = "runtime"
DATACOLLECT = "datacollect"
SHUTDOWN = "shutdown"
INSTANTIATION = "instantiation"
class CoreToolbar(object):
"""
Core toolbar class
"""
def __init__(self, master, edit_frame, menubar):
def __init__(self, application, edit_frame, menubar):
"""
Create a CoreToolbar instance
:param tkinter.Frame edit_frame: edit frame
"""
self.master = master
self.application = application
self.master = application.master
self.edit_frame = edit_frame
self.menubar = menubar
self.radio_value = tk.IntVar()
@ -26,7 +41,6 @@ class CoreToolbar(object):
self.width = 32
self.height = 32
# Used for drawing the horizontally displayed menu items for network-layer nodes and link-layer node
self.selection_tool_button = None
# Reference to the option menus
@ -34,37 +48,7 @@ class CoreToolbar(object):
self.marker_option_menu = None
self.network_layer_option_menu = None
def load_toolbar_images(self):
"""
Load the images that appear in core toolbar
:return: nothing
"""
Images.load("core", "core-icon.png")
Images.load("start", "start.gif")
Images.load("switch", "lanswitch.gif")
Images.load("marker", "marker.gif")
Images.load("router", "router.gif")
Images.load("select", "select.gif")
Images.load("link", "link.gif")
Images.load("hub", "hub.gif")
Images.load("wlan", "wlan.gif")
Images.load("rj45", "rj45.gif")
Images.load("tunnel", "tunnel.gif")
Images.load("oval", "oval.gif")
Images.load("rectangle", "rectangle.gif")
Images.load("text", "text.gif")
Images.load("host", "host.gif")
Images.load("pc", "pc.gif")
Images.load("mdr", "mdr.gif")
Images.load("prouter", "router_green.gif")
Images.load("ovs", "OVS.gif")
Images.load("editnode", "document-properties.gif")
Images.load("run", "run.gif")
Images.load("plot", "plot.gif")
Images.load("twonode", "twonode.gif")
Images.load("stop", "stop.gif")
Images.load("observe", "observe.gif")
self.canvas = None
def destroy_previous_frame(self):
"""
@ -82,7 +66,7 @@ class CoreToolbar(object):
if self.marker_option_menu and self.marker_option_menu.winfo_exists():
self.marker_option_menu.destroy()
def destroy_children_widgets(self, parent):
def destroy_children_widgets(self):
"""
Destroy all children of a parent widget
@ -90,11 +74,11 @@ class CoreToolbar(object):
:return: nothing
"""
for i in parent.winfo_children():
for i in self.edit_frame.winfo_children():
if i.winfo_name() != "!frame":
i.destroy()
def create_button(self, img, func, frame, main_button):
def create_button(self, img, func, frame, main_button, btt_message):
"""
Create button and put it on the frame
@ -106,6 +90,7 @@ class CoreToolbar(object):
"""
button = tk.Button(frame, width=self.width, height=self.height, image=img)
button.pack(side=tk.LEFT, pady=1)
CreateToolTip(button, btt_message)
button.bind("<Button-1>", lambda mb: func(main_button))
def create_radio_button(self, frame, image, func, variable, value, tooltip_msg):
@ -122,11 +107,12 @@ class CoreToolbar(object):
button.pack(side=tk.TOP, pady=1)
CreateToolTip(button, tooltip_msg)
def create_regular_button(self, frame, image, func):
def create_regular_button(self, frame, image, func, btt_message):
button = tk.Button(
frame, width=self.width, height=self.height, image=image, command=func
)
button.pack(side=tk.TOP, pady=1)
CreateToolTip(button, btt_message)
def draw_button_menu_frame(self, edit_frame, option_frame, main_button):
"""
@ -165,48 +151,104 @@ class CoreToolbar(object):
def click_selection_tool(self):
logging.debug("Click SELECTION TOOL")
self.canvas.mode = GraphMode.SELECT
def click_start_stop_session_tool(self):
def click_start_session_tool(self):
"""
Start session handler: redraw buttons, send node and link messages to grpc server
:return: nothing
"""
logging.debug("Click START STOP SESSION button")
self.destroy_children_widgets(self.edit_frame)
helper = CoreToolbarHelp(self.application)
# self.destroy_children_widgets(self.edit_frame)
self.destroy_children_widgets()
self.canvas.mode = GraphMode.SELECT
# set configuration state
state = self.canvas.core_grpc.get_session_state()
if state == core_pb2.SessionState.SHUTDOWN or self.application.is_open_xml:
self.canvas.core_grpc.set_session_state(SessionStateEnum.DEFINITION.value)
self.application.is_open_xml = False
self.canvas.core_grpc.set_session_state(SessionStateEnum.CONFIGURATION.value)
helper.add_nodes()
helper.add_edges()
# for node in self.canvas.grpc_manager.nodes.values():
# print(node.type, node.model, int(node.x), int(node.y), node.name, node.node_id)
# self.canvas.core_grpc.add_node(
# node.type, node.model, int(node.x), int(node.y), node.name, node.node_id
# )
# print(len(self.canvas.grpc_manager.edges))
# for edge in self.canvas.grpc_manager.edges.values():
# print(edge.id1, edge.id2, edge.type1, edge.type2)
# self.canvas.core_grpc.add_link(
# edge.id1, edge.id2, edge.type1, edge.type2, edge
# )
self.canvas.core_grpc.set_session_state(SessionStateEnum.INSTANTIATION.value)
# self.canvas.core_grpc.get_session()
# self.application.is_open_xml = False
self.create_runtime_toolbar()
def click_link_tool(self):
logging.debug("Click LINK button")
self.canvas.mode = GraphMode.EDGE
def pick_router(self, main_button):
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get("router"))
logging.debug("Pick router option")
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get(ImageEnum.ROUTER.value))
self.canvas.mode = GraphMode.PICKNODE
self.canvas.draw_node_image = Images.get(ImageEnum.ROUTER.value)
self.canvas.draw_node_name = "router"
def pick_host(self, main_button):
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get("host"))
logging.debug("Pick host option")
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get(ImageEnum.HOST.value))
self.canvas.mode = GraphMode.PICKNODE
self.canvas.draw_node_image = Images.get(ImageEnum.HOST.value)
self.canvas.draw_node_name = "host"
def pick_pc(self, main_button):
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get("pc"))
logging.debug("Pick PC option")
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get(ImageEnum.PC.value))
self.canvas.mode = GraphMode.PICKNODE
self.canvas.draw_node_image = Images.get(ImageEnum.PC.value)
self.canvas.draw_node_name = "PC"
def pick_mdr(self, main_button):
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get("mdr"))
logging.debug("Pick MDR option")
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get(ImageEnum.MDR.value))
self.canvas.mode = GraphMode.PICKNODE
self.canvas.draw_node_image = Images.get(ImageEnum.MDR.value)
self.canvas.draw_node_name = "mdr"
def pick_prouter(self, main_button):
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get("prouter"))
logging.debug("Pick prouter option")
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get(ImageEnum.PROUTER.value))
self.canvas.mode = GraphMode.PICKNODE
self.canvas.draw_node_image = Images.get(ImageEnum.PROUTER.value)
self.canvas.draw_node_name = "prouter"
def pick_ovs(self, main_button):
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get("ovs"))
logging.debug("Pick OVS option")
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get(ImageEnum.OVS.value))
self.canvas.mode = GraphMode.PICKNODE
self.canvas.draw_node_image = Images.get(ImageEnum.OVS.value)
self.canvas.draw_node_name = "OVS"
# TODO what graph node is this
def pick_editnode(self, main_button):
self.network_layer_option_menu.destroy()
main_button.configure(image=Images.get("editnode"))
main_button.configure(image=Images.get(ImageEnum.EDITNODE.value))
logging.debug("Pick editnode option")
def draw_network_layer_options(self, network_layer_button):
@ -220,13 +262,13 @@ class CoreToolbar(object):
self.destroy_previous_frame()
option_frame = tk.Frame(self.master, padx=1, pady=1)
img_list = [
Images.get("router"),
Images.get("host"),
Images.get("pc"),
Images.get("mdr"),
Images.get("prouter"),
Images.get("ovs"),
Images.get("editnode"),
Images.get(ImageEnum.ROUTER.value),
Images.get(ImageEnum.HOST.value),
Images.get(ImageEnum.PC.value),
Images.get(ImageEnum.MDR.value),
Images.get(ImageEnum.PROUTER.value),
Images.get(ImageEnum.OVS.value),
Images.get(ImageEnum.EDITNODE.value),
]
func_list = [
self.pick_router,
@ -237,9 +279,22 @@ class CoreToolbar(object):
self.pick_ovs,
self.pick_editnode,
]
tooltip_list = [
"router",
"host",
"PC",
"mdr",
"prouter",
"OVS",
"edit node types",
]
for i in range(len(img_list)):
self.create_button(
img_list[i], func_list[i], option_frame, network_layer_button
img_list[i],
func_list[i],
option_frame,
network_layer_button,
tooltip_list[i],
)
# place frame at a calculated position as well as keep a reference of that frame
@ -257,7 +312,7 @@ class CoreToolbar(object):
:return: nothing
"""
router_image = Images.get("router")
router_image = Images.get(ImageEnum.ROUTER.value)
network_layer_button = tk.Radiobutton(
self.edit_frame,
indicatoron=False,
@ -272,29 +327,44 @@ class CoreToolbar(object):
CreateToolTip(network_layer_button, "Network-layer virtual nodes")
def pick_hub(self, main_button):
self.link_layer_option_menu.destroy()
main_button.configure(image=Images.get("hub"))
logging.debug("Pick link-layer node HUB")
self.link_layer_option_menu.destroy()
main_button.configure(image=Images.get(ImageEnum.HUB.value))
self.canvas.mode = GraphMode.PICKNODE
self.canvas.draw_node_image = Images.get(ImageEnum.HUB.value)
self.canvas.draw_node_name = "hub"
def pick_switch(self, main_button):
self.link_layer_option_menu.destroy()
main_button.configure(image=Images.get("switch"))
logging.debug("Pick link-layer node SWITCH")
self.link_layer_option_menu.destroy()
main_button.configure(image=Images.get(ImageEnum.SWITCH.value))
self.canvas.mode = GraphMode.PICKNODE
self.canvas.draw_node_image = Images.get(ImageEnum.SWITCH.value)
self.canvas.draw_node_name = "switch"
def pick_wlan(self, main_button):
self.link_layer_option_menu.destroy()
main_button.configure(image=Images.get("wlan"))
logging.debug("Pick link-layer node WLAN")
self.link_layer_option_menu.destroy()
main_button.configure(image=Images.get(ImageEnum.WLAN.value))
self.canvas.mode = GraphMode.PICKNODE
self.canvas.draw_node_image = Images.get(ImageEnum.WLAN.value)
self.canvas.draw_node_name = "wlan"
def pick_rj45(self, main_button):
self.link_layer_option_menu.destroy()
main_button.configure(image=Images.get("rj45"))
logging.debug("Pick link-layer node RJ45")
self.link_layer_option_menu.destroy()
main_button.configure(image=Images.get(ImageEnum.RJ45.value))
self.canvas.mode = GraphMode.PICKNODE
self.canvas.draw_node_image = Images.get(ImageEnum.RJ45.value)
self.canvas.draw_node_name = "rj45"
def pick_tunnel(self, main_button):
self.link_layer_option_menu.destroy()
main_button.configure(image=Images.get("tunnel"))
logging.debug("Pick link-layer node TUNNEL")
self.link_layer_option_menu.destroy()
main_button.configure(image=Images.get(ImageEnum.TUNNEL.value))
self.canvas.mode = GraphMode.PICKNODE
self.canvas.draw_node_image = Images.get(ImageEnum.TUNNEL.value)
self.canvas.draw_node_name = "tunnel"
def draw_link_layer_options(self, link_layer_button):
"""
@ -307,11 +377,11 @@ class CoreToolbar(object):
self.destroy_previous_frame()
option_frame = tk.Frame(self.master, padx=1, pady=1)
img_list = [
Images.get("hub"),
Images.get("switch"),
Images.get("wlan"),
Images.get("rj45"),
Images.get("tunnel"),
Images.get(ImageEnum.HUB.value),
Images.get(ImageEnum.SWITCH.value),
Images.get(ImageEnum.WLAN.value),
Images.get(ImageEnum.RJ45.value),
Images.get(ImageEnum.TUNNEL.value),
]
func_list = [
self.pick_hub,
@ -320,9 +390,20 @@ class CoreToolbar(object):
self.pick_rj45,
self.pick_tunnel,
]
tooltip_list = [
"ethernet hub",
"ethernet switch",
"wireless LAN",
"rj45 physical interface tool",
"tunnel tool",
]
for i in range(len(img_list)):
self.create_button(
img_list[i], func_list[i], option_frame, link_layer_button
img_list[i],
func_list[i],
option_frame,
link_layer_button,
tooltip_list[i],
)
# place frame at a calculated position as well as keep a reference of the frame
@ -340,7 +421,7 @@ class CoreToolbar(object):
:return: nothing
"""
hub_image = Images.get("hub")
hub_image = Images.get(ImageEnum.HUB.value)
link_layer_button = tk.Radiobutton(
self.edit_frame,
indicatoron=False,
@ -356,23 +437,22 @@ class CoreToolbar(object):
def pick_marker(self, main_button):
self.marker_option_menu.destroy()
main_button.configure(image=Images.get("marker"))
main_button.configure(image=Images.get(ImageEnum.MARKER.value))
logging.debug("Pick MARKER")
return "break"
def pick_oval(self, main_button):
self.marker_option_menu.destroy()
main_button.configure(image=Images.get("oval"))
main_button.configure(image=Images.get(ImageEnum.OVAL.value))
logging.debug("Pick OVAL")
def pick_rectangle(self, main_button):
self.marker_option_menu.destroy()
main_button.configure(image=Images.get("rectangle"))
main_button.configure(image=Images.get(ImageEnum.RECTANGLE.value))
logging.debug("Pick RECTANGLE")
def pick_text(self, main_button):
self.marker_option_menu.destroy()
main_button.configure(image=Images.get("text"))
main_button.configure(image=Images.get(ImageEnum.TEXT.value))
logging.debug("Pick TEXT")
def draw_marker_options(self, main_button):
@ -386,10 +466,10 @@ class CoreToolbar(object):
self.destroy_previous_frame()
option_frame = tk.Frame(self.master, padx=1, pady=1)
img_list = [
Images.get("marker"),
Images.get("oval"),
Images.get("rectangle"),
Images.get("text"),
Images.get(ImageEnum.MARKER.value),
Images.get(ImageEnum.OVAL.value),
Images.get(ImageEnum.RECTANGLE.value),
Images.get(ImageEnum.TEXT.value),
]
func_list = [
self.pick_marker,
@ -397,8 +477,11 @@ class CoreToolbar(object):
self.pick_rectangle,
self.pick_text,
]
tooltip_list = ["marker", "oval", "rectangle", "text"]
for i in range(len(img_list)):
self.create_button(img_list[i], func_list[i], option_frame, main_button)
self.create_button(
img_list[i], func_list[i], option_frame, main_button, tooltip_list[i]
)
# place the frame at a calculated position as well as keep a reference of that frame
self.draw_button_menu_frame(self.edit_frame, option_frame, main_button)
@ -415,7 +498,7 @@ class CoreToolbar(object):
:return: nothing
"""
marker_image = Images.get("marker")
marker_image = Images.get(ImageEnum.MARKER.value)
marker_main_button = tk.Radiobutton(
self.edit_frame,
indicatoron=False,
@ -430,13 +513,20 @@ class CoreToolbar(object):
CreateToolTip(marker_main_button, "background annotation tools")
def create_toolbar(self):
self.load_toolbar_images()
"""
Create buttons for toolbar in edit mode
:return: nothing
"""
self.create_regular_button(
self.edit_frame, Images.get("start"), self.click_start_stop_session_tool
self.edit_frame,
Images.get(ImageEnum.START.value),
self.click_start_session_tool,
"start the session",
)
self.create_radio_button(
self.edit_frame,
Images.get("select"),
Images.get(ImageEnum.SELECT.value),
self.click_selection_tool,
self.radio_value,
1,
@ -444,7 +534,7 @@ class CoreToolbar(object):
)
self.create_radio_button(
self.edit_frame,
Images.get("link"),
Images.get(ImageEnum.LINK.value),
self.click_link_tool,
self.radio_value,
2,
@ -453,11 +543,12 @@ class CoreToolbar(object):
self.create_network_layer_button()
self.create_link_layer_button()
self.create_marker_button()
self.radio_value.set(1)
def create_observe_button(self):
menu_button = tk.Menubutton(
self.edit_frame,
image=Images.get("observe"),
image=Images.get(ImageEnum.OBSERVE.value),
width=self.width,
height=self.height,
direction=tk.RIGHT,
@ -485,8 +576,18 @@ class CoreToolbar(object):
menu_button.menu.add_command(label="Edit...")
def click_stop_button(self):
"""
redraw buttons on the toolbar, send node and link messages to grpc server
:return: nothing
"""
logging.debug("Click on STOP button ")
self.destroy_children_widgets(self.edit_frame)
# self.destroy_children_widgets(self.edit_frame)
self.destroy_children_widgets()
self.canvas.core_grpc.set_session_state(SessionStateEnum.DATACOLLECT.value)
self.canvas.core_grpc.delete_links()
self.canvas.core_grpc.delete_nodes()
self.create_toolbar()
def click_run_button(self):
@ -503,11 +604,14 @@ class CoreToolbar(object):
def create_runtime_toolbar(self):
self.create_regular_button(
self.edit_frame, Images.get("stop"), self.click_stop_button
self.edit_frame,
Images.get(ImageEnum.STOP.value),
self.click_stop_button,
"stop the session",
)
self.create_radio_button(
self.edit_frame,
Images.get("select"),
Images.get(ImageEnum.SELECT.value),
self.click_selection_tool,
self.exec_radio_value,
1,
@ -516,7 +620,7 @@ class CoreToolbar(object):
self.create_observe_button()
self.create_radio_button(
self.edit_frame,
Images.get("plot"),
Images.get(ImageEnum.PLOT.value),
self.click_plot_button,
self.exec_radio_value,
2,
@ -524,7 +628,7 @@ class CoreToolbar(object):
)
self.create_radio_button(
self.edit_frame,
Images.get("marker"),
Images.get(ImageEnum.MARKER.value),
self.click_marker_button,
self.exec_radio_value,
3,
@ -532,12 +636,16 @@ class CoreToolbar(object):
)
self.create_radio_button(
self.edit_frame,
Images.get("twonode"),
Images.get(ImageEnum.TWONODE.value),
self.click_two_node_button,
self.exec_radio_value,
4,
"run command from one node to another",
)
self.create_regular_button(
self.edit_frame, Images.get("run"), self.click_run_button
self.edit_frame,
Images.get(ImageEnum.RUN.value),
self.click_run_button,
"run",
)
self.exec_radio_value.set(1)

View file

@ -0,0 +1,31 @@
"""
CoreToolbar help to draw on canvas, and make grpc client call
"""
class CoreToolbarHelp:
def __init__(self, application):
self.application = application
self.core_grpc = application.core_grpc
def add_nodes(self):
"""
add the nodes stored in grpc manager
:return: nothing
"""
grpc_manager = self.application.canvas.grpc_manager
for node in grpc_manager.nodes.values():
self.application.core_grpc.add_node(
node.type, node.model, int(node.x), int(node.y), node.name, node.node_id
)
def add_edges(self):
"""
add the edges stored in grpc manager
:return:
"""
grpc_manager = self.application.canvas.grpc_manager
for edge in grpc_manager.edges.values():
self.application.core_grpc.add_link(
edge.id1, edge.id2, edge.type1, edge.type2, edge
)

View file

@ -2,29 +2,51 @@ import enum
import logging
import tkinter as tk
from core.api.grpc import core_pb2
from coretk.graph_helper import GraphHelper, WlanAntennaManager
from coretk.grpcmanagement import GrpcManager
from coretk.images import Images
from coretk.interface import Interface
from coretk.linkinfo import LinkInfo
from coretk.nodeconfigtable import NodeConfig
class GraphMode(enum.Enum):
SELECT = 0
EDGE = 1
NODE = 2
PICKNODE = 2
NODE = 3
OTHER = 4
class CanvasGraph(tk.Canvas):
def __init__(self, master=None, cnf=None, **kwargs):
def __init__(self, master=None, grpc=None, cnf=None, **kwargs):
if cnf is None:
cnf = {}
kwargs["highlightthickness"] = 0
super().__init__(master, cnf, **kwargs)
self.mode = GraphMode.SELECT
self.draw_node_image = None
self.draw_node_name = None
self.selected = None
self.node_context = None
self.nodes = {}
self.edges = {}
self.drawing_edge = None
self.grid = None
self.meters_per_pixel = 1.5
self.setup_menus()
self.setup_bindings()
self.draw_grid()
self.core_grpc = grpc
self.grpc_manager = GrpcManager(grpc)
self.helper = GraphHelper(self, grpc)
# self.core_id_to_canvas_id = {}
# self.core_map = CoreToCanvasMapping()
# self.draw_existing_component()
def setup_menus(self):
self.node_context = tk.Menu(self.master)
@ -32,21 +54,201 @@ class CanvasGraph(tk.Canvas):
self.node_context.add_command(label="Two")
self.node_context.add_command(label="Three")
def canvas_reset_and_redraw(self, new_grpc):
"""
Reset the private variables CanvasGraph object, redraw nodes given the new grpc client
:param new_grpc:
:return:
"""
# delete any existing drawn items
# self.delete_components()
self.helper.delete_canvas_components()
# set the private variables to default value
self.mode = GraphMode.SELECT
self.draw_node_image = None
self.draw_node_name = None
self.selected = None
self.node_context = None
self.nodes = {}
self.edges = {}
self.drawing_edge = None
print("graph.py create a new grpc manager")
self.grpc_manager = GrpcManager(new_grpc)
# new grpc
self.core_grpc = new_grpc
print("grpah.py draw existing component")
self.draw_existing_component()
print(self.grpc_manager.edges)
def setup_bindings(self):
"""
Bind any mouse events or hot keys to the matching action
:return: nothing
"""
self.bind("<ButtonPress-1>", self.click_press)
self.bind("<ButtonRelease-1>", self.click_release)
self.bind("<B1-Motion>", self.click_motion)
self.bind("<Button-3>", self.context)
self.bind("e", self.set_mode)
self.bind("s", self.set_mode)
self.bind("n", self.set_mode)
def draw_grid(self, width=1000, height=800):
"""
Create grid
:param int width: the width
:param int height: the height
:return: nothing
"""
self.grid = self.create_rectangle(
0,
0,
width,
height,
outline="#000000",
fill="#ffffff",
width=1,
tags="rectangle",
)
self.tag_lower(self.grid)
for i in range(0, width, 27):
self.create_line(i, 0, i, height, dash=(2, 4), tags="gridline")
for i in range(0, height, 27):
self.create_line(0, i, width, i, dash=(2, 4), tags="gridline")
def draw_existing_component(self):
"""
Draw existing node and update the information in grpc manager to match
:return: nothing
"""
core_id_to_canvas_id = {}
session_id = self.core_grpc.session_id
session = self.core_grpc.core.get_session(session_id).session
# redraw existing nodes
for node in session.nodes:
# peer to peer node is not drawn on the GUI
if node.type != core_pb2.NodeType.PEER_TO_PEER:
# draw nodes on the canvas
image, name = Images.convert_type_and_model_to_image(
node.type, node.model
)
n = CanvasNode(
node.position.x, node.position.y, image, name, self, node.id
)
self.nodes[n.id] = n
core_id_to_canvas_id[node.id] = n.id
# store the node in grpc manager
self.grpc_manager.add_preexisting_node(n, session_id, node, name)
self.grpc_manager.update_reusable_id()
# draw existing links
for link in session.links:
n1 = self.nodes[core_id_to_canvas_id[link.node_one_id]]
n2 = self.nodes[core_id_to_canvas_id[link.node_two_id]]
if link.type == core_pb2.LinkType.WIRED:
e = CanvasEdge(
n1.x_coord,
n1.y_coord,
n2.x_coord,
n2.y_coord,
n1.id,
self,
is_wired=True,
)
elif link.type == core_pb2.LinkType.WIRELESS:
e = CanvasEdge(
n1.x_coord,
n1.y_coord,
n2.x_coord,
n2.y_coord,
n1.id,
self,
is_wired=False,
)
edge_token = tuple(sorted((n1.id, n2.id)))
e.token = edge_token
e.dst = n2.id
n1.edges.add(e)
n2.edges.add(e)
self.edges[e.token] = e
self.grpc_manager.add_edge(session_id, e.token, n1.id, n2.id)
self.helper.redraw_antenna(link, n1, n2)
# TODO add back the link info to grpc manager also redraw
grpc_if1 = link.interface_one
grpc_if2 = link.interface_two
ip4_src = None
ip4_dst = None
ip6_src = None
ip6_dst = None
if grpc_if1 is not None:
ip4_src = grpc_if1.ip4
ip6_src = grpc_if1.ip6
if grpc_if2 is not None:
ip4_dst = grpc_if2.ip4
ip6_dst = grpc_if2.ip6
e.link_info = LinkInfo(
canvas=self,
edge=e,
ip4_src=ip4_src,
ip6_src=ip6_src,
ip4_dst=ip4_dst,
ip6_dst=ip6_dst,
)
# TODO will include throughput and ipv6 in the future
if1 = Interface(grpc_if1.name, grpc_if1.ip4, ifid=grpc_if1.id)
if2 = Interface(grpc_if2.name, grpc_if2.ip4, ifid=grpc_if2.id)
self.grpc_manager.edges[e.token].interface_1 = if1
self.grpc_manager.edges[e.token].interface_2 = if2
self.grpc_manager.nodes[
core_id_to_canvas_id[link.node_one_id]
].interfaces.append(if1)
self.grpc_manager.nodes[
core_id_to_canvas_id[link.node_two_id]
].interfaces.append(if2)
# lift the nodes so they on top of the links
# for i in core_id_to_canvas_id.values():
# self.lift(i)
for i in self.find_withtag("node"):
self.lift(i)
# def delete_components(self):
# tags = ["node", "edge", "linkinfo", "nodename"]
# for i in tags:
# for id in self.find_withtag(i):
# self.delete(id)
def canvas_xy(self, event):
"""
Convert window coordinate to canvas coordinate
:param event:
:rtype: (int, int)
:return: x, y canvas coordinate
"""
x = self.canvasx(event.x)
y = self.canvasy(event.y)
return x, y
def get_selected(self, event):
"""
Retrieve the item id that is on the mouse position
:param event: mouse event
:rtype: int
:return: the item that the mouse point to
"""
overlapping = self.find_overlapping(event.x, event.y, event.x, event.y)
nodes = set(self.find_withtag("node"))
selected = None
@ -64,6 +266,12 @@ class CanvasGraph(tk.Canvas):
return selected
def click_release(self, event):
"""
Draw a node or finish drawing an edge according to the current graph mode
:param event: mouse event
:return: nothing
"""
self.focus_set()
self.selected = self.get_selected(event)
logging.debug(f"click release selected: {self.selected}")
@ -71,7 +279,9 @@ class CanvasGraph(tk.Canvas):
self.handle_edge_release(event)
elif self.mode == GraphMode.NODE:
x, y = self.canvas_xy(event)
self.add_node(x, y, "switch")
self.add_node(x, y, self.draw_node_image, self.draw_node_name)
elif self.mode == GraphMode.PICKNODE:
self.mode = GraphMode.NODE
def handle_edge_release(self, event):
edge = self.drawing_edge
@ -106,9 +316,37 @@ class CanvasGraph(tk.Canvas):
node_dst = self.nodes[edge.dst]
node_dst.edges.add(edge)
self.grpc_manager.add_edge(
self.core_grpc.session_id, edge.token, node_src.id, node_dst.id
)
# draw link info on the edge
if1 = self.grpc_manager.edges[edge.token].interface_1
if2 = self.grpc_manager.edges[edge.token].interface_2
ip4_and_prefix_1 = None
ip4_and_prefix_2 = None
if if1 is not None:
ip4_and_prefix_1 = if1.ip4_and_prefix
if if2 is not None:
ip4_and_prefix_2 = if2.ip4_and_prefix
edge.link_info = LinkInfo(
self,
edge,
ip4_src=ip4_and_prefix_1,
ip6_src=None,
ip4_dst=ip4_and_prefix_2,
ip6_dst=None,
)
logging.debug(f"edges: {self.find_withtag('edge')}")
def click_press(self, event):
"""
Start drawing an edge if mouse click is on a node
:param event: mouse event
:return: nothing
"""
logging.debug(f"click press: {event}")
selected = self.get_selected(event)
is_node = selected in self.find_withtag("node")
@ -117,6 +355,12 @@ class CanvasGraph(tk.Canvas):
self.drawing_edge = CanvasEdge(x, y, x, y, selected, self)
def click_motion(self, event):
"""
Redraw drawing edge according to the current position of the mouse
:param event: mouse event
:return: nothing
"""
if self.mode == GraphMode.EDGE and self.drawing_edge is not None:
x2, y2 = self.canvas_xy(event)
x1, y1, _, _ = self.coords(self.drawing_edge.id)
@ -129,70 +373,136 @@ class CanvasGraph(tk.Canvas):
logging.debug(f"node context: {selected}")
self.node_context.post(event.x_root, event.y_root)
def set_mode(self, event):
logging.debug(f"mode event: {event}")
if event.char == "e":
self.mode = GraphMode.EDGE
elif event.char == "s":
self.mode = GraphMode.SELECT
elif event.char == "n":
self.mode = GraphMode.NODE
logging.debug(f"graph mode: {self.mode}")
def add_node(self, x, y, image_name):
image = Images.get(image_name)
node = CanvasNode(x, y, image, self)
self.nodes[node.id] = node
return node
def add_node(self, x, y, image, node_name):
plot_id = self.find_all()[0]
if self.selected == plot_id:
node = CanvasNode(
x=x,
y=y,
image=image,
node_type=node_name,
canvas=self,
core_id=self.grpc_manager.peek_id(),
)
self.nodes[node.id] = node
self.grpc_manager.add_node(
self.core_grpc.session_id, node.id, x, y, node_name
)
return node
class CanvasEdge:
width = 3
"""
Canvas edge class
"""
def __init__(self, x1, y1, x2, y2, src, canvas):
width = 1.4
def __init__(self, x1, y1, x2, y2, src, canvas, is_wired=None):
"""
Create an instance of canvas edge object
:param int x1: source x-coord
:param int y1: source y-coord
:param int x2: destination x-coord
:param int y2: destination y-coord
:param int src: source id
:param tkinter.Canvas canvas: canvas object
"""
self.src = src
self.dst = None
self.canvas = canvas
self.id = self.canvas.create_line(x1, y1, x2, y2, tags="edge", width=self.width)
if is_wired is None or is_wired is True:
self.id = self.canvas.create_line(
x1, y1, x2, y2, tags="edge", width=self.width, fill="#ff0000"
)
else:
self.id = self.canvas.create_line(
x1,
y1,
x2,
y2,
tags="edge",
width=self.width,
fill="#ff0000",
state=tk.HIDDEN,
)
self.token = None
self.canvas.tag_lower(self.id)
# link info object
self.link_info = None
self.throughput = None
self.wired = is_wired
# TODO resolve this
# self.canvas.tag_lower(self.id)
def complete(self, dst, x, y):
self.dst = dst
self.token = tuple(sorted((self.src, self.dst)))
x1, y1, _, _ = self.canvas.coords(self.id)
self.canvas.coords(self.id, x1, y1, x, y)
self.canvas.helper.draw_wireless_case(self.src, self.dst, self)
self.canvas.lift(self.src)
self.canvas.lift(self.dst)
def delete(self):
self.canvas.delete(self.id)
class CanvasNode:
def __init__(self, x, y, image, canvas):
def __init__(self, x, y, image, node_type, canvas, core_id):
self.image = image
self.node_type = node_type
self.canvas = canvas
self.id = self.canvas.create_image(
x, y, anchor=tk.CENTER, image=self.image, tags="node"
)
self.name = f"Node {self.id}"
self.text_id = self.canvas.create_text(x, y + 20, text=self.name)
self.core_id = core_id
self.x_coord = x
self.y_coord = y
self.name = f"N{self.core_id}"
self.text_id = self.canvas.create_text(
x, y + 20, text=self.name, tags="nodename"
)
self.antenna_draw = WlanAntennaManager(self.canvas, self.id)
self.canvas.tag_bind(self.id, "<ButtonPress-1>", self.click_press)
self.canvas.tag_bind(self.id, "<ButtonRelease-1>", self.click_release)
self.canvas.tag_bind(self.id, "<B1-Motion>", self.motion)
self.canvas.tag_bind(self.id, "<Button-3>", self.context)
self.canvas.tag_bind(self.id, "<Double-Button-1>", self.double_click)
self.edges = set()
self.wlans = []
self.moving = None
def double_click(self, event):
node_id = self.canvas.grpc_manager.nodes[self.id].node_id
state = self.canvas.core_grpc.get_session_state()
if state == core_pb2.SessionState.RUNTIME:
self.canvas.core_grpc.launch_terminal(node_id)
else:
print("config table show up")
NodeConfig(self, self.image, self.node_type, self.name)
def update_coords(self):
self.x_coord, self.y_coord = self.canvas.coords(self.id)
def click_press(self, event):
logging.debug(f"click press {self.name}: {event}")
self.moving = self.canvas.canvas_xy(event)
# return "break"
def click_release(self, event):
logging.debug(f"click release {self.name}: {event}")
self.update_coords()
self.canvas.grpc_manager.update_node_location(
self.id, self.x_coord, self.y_coord
)
self.moving = None
def motion(self, event):
if self.canvas.mode == GraphMode.EDGE:
if self.canvas.mode == GraphMode.EDGE or self.canvas.mode == GraphMode.NODE:
return
x, y = self.canvas.canvas_xy(event)
moving_x, moving_y = self.moving
@ -202,13 +512,25 @@ class CanvasNode:
old_x, old_y = self.canvas.coords(self.id)
self.canvas.move(self.id, offset_x, offset_y)
self.canvas.move(self.text_id, offset_x, offset_y)
self.antenna_draw.update_antennas_position(offset_x, offset_y)
new_x, new_y = self.canvas.coords(self.id)
if self.canvas.core_grpc.get_session_state() == core_pb2.SessionState.RUNTIME:
self.canvas.core_grpc.edit_node(self.core_id, int(new_x), int(new_y))
for edge in self.edges:
x1, y1, x2, y2 = self.canvas.coords(edge.id)
if x1 == old_x and y1 == old_y:
self.canvas.coords(edge.id, new_x, new_y, x2, y2)
else:
self.canvas.coords(edge.id, x1, y1, new_x, new_y)
edge.link_info.recalculate_info()
# self.canvas.core_grpc.throughput_draw.update_throughtput_location(edge)
self.canvas.helper.update_wlan_connection(
old_x, old_y, new_x, new_y, self.wlans
)
def context(self, event):
logging.debug(f"context click {self.name}: {event}")

View file

@ -0,0 +1,211 @@
"""
Some graph helper functions
"""
import logging
import tkinter as tk
from core.api.grpc import core_pb2
from coretk.images import ImageEnum, Images
CANVAS_COMPONENT_TAGS = ["edge", "node", "nodename", "wallpaper", "linkinfo"]
class GraphHelper:
def __init__(self, canvas, grpc):
"""
create an instance of GraphHelper object
"""
self.canvas = canvas
self.core_grpc = grpc
def delete_canvas_components(self):
"""
delete the components of the graph leaving only the blank canvas
:return: nothing
"""
for tag in CANVAS_COMPONENT_TAGS:
for i in self.canvas.find_withtag(tag):
self.canvas.delete(i)
def draw_wireless_case(self, src_id, dst_id, edge):
src_node_name = self.canvas.nodes[src_id].node_type
dst_node_name = self.canvas.nodes[dst_id].node_type
if src_node_name == "wlan" or dst_node_name == "wlan":
self.canvas.itemconfig(edge.id, state=tk.HIDDEN)
edge.wired = False
if edge.token not in self.canvas.edges:
if src_node_name == "wlan" and dst_node_name == "wlan":
self.canvas.nodes[src_id].antenna_draw.add_antenna()
elif src_node_name == "wlan":
self.canvas.nodes[dst_id].antenna_draw.add_antenna()
else:
self.canvas.nodes[src_id].antenna_draw.add_antenna()
edge.wired = True
def redraw_antenna(self, link, node_one, node_two):
if link.type == core_pb2.LinkType.WIRELESS:
if node_one.node_type == "wlan" and node_two.node_type == "wlan":
node_one.antenna_draw.add_antenna()
elif node_one.node_type == "wlan" and node_two.node_type != "wlan":
node_two.antenna_draw.add_antenna()
elif node_one.node_type != "wlan" and node_two.node_type == "wlan":
node_one.antenna_draw.add_antenna()
else:
logging.error(
"graph_helper.py WIRELESS link but both nodes are non-wireless node"
)
def update_wlan_connection(self, old_x, old_y, new_x, new_y, edge_ids):
for eid in edge_ids:
x1, y1, x2, y2 = self.canvas.coords(eid)
if x1 == old_x and y1 == old_y:
self.canvas.coords(eid, new_x, new_y, x2, y2)
else:
self.canvas.coords(eid, x1, y1, new_x, new_y)
class WlanAntennaManager:
def __init__(self, canvas, node_id):
"""
crate an instance for AntennaManager
"""
self.canvas = canvas
self.node_id = node_id
self.quantity = 0
self._max = 5
self.antennas = []
# distance between each antenna
self.offset = 0
def add_antenna(self):
"""
add an antenna to a node
:return: nothing
"""
if self.quantity < 5:
x, y = self.canvas.coords(self.node_id)
self.antennas.append(
self.canvas.create_image(
x - 16 + self.offset,
y - 16,
anchor=tk.CENTER,
image=Images.get(ImageEnum.ANTENNA.value),
tags="antenna",
)
)
self.quantity = self.quantity + 1
self.offset = self.offset + 8
def update_antennas_position(self, offset_x, offset_y):
"""
redraw antennas of a node according to the new node position
:return: nothing
"""
for i in self.antennas:
self.canvas.move(i, offset_x, offset_y)
def delete_antenna(self, canvas_id):
return
def delete_antennas(self):
"""
Delete all the antennas of a node
:return: nothing
"""
for i in self.antennas:
self.canvas.delete(i)
# class WlanConnection:
# def __init__(self, canvas, grpc):
# """
# create in
# :param canvas:
# """
# self.canvas = canvas
# self.core_grpc = grpc
# self.throughput_on = False
# self.map_node_link = {}
# self.links = []
#
# def wireless_nodes(self):
# """
# retrieve all the wireless clouds in the canvas
#
# :return: list(coretk.graph.CanvasNode)
# """
# wireless_nodes = []
# for n in self.canvas.nodes.values():
# if n.node_type == "wlan":
# wireless_nodes.append(n)
# return wireless_nodes
#
# def draw_wireless_link(self, src, dst):
# """
# draw a line between 2 nodes that are connected to the same wireless cloud
#
# :param coretk.graph.CanvasNode src: source node
# :param coretk.graph.CanvasNode dst: destination node
# :return: nothing
# """
# cid = self.canvas.create_line(src.x_coord, src.y_coord, dst.x_coord, dst.y_coord, tags="wlanconnection")
# if src.id not in self.map_node_link:
# self.map_node_link[src.id] = []
# if dst.id not in self.map_node_link:
# self.map_node_link[dst.id] = []
# self.map_node_link[src.id].append(cid)
# self.map_node_link[dst.id].append(cid)
# self.links.append(cid)
#
# def subnet_wireless_connection(self, wlan_node):
# """
# retrieve all the non-wireless nodes connected to wireless_node and create line (represent wireless connection) between each pair of nodes
# :param coretk.grpah.CanvasNode wlan_node: wireless node
#
# :return: nothing
# """
# non_wlan_nodes = []
# for e in wlan_node.edges:
# src = self.canvas.nodes[e.src]
# dst = self.canvas.nodes[e.dst]
# if src.node_type == "wlan" and dst.node_type != "wlan":
# non_wlan_nodes.append(dst)
# elif src.node_type != "wlan" and dst.node_type == "wlan":
# non_wlan_nodes.append(src)
#
# size = len(non_wlan_nodes)
# for i in range(size):
# for j in range(i+1, size):
# self.draw_wireless_link(non_wlan_nodes[i], non_wlan_nodes[j])
#
# def session_wireless_connection(self):
# """
# draw all the wireless connection in the canvas
#
# :return: nothing
# """
# wlan_nodes = self.wireless_nodes()
# for n in wlan_nodes:
# self.subnet_wireless_connection(n)
#
# def show_links(self):
# """
# show all the links
# """
# for l in self.links:
# self.canvas.itemconfig(l, state=tk.NORMAL)
#
# def hide_links(self):
# """
# hide all the links
# :return:
# """
# for l in self.links:
# self.canvas.itemconfig(l, state=tk.HIDDEN)

View file

@ -0,0 +1,326 @@
"""
Manage useful informations about the nodes, edges and configuration
that can be useful for grpc, acts like a session class
"""
import logging
from core.api.grpc import core_pb2
from coretk.coretocanvas import CoreToCanvasMapping
from coretk.interface import Interface, InterfaceManager
link_layer_nodes = ["switch", "hub", "wlan", "rj45", "tunnel"]
network_layer_nodes = ["router", "host", "PC", "mdr", "prouter", "OVS"]
class Node:
def __init__(self, session_id, node_id, node_type, model, x, y, name):
"""
Create an instance of a node
:param int session_id: session id
:param int node_id: node id
:param core_pb2.NodeType node_type: node type
:param int x: x coordinate
:param int y: coordinate
:param str name: node name
"""
self.session_id = session_id
self.node_id = node_id
self.type = node_type
self.x = x
self.y = y
self.model = model
self.name = name
self.interfaces = []
class Edge:
def __init__(self, session_id, node_id_1, node_type_1, node_id_2, node_type_2):
"""
Create an instance of an edge
:param int session_id: session id
:param int node_id_1: node 1 id
:param int node_type_1: node 1 type
:param core_pb2.NodeType node_id_2: node 2 id
:param core_pb2.NodeType node_type_2: node 2 type
"""
self.session_id = session_id
self.id1 = node_id_1
self.id2 = node_id_2
self.type1 = node_type_1
self.type2 = node_type_2
self.interface_1 = None
self.interface_2 = None
class GrpcManager:
def __init__(self, grpc):
self.nodes = {}
self.edges = {}
self.id = None
# A list of id for re-use, keep in increasing order
self.reusable = []
self.preexisting = []
self.core_grpc = None
# self.update_preexisting_ids()
# self.core_id_to_canvas_id = {}
self.interfaces_manager = InterfaceManager()
# map tuple(core_node_id, interface_id) to and edge
# self.node_id_and_interface_to_edge_token = {}
self.core_mapping = CoreToCanvasMapping()
def update_preexisting_ids(self):
"""
get preexisting node ids
:return:
"""
max_id = 0
client = self.core_grpc.core
sessions = client.get_sessions().sessions
for session_summary in sessions:
session = client.get_session(session_summary.id).session
for node in session.nodes:
if node.id > max_id:
max_id = node.id
self.preexisting.append(node.id)
self.id = max_id + 1
self.update_reusable_id()
def peek_id(self):
"""
Peek the next id to be used
:return: nothing
"""
if len(self.reusable) == 0:
return self.id
else:
return self.reusable[0]
def get_id(self):
"""
Get the next node id as well as update id status and reusable ids
:rtype: int
:return: the next id to be used
"""
if len(self.reusable) == 0:
new_id = self.id
self.id = self.id + 1
return new_id
else:
return self.reusable.pop(0)
def add_node(self, session_id, canvas_id, x, y, name):
"""
Add node, with information filled in, to grpc manager
:param int session_id: session id
:param int canvas_id: node's canvas id
:param int x: x coord
:param int y: y coord
:param str name: node type
:return: nothing
"""
node_type = None
node_model = None
if name in link_layer_nodes:
if name == "switch":
node_type = core_pb2.NodeType.SWITCH
elif name == "hub":
node_type = core_pb2.NodeType.HUB
elif name == "wlan":
node_type = core_pb2.NodeType.WIRELESS_LAN
elif name == "rj45":
node_type = core_pb2.NodeType.RJ45
elif name == "tunnel":
node_type = core_pb2.NodeType.TUNNEL
elif name in network_layer_nodes:
node_type = core_pb2.NodeType.DEFAULT
node_model = name
else:
logging.error("grpcmanagemeny.py INVALID node name")
nid = self.get_id()
create_node = Node(session_id, nid, node_type, node_model, x, y, name)
self.nodes[canvas_id] = create_node
self.core_mapping.map_core_id_to_canvas_id(nid, canvas_id)
# self.core_id_to_canvas_id[nid] = canvas_id
logging.debug(
"Adding node to GrpcManager.. Session id: %s, Coords: (%s, %s), Name: %s",
session_id,
x,
y,
name,
)
def add_preexisting_node(self, canvas_node, session_id, core_node, name):
"""
Add preexisting nodes to grpc manager
:param str name: node_type
:param core_pb2.Node core_node: core node grpc message
:param coretk.graph.CanvasNode canvas_node: canvas node
:param int session_id: session id
:return: nothing
"""
# update the next available id
core_id = core_node.id
print(core_id)
if self.id is None or core_id >= self.id:
self.id = core_id + 1
self.preexisting.append(core_id)
n = Node(
session_id,
core_id,
core_node.type,
core_node.model,
canvas_node.x_coord,
canvas_node.y_coord,
name,
)
self.nodes[canvas_node.id] = n
def update_node_location(self, canvas_id, new_x, new_y):
"""
update node
:param int canvas_id: canvas id of that node
:param int new_x: new x coord
:param int new_y: new y coord
:return: nothing
"""
self.nodes[canvas_id].x = new_x
self.nodes[canvas_id].y = new_y
def update_reusable_id(self):
"""
Update available id for reuse
:return: nothing
"""
if len(self.preexisting) > 0:
for i in range(1, self.id):
if i not in self.preexisting:
self.reusable.append(i)
self.preexisting.clear()
logging.debug("Next id: %s, Reusable: %s", self.id, self.reusable)
def delete_node(self, canvas_id):
"""
Delete a node from the session
:param int canvas_id: node's id in the canvas
:return: thing
"""
try:
self.nodes.pop(canvas_id)
self.reuseable.append(canvas_id)
self.reuseable.sort()
except KeyError:
logging.error("grpcmanagement.py INVALID NODE CANVAS ID")
def create_interface(self, edge, src_canvas_id, dst_canvas_id):
"""
Create the interface for the two end of an edge, add a copy to node's interfaces
:param coretk.grpcmanagement.Edge edge: edge to add interfaces to
:param int src_canvas_id: canvas id for the source node
:param int dst_canvas_id: canvas id for the destination node
:return: nothing
"""
src_interface = None
dst_interface = None
print("create interface")
self.interfaces_manager.new_subnet()
src_node = self.nodes[src_canvas_id]
if src_node.model in network_layer_nodes:
ifid = len(src_node.interfaces)
name = "eth" + str(ifid)
src_interface = Interface(
name=name, ifid=ifid, ipv4=str(self.interfaces_manager.get_address())
)
self.nodes[src_canvas_id].interfaces.append(src_interface)
logging.debug(
"Create source interface 1... IP: %s, name: %s",
src_interface.ipv4,
src_interface.name,
)
dst_node = self.nodes[dst_canvas_id]
if dst_node.model in network_layer_nodes:
ifid = len(dst_node.interfaces)
name = "eth" + str(ifid)
dst_interface = Interface(
name=name, ifid=ifid, ipv4=str(self.interfaces_manager.get_address())
)
self.nodes[dst_canvas_id].interfaces.append(dst_interface)
logging.debug(
"Create destination interface... IP: %s, name: %s",
dst_interface.ipv4,
dst_interface.name,
)
edge.interface_1 = src_interface
edge.interface_2 = dst_interface
return src_interface, dst_interface
def add_edge(self, session_id, token, canvas_id_1, canvas_id_2):
"""
Add an edge to grpc manager
:param int session_id: core session id
:param tuple(int, int) token: edge's identification in the canvas
:param int canvas_id_1: canvas id of source node
:param int canvas_id_2: canvas_id of destination node
:return: nothing
"""
if canvas_id_1 in self.nodes and canvas_id_2 in self.nodes:
edge = Edge(
session_id,
self.nodes[canvas_id_1].node_id,
self.nodes[canvas_id_1].type,
self.nodes[canvas_id_2].node_id,
self.nodes[canvas_id_2].type,
)
self.edges[token] = edge
src_interface, dst_interface = self.create_interface(
edge, canvas_id_1, canvas_id_2
)
node_one_id = self.nodes[canvas_id_1].node_id
node_two_id = self.nodes[canvas_id_2].node_id
# provide a way to get an edge from a core node and an interface id
if src_interface is not None:
# self.node_id_and_interface_to_edge_token[tuple([node_one_id, src_interface.id])] = token
self.core_mapping.map_node_and_interface_to_canvas_edge(
node_one_id, src_interface.id, token
)
logging.debug(
"map node id %s, interface_id %s to edge token %s",
node_one_id,
src_interface.id,
token,
)
if dst_interface is not None:
# self.node_id_and_interface_to_edge_token[tuple([node_two_id, dst_interface.id])] = token
self.core_mapping.map_node_and_interface_to_canvas_edge(
node_two_id, dst_interface.id, token
)
logging.debug(
"map node id %s, interface_id %s to edge token %s",
node_two_id,
dst_interface.id,
token,
)
logging.debug("Adding edge to grpc manager...")
else:
logging.error("grpcmanagement.py INVALID CANVAS NODE ID")

View file

Before

Width:  |  Height:  |  Size: 744 B

After

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 635 B

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 719 B

After

Width:  |  Height:  |  Size: 719 B

View file

Before

Width:  |  Height:  |  Size: 744 B

After

Width:  |  Height:  |  Size: 744 B

View file

Before

Width:  |  Height:  |  Size: 86 B

After

Width:  |  Height:  |  Size: 86 B

View file

Before

Width:  |  Height:  |  Size: 375 B

After

Width:  |  Height:  |  Size: 375 B

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 174 B

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 265 B

After

Width:  |  Height:  |  Size: 265 B

View file

Before

Width:  |  Height:  |  Size: 160 B

After

Width:  |  Height:  |  Size: 160 B

View file

Before

Width:  |  Height:  |  Size: 755 B

After

Width:  |  Height:  |  Size: 755 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 753 B

After

Width:  |  Height:  |  Size: 753 B

View file

Before

Width:  |  Height:  |  Size: 324 B

After

Width:  |  Height:  |  Size: 324 B

View file

Before

Width:  |  Height:  |  Size: 925 B

After

Width:  |  Height:  |  Size: 925 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 127 B

After

Width:  |  Height:  |  Size: 127 B

View file

Before

Width:  |  Height:  |  Size: 799 B

After

Width:  |  Height:  |  Size: 799 B

View file

Before

Width:  |  Height:  |  Size: 220 B

After

Width:  |  Height:  |  Size: 220 B

View file

Before

Width:  |  Height:  |  Size: 146 B

After

Width:  |  Height:  |  Size: 146 B

View file

@ -1,8 +1,13 @@
import logging
import os
from enum import Enum
from PIL import Image, ImageTk
from core.api.grpc import core_pb2
PATH = os.path.abspath(os.path.dirname(__file__))
ICONS_DIR = os.path.join(PATH, "icons")
class Images:
@ -10,7 +15,7 @@ class Images:
@classmethod
def load(cls, name, file_path):
file_path = os.path.join(PATH, file_path)
# file_path = os.path.join(PATH, file_path)
image = Image.open(file_path)
tk_image = ImageTk.PhotoImage(image)
cls.images[name] = tk_image
@ -18,3 +23,78 @@ class Images:
@classmethod
def get(cls, name):
return cls.images[name]
@classmethod
def convert_type_and_model_to_image(cls, node_type, node_model):
"""
Retrieve image based on type and model
:param core_pb2.NodeType node_type: core node type
:param string node_model: the node model
:rtype: tuple(PhotoImage, str)
:return: the matching image and its name
"""
if node_type == core_pb2.NodeType.SWITCH:
return Images.get(ImageEnum.SWITCH.value), "switch"
if node_type == core_pb2.NodeType.HUB:
return Images.get(ImageEnum.HUB.value), "hub"
if node_type == core_pb2.NodeType.WIRELESS_LAN:
return Images.get(ImageEnum.WLAN.value), "wlan"
if node_type == core_pb2.NodeType.RJ45:
return Images.get(ImageEnum.RJ45.value), "rj45"
if node_type == core_pb2.NodeType.TUNNEL:
return Images.get(ImageEnum.TUNNEL.value), "tunnel"
if node_type == core_pb2.NodeType.DEFAULT:
if node_model == "router":
return Images.get(ImageEnum.ROUTER.value), "router"
if node_model == "host":
return Images.get((ImageEnum.HOST.value)), "host"
if node_model == "PC":
return Images.get(ImageEnum.PC.value), "PC"
if node_model == "mdr":
return Images.get(ImageEnum.MDR.value), "mdr"
if node_model == "prouter":
return Images.get(ImageEnum.PROUTER.value), "prouter"
if node_model == "OVS":
return Images.get(ImageEnum.OVS.value), "ovs"
else:
logging.debug("INVALID INPUT OR NOT CONSIDERED YET")
class ImageEnum(Enum):
SWITCH = "lanswitch"
CORE = "core-icon"
START = "start"
MARKER = "marker"
ROUTER = "router"
SELECT = "select"
LINK = "link"
HUB = "hub"
WLAN = "wlan"
RJ45 = "rj45"
TUNNEL = "tunnel"
OVAL = "oval"
RECTANGLE = "rectangle"
TEXT = "text"
HOST = "host"
PC = "pc"
MDR = "mdr"
PROUTER = "router_green"
OVS = "OVS"
EDITNODE = "document-properties"
PLOT = "plot"
TWONODE = "twonode"
STOP = "stop"
OBSERVE = "observe"
RUN = "run"
DOCUMENTNEW = "document-new"
FILEOPEN = "fileopen"
EDITDELETE = "edit-delete"
ANTENNA = "antenna"
def load_core_images(images):
for file_name in os.listdir(ICONS_DIR):
file_path = os.path.join(ICONS_DIR, file_name)
name = file_name.split(".")[0]
images.load(name, file_path)

View file

@ -0,0 +1,95 @@
import ipaddress
import random
class Interface:
def __init__(self, name, ipv4, ifid=None):
"""
Create an interface instance
:param str name: interface name
:param str ip4: IPv4
:param str mac: MAC address
:param int ifid: interface id
"""
self.name = name
self.ipv4 = ipv4
self.ip4prefix = 24
self.ip4_and_prefix = ipv4 + "/" + str(self.ip4prefix)
self.mac = self.random_mac_address()
self.id = ifid
def random_mac_address(self):
"""
create a random MAC address for an interface
:return: nothing
"""
return "02:00:00:%02x:%02x:%02x" % (
random.randint(0, 255),
random.randint(0, 255),
random.randint(0, 225),
)
class SubnetAddresses:
def __init__(self, network, addresses):
self.network = network
self.address = addresses
self.address_index = 0
def get_new_ip_address(self):
ipaddr = self.address[self.address_index]
self.address_index = self.address_index + 1
return ipaddr
class InterfaceManager:
def __init__(self):
# self.prefix = None
self.core_subnets = list(
ipaddress.ip_network("10.0.0.0/12").subnets(prefixlen_diff=12)
)
self.subnet_index = 0
self.address_index = None
# self.network = ipaddress.ip_network("10.0.0.0/24")
# self.addresses = list(self.network.hosts())
self.network = None
self.addresses = None
# self.start_interface_manager()
def start_interface_manager(self):
self.subnet_index = 0
self.network = self.core_subnets[self.subnet_index]
self.subnet_index = self.subnet_index + 1
self.addresses = list(self.network.hosts())
self.address_index = 0
def get_address(self):
"""
Retrieve a new ipv4 address
:return:
"""
# i = self.index
# self.address_index = self.index + 1
# return self.addresses[i]
ipaddr = self.addresses[self.address_index]
self.address_index = self.address_index + 1
return ipaddr
def new_subnet(self):
self.network = self.core_subnets[self.subnet_index]
self.subnet_index = self.subnet_index + 1
self.addresses = list(self.network.hosts())
self.address_index = 0
# def new_subnet(self):
# """
# retrieve a new subnet
# :return:
# """
# if self.prefix is None:
# self.prefix =
# self.addresses = list(ipaddress.ip_network("10.0.0.0/24").hosts())

272
coretk/coretk/linkinfo.py Normal file
View file

@ -0,0 +1,272 @@
"""
Link information, such as IPv4, IPv6 and throughput drawn in the canvas
"""
import logging
import math
WIRELESS_DEF = ["mdr", "wlan"]
class LinkInfo:
def __init__(self, canvas, edge, ip4_src, ip6_src, ip4_dst, ip6_dst):
"""
create an instance of LinkInfo object
:param tkinter.Canvas canvas: canvas object
:param coretk.graph.CanvasEdge edge: canvas edge onject
:param ip4_src:
:param ip6_src:
:param ip4_dst:
:param ip6_dst:
"""
self.canvas = canvas
self.edge = edge
# self.edge_id = edge.id
self.radius = 37
self.core_grpc = self.canvas.core_grpc
self.ip4_address_1 = ip4_src
self.ip6_address_1 = ip6_src
self.ip4_address_2 = ip4_dst
self.ip6_address_2 = ip6_dst
self.id1 = self.create_edge_src_info()
self.id2 = self.create_edge_dst_info()
def slope_src_dst(self):
"""
calculate slope of the line connecting source node to destination node
:rtype: float
:return: slope of line
"""
x1, y1, x2, y2 = self.canvas.coords(self.edge.id)
if x2 - x1 == 0:
return 9999.0
else:
return (y2 - y1) / (x2 - x1)
def create_edge_src_info(self):
"""
draw the ip address for source node
:return: nothing
"""
x1, y1, x2, _ = self.canvas.coords(self.edge.id)
m = self.slope_src_dst()
distance = math.cos(math.atan(m)) * self.radius
if x1 > x2:
distance = -distance
# id1 = self.canvas.create_text(x1, y1, text=self.ip4_address_1)
id1 = self.canvas.create_text(
x1 + distance, y1 + distance * m, text=self.ip4_address_1, tags="linkinfo"
)
return id1
def create_edge_dst_info(self):
"""
draw the ip address for destination node
:return: nothing
"""
x1, _, x2, y2 = self.canvas.coords(self.edge.id)
m = self.slope_src_dst()
distance = math.cos(math.atan(m)) * self.radius
if x1 > x2:
distance = -distance
# id2 = self.canvas.create_text(x2, y2, text=self.ip4_address_2)
id2 = self.canvas.create_text(
x2 - distance, y2 - distance * m, text=self.ip4_address_2, tags="linkinfo"
)
return id2
def recalculate_info(self):
"""
move the node info when the canvas node move
:return: nothing
"""
x1, y1, x2, y2 = self.canvas.coords(self.edge.id)
m = self.slope_src_dst()
distance = math.cos(math.atan(m)) * self.radius
if x1 > x2:
distance = -distance
new_x1 = x1 + distance
new_y1 = y1 + distance * m
new_x2 = x2 - distance
new_y2 = y2 - distance * m
self.canvas.coords(self.id1, new_x1, new_y1)
self.canvas.coords(self.id2, new_x2, new_y2)
# def link_througput(self):
# x1, y1, x2, y2 = self.canvas.coords(self.edge.id)
# x = (x1 + x2) / 2
# y = (y1 + y2) / 2
# tid = self.canvas.create_text(x, y, text="place text here")
# return tid
class Throughput:
def __init__(self, canvas, grpc):
"""
create an instance of Throughput object
:param tkinter.Canvas canvas: canvas object
:param coretk.coregrpc,CoreGrpc grpc: grpc object
"""
self.canvas = canvas
self.core_grpc = grpc
self.grpc_manager = canvas.grpc_manager
# edge canvas id mapped to throughput value
self.tracker = {}
# map an edge canvas id to a throughput canvas id
self.map = {}
self.edge_id_to_token = {}
def load_throughput_info(self, interface_throughputs):
"""
load all interface throughouts from an event
:param repeated core_bp2.InterfaceThroughputinterface_throughputs: interface throughputs
:return: nothing
"""
for t in interface_throughputs:
nid = t.node_id
iid = t.interface_id
tp = t.throughput
# token = self.grpc_manager.node_id_and_interface_to_edge_token[nid, iid]
token = self.grpc_manager.core_mapping.get_token_from_node_and_interface(
nid, iid
)
print(token)
edge_id = self.canvas.edges[token].id
self.edge_id_to_token[edge_id] = token
if edge_id not in self.tracker:
self.tracker[edge_id] = tp
else:
temp = self.tracker[edge_id]
self.tracker[edge_id] = (temp + tp) / 2
def edge_is_wired(self, token):
"""
determine whether link is a WIRED link
:param token:
:return:
"""
canvas_edge = self.canvas.edges[token]
canvas_src_id = canvas_edge.src
canvas_dst_id = canvas_edge.dst
src_node = self.canvas.nodes[canvas_src_id]
dst_node = self.canvas.nodes[canvas_dst_id]
if src_node.node_type == "wlan":
if dst_node.node_type == "mdr":
return False
else:
logging.debug("linkinfo.py is_wired WARNING wlan only connected to mdr")
return True
if dst_node.node_type == "wlan":
if src_node.node_type == "mdr":
return False
else:
logging.debug("linkinfo.py is_wired WARNING wlan only connected to mdr")
return True
return True
def draw_wired_throughput(self, edge_id):
x1, y1, x2, y2 = self.canvas.coords(edge_id)
x = (x1 + x2) / 2
y = (y1 + y2) / 2
if edge_id not in self.map:
tp_id = self.canvas.create_text(
x, y, text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id])
)
self.map[edge_id] = tp_id
# redraw throughput
else:
self.canvas.itemconfig(
self.map[edge_id],
text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]),
)
def draw_wireless_throughput(self, edge_id):
token = self.edge_id_to_token[edge_id]
canvas_edge = self.canvas.edges[token]
canvas_src_id = canvas_edge.src
canvas_dst_id = canvas_edge.dst
src_node = self.canvas.nodes[canvas_src_id]
dst_node = self.canvas.nodes[canvas_dst_id]
# non_wlan_node = None
if src_node.node_type == "wlan":
non_wlan_node = dst_node
else:
non_wlan_node = src_node
x, y = self.canvas.coords(non_wlan_node.id)
if edge_id not in self.map:
tp_id = self.canvas.create_text(
x + 50,
y + 25,
text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]),
)
self.map[edge_id] = tp_id
# redraw throughput
else:
self.canvas.itemconfig(
self.map[edge_id],
text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]),
)
def draw_throughputs(self):
for edge_id in self.tracker:
if self.edge_is_wired(self.edge_id_to_token[edge_id]):
self.draw_wired_throughput(edge_id)
else:
self.draw_wireless_throughput(edge_id)
# draw wireless throughput
# x1, y1, x2, y2 = self.canvas.coords(edge_id)
# x = (x1 + x2) / 2
# y = (y1 + y2) / 2
#
# print(self.is_wired(self.edge_id_to_token[edge_id]))
# # new throughput
# if edge_id not in self.map:
# tp_id = self.canvas.create_text(
# x, y, text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id])
# )
# self.map[edge_id] = tp_id
#
# # redraw throughput
# else:
# self.canvas.itemconfig(
# self.map[edge_id],
# text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]),
# )
def process_grpc_throughput_event(self, interface_throughputs):
self.load_throughput_info(interface_throughputs)
self.draw_throughputs()
def update_throughtput_location(self, edge):
tp_id = self.map[edge.id]
if self.edge_is_wired(self.edge_id_to_token[edge.id]):
x1, y1 = self.canvas.coords(edge.src)
x2, y2 = self.canvas.coords(edge.dst)
x = (x1 + x2) / 2
y = (y1 + y2) / 2
self.canvas.coords(tp_id, x, y)
else:
if self.canvas.nodes[edge.src].node_type == "wlan":
x, y = self.canvas.coords(edge.dst)
self.canvas.coords(tp_id, x + 50, y + 20)
else:
x, y = self.canvas.coords(edge.src)
self.canvas.coords(tp_id, x + 50, y + 25)

View file

@ -4,6 +4,13 @@ The actions taken when each menubar option is clicked
import logging
import webbrowser
from tkinter import filedialog, messagebox
from core.api.grpc import core_pb2
from coretk.setwallpaper import CanvasWallpaper
from coretk.sizeandscale import SizeAndScale
SAVEDIR = "/home/ncs/Desktop/"
def sub_menu_items():
@ -30,22 +37,14 @@ def file_reload():
logging.debug("Click file Reload")
def file_save():
logging.debug("Click file save")
# def file_save():
# logging.debug("Click file save")
def file_save_shortcut(event):
logging.debug("Shortcut for file save")
def file_save_as_xml():
logging.debug("Click save as XML")
def file_save_as_imn():
logging.debug("Click save as imn")
def file_export_python_script():
logging.debug("Click file export python script")
@ -70,10 +69,6 @@ def file_save_screenshot():
logging.debug("Click file save screenshot")
def file_example_link():
logging.debug("Click file example link")
def edit_undo():
logging.debug("Click edit undo")
@ -160,6 +155,7 @@ def canvas_delete():
def canvas_size_scale():
logging.debug("Click canvas size/scale")
SizeAndScale()
def canvas_wallpaper():
@ -294,10 +290,6 @@ def widgets_configure_throughput():
logging.debug("Click widgets configure throughput")
def session_start():
logging.debug("Click session start")
def session_change_sessions():
logging.debug("Click session change sessions")
@ -326,13 +318,118 @@ def session_options():
logging.debug("Click session options")
def help_core_github():
webbrowser.open_new("https://github.com/coreemu/core")
def help_core_documentation():
webbrowser.open_new("http://coreemu.github.io/core/")
def help_about():
logging.debug("Click help About")
class MenuAction:
"""
Actions performed when choosing menu items
"""
def __init__(self, application, master):
self.master = master
self.application = application
self.core_grpc = application.core_grpc
def clean_nodes_links_and_set_configuarations(self):
"""
Prompt use to stop running session before application is closed
:return: nothing
"""
logging.info(
"menuaction.py: clean_nodes_links_and_set_configuration() Exiting the program"
)
grpc = self.application.core_grpc
state = grpc.get_session_state()
if (
state == core_pb2.SessionState.SHUTDOWN
or state == core_pb2.SessionState.DEFINITION
):
grpc.delete_session()
grpc.core.close()
# self.application.quit()
else:
msgbox = messagebox.askyesnocancel("stop", "Stop the running session?")
if msgbox or msgbox is False:
if msgbox:
grpc.set_session_state("datacollect")
grpc.delete_links()
grpc.delete_nodes()
grpc.delete_session()
# else:
# grpc.set_session_state("definition")
grpc.core.close()
# self.application.quit()
def on_quit(self):
"""
Prompt user whether so save running session, and then close the application
:return: nothing
"""
self.clean_nodes_links_and_set_configuarations()
# self.application.core_grpc.close()
self.application.quit()
def file_save_as_xml(self):
logging.info("menuaction.py file_save_as_xml()")
grpc = self.application.core_grpc
file_path = filedialog.asksaveasfilename(
initialdir=SAVEDIR,
title="Save As",
filetypes=(("EmulationScript XML files", "*.xml"), ("All files", "*")),
defaultextension=".xml",
)
# with open("prev_saved_xml.txt", "a") as file:
# file.write(file_path + "\n")
grpc.save_xml(file_path)
def file_open_xml(self):
logging.info("menuaction.py file_open_xml()")
self.application.is_open_xml = True
file_path = filedialog.askopenfilename(
initialdir=SAVEDIR,
title="Open",
filetypes=(("EmulationScript XML File", "*.xml"), ("All Files", "*")),
)
# clean up before opening a new session
self.clean_nodes_links_and_set_configuarations()
# grpc = CoreGrpc(self.application.master)
# grpc.core.connect()
core_grpc = self.application.core_grpc
core_grpc.core.connect()
# session_id = core_grpc.open_xml(file_path)
# core_grpc.session_id = session_id
core_grpc.open_xml(file_path)
# print("Print session state")
# print(grpc.get_session_state())
self.application.canvas.canvas_reset_and_redraw(core_grpc)
# Todo might not need
self.application.core_grpc = core_grpc
self.application.core_editbar.destroy_children_widgets()
self.application.core_editbar.create_toolbar()
# self.application.is_open_xml = False
# self.application.core_editbar.create_runtime_toolbar()
# self.application.canvas.draw_existing_component()
# t1 = time.clock()
# print(t1 - t0)
def canvas_size_and_scale(self):
self.application.size_and_scale = SizeAndScale(self.application)
def canvas_set_wallpaper(self):
self.application.set_wallpaper = CanvasWallpaper(self.application)
def help_core_github(self):
webbrowser.open_new("https://github.com/coreemu/core")
def help_core_documentation(self):
webbrowser.open_new("http://coreemu.github.io/core/")

View file

@ -0,0 +1,150 @@
"""
Create toplevel for node configuration
"""
import logging
import os
import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk
PATH = os.path.abspath(os.path.dirname(__file__))
ICONS_DIR = os.path.join(PATH, "icons")
NETWORKNODETYPES = ["switch", "hub", "wlan", "rj45", "tunnel"]
DEFAULTNODES = ["router", "host", "PC"]
class NodeConfig:
def __init__(self, canvas_node, image, node_type, name):
self.image = image
self.node_type = node_type
self.name = name
self.canvas_node = canvas_node
self.top = tk.Toplevel()
self.top.title(node_type + " configuration")
self.namevar = tk.StringVar(self.top, value="default name")
self.name_and_image_definition()
self.type_and_service_definition()
self.select_definition()
def open_icon_dir(self, toplevel, entry_text):
filename = filedialog.askopenfilename(
initialdir=ICONS_DIR,
title="Open",
filetypes=(
("images", "*.gif *.jpg *.png *.bmp *pcx *.tga ..."),
("All Files", "*"),
),
)
if len(filename) > 0:
img = Image.open(filename)
tk_img = ImageTk.PhotoImage(img)
lb = toplevel.grid_slaves(1, 0)[0]
lb.configure(image=tk_img)
lb.image = tk_img
entry_text.set(filename)
def click_apply(self, toplevel, entry_text):
imgfile = entry_text.get()
if imgfile:
img = Image.open(imgfile)
tk_img = ImageTk.PhotoImage(img)
lb = self.top.grid_slaves(row=0, column=3)[0]
lb.configure(image=tk_img)
lb.image = tk_img
self.image = tk_img
toplevel.destroy()
def img_modification(self):
print("image modification")
t = tk.Toplevel()
t.title(self.name + " image")
f = tk.Frame(t)
entry_text = tk.StringVar()
image_file_label = tk.Label(f, text="Image file: ")
image_file_label.pack(side=tk.LEFT, padx=2, pady=2)
image_file_entry = tk.Entry(f, textvariable=entry_text, width=60)
image_file_entry.pack(side=tk.LEFT, padx=2, pady=2)
image_file_button = tk.Button(
f, text="...", command=lambda: self.open_icon_dir(t, entry_text)
)
image_file_button.pack(side=tk.LEFT, padx=2, pady=2)
f.grid(sticky=tk.W + tk.E)
img = tk.Label(t, image=self.image)
img.grid(sticky=tk.W + tk.E)
f = tk.Frame(t)
apply_button = tk.Button(
f, text="Apply", command=lambda: self.click_apply(t, entry_text)
)
apply_button.pack(side=tk.LEFT, padx=2, pady=2)
apply_to_multiple_button = tk.Button(f, text="Apply to multiple...")
apply_to_multiple_button.pack(side=tk.LEFT, padx=2, pady=2)
cancel_button = tk.Button(f, text="Cancel", command=t.destroy)
cancel_button.pack(side=tk.LEFT, padx=2, pady=2)
f.grid(sticky=tk.E + tk.W)
def name_and_image_definition(self):
name_label = tk.Label(self.top, text="Node name: ")
name_label.grid()
name_entry = tk.Entry(self.top, textvariable=self.namevar)
name_entry.grid(row=0, column=1)
core_button = tk.Button(self.top, text="None")
core_button.grid(row=0, column=2)
img_button = tk.Button(
self.top,
image=self.image,
width=40,
height=40,
command=self.img_modification,
)
img_button.grid(row=0, column=3)
def type_and_service_definition(self):
f = tk.Frame(self.top)
type_label = tk.Label(f, text="Type: ")
type_label.pack(side=tk.LEFT)
type_button = tk.Button(f, text="None")
type_button.pack(side=tk.LEFT)
service_button = tk.Button(f, text="Services...")
service_button.pack(side=tk.LEFT)
f.grid(row=1, column=1, columnspan=2, sticky=tk.W)
def config_apply(self):
"""
modify image of the canvas node
:return: nothing
"""
logging.debug("nodeconfigtable.py configuration apply")
self.canvas_node.image = self.image
self.canvas_node.canvas.itemconfig(self.canvas_node.id, image=self.image)
self.top.destroy()
def config_cancel(self):
"""
save chosen image but not modify canvas node
:return: nothing
"""
logging.debug("nodeconfigtable.py configuration cancel")
self.canvas_node.image = self.image
self.top.destroy()
def select_definition(self):
f = tk.Frame(self.top)
apply_button = tk.Button(f, text="Apply", command=self.config_apply)
apply_button.pack(side=tk.LEFT)
cancel_button = tk.Button(f, text="Cancel", command=self.config_cancel)
cancel_button.pack(side=tk.LEFT)
f.grid(row=3, column=1, sticky=tk.W)
def network_node_config(self):
self.name_and_image_definition()
self.select_definition()

BIN
coretk/coretk/oldimage/OVS.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

View file

@ -0,0 +1,183 @@
import logging
import tkinter as tk
from tkinter.ttk import Scrollbar, Treeview
from coretk.images import ImageEnum, Images
class SessionTable:
def __init__(self, grpc, master):
"""
create session table instance
:param coretk.coregrpc.CoreGrpc grpc: coregrpc
:param root.master master:
"""
self.grpc = grpc
self.selected = False
self.selected_sid = None
self.master = master
self.top = tk.Toplevel(self.master)
self.description_definition()
self.top.title("CORE sessions")
self.tree = Treeview(self.top)
# self.tree.pack(side=tk.TOP)
self.tree.grid(row=1, column=0, columnspan=2)
self.draw_scrollbar()
self.draw()
def description_definition(self):
"""
write a short description
:return: nothing
"""
lable = tk.Label(
self.top,
text="Below is a list of active CORE sessions. Double-click to "
"\nconnect to an existing session. Usually, only sessions in "
"\nthe RUNTIME state persist in the daemon, except for the "
"\none you might be concurrently editting.",
)
lable.grid(sticky=tk.W)
def column_definition(self):
# self.tree["columns"] = ("name", "nodecount", "filename", "date")
self.tree["columns"] = "nodecount"
self.tree.column("#0", width=300, minwidth=30)
# self.tree.column("name", width=72, miwidth=30)
self.tree.column("nodecount", width=300, minwidth=30)
# self.tree.column("filename", width=92, minwidth=30)
# self.tree.column("date", width=170, minwidth=30)
def draw_scrollbar(self):
yscrollbar = Scrollbar(self.top, orient="vertical", command=self.tree.yview)
yscrollbar.grid(row=1, column=3, sticky=tk.N + tk.S + tk.W)
self.tree.configure(yscrollcommand=yscrollbar.set)
xscrollbar = Scrollbar(self.top, orient="horizontal", command=self.tree.xview)
xscrollbar.grid(row=2, columnspan=2, sticky=tk.E + tk.W + tk.S)
self.tree.configure(xscrollcommand=xscrollbar.set)
def heading_definition(self):
self.tree.heading("#0", text="ID", anchor=tk.W)
# self.tree.heading("name", text="Name", anchor=tk.CENTER)
self.tree.heading("nodecount", text="Node Count", anchor=tk.W)
# self.tree.heading("filename", text="Filename", anchor=tk.CENTER)
# self.tree.heading("date", text="Date", anchor=tk.CENTER)
def enter_session(self, sid):
self.top.destroy()
response = self.grpc.core.get_session(sid)
self.grpc.session_id = sid
self.grpc.core.events(sid, self.grpc.log_event)
logging.info("Entering session_id %s.... Result: %s", sid, response)
def new_session(self):
self.top.destroy()
self.grpc.create_new_session()
def on_selected(self, event):
item = self.tree.selection()
sid = int(self.tree.item(item, "text"))
self.enter_session(sid)
def click_select(self, event):
# logging.debug("Click on %s ", event)
item = self.tree.selection()
sid = int(self.tree.item(item, "text"))
self.selected = True
self.selected_sid = sid
def session_definition(self):
response = self.grpc.core.get_sessions()
# logging.info("querysessiondrawing.py Get all sessions %s", response)
index = 1
for session in response.sessions:
self.tree.insert(
"", index, None, text=str(session.id), values=(str(session.nodes))
)
index = index + 1
self.tree.bind("<Double-1>", self.on_selected)
self.tree.bind("<<TreeviewSelect>>", self.click_select)
def click_connect(self):
"""
if no session is selected yet, create a new one else join that session
:return: nothing
"""
if self.selected and self.selected_sid is not None:
self.enter_session(self.selected_sid)
elif not self.selected and self.selected_sid is None:
self.new_session()
else:
logging.error("querysessiondrawing.py invalid state")
def shutdown_session(self, sid):
self.grpc.terminate_session(sid)
self.new_session()
self.top.destroy()
def click_shutdown(self):
"""
if no session is currently selected create a new session else shut the selected session down
:return: nothing
"""
if self.selected and self.selected_sid is not None:
self.shutdown_session(self.selected_sid)
elif not self.selected and self.selected_sid is None:
self.new_session()
else:
logging.error("querysessiondrawing.py invalid state")
# if self.selected and self.selected_sid is not None:
def draw_buttons(self):
f = tk.Frame(self.top)
f.grid(row=3, sticky=tk.W)
b = tk.Button(
f,
image=Images.get(ImageEnum.DOCUMENTNEW.value),
text="New",
compound=tk.LEFT,
command=self.new_session,
)
b.pack(side=tk.LEFT, padx=3, pady=4)
b = tk.Button(
f,
image=Images.get(ImageEnum.FILEOPEN.value),
text="Connect",
compound=tk.LEFT,
command=self.click_connect,
)
b.pack(side=tk.LEFT, padx=3, pady=4)
b = tk.Button(
f,
image=Images.get(ImageEnum.EDITDELETE.value),
text="Shutdown",
compound=tk.LEFT,
command=self.click_shutdown,
)
b.pack(side=tk.LEFT, padx=3, pady=4)
b = tk.Button(f, text="Cancel", command=self.new_session)
b.pack(side=tk.LEFT, padx=3, pady=4)
def center(self):
window_width = self.master.winfo_width()
window_height = self.master.winfo_height()
self.top.update()
top_level_width = self.top.winfo_width()
top_level_height = self.top.winfo_height()
x = window_width / 2 - top_level_width / 2
y = window_height / 2 - top_level_height / 2
self.top.geometry("+%d+%d" % (x, y))
def draw(self):
self.column_definition()
self.heading_definition()
self.session_definition()
self.draw_buttons()
self.center()
self.top.wait_window()

View file

@ -0,0 +1,377 @@
"""
set wallpaper
"""
import enum
import logging
import os
import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk
PATH = os.path.abspath(os.path.dirname(__file__))
WALLPAPER_DIR = os.path.join(PATH, "wallpaper")
class ScaleOption(enum.Enum):
NONE = 0
UPPER_LEFT = 1
CENTERED = 2
SCALED = 3
TILED = 4
class CanvasWallpaper:
def __init__(self, application):
"""
create an instance of CanvasWallpaper object
:param coretk.app.Application application: root application
"""
self.application = application
self.canvas = self.application.canvas
self.top = tk.Toplevel()
self.top.title("Set Canvas Wallpaper")
self.radiovar = tk.IntVar()
print(self.application.radiovar.get())
self.radiovar.set(self.application.radiovar.get())
self.show_grid_var = tk.IntVar()
self.show_grid_var.set(self.application.show_grid_var.get())
self.adjust_to_dim_var = tk.IntVar()
self.adjust_to_dim_var.set(self.application.adjust_to_dim_var.get())
self.create_image_label()
self.create_text_label()
self.open_image()
self.display_options()
self.additional_options()
self.apply_cancel()
def create_image_label(self):
image_label = tk.Label(
self.top, text="(image preview)", height=8, width=32, bg="white"
)
image_label.grid(pady=5)
def create_text_label(self):
text_label = tk.Label(self.top, text="Image filename: ")
text_label.grid()
def open_image_link(self):
filename = filedialog.askopenfilename(
initialdir=WALLPAPER_DIR,
title="Open",
filetypes=(
("images", "*.gif *.jpg *.png *.bmp *pcx *.tga ..."),
("All Files", "*"),
),
)
# fill the file name into the file name entry
img_open_frame = self.top.grid_slaves(2, 0)[0]
filename_entry = img_open_frame.grid_slaves(0, 0)[0]
filename_entry.delete(0, tk.END)
filename_entry.insert(tk.END, filename)
# display that onto the label
img_label = self.top.grid_slaves(0, 0)[0]
if filename:
img = Image.open(filename)
img = img.resize((250, 135), Image.ANTIALIAS)
tk_img = ImageTk.PhotoImage(img)
img_label.config(image=tk_img, width=250, height=135)
img_label.image = tk_img
def clear_link(self):
"""
delete like shown in image link entry if there is any
:return: nothing
"""
# delete entry
img_open_frame = self.top.grid_slaves(2, 0)[0]
filename_entry = img_open_frame.grid_slaves(0, 0)[0]
filename_entry.delete(0, tk.END)
# delete display image
img_label = self.top.grid_slaves(0, 0)[0]
img_label.config(image="", width=32, height=8)
def open_image(self):
f = tk.Frame(self.top)
var = tk.StringVar(f, value="")
e = tk.Entry(f, textvariable=var)
e.focus()
e.grid()
b = tk.Button(f, text="...", command=self.open_image_link)
b.grid(row=0, column=1)
b = tk.Button(f, text="Clear", command=self.clear_link)
b.grid(row=0, column=2)
f.grid()
def display_options(self):
f = tk.Frame(self.top)
b1 = tk.Radiobutton(f, text="upper-left", value=1, variable=self.radiovar)
b1.grid(row=0, column=0)
b2 = tk.Radiobutton(f, text="centered", value=2, variable=self.radiovar)
b2.grid(row=0, column=1)
b3 = tk.Radiobutton(f, text="scaled", value=3, variable=self.radiovar)
b3.grid(row=0, column=2)
b4 = tk.Radiobutton(f, text="titled", value=4, variable=self.radiovar)
b4.grid(row=0, column=3)
# self.radiovar.set(1)
f.grid()
def adjust_canvas_size(self):
# deselect all radio buttons and grey them out
if self.adjust_to_dim_var.get() == 1:
self.radiovar.set(0)
option_frame = self.top.grid_slaves(3, 0)[0]
for i in option_frame.grid_slaves():
i.config(state=tk.DISABLED)
# turn back the radio button to active state so that user can choose again
elif self.adjust_to_dim_var.get() == 0:
option_frame = self.top.grid_slaves(3, 0)[0]
for i in option_frame.grid_slaves():
i.config(state=tk.NORMAL)
self.radiovar.set(1)
else:
logging.error("setwallpaper.py adjust_canvas_size invalid value")
def additional_options(self):
b = tk.Checkbutton(self.top, text="Show grid", variable=self.show_grid_var)
b.grid(sticky=tk.W, padx=5)
b = tk.Checkbutton(
self.top,
text="Adjust canvas size to image dimensions",
variable=self.adjust_to_dim_var,
command=self.adjust_canvas_size,
)
b.grid(sticky=tk.W, padx=5)
self.show_grid_var.set(1)
self.adjust_to_dim_var.set(0)
def delete_canvas_components(self, tag_list):
"""
delete canvas items whose tag is in the tag list
:param list[string] tag_list: list of tags
:return: nothing
"""
for tag in tag_list:
for i in self.canvas.find_withtag(tag):
self.canvas.delete(i)
def get_canvas_width_and_height(self):
"""
retrieve canvas width and height in pixels
:return: nothing
"""
canvas = self.application.canvas
grid = canvas.find_withtag("rectangle")[0]
x0, y0, x1, y1 = canvas.coords(grid)
canvas_w = abs(x0 - x1)
canvas_h = abs(y0 - y1)
return canvas_w, canvas_h
def determine_cropped_image_dimension(self):
"""
determine the dimension of the image after being cropped
:return: nothing
"""
return
def upper_left(self, img):
print("upperleft")
tk_img = ImageTk.PhotoImage(img)
# crop image if it is bigger than canvas
canvas_w, canvas_h = self.get_canvas_width_and_height()
cropx = img_w = tk_img.width()
cropy = img_h = tk_img.height()
if img_w > canvas_w:
cropx -= img_w - canvas_w
if img_h > canvas_h:
cropy -= img_h - canvas_h
cropped = img.crop((0, 0, cropx, cropy))
cropped_tk = ImageTk.PhotoImage(cropped)
# place left corner of image to the left corner of the canvas
self.application.croppedwallpaper = cropped_tk
self.delete_canvas_components(["wallpaper"])
# self.delete_previous_wallpaper()
wid = self.canvas.create_image(
(cropx / 2, cropy / 2), image=cropped_tk, tags="wallpaper"
)
self.application.wallpaper_id = wid
def center(self, img):
"""
place the image at the center of canvas
:param Image img: image object
:return: nothing
"""
tk_img = ImageTk.PhotoImage(img)
canvas_w, canvas_h = self.get_canvas_width_and_height()
cropx = img_w = tk_img.width()
cropy = img_h = tk_img.height()
# dimension of the cropped image
if img_w > canvas_w:
cropx -= img_w - canvas_w
if img_h > canvas_h:
cropy -= img_h - canvas_h
x0 = (img_w - cropx) / 2
y0 = (img_h - cropy) / 2
x1 = x0 + cropx
y1 = y0 + cropy
cropped = img.crop((x0, y0, x1, y1))
cropped_tk = ImageTk.PhotoImage(cropped)
# place the center of the image at the center of the canvas
self.application.croppedwallpaper = cropped_tk
self.delete_canvas_components(["wallpaper"])
# self.delete_previous_wallpaper()
wid = self.canvas.create_image(
(canvas_w / 2, canvas_h / 2), image=cropped_tk, tags="wallpaper"
)
self.application.wallpaper_id = wid
def scaled(self, img):
"""
scale image based on canvas dimension
:param Image img: image object
:return: nothing
"""
canvas_w, canvas_h = self.get_canvas_width_and_height()
resized_image = img.resize((int(canvas_w), int(canvas_h)), Image.ANTIALIAS)
image_tk = ImageTk.PhotoImage(resized_image)
self.application.croppedwallpaper = image_tk
self.delete_canvas_components(["wallpaper"])
# self.delete_previous_wallpaper()
wid = self.canvas.create_image(
(canvas_w / 2, canvas_h / 2), image=image_tk, tags="wallpaper"
)
self.application.wallpaper_id = wid
def tiled(self, img):
return
def draw_new_canvas(self, canvas_width, canvas_height):
"""
delete the old canvas and draw a new one
:param int canvas_width: canvas width in pixel
:param int canvas_height: canvas height in pixel
:return:
"""
self.delete_canvas_components(["rectangle", "gridline"])
self.canvas.draw_grid(canvas_width, canvas_height)
def canvas_to_image_dimension(self, img):
image_tk = ImageTk.PhotoImage(img)
img_w = image_tk.width()
img_h = image_tk.height()
self.delete_canvas_components(["wallpaper"])
self.draw_new_canvas(img_w, img_h)
wid = self.canvas.create_image((img_w / 2, img_h / 2), image=image_tk)
self.application.croppedwallpaper = image_tk
self.application.wallpaper_id = wid
def show_grid(self):
"""
:return: nothing
"""
self.application.adjust_to_dim_var.set(self.adjust_to_dim_var.get())
if self.show_grid_var.get() == 0:
for i in self.canvas.find_withtag("gridline"):
self.canvas.itemconfig(i, state=tk.HIDDEN)
elif self.show_grid_var.get() == 1:
for i in self.canvas.find_withtag("gridline"):
self.canvas.itemconfig(i, state=tk.NORMAL)
self.canvas.lift(i)
else:
logging.error("setwallpaper.py show_grid invalid value")
def save_wallpaper_options(self):
self.application.radiovar.set(self.radiovar.get())
self.application.show_grid_var.set(self.show_grid_var.get())
self.application.adjust_to_dim_var.set(self.adjust_to_dim_var.get())
def click_apply(self):
img_link_frame = self.top.grid_slaves(2, 0)[0]
filename = img_link_frame.grid_slaves(0, 0)[0].get()
if not filename:
self.delete_canvas_components(["wallpaper"])
self.top.destroy()
self.application.current_wallpaper = None
self.save_wallpaper_options()
return
try:
img = Image.open(filename)
self.application.current_wallpaper = img
except FileNotFoundError:
print("invalid filename, draw original white plot")
if self.application.wallpaper_id:
self.canvas.delete(self.application.wallpaper_id)
self.top.destroy()
return
self.application.adjust_to_dim_var.set(self.adjust_to_dim_var.get())
if self.adjust_to_dim_var.get() == 0:
self.application.radiovar.set(self.radiovar.get())
if self.radiovar.get() == ScaleOption.UPPER_LEFT.value:
self.upper_left(img)
elif self.radiovar.get() == ScaleOption.CENTERED.value:
self.center(img)
elif self.radiovar.get() == ScaleOption.SCALED.value:
self.scaled(img)
elif self.radiovar.get() == ScaleOption.TILED.value:
print("not implemented yet")
elif self.adjust_to_dim_var.get() == 1:
self.canvas_to_image_dimension(img)
self.show_grid()
self.top.destroy()
def apply_cancel(self):
f = tk.Frame(self.top)
b = tk.Button(f, text="Apply", command=self.click_apply)
b.grid(row=0, column=0)
b = tk.Button(f, text="Cancel", command=self.top.destroy)
b.grid(row=0, column=1)
f.grid(pady=5)

View file

@ -0,0 +1,277 @@
"""
size and scale
"""
import tkinter as tk
from functools import partial
from coretk.setwallpaper import ScaleOption
DRAW_OBJECT_TAGS = ["edge", "node", "nodename", "linkinfo", "antenna"]
class SizeAndScale:
def __init__(self, application):
"""
create an instance for size and scale object
:param application: main application
"""
self.application = application
self.top = tk.Toplevel()
self.top.title("Canvas Size and Scale")
self.meter_per_pixel = self.application.canvas.meters_per_pixel
self.size_chart()
self.scale_chart()
self.reference_point_chart()
self.save_as_default()
self.apply_cancel()
def pixel_scrollbar_command(self, size_frame, entry_row, entry_column, event):
"""
change the value shown based on scrollbar action
:param tkinter.Frame frame: pixel dimension frame
:param int entry_row: row number of entry of the frame
:param int entry_column: column number of entry of the frame
:param event: scrollbar event
:return: nothing
"""
pixel_frame = size_frame.grid_slaves(0, 0)[0]
pixel_entry = pixel_frame.grid_slaves(entry_row, entry_column)[0]
val = int(pixel_entry.get())
if event == "-1":
new_val = val + 2
elif event == "1":
new_val = val - 2
pixel_entry.delete(0, tk.END)
pixel_entry.insert(tk.END, str(new_val))
# change meter dimension
meter_frame = size_frame.grid_slaves(1, 0)[0]
meter_entry = meter_frame.grid_slaves(entry_row, entry_column)[0]
meter_entry.delete(0, tk.END)
meter_entry.insert(tk.END, str(new_val * self.meter_per_pixel))
def meter_scrollbar_command(self, size_frame, entry_row, entry_column, event):
"""
change the value shown based on scrollbar action
:param tkinter.Frame size_frame: size frame
:param int entry_row: row number of entry in the frame it is contained in
:param int entry_column: column number of entry in the frame in is contained in
:param event: scroolbar event
:return: nothing
"""
meter_frame = size_frame.grid_slaves(1, 0)[0]
meter_entry = meter_frame.grid_slaves(entry_row, entry_column)[0]
val = float(meter_entry.get())
if event == "-1":
val += 100.0
elif event == "1":
val -= 100.0
meter_entry.delete(0, tk.END)
meter_entry.insert(tk.END, str(val))
# change pixel dimension
pixel_frame = size_frame.grid_slaves(0, 0)[0]
pixel_entry = pixel_frame.grid_slaves(entry_row, entry_column)[0]
pixel_entry.delete(0, tk.END)
pixel_entry.insert(tk.END, str(int(val / self.meter_per_pixel)))
def create_text_label(self, frame, text, row, column, sticky=None):
"""
create text label
:param tkinter.Frame frame: parent frame
:param str text: label text
:param int row: row number
:param int column: column number
:param sticky: sticky value
:return: nothing
"""
text_label = tk.Label(frame, text=text)
text_label.grid(row=row, column=column, sticky=sticky, padx=3, pady=3)
def create_entry(self, frame, default_value, row, column, width):
text_var = tk.StringVar(frame, value=str(default_value))
entry = tk.Entry(
frame, textvariable=text_var, width=width, bg="white", state=tk.NORMAL
)
entry.focus()
entry.grid(row=row, column=column, padx=3, pady=3)
def size_chart(self):
label = tk.Label(self.top, text="Size")
label.grid(sticky=tk.W, padx=5)
canvas = self.application.canvas
plot = canvas.find_withtag("rectangle")
x0, y0, x1, y1 = canvas.bbox(plot[0])
w = abs(x0 - x1) - 2
h = abs(y0 - y1) - 2
f = tk.Frame(
self.top,
highlightbackground="#b3b3b3",
highlightcolor="#b3b3b3",
highlightthickness=0.5,
bd=0,
)
f1 = tk.Frame(f)
pw_scrollbar = tk.Scrollbar(f1, orient=tk.VERTICAL)
pw_scrollbar.grid(row=0, column=1)
self.create_entry(f1, w, 0, 0, 6)
pw_scrollbar.config(command=partial(self.pixel_scrollbar_command, f, 0, 0))
self.create_text_label(f1, " W x ", 0, 2)
scrollbar = tk.Scrollbar(f1, orient=tk.VERTICAL)
scrollbar.grid(row=0, column=4)
self.create_entry(f1, h, 0, 3, 6)
scrollbar.config(command=partial(self.pixel_scrollbar_command, f, 0, 3))
self.create_text_label(f1, " H pixels ", 0, 7)
f1.grid(sticky=tk.W, pady=3)
f2 = tk.Frame(f)
scrollbar = tk.Scrollbar(f2, orient=tk.VERTICAL)
scrollbar.grid(row=0, column=1)
self.create_entry(f2, w * self.meter_per_pixel, 0, 0, 8)
scrollbar.config(command=partial(self.meter_scrollbar_command, f, 0, 0))
self.create_text_label(f2, " x ", 0, 2)
scrollbar = tk.Scrollbar(f2, orient=tk.VERTICAL)
scrollbar.grid(row=0, column=4)
self.create_entry(f2, h * self.meter_per_pixel, 0, 3, 8)
scrollbar.config(command=partial(self.meter_scrollbar_command, f, 0, 3))
self.create_text_label(f2, " meters ", 0, 5)
f2.grid(sticky=tk.W, pady=3)
f.grid(sticky=tk.W + tk.E, padx=5, pady=5, columnspan=2)
def scale_chart(self):
label = tk.Label(self.top, text="Scale")
label.grid(padx=5, sticky=tk.W)
f = tk.Frame(
self.top,
highlightbackground="#b3b3b3",
highlightcolor="#b3b3b3",
highlightthickness=0.5,
bd=0,
)
# self.create_text_label(f, "Scale", 0, 0, tk.W)
# f1 = tk.Frame(f)
self.create_text_label(f, "100 pixels = ", 0, 0)
self.create_entry(f, self.meter_per_pixel * 100, 0, 1, 10)
self.create_text_label(f, "meters", 0, 2)
# f1.grid(sticky=tk.W, pady=3)
f.grid(sticky=tk.W + tk.E, padx=5, pady=5, columnspan=2)
def reference_point_chart(self):
label = tk.Label(self.top, text="Reference point")
label.grid(padx=5, sticky=tk.W)
f = tk.Frame(
self.top,
highlightbackground="#b3b3b3",
highlightcolor="#b3b3b3",
highlightthickness=0.5,
bd=0,
)
self.create_text_label(
f,
"The default reference point is (0, 0), the upper left corner of the canvas.",
1,
0,
tk.W,
)
f1 = tk.Frame(f)
self.create_entry(f1, 0, 0, 0, 4)
self.create_text_label(f1, " X, ", 0, 1)
self.create_entry(f1, 0, 0, 2, 4)
self.create_text_label(f1, "Y = ", 0, 3)
self.create_entry(f1, 47.5791667, 0, 4, 13)
self.create_text_label(f1, " lat, ", 0, 5)
self.create_entry(f1, -122.132322, 0, 6, 13)
self.create_text_label(f1, "long", 0, 7)
f1.grid(row=2, column=0, sticky=tk.W, pady=3)
f2 = tk.Frame(f)
self.create_text_label(f2, "Altitude: ", 0, 0)
self.create_entry(f2, 2.0, 0, 1, 11)
self.create_text_label(f2, " meters ", 0, 2)
f2.grid(row=3, column=0, sticky=tk.W, pady=3)
f.grid(sticky=tk.W, padx=5, pady=5, columnspan=2)
def save_as_default(self):
var = tk.IntVar()
button = tk.Checkbutton(self.top, text="Save as default", variable=var)
button.grid(sticky=tk.W, padx=5, pady=5, columnspan=2)
def redraw_grid(self, pixel_width, pixel_height):
"""
redraw grid with new dimension
:param int pixel_width: width in pixel
:param int pixel_height: height in pixel
:return: nothing
"""
canvas = self.application.canvas
canvas.config(scrollregion=(0, 0, pixel_width + 200, pixel_height + 200))
# delete old plot and redraw
for i in canvas.find_withtag("gridline"):
canvas.delete(i)
for i in canvas.find_withtag("rectangle"):
canvas.delete(i)
canvas.draw_grid(width=pixel_width, height=pixel_height)
# lift anything that is drawn on the plot before
for tag in DRAW_OBJECT_TAGS:
for i in canvas.find_withtag(tag):
canvas.lift(i)
def click_apply(self):
size_frame = self.top.grid_slaves(1, 0)[0]
pixel_size_frame = size_frame.grid_slaves(0, 0)[0]
pixel_width = int(pixel_size_frame.grid_slaves(0, 0)[0].get())
pixel_height = int(pixel_size_frame.grid_slaves(0, 3)[0].get())
scale_frame = self.top.grid_slaves(3, 0)[0]
meter_per_pixel = float(scale_frame.grid_slaves(0, 1)[0].get()) / 100
self.application.canvas.meters_per_pixel = meter_per_pixel
self.redraw_grid(pixel_width, pixel_height)
print(self.application.current_wallpaper)
print(self.application.radiovar)
# if there is a current wallpaper showing, redraw it based on current wallpaper options
wallpaper_tool = self.application.set_wallpaper
current_wallpaper = self.application.current_wallpaper
if current_wallpaper:
if self.application.adjust_to_dim_var.get() == 0:
if self.application.radiovar.get() == ScaleOption.UPPER_LEFT.value:
wallpaper_tool.upper_left(current_wallpaper)
elif self.application.radiovar.get() == ScaleOption.CENTERED.value:
wallpaper_tool.center(current_wallpaper)
elif self.application.radiovar.get() == ScaleOption.SCALED.value:
wallpaper_tool.scaled(current_wallpaper)
elif self.application.radiovar.get() == ScaleOption.TILED.value:
print("not implemented")
elif self.application.adjust_to_dim_var.get() == 1:
wallpaper_tool.canvas_to_image_dimension(current_wallpaper)
wallpaper_tool.show_grid()
self.top.destroy()
def apply_cancel(self):
apply_button = tk.Button(self.top, text="Apply", command=self.click_apply)
apply_button.grid(row=7, column=0, pady=5)
cancel_button = tk.Button(self.top, text="Cancel", command=self.top.destroy)
cancel_button.grid(row=7, column=1, pady=5)

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

View file

@ -0,0 +1,53 @@
"""
Wireless connection handler
"""
from core.api.grpc import core_pb2
class WirelessConnection:
def __init__(self, canvas, grpc):
self.canvas = canvas
self.core_grpc = grpc
self.core_mapping = canvas.grpc_manager.core_mapping
# map a (node_one_id, node_two_id) to a wlan canvas id
self.map = {}
def add_wlan_connection(self, node_one_id, node_two_id):
canvas_id_one = self.core_mapping.get_canvas_id_from_core_id(node_one_id)
canvas_id_two = self.core_mapping.get_canvas_id_from_core_id(node_two_id)
key = tuple(sorted((node_one_id, node_two_id)))
if key not in self.map:
x1, y1 = self.canvas.coords(canvas_id_one)
x2, y2 = self.canvas.coords(canvas_id_two)
wlan_canvas_id = self.canvas.create_line(
x1, y1, x2, y2, fill="#009933", tags="wlan", width=1.5
)
self.map[key] = wlan_canvas_id
self.canvas.nodes[canvas_id_one].wlans.append(wlan_canvas_id)
self.canvas.nodes[canvas_id_two].wlans.append(wlan_canvas_id)
def delete_wlan_connection(self, node_one_id, node_two_id):
canvas_id_one = self.core_mapping.get_canvas_id_from_core_id(node_one_id)
canvas_id_two = self.core_mapping.get_canvas_id_from_core_id(node_two_id)
key = tuple(sorted((node_one_id, node_two_id)))
wlan_canvas_id = self.map[key]
self.canvas.nodes[canvas_id_one].wlans.remove(wlan_canvas_id)
self.canvas.nodes[canvas_id_two].wlans.remove(wlan_canvas_id)
self.canvas.delete(wlan_canvas_id)
self.map.pop(key, None)
def hangle_link_event(self, link_event):
if link_event.message_type == core_pb2.MessageType.ADD:
self.add_wlan_connection(
link_event.link.node_one_id, link_event.link.node_two_id
)
if link_event.message_type == core_pb2.MessageType.DELETE:
self.delete_wlan_connection(
link_event.link.node_one_id, link_event.link.node_two_id
)

View file

@ -194,7 +194,7 @@ class CoreGrpcClient(object):
def get_session_options(self, session_id):
"""
Retrieve session options.
Retrieve session options as a dict with id mapping.
:param int session_id: id of session
:return: response with a list of configuration groups
@ -204,6 +204,18 @@ class CoreGrpcClient(object):
request = core_pb2.GetSessionOptionsRequest(session_id=session_id)
return self.stub.GetSessionOptions(request)
def get_session_options_group(self, session_id):
"""
Retrieve session options in a group list.
:param int session_id: id of session
:return: response with a list of configuration groups
:rtype: core_pb2.GetSessionOptionsGroupResponse
:raises grpc.RpcError: when session doesn't exist
"""
request = core_pb2.GetSessionOptionsGroupRequest(session_id=session_id)
return self.stub.GetSessionOptionsGroup(request)
def set_session_options(self, session_id, config):
"""
Set options for a session.
@ -868,17 +880,18 @@ class CoreGrpcClient(object):
with open(file_path, "w") as xml_file:
xml_file.write(response.data)
def open_xml(self, file_path):
def open_xml(self, file_path, start=False):
"""
Load a local scenario XML file to open as a new session.
:param str file_path: path of scenario XML file
:param bool start: True to start session, False otherwise
:return: response with opened session id
:rtype: core_pb2.OpenXmlResponse
"""
with open(file_path, "r") as xml_file:
data = xml_file.read()
request = core_pb2.OpenXmlRequest(data=data)
request = core_pb2.OpenXmlRequest(data=data, start=start, file=file_path)
return self.stub.OpenXml(request)
def emane_link(self, session_id, nem_one, nem_two, linked):

View file

@ -46,38 +46,33 @@ def convert_value(value):
return value
def get_config_groups(config, configurable_options):
def get_config_options(config, configurable_options):
"""
Retrieve configuration groups in a form that is used by the grpc server
Retrieve configuration options in a form that is used by the grpc server.
:param core.config.Configuration config: configuration
:param dict config: configuration
:param core.config.ConfigurableOptions configurable_options: configurable options
:return: list of configuration groups
:rtype: [core.api.grpc.core_pb2.ConfigGroup]
:return: mapping of configuration ids to configuration options
:rtype: dict[str,core.api.grpc.core_pb2.ConfigOption]
"""
groups = []
config_options = []
results = {}
for configuration in configurable_options.configurations():
value = config[configuration.id]
config_option = core_pb2.ConfigOption()
config_option.label = configuration.label
config_option.name = configuration.id
config_option.value = value
config_option.type = configuration.type.value
config_option.select.extend(configuration.options)
config_options.append(config_option)
config_option = core_pb2.ConfigOption(
label=configuration.label,
name=configuration.id,
value=value,
type=configuration.type.value,
select=configuration.options,
)
results[configuration.id] = config_option
for config_group in configurable_options.config_groups():
start = config_group.start - 1
stop = config_group.stop
options = config_options[start:stop]
config_group_proto = core_pb2.ConfigGroup(
name=config_group.name, options=options
)
groups.append(config_group_proto)
return groups
options = list(results.values())[start:stop]
for option in options:
option.group = config_group.name
return results
def get_links(session, node):
@ -310,7 +305,10 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
for session_id in self.coreemu.sessions:
session = self.coreemu.sessions[session_id]
session_summary = core_pb2.SessionSummary(
id=session_id, state=session.state, nodes=session.get_node_count()
id=session_id,
state=session.state,
nodes=session.get_node_count(),
file=session.file_name,
)
sessions.append(session_summary)
return core_pb2.GetSessionsResponse(sessions=sessions)
@ -390,20 +388,21 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
def GetSessionOptions(self, request, context):
"""
Retrieve session options
Retrieve session options.
:param core.api.grpc.core_pb2.GetSessionOptions request: get-session-options request
:param core.api.grpc.core_pb2.GetSessionOptions request:
get-session-options request
:param grpc.ServicerContext context: context object
:return: get-session-options response about all session's options
:rtype: core.api.grpc.core_pb2.GetSessionOptions
"""
logging.debug("get session options: %s", request)
session = self.get_session(request.session_id, context)
config = session.options.get_configs()
defaults = session.options.default_values()
defaults.update(config)
groups = get_config_groups(defaults, session.options)
return core_pb2.GetSessionOptionsResponse(groups=groups)
current_config = session.options.get_configs()
default_config = session.options.default_values()
default_config.update(current_config)
config = get_config_options(default_config, session.options)
return core_pb2.GetSessionOptionsResponse(config=config)
def SetSessionOptions(self, request, context):
"""
@ -1115,7 +1114,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
Retrieve all mobility configurations from a session
:param core.api.grpc.core_pb2.GetMobilityConfigsRequest request: get-mobility-configurations request
:param core.api.grpc.core_pb2.GetMobilityConfigsRequest request:
get-mobility-configurations request
:param grpc.ServicerContext context: context object
:return: get-mobility-configurations response that has a list of configurations
:rtype: core.api.grpc.core_pb2.GetMobilityConfigsResponse
@ -1130,33 +1130,36 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
for model_name in model_config:
if model_name != Ns2ScriptedMobility.name:
continue
config = session.mobility.get_model_config(node_id, model_name)
groups = get_config_groups(config, Ns2ScriptedMobility)
response.configs[node_id].groups.extend(groups)
current_config = session.mobility.get_model_config(node_id, model_name)
config = get_config_options(current_config, Ns2ScriptedMobility)
mapped_config = core_pb2.MappedConfig(config=config)
response.configs[node_id].CopyFrom(mapped_config)
return response
def GetMobilityConfig(self, request, context):
"""
Retrieve mobility configuration of a node
:param core.api.grpc.core_pb2.GetMobilityConfigRequest request: get-mobility-configuration request
:param core.api.grpc.core_pb2.GetMobilityConfigRequest request:
get-mobility-configuration request
:param grpc.ServicerContext context: context object
:return: get-mobility-configuration response
:rtype: core.api.grpc.core_pb2.GetMobilityConfigResponse
"""
logging.debug("get mobility config: %s", request)
session = self.get_session(request.session_id, context)
config = session.mobility.get_model_config(
current_config = session.mobility.get_model_config(
request.node_id, Ns2ScriptedMobility.name
)
groups = get_config_groups(config, Ns2ScriptedMobility)
return core_pb2.GetMobilityConfigResponse(groups=groups)
config = get_config_options(current_config, Ns2ScriptedMobility)
return core_pb2.GetMobilityConfigResponse(config=config)
def SetMobilityConfig(self, request, context):
"""
Set mobility configuration of a node
:param core.api.grpc.core_pb2.SetMobilityConfigRequest request: set-mobility-configuration request
:param core.api.grpc.core_pb2.SetMobilityConfigRequest request:
set-mobility-configuration request
:param grpc.ServicerContext context: context object
:return: set-mobility-configuration response
"rtype" core.api.grpc.SetMobilityConfigResponse
@ -1172,7 +1175,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
Take mobility action whether to start, pause, stop or none of those
:param core.api.grpc.core_pb2.MobilityActionRequest request: mobility-action request
:param core.api.grpc.core_pb2.MobilityActionRequest request: mobility-action
request
:param grpc.ServicerContext context: context object
:return: mobility-action response
:rtype: core.api.grpc.core_pb2.MobilityActionResponse
@ -1212,7 +1216,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
Retrieve all the default services of all node types in a session
:param core.api.grpc.core_pb2.GetServiceDefaultsRequest request: get-default-service request
:param core.api.grpc.core_pb2.GetServiceDefaultsRequest request:
get-default-service request
:param grpc.ServicerContext context: context object
:return: get-service-defaults response about all the available default services
:rtype: core.api.grpc.core_pb2.GetServiceDefaultsResponse
@ -1231,7 +1236,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
def SetServiceDefaults(self, request, context):
"""
Set new default services to the session after whipping out the old ones
:param core.api.grpc.core_pb2.SetServiceDefaults request: set-service-defaults request
:param core.api.grpc.core_pb2.SetServiceDefaults request: set-service-defaults
request
:param grpc.ServicerContext context: context object
:return: set-service-defaults response
:rtype: core.api.grpc.core_pb2 SetServiceDefaultsResponse
@ -1249,7 +1255,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
Retrieve a requested service from a node
:param core.api.grpc.core_pb2.GetNodeServiceRequest request: get-node-service request
:param core.api.grpc.core_pb2.GetNodeServiceRequest request: get-node-service
request
:param grpc.ServicerContext context: context object
:return: get-node-service response about the requested service
:rtype: core.api.grpc.core_pb2.GetNodeServiceResponse
@ -1277,7 +1284,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
Retrieve a requested service file from a node
:param core.api.grpc.core_pb2.GetNodeServiceFileRequest request: get-node-service request
:param core.api.grpc.core_pb2.GetNodeServiceFileRequest request:
get-node-service request
:param grpc.ServicerContext context: context object
:return: get-node-service response about the requested service
:rtype: core.api.grpc.core_pb2.GetNodeServiceFileResponse
@ -1301,8 +1309,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
Set a node service for a node
:param core.api.grpc.core_pb2.SetNodeServiceRequest request: set-node-service request
that has info to set a node service
:param core.api.grpc.core_pb2.SetNodeServiceRequest request: set-node-service
request that has info to set a node service
:param grpc.ServicerContext context: context object
:return: set-node-service response
:rtype: core.api.grpc.core_pb2.SetNodeServiceResponse
@ -1320,7 +1328,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
Store the customized service file in the service config
:param core.api.grpc.core_pb2.SetNodeServiceFileRequest request: set-node-service-file request
:param core.api.grpc.core_pb2.SetNodeServiceFileRequest request:
set-node-service-file request
:param grpc.ServicerContext context: context object
:return: set-node-service-file response
:rtype: core.api.grpc.core_pb2.SetNodeServiceFileResponse
@ -1382,11 +1391,11 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
logging.debug("get wlan config: %s", request)
session = self.get_session(request.session_id, context)
config = session.mobility.get_model_config(
current_config = session.mobility.get_model_config(
request.node_id, BasicRangeModel.name
)
groups = get_config_groups(config, BasicRangeModel)
return core_pb2.GetWlanConfigResponse(groups=groups)
config = get_config_options(current_config, BasicRangeModel)
return core_pb2.GetWlanConfigResponse(config=config)
def SetWlanConfig(self, request, context):
"""
@ -1418,9 +1427,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
logging.debug("get emane config: %s", request)
session = self.get_session(request.session_id, context)
config = session.emane.get_configs()
groups = get_config_groups(config, session.emane.emane_config)
return core_pb2.GetEmaneConfigResponse(groups=groups)
current_config = session.emane.get_configs()
config = get_config_options(current_config, session.emane.emane_config)
return core_pb2.GetEmaneConfigResponse(config=config)
def SetEmaneConfig(self, request, context):
"""
@ -1459,7 +1468,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
Retrieve EMANE model configuration of a node
:param core.api.grpc.core_pb2.GetEmaneModelConfigRequest request: get-EMANE-model-configuration request
:param core.api.grpc.core_pb2.GetEmaneModelConfigRequest request:
get-EMANE-model-configuration request
:param grpc.ServicerContext context: context object
:return: get-EMANE-model-configuration response
:rtype: core.api.grpc.core_pb2.GetEmaneModelConfigResponse
@ -1468,15 +1478,16 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
session = self.get_session(request.session_id, context)
model = session.emane.models[request.model]
_id = get_emane_model_id(request.node_id, request.interface)
config = session.emane.get_model_config(_id, request.model)
groups = get_config_groups(config, model)
return core_pb2.GetEmaneModelConfigResponse(groups=groups)
current_config = session.emane.get_model_config(_id, request.model)
config = get_config_options(current_config, model)
return core_pb2.GetEmaneModelConfigResponse(config=config)
def SetEmaneModelConfig(self, request, context):
"""
Set EMANE model configuration of a node
:param core.api.grpc.core_pb2.SetEmaneModelConfigRequest request: set-EMANE-model-configuration request
:param core.api.grpc.core_pb2.SetEmaneModelConfigRequest request:
set-EMANE-model-configuration request
:param grpc.ServicerContext context: context object
:return: set-EMANE-model-configuration response
:rtype: core.api.grpc.core_pb2.SetEmaneModelConfigResponse
@ -1491,9 +1502,11 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
Retrieve all EMANE model configurations of a session
:param core.api.grpc.core_pb2.GetEmaneModelConfigsRequest request: get-EMANE-model-configurations request
:param core.api.grpc.core_pb2.GetEmaneModelConfigsRequest request:
get-EMANE-model-configurations request
:param grpc.ServicerContext context: context object
:return: get-EMANE-model-configurations response that has all the EMANE configurations
:return: get-EMANE-model-configurations response that has all the EMANE
configurations
:rtype: core.api.grpc.core_pb2.GetEmaneModelConfigsResponse
"""
logging.debug("get emane model configs: %s", request)
@ -1506,11 +1519,12 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
for model_name in model_config:
model = session.emane.models[model_name]
config = session.emane.get_model_config(node_id, model_name)
config_groups = get_config_groups(config, model)
node_configurations = response.configs[node_id]
node_configurations.model = model_name
node_configurations.groups.extend(config_groups)
current_config = session.emane.get_model_config(node_id, model_name)
config = get_config_options(current_config, model)
model_config = core_pb2.GetEmaneModelConfigsResponse.ModelConfig(
model=model_name, config=config
)
response.configs[node_id].CopyFrom(model_config)
return response
def SaveXml(self, request, context):
@ -1544,19 +1558,22 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
logging.debug("open xml: %s", request)
session = self.coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
_, temp_path = tempfile.mkstemp()
with open(temp_path, "w") as xml_file:
xml_file.write(request.data)
temp = tempfile.NamedTemporaryFile(delete=False)
temp.write(request.data.encode("utf-8"))
temp.close()
try:
session.open_xml(temp_path, start=True)
session.open_xml(temp.name, request.start)
session.name = os.path.basename(request.file)
session.file_name = request.file
return core_pb2.OpenXmlResponse(session_id=session.id, result=True)
except IOError:
logging.exception("error opening session file")
self.coreemu.delete_session(session.id)
context.abort(grpc.StatusCode.INVALID_ARGUMENT, "invalid xml file")
finally:
os.unlink(temp.name)
def GetInterfaces(self, request, context):
"""

View file

@ -944,7 +944,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
if os.path.splitext(file_name)[1].lower() == ".xml":
session = self.coreemu.create_session(master=False)
try:
session.open_xml(file_name, start=True)
session.open_xml(file_name)
except Exception:
self.coreemu.delete_session(session.id)
raise

View file

@ -18,6 +18,7 @@ from core.nodes.ipaddress import IpAddress
from core.nodes.network import CoreNetwork, CtrlNet
LOCK = threading.Lock()
CMD_HIDE = True
class DistributedServer(object):
@ -54,18 +55,18 @@ class DistributedServer(object):
replace_env = env is not None
if not wait:
cmd += " &"
logging.info(
logging.debug(
"remote cmd server(%s) cwd(%s) wait(%s): %s", self.host, cwd, wait, cmd
)
try:
if cwd is None:
result = self.conn.run(
cmd, hide=False, env=env, replace_env=replace_env
cmd, hide=CMD_HIDE, env=env, replace_env=replace_env
)
else:
with self.conn.cd(cwd):
result = self.conn.run(
cmd, hide=False, env=env, replace_env=replace_env
cmd, hide=CMD_HIDE, env=env, replace_env=replace_env
)
return result.stdout.strip()
except UnexpectedExit as e:

View file

@ -634,19 +634,23 @@ class Session(object):
if node_two:
node_two.lock.release()
def add_node(self, _type=NodeTypes.DEFAULT, _id=None, node_options=None):
def add_node(self, _type=NodeTypes.DEFAULT, _id=None, node_options=None, _cls=None):
"""
Add a node to the session, based on the provided node data.
:param core.emulator.enumerations.NodeTypes _type: type of node to create
:param int _id: id for node, defaults to None for generated id
:param core.emulator.emudata.NodeOptions node_options: data to create node with
:param class _cls: optional custom class to use for a created node
:return: created node
:raises core.CoreError: when an invalid node type is given
"""
# validate node type, get class, or throw error
node_class = self.get_node_class(_type)
if _cls is None:
node_class = self.get_node_class(_type)
else:
node_class = _cls
# set node start based on current session state, override and check when rj45
start = self.state > EventTypes.DEFINITION_STATE.value
@ -705,13 +709,8 @@ class Session(object):
# set node position and broadcast it
self.set_node_position(node, node_options)
# add services to default and physical nodes only
if _type in [
NodeTypes.DEFAULT,
NodeTypes.PHYSICAL,
NodeTypes.DOCKER,
NodeTypes.LXC,
]:
# add services to needed nodes
if isinstance(node, (CoreNode, PhysicalNode, DockerNode, LxcNode)):
node.type = node_options.model
logging.debug("set node type: %s", node.type)
self.services.add_services(node, node.type, node_options.services)
@ -820,19 +819,24 @@ class Session(object):
:param bool start: instantiate session if true, false otherwise
:return: nothing
"""
logging.info("opening xml: %s", file_name)
# clear out existing session
self.clear()
if start:
self.set_state(EventTypes.CONFIGURATION_STATE)
state = EventTypes.CONFIGURATION_STATE
else:
state = EventTypes.DEFINITION_STATE
self.set_state(state)
self.name = os.path.basename(file_name)
self.file_name = file_name
# write out xml file
CoreXmlReader(self).read(file_name)
# start session if needed
if start:
self.name = os.path.basename(file_name)
self.file_name = file_name
self.instantiate()
def save_xml(self, file_name):

View file

@ -284,7 +284,7 @@ class CoreNetwork(CoreNetworkBase):
:rtype: str
:raises CoreCommandError: when a non-zero exit status occurs
"""
logging.info("network node(%s) cmd", self.name)
logging.debug("network node(%s) cmd", self.name)
output = utils.cmd(args, env, cwd, wait, shell)
self.session.distributed.execute(lambda x: x.remote_cmd(args, env, cwd, wait))
return output

View file

@ -206,7 +206,7 @@ def cmd(args, env=None, cwd=None, wait=True, shell=False):
:raises CoreCommandError: when there is a non-zero exit status or the file to
execute is not found
"""
logging.info("command cwd(%s) wait(%s): %s", cwd, wait, args)
logging.debug("command cwd(%s) wait(%s): %s", cwd, wait, args)
if shell is False:
args = shlex.split(args)
try:

View file

@ -304,10 +304,8 @@ class CoreXmlWriter(object):
default_options = self.session.options.default_values()
for _id in default_options:
default_value = default_options[_id]
# TODO: should we just save the current config regardless, since it may change?
value = options_config[_id]
if value != default_value:
add_configuration(option_elements, _id, value)
value = options_config.get(_id, default_value)
add_configuration(option_elements, _id, value)
if option_elements.getchildren():
self.scenario.append(option_elements)

View file

@ -163,7 +163,7 @@ message GetSessionOptionsRequest {
}
message GetSessionOptionsResponse {
repeated ConfigGroup groups = 1;
map<string, ConfigOption> config = 2;
}
message SetSessionOptionsRequest {
@ -428,10 +428,7 @@ message GetMobilityConfigsRequest {
}
message GetMobilityConfigsResponse {
message MobilityConfig {
repeated ConfigGroup groups = 1;
}
map<int32, MobilityConfig> configs = 1;
map<int32, MappedConfig> configs = 1;
}
message GetMobilityConfigRequest {
@ -440,7 +437,7 @@ message GetMobilityConfigRequest {
}
message GetMobilityConfigResponse {
repeated ConfigGroup groups = 1;
map<string, ConfigOption> config = 1;
}
message SetMobilityConfigRequest {
@ -551,7 +548,7 @@ message GetWlanConfigRequest {
}
message GetWlanConfigResponse {
repeated ConfigGroup groups = 1;
map<string, ConfigOption> config = 1;
}
message SetWlanConfigRequest {
@ -569,7 +566,7 @@ message GetEmaneConfigRequest {
}
message GetEmaneConfigResponse {
repeated ConfigGroup groups = 1;
map<string, ConfigOption> config = 1;
}
message SetEmaneConfigRequest {
@ -597,7 +594,7 @@ message GetEmaneModelConfigRequest {
}
message GetEmaneModelConfigResponse {
repeated ConfigGroup groups = 1;
map<string, ConfigOption> config = 1;
}
message SetEmaneModelConfigRequest {
@ -619,7 +616,7 @@ message GetEmaneModelConfigsRequest {
message GetEmaneModelConfigsResponse {
message ModelConfig {
string model = 1;
repeated ConfigGroup groups = 2;
map<string, ConfigOption> config = 2;
}
map<int32, ModelConfig> configs = 1;
}
@ -634,6 +631,8 @@ message SaveXmlResponse {
message OpenXmlRequest {
string data = 1;
bool start = 2;
string file = 3;
}
message OpenXmlResponse {
@ -777,9 +776,8 @@ message NodeServiceData {
string meta = 10;
}
message ConfigGroup {
string name = 1;
repeated ConfigOption options = 2;
message MappedConfig {
map<string, ConfigOption> config = 1;
}
message ConfigOption {
@ -788,6 +786,7 @@ message ConfigOption {
string value = 3;
int32 type = 4;
repeated string select = 5;
string group = 6;
}
message Session {
@ -801,6 +800,7 @@ message SessionSummary {
int32 id = 1;
SessionState.Enum state = 2;
int32 nodes = 3;
string file = 4;
}
message Node {

View file

@ -100,7 +100,7 @@ class TestGrpc:
response = client.get_session_options(session.id)
# then
assert len(response.groups) > 0
assert len(response.config) > 0
def test_get_session_location(self, grpc_server):
# given
@ -457,7 +457,7 @@ class TestGrpc:
response = client.get_wlan_config(session.id, wlan.id)
# then
assert len(response.groups) > 0
assert len(response.config) > 0
def test_set_wlan_config(self, grpc_server):
# given
@ -501,7 +501,7 @@ class TestGrpc:
response = client.get_emane_config(session.id)
# then
assert len(response.groups) > 0
assert len(response.config) > 0
def test_set_emane_config(self, grpc_server):
# given
@ -540,6 +540,9 @@ class TestGrpc:
# then
assert len(response.configs) == 1
assert emane_network.id in response.configs
model_config = response.configs[emane_network.id]
assert model_config.model == EmaneIeee80211abgModel.name
assert len(model_config.config) > 0
def test_set_emane_model_config(self, grpc_server):
# given
@ -582,7 +585,7 @@ class TestGrpc:
)
# then
assert len(response.groups) > 0
assert len(response.config) > 0
def test_get_emane_models(self, grpc_server):
# given
@ -610,6 +613,8 @@ class TestGrpc:
# then
assert len(response.configs) > 0
assert wlan.id in response.configs
mapped_config = response.configs[wlan.id]
assert len(mapped_config.config) > 0
def test_get_mobility_config(self, grpc_server):
# given
@ -623,7 +628,7 @@ class TestGrpc:
response = client.get_mobility_config(session.id, wlan.id)
# then
assert len(response.groups) > 0
assert len(response.config) > 0
def test_set_mobility_config(self, grpc_server):
# given

23
output.txt Normal file

File diff suppressed because one or more lines are too long