pygui: add support to rename interfaces in the node config dialog, some small cleanup to interface validation

This commit is contained in:
Blake Harnden 2020-12-08 10:02:34 -08:00
parent 5b93c2d7ac
commit 836e929fbc

View file

@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Dict, Optional
import netaddr import netaddr
from PIL.ImageTk import PhotoImage from PIL.ImageTk import PhotoImage
from core.api.grpc.wrappers import Node from core.api.grpc.wrappers import Interface, Node
from core.gui import nodeutils, validation from core.gui import nodeutils, validation
from core.gui.appconfig import ICONS_PATH from core.gui.appconfig import ICONS_PATH
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
@ -21,8 +21,10 @@ if TYPE_CHECKING:
from core.gui.app import Application from core.gui.app import Application
from core.gui.graph.node import CanvasNode from core.gui.graph.node import CanvasNode
IFACE_NAME_LEN: int = 15
def check_ip6(parent, name: str, value: str) -> bool:
def check_ip6(parent: tk.BaseWidget, name: str, value: str) -> bool:
if not value: if not value:
return True return True
title = f"IP6 Error for {name}" title = f"IP6 Error for {name}"
@ -47,7 +49,7 @@ def check_ip6(parent, name: str, value: str) -> bool:
return True return True
def check_ip4(parent, name: str, value: str) -> bool: def check_ip4(parent: tk.BaseWidget, name: str, value: str) -> bool:
if not value: if not value:
return True return True
title = f"IP4 Error for {name}" title = f"IP4 Error for {name}"
@ -84,16 +86,88 @@ def mac_auto(is_auto: tk.BooleanVar, entry: ttk.Entry, mac: tk.StringVar) -> Non
class InterfaceData: class InterfaceData:
def __init__( def __init__(
self, self,
name: tk.StringVar,
is_auto: tk.BooleanVar, is_auto: tk.BooleanVar,
mac: tk.StringVar, mac: tk.StringVar,
ip4: tk.StringVar, ip4: tk.StringVar,
ip6: tk.StringVar, ip6: tk.StringVar,
) -> None: ) -> None:
self.name: tk.StringVar = name
self.is_auto: tk.BooleanVar = is_auto self.is_auto: tk.BooleanVar = is_auto
self.mac: tk.StringVar = mac self.mac: tk.StringVar = mac
self.ip4: tk.StringVar = ip4 self.ip4: tk.StringVar = ip4
self.ip6: tk.StringVar = ip6 self.ip6: tk.StringVar = ip6
def validate(self, parent: tk.BaseWidget, iface: Interface) -> bool:
valid_name = self._validate_name(parent, iface)
valid_ip4 = self._validate_ip4(parent, iface)
valid_ip6 = self._validate_ip6(parent, iface)
valid_mac = self._validate_mac(parent, iface)
return all([valid_name, valid_ip4, valid_ip6, valid_mac])
def _validate_name(self, parent: tk.BaseWidget, iface: Interface) -> bool:
name = self.name.get()
title = f"Interface Name Error for {iface.name}"
if not name:
messagebox.showerror(title, "Name cannot be empty", parent=parent)
return False
if len(name) > IFACE_NAME_LEN:
messagebox.showerror(
title,
f"Name cannot be greater than {IFACE_NAME_LEN} chars",
parent=parent,
)
return False
for x in name:
if x.isspace() or x == "/":
messagebox.showerror(
title, "Name cannot contain space or /", parent=parent
)
return False
iface.name = name
return True
def _validate_ip4(self, parent: tk.BaseWidget, iface: Interface) -> bool:
ip4_net = self.ip4.get()
if not check_ip4(parent, iface.name, ip4_net):
return False
if ip4_net:
ip4, ip4_mask = ip4_net.split("/")
ip4_mask = int(ip4_mask)
else:
ip4, ip4_mask = "", 0
iface.ip4 = ip4
iface.ip4_mask = ip4_mask
return True
def _validate_ip6(self, parent: tk.BaseWidget, iface: Interface) -> bool:
ip6_net = self.ip6.get()
if not check_ip6(parent, iface.name, ip6_net):
return False
if ip6_net:
ip6, ip6_mask = ip6_net.split("/")
ip6_mask = int(ip6_mask)
else:
ip6, ip6_mask = "", 0
iface.ip6 = ip6
iface.ip6_mask = ip6_mask
return True
def _validate_mac(self, parent: tk.BaseWidget, iface: Interface) -> bool:
mac = self.mac.get()
auto_mac = self.is_auto.get()
if auto_mac:
iface.mac = None
else:
if not netaddr.valid_mac(mac):
title = f"MAC Error for {iface.name}"
messagebox.showerror(title, "Invalid MAC Address", parent=parent)
return False
else:
mac = netaddr.EUI(mac, dialect=netaddr.mac_unix_expanded)
iface.mac = str(mac)
return True
class NodeConfigDialog(Dialog): class NodeConfigDialog(Dialog):
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None: def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
@ -229,6 +303,14 @@ class NodeConfigDialog(Dialog):
button.grid(row=row, sticky=tk.EW, columnspan=3, pady=PADY) button.grid(row=row, sticky=tk.EW, columnspan=3, pady=PADY)
row += 1 row += 1
label = ttk.Label(tab, text="Name")
label.grid(row=row, column=0, padx=PADX, pady=PADY)
name = tk.StringVar(value=iface.name)
entry = ttk.Entry(tab, textvariable=name, state=state)
entry.var = name
entry.grid(row=row, column=1, columnspan=2, sticky=tk.EW)
row += 1
label = ttk.Label(tab, text="MAC") label = ttk.Label(tab, text="MAC")
label.grid(row=row, column=0, padx=PADX, pady=PADY) label.grid(row=row, column=0, padx=PADX, pady=PADY)
auto_set = not iface.mac auto_set = not iface.mac
@ -267,7 +349,7 @@ class NodeConfigDialog(Dialog):
entry = ttk.Entry(tab, textvariable=ip6, state=state) entry = ttk.Entry(tab, textvariable=ip6, state=state)
entry.grid(row=row, column=1, columnspan=2, sticky=tk.EW) entry.grid(row=row, column=1, columnspan=2, sticky=tk.EW)
self.ifaces[iface.id] = InterfaceData(is_auto, mac, ip4, ip6) self.ifaces[iface.id] = InterfaceData(name, is_auto, mac, ip4, ip6)
def draw_buttons(self) -> None: def draw_buttons(self) -> None:
frame = ttk.Frame(self.top) frame = ttk.Frame(self.top)
@ -313,45 +395,9 @@ class NodeConfigDialog(Dialog):
# update node interface data # update node interface data
for iface in self.canvas_node.ifaces.values(): for iface in self.canvas_node.ifaces.values():
data = self.ifaces[iface.id] data = self.ifaces[iface.id]
error = not data.validate(self, iface)
# validate ip4 if error:
ip4_net = data.ip4.get()
if not check_ip4(self, iface.name, ip4_net):
error = True
break break
if ip4_net:
ip4, ip4_mask = ip4_net.split("/")
ip4_mask = int(ip4_mask)
else:
ip4, ip4_mask = "", 0
iface.ip4 = ip4
iface.ip4_mask = ip4_mask
# validate ip6
ip6_net = data.ip6.get()
if not check_ip6(self, iface.name, ip6_net):
error = True
break
if ip6_net:
ip6, ip6_mask = ip6_net.split("/")
ip6_mask = int(ip6_mask)
else:
ip6, ip6_mask = "", 0
iface.ip6 = ip6
iface.ip6_mask = ip6_mask
mac = data.mac.get()
auto_mac = data.is_auto.get()
if auto_mac:
iface.mac = None
elif not auto_mac and not netaddr.valid_mac(mac):
title = f"MAC Error for {iface.name}"
messagebox.showerror(title, "Invalid MAC Address")
error = True
break
elif not auto_mac:
mac = netaddr.EUI(mac, dialect=netaddr.mac_unix_expanded)
iface.mac = str(mac)
# redraw # redraw
if not error: if not error: