|
@@ -19,13 +19,11 @@ import urwid
|
|
|
import logging
|
|
import logging
|
|
|
import asyncio
|
|
import asyncio
|
|
|
import datetime as dt
|
|
import datetime as dt
|
|
|
|
|
+from enum import Enum
|
|
|
|
|
|
|
|
from src.scroll import ScrollBar, Scrollable
|
|
from src.scroll import ScrollBar, Scrollable
|
|
|
from src.model import Model
|
|
from src.model import Model
|
|
|
|
|
|
|
|
-# TODO: re-implement offline nodes comes online, online node goes offline
|
|
|
|
|
-# handling.
|
|
|
|
|
-# TODO: update to latest urwid version.
|
|
|
|
|
class DnetWidget(urwid.WidgetWrap):
|
|
class DnetWidget(urwid.WidgetWrap):
|
|
|
def __init__(self, name, kind):
|
|
def __init__(self, name, kind):
|
|
|
self.name = name
|
|
self.name = name
|
|
@@ -42,17 +40,23 @@ class DnetWidget(urwid.WidgetWrap):
|
|
|
self._w = urwid.AttrWrap(self._w, None)
|
|
self._w = urwid.AttrWrap(self._w, None)
|
|
|
self._w.focus_attr = 'line'
|
|
self._w.focus_attr = 'line'
|
|
|
|
|
|
|
|
|
|
+class NodeState(Enum):
|
|
|
|
|
+ ON = 0
|
|
|
|
|
+ OFF = 1
|
|
|
|
|
|
|
|
class Node(DnetWidget):
|
|
class Node(DnetWidget):
|
|
|
- def set_txt(self, is_empty: bool):
|
|
|
|
|
- if is_empty:
|
|
|
|
|
|
|
+ def __init__(self, name, kind, state):
|
|
|
|
|
+ self.state = state
|
|
|
|
|
+ super().__init__(name, kind)
|
|
|
|
|
+
|
|
|
|
|
+ def set_txt(self):
|
|
|
|
|
+ if self.state == NodeState.OFF:
|
|
|
txt = urwid.Text(f"{self.name} (offline)")
|
|
txt = urwid.Text(f"{self.name} (offline)")
|
|
|
super().update(txt)
|
|
super().update(txt)
|
|
|
else:
|
|
else:
|
|
|
txt = urwid.Text(f"{self.name}")
|
|
txt = urwid.Text(f"{self.name}")
|
|
|
super().update(txt)
|
|
super().update(txt)
|
|
|
|
|
|
|
|
-
|
|
|
|
|
class Session(DnetWidget):
|
|
class Session(DnetWidget):
|
|
|
def set_txt(self):
|
|
def set_txt(self):
|
|
|
txt = urwid.Text(f" {self.kind}")
|
|
txt = urwid.Text(f" {self.kind}")
|
|
@@ -93,20 +97,16 @@ class View():
|
|
|
leftbox = urwid.LineBox(self.list)
|
|
leftbox = urwid.LineBox(self.list)
|
|
|
columns = urwid.Columns([leftbox, rightbox], focus_column=0)
|
|
columns = urwid.Columns([leftbox, rightbox], focus_column=0)
|
|
|
self.ui = urwid.Frame(urwid.AttrWrap( columns, 'body' ))
|
|
self.ui = urwid.Frame(urwid.AttrWrap( columns, 'body' ))
|
|
|
- self.active_sessions = set()
|
|
|
|
|
- self.active_nodes = set()
|
|
|
|
|
|
|
+ self.sessions = set()
|
|
|
|
|
+ self.nodes = set()
|
|
|
self.refresh_needed = False
|
|
self.refresh_needed = False
|
|
|
|
|
|
|
|
- def add_node(self, name, info):
|
|
|
|
|
- logging.debug("add_node() [START]")
|
|
|
|
|
- node = Node(name, "node")
|
|
|
|
|
- if not info:
|
|
|
|
|
- node.set_txt(True)
|
|
|
|
|
- else:
|
|
|
|
|
- node.set_txt(False)
|
|
|
|
|
|
|
+ def add_node(self, name, info, state):
|
|
|
|
|
+ logging.debug(f"Adding node: {name} {info} {state}")
|
|
|
|
|
+ node = Node(name, "node", state)
|
|
|
|
|
+ node.set_txt()
|
|
|
|
|
+ self.nodes.add(name)
|
|
|
self.listwalker.append(node)
|
|
self.listwalker.append(node)
|
|
|
- self.active_nodes.add(name)
|
|
|
|
|
-
|
|
|
|
|
self.add_sessions(name, info)
|
|
self.add_sessions(name, info)
|
|
|
|
|
|
|
|
def add_sessions(self, name, info):
|
|
def add_sessions(self, name, info):
|
|
@@ -125,28 +125,46 @@ class View():
|
|
|
match session:
|
|
match session:
|
|
|
case "outbound":
|
|
case "outbound":
|
|
|
if addr[1] > 0:
|
|
if addr[1] > 0:
|
|
|
- self.active_sessions.add(addr[1])
|
|
|
|
|
|
|
+ self.sessions.add(addr[1])
|
|
|
case "inbound" | "manual " | "seed":
|
|
case "inbound" | "manual " | "seed":
|
|
|
- self.active_sessions.add(i)
|
|
|
|
|
|
|
+ self.sessions.add(i)
|
|
|
|
|
|
|
|
- def add_lilith(self, name, info):
|
|
|
|
|
- node = Node(name, "lilith-node")
|
|
|
|
|
|
|
+ def add_lilith(self, name, info, state):
|
|
|
|
|
+ logging.debug(f"Adding lilith: {name} {info} {state}")
|
|
|
|
|
+ node = Node(name, "lilith-node", state)
|
|
|
node.set_txt()
|
|
node.set_txt()
|
|
|
|
|
+ self.nodes.add(name)
|
|
|
self.listwalker.append(node)
|
|
self.listwalker.append(node)
|
|
|
- self.active_nodes.add(name)
|
|
|
|
|
- for (i, key) in enumerate(info['spawns'].keys()):
|
|
|
|
|
- slot = Slot(name, "spawn-slot")
|
|
|
|
|
- slot.set_txt(i, key)
|
|
|
|
|
- self.listwalker.append(slot)
|
|
|
|
|
|
|
+ if state == NodeState.OFF:
|
|
|
|
|
+ return
|
|
|
|
|
+ else:
|
|
|
|
|
+ for (i, key) in enumerate(info['spawns'].keys()):
|
|
|
|
|
+ slot = Slot(name, "spawn-slot")
|
|
|
|
|
+ slot.set_txt(i, key)
|
|
|
|
|
+ self.listwalker.append(slot)
|
|
|
|
|
+
|
|
|
|
|
+ def update_lilith(self, name, info):
|
|
|
|
|
+ for index, widget in enumerate(self.listwalker):
|
|
|
|
|
+ if isinstance(widget, Node) and widget.name == name:
|
|
|
|
|
+ # Offline node has come online
|
|
|
|
|
+ if widget.state == NodeState.OFF and info:
|
|
|
|
|
+ self.refresh_needed = True
|
|
|
|
|
+ # Online node has gone offline
|
|
|
|
|
+ elif widget.state == NodeState.ON and not info:
|
|
|
|
|
+ self.refresh_needed = True
|
|
|
|
|
|
|
|
def update_node(self, name, info):
|
|
def update_node(self, name, info):
|
|
|
for index, widget in enumerate(self.listwalker):
|
|
for index, widget in enumerate(self.listwalker):
|
|
|
if isinstance(widget, Node) and widget.name == name:
|
|
if isinstance(widget, Node) and widget.name == name:
|
|
|
- if not info:
|
|
|
|
|
- widget.set_txt(True)
|
|
|
|
|
|
|
+ # Offline node has come online
|
|
|
|
|
+ if widget.state == NodeState.OFF and info:
|
|
|
|
|
+ self.refresh_needed = True
|
|
|
|
|
+ # Online node has gone offline
|
|
|
|
|
+ elif widget.state == NodeState.ON and not info:
|
|
|
|
|
+ self.refresh_needed = True
|
|
|
else:
|
|
else:
|
|
|
- widget.set_txt(False)
|
|
|
|
|
- return index + 1
|
|
|
|
|
|
|
+ widget.set_txt()
|
|
|
|
|
+ return index + 1
|
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
def update_slots(self, name, info):
|
|
def update_slots(self, name, info):
|
|
@@ -163,24 +181,13 @@ class View():
|
|
|
widget.name == name and \
|
|
widget.name == name and \
|
|
|
widget.kind == f"{session}-slot" and \
|
|
widget.kind == f"{session}-slot" and \
|
|
|
widget.i == i:
|
|
widget.i == i:
|
|
|
- widget.set_txt(i, addr)
|
|
|
|
|
- break
|
|
|
|
|
-
|
|
|
|
|
- #-----------------------------------------------------------------
|
|
|
|
|
- # Render dnet.subscribe_events() RPC call
|
|
|
|
|
- # Left hand panel only
|
|
|
|
|
- #-----------------------------------------------------------------
|
|
|
|
|
- def update_left_box(self):
|
|
|
|
|
- for index, item in enumerate(self.listwalker):
|
|
|
|
|
- # Update outbound slot info
|
|
|
|
|
- if item.kind == "outbound-slot":
|
|
|
|
|
- key = (f"{item.name}", f"{item.i}")
|
|
|
|
|
- if key in self.model.nodes[item.name]['event']:
|
|
|
|
|
- info = self.model.nodes[item.name]['event'].get(key)
|
|
|
|
|
- slot = Slot(item.name, item.kind)
|
|
|
|
|
- slot.set_txt(item.i, info)
|
|
|
|
|
- self.listwalker[index] = slot
|
|
|
|
|
-
|
|
|
|
|
|
|
+ key = (f"{widget.name}", f"{widget.i}")
|
|
|
|
|
+ if key in self.model.nodes[widget.name]['event']:
|
|
|
|
|
+ info = self.model.nodes[widget.name]['event'].get(key)
|
|
|
|
|
+ widget.set_txt(i, info)
|
|
|
|
|
+ self.listwalker[index] = widget
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
#-----------------------------------------------------------------
|
|
#-----------------------------------------------------------------
|
|
|
# Render dnet.subscribe_events() RPC call
|
|
# Render dnet.subscribe_events() RPC call
|
|
|
# Right hand menu only
|
|
# Right hand menu only
|
|
@@ -217,65 +224,78 @@ class View():
|
|
|
f"{time}: {event}: {msg}"),
|
|
f"{time}: {event}: {msg}"),
|
|
|
self.pile.options()))
|
|
self.pile.options()))
|
|
|
case "spawn-slot":
|
|
case "spawn-slot":
|
|
|
- if session == "spawn-slot":
|
|
|
|
|
- name = focus_w[0].name
|
|
|
|
|
- spawn_name = focus_w[0].id
|
|
|
|
|
- lilith = self.model.liliths.get(name)
|
|
|
|
|
- spawns = lilith.get('spawns')
|
|
|
|
|
- info = spawns.get(spawn_name)
|
|
|
|
|
-
|
|
|
|
|
- if info['urls']:
|
|
|
|
|
- urls = info['urls']
|
|
|
|
|
|
|
+ name = focus_w[0].name
|
|
|
|
|
+ spawn_name = focus_w[0].id
|
|
|
|
|
+ lilith = self.model.liliths.get(name)
|
|
|
|
|
+ spawns = lilith.get('spawns')
|
|
|
|
|
+ info = spawns.get(spawn_name)
|
|
|
|
|
+
|
|
|
|
|
+ if info['urls']:
|
|
|
|
|
+ urls = info['urls']
|
|
|
|
|
+ self.pile.contents.append((urwid.Text(
|
|
|
|
|
+ f"Accept addrs:"),
|
|
|
|
|
+ self.pile.options()))
|
|
|
|
|
+ for url in urls:
|
|
|
self.pile.contents.append((urwid.Text(
|
|
self.pile.contents.append((urwid.Text(
|
|
|
- f"Accept addrs:"),
|
|
|
|
|
|
|
+ f" {url}"),
|
|
|
self.pile.options()))
|
|
self.pile.options()))
|
|
|
- for url in urls:
|
|
|
|
|
- self.pile.contents.append((urwid.Text(
|
|
|
|
|
- f" {url}"),
|
|
|
|
|
- self.pile.options()))
|
|
|
|
|
|
|
|
|
|
- if info['whitelist']:
|
|
|
|
|
- whitelist = info['whitelist']
|
|
|
|
|
|
|
+ if info['whitelist']:
|
|
|
|
|
+ whitelist = info['whitelist']
|
|
|
|
|
+ self.pile.contents.append((urwid.Text(
|
|
|
|
|
+ f"Whitelist:"),
|
|
|
|
|
+ self.pile.options()))
|
|
|
|
|
+ for host in whitelist:
|
|
|
self.pile.contents.append((urwid.Text(
|
|
self.pile.contents.append((urwid.Text(
|
|
|
- f"Whitelist:"),
|
|
|
|
|
|
|
+ f" {host}"),
|
|
|
self.pile.options()))
|
|
self.pile.options()))
|
|
|
- for host in whitelist:
|
|
|
|
|
- self.pile.contents.append((urwid.Text(
|
|
|
|
|
- f" {host}"),
|
|
|
|
|
- self.pile.options()))
|
|
|
|
|
|
|
|
|
|
- if info['greylist']:
|
|
|
|
|
- greylist = info['greylist']
|
|
|
|
|
|
|
+ if info['greylist']:
|
|
|
|
|
+ greylist = info['greylist']
|
|
|
|
|
+ self.pile.contents.append((urwid.Text(
|
|
|
|
|
+ f"Greylist:"),
|
|
|
|
|
+ self.pile.options()))
|
|
|
|
|
+ for host in greylist:
|
|
|
self.pile.contents.append((urwid.Text(
|
|
self.pile.contents.append((urwid.Text(
|
|
|
- f"Greylist:"),
|
|
|
|
|
|
|
+ f" {host}"),
|
|
|
self.pile.options()))
|
|
self.pile.options()))
|
|
|
- for host in greylist:
|
|
|
|
|
- self.pile.contents.append((urwid.Text(
|
|
|
|
|
- f" {host}"),
|
|
|
|
|
- self.pile.options()))
|
|
|
|
|
|
|
|
|
|
- if info['goldlist']:
|
|
|
|
|
- goldlist = info['goldlist']
|
|
|
|
|
|
|
+ if info['goldlist']:
|
|
|
|
|
+ goldlist = info['goldlist']
|
|
|
|
|
+ self.pile.contents.append((urwid.Text(
|
|
|
|
|
+ f"Goldlist:"),
|
|
|
|
|
+ self.pile.options()))
|
|
|
|
|
+ for host in goldlist:
|
|
|
self.pile.contents.append((urwid.Text(
|
|
self.pile.contents.append((urwid.Text(
|
|
|
- f"Goldlist:"),
|
|
|
|
|
|
|
+ f" {host}"),
|
|
|
self.pile.options()))
|
|
self.pile.options()))
|
|
|
- for host in goldlist:
|
|
|
|
|
- self.pile.contents.append((urwid.Text(
|
|
|
|
|
- f" {host}"),
|
|
|
|
|
- self.pile.options()))
|
|
|
|
|
|
|
+
|
|
|
|
|
+ def update_node_state(self, info):
|
|
|
|
|
+ if info:
|
|
|
|
|
+ logging.debug(f"update_node_state(): Returning {NodeState.ON}")
|
|
|
|
|
+ return NodeState.ON
|
|
|
|
|
+ else:
|
|
|
|
|
+ logging.debug(f"update_node_state(): Returning {NodeState.OFF}")
|
|
|
|
|
+ return NodeState.OFF
|
|
|
|
|
|
|
|
def refresh(self):
|
|
def refresh(self):
|
|
|
logging.debug("Refresh initiated.")
|
|
logging.debug("Refresh initiated.")
|
|
|
self.listwalker.clear()
|
|
self.listwalker.clear()
|
|
|
- self.active_nodes.clear()
|
|
|
|
|
- self.active_sessions.clear()
|
|
|
|
|
|
|
+ self.sessions.clear()
|
|
|
|
|
+ self.nodes.clear()
|
|
|
|
|
|
|
|
# Repopulate
|
|
# Repopulate
|
|
|
for name, info in self.model.nodes.items():
|
|
for name, info in self.model.nodes.items():
|
|
|
- logging.debug(f"refresh(): {name}, {info}")
|
|
|
|
|
- self.add_node(name, info)
|
|
|
|
|
|
|
+ logging.debug(f"refresh nodes(): (nodes) updating node state")
|
|
|
|
|
+ state = self.update_node_state(info)
|
|
|
|
|
+ logging.debug(f"refresh nodes(): {name}, {info}, {state}")
|
|
|
|
|
+ self.add_node(name, info, state)
|
|
|
|
|
+
|
|
|
for name, info in self.model.liliths.items():
|
|
for name, info in self.model.liliths.items():
|
|
|
- self.add_lilith(name, info)
|
|
|
|
|
|
|
+ logging.debug(f"refresh nodes(): (lilith) updating node state")
|
|
|
|
|
+ state = self.update_node_state(info)
|
|
|
|
|
+ logging.debug(f"refresh liliths(): {name}, {info}, {state}")
|
|
|
|
|
+ self.add_lilith(name, info, state)
|
|
|
|
|
|
|
|
logging.debug("Refresh complete.")
|
|
logging.debug("Refresh complete.")
|
|
|
|
|
|
|
@@ -289,44 +309,47 @@ class View():
|
|
|
self.refresh_needed = False
|
|
self.refresh_needed = False
|
|
|
else:
|
|
else:
|
|
|
for name, info in self.model.nodes.items():
|
|
for name, info in self.model.nodes.items():
|
|
|
- if info is None:
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
|
|
+ #logging.debug(f"update_view(): found {name}, {info}")
|
|
|
# Check for new nodes or update existing slots.
|
|
# Check for new nodes or update existing slots.
|
|
|
- if name not in self.active_nodes:
|
|
|
|
|
- self.add_node(name, info)
|
|
|
|
|
|
|
+ if name not in self.nodes:
|
|
|
|
|
+ logging.debug(f"update_view(): (node) updating node state")
|
|
|
|
|
+ state = self.update_node_state(info)
|
|
|
|
|
+ self.add_node(name, info, state)
|
|
|
else:
|
|
else:
|
|
|
start_index = self.update_node(name, info)
|
|
start_index = self.update_node(name, info)
|
|
|
if start_index is not None:
|
|
if start_index is not None:
|
|
|
self.update_slots(name, info)
|
|
self.update_slots(name, info)
|
|
|
-
|
|
|
|
|
- for name, info in self.model.liliths.items():
|
|
|
|
|
- if name not in self.active_nodes:
|
|
|
|
|
- self.add_lilith(name, info)
|
|
|
|
|
-
|
|
|
|
|
# Check for outbound or inbound connections coming
|
|
# Check for outbound or inbound connections coming
|
|
|
# online or going offline, which requires a redraw.
|
|
# online or going offline, which requires a redraw.
|
|
|
if 'outbound' in info:
|
|
if 'outbound' in info:
|
|
|
for i, (addr, id) in info['outbound'].items():
|
|
for i, (addr, id) in info['outbound'].items():
|
|
|
- if id > 0 and id not in self.active_sessions:
|
|
|
|
|
|
|
+ if id > 0 and id not in self.sessions:
|
|
|
logging.debug(f"Outbound {id}, {addr} came online.")
|
|
logging.debug(f"Outbound {id}, {addr} came online.")
|
|
|
self.refresh_needed = True
|
|
self.refresh_needed = True
|
|
|
break
|
|
break
|
|
|
|
|
|
|
|
if 'inbound' in info:
|
|
if 'inbound' in info:
|
|
|
for key, addr in info['inbound'].items():
|
|
for key, addr in info['inbound'].items():
|
|
|
- if key in self.active_sessions and not addr:
|
|
|
|
|
|
|
+ if key in self.sessions and not addr:
|
|
|
logging.debug(f"Inbound {key} went offline.")
|
|
logging.debug(f"Inbound {key} went offline.")
|
|
|
# Delete this key from the model.
|
|
# Delete this key from the model.
|
|
|
del(info['inbound'][f'{key}'])
|
|
del(info['inbound'][f'{key}'])
|
|
|
self.refresh_needed = True
|
|
self.refresh_needed = True
|
|
|
break
|
|
break
|
|
|
- if key not in self.active_sessions:
|
|
|
|
|
|
|
+ if key not in self.sessions:
|
|
|
logging.debug(f"Inbound {key}, {addr} came online.")
|
|
logging.debug(f"Inbound {key}, {addr} came online.")
|
|
|
self.refresh_needed = True
|
|
self.refresh_needed = True
|
|
|
break
|
|
break
|
|
|
|
|
|
|
|
- self.update_left_box()
|
|
|
|
|
- self.update_right_box()
|
|
|
|
|
|
|
+ # Check for new lilith nodes.
|
|
|
|
|
+ for name, info in self.model.liliths.items():
|
|
|
|
|
+ if name not in self.nodes:
|
|
|
|
|
+ logging.debug(f"update_view(): (lilith) updating node state")
|
|
|
|
|
+ state = self.update_node_state(info)
|
|
|
|
|
+ self.add_lilith(name, info, state)
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.update_lilith(name, info)
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ self.update_right_box()
|
|
|
evloop.call_soon(loop.draw_screen)
|
|
evloop.call_soon(loop.draw_screen)
|