mirror of https://github.com/oxen-io/lokinet
redo commit
parent
0a99a849a4
commit
94172e191e
@ -1,160 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
import curses
|
||||
|
||||
class Canvas():
|
||||
|
||||
def __init__(self):
|
||||
self.data = dict()
|
||||
self.win = curses.initscr()
|
||||
|
||||
def __del__(self):
|
||||
self.win.clear()
|
||||
curses.endwin()
|
||||
|
||||
def on_timer(self, event):
|
||||
"""called on timer event"""
|
||||
self.update_data()
|
||||
|
||||
def _log(self, *args):
|
||||
print("{}".format(*args))
|
||||
|
||||
def jsonrpc(self, meth, params, url="http://127.0.0.1:1190/jsonrpc"):
|
||||
r = requests.post(url, headers={"Content-Type": "application/json"}, json={"jsonrpc": "2.0", "id": '0', "method":'{}'.format(meth), "params": params})
|
||||
return r.json()
|
||||
|
||||
def update_data(self):
|
||||
"""update data from lokinet"""
|
||||
try:
|
||||
j = self.jsonrpc("llarp.admin.dumpstate", {})
|
||||
self.data = j['result']
|
||||
except:
|
||||
self.data = None
|
||||
|
||||
def _render_path(self,y, path, name):
|
||||
"""render a path at current position"""
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("({}) ".format(name))
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
y += 1
|
||||
self.win.addstr("me -> ")
|
||||
for hop in path["hops"]:
|
||||
self.win.addstr(" {} ->".format(hop['router'][:4]))
|
||||
self.win.addstr(" [{} ms latency]".format(path['intro']['latency']))
|
||||
self.win.addstr(" [{} until expire]".format(self.timeTo(path["expiresAt"])))
|
||||
if path["expiresSoon"]:
|
||||
self.win.addstr("(expiring)")
|
||||
elif path["expired"]:
|
||||
self.win.addstr("(expired)")
|
||||
return y
|
||||
|
||||
def timeTo(self, ts):
|
||||
now = time.time() * 1000
|
||||
return '{} seconds'.format(int((ts - now) / 1000))
|
||||
|
||||
def display_service(self, y, name, status):
|
||||
"""display a service at current position"""
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("service [{}]".format(name))
|
||||
build = status["buildStats"]
|
||||
ratio = build["success"] / ( build["attempts"] or 1)
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("build success: {} %".format(int(100 * ratio)))
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
paths = status["paths"]
|
||||
self.win.addstr("paths: {}".format(len(paths)))
|
||||
for path in paths:
|
||||
y = self._render_path(y, path, "inbound")
|
||||
for session in status["remoteSessions"]:
|
||||
for path in session["paths"]:
|
||||
y = self._render_path(y, path, "[active] {}".format(session["currentConvoTag"]))
|
||||
for session in status["snodeSessions"]:
|
||||
for path in session["paths"]:
|
||||
y = self._render_path(y, path, "[snode]")
|
||||
return y
|
||||
|
||||
#for k in status:
|
||||
# self.win.move(y + 1, 1)
|
||||
# y += 1
|
||||
# self.win.addstr('{}: {}'.format(k, json.dumps(status[k])))
|
||||
|
||||
def display_links(self, y, data):
|
||||
for link in data["outbound"]:
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("outbound sessions:")
|
||||
y = self.display_link(y, link)
|
||||
for link in data["inbound"]:
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("inbound sessions:")
|
||||
y = self.display_link(y, link)
|
||||
return y
|
||||
|
||||
def display_link(self, y, link):
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
sessions = link["sessions"]["established"]
|
||||
for s in sessions:
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr('{}\t[{}\ttx]\t[{}\trx]'.format(s['remoteAddr'], s['tx'], s['rx']))
|
||||
if s['sendBacklog'] > 0:
|
||||
self.win.addstr('[backlog {}]'.format(s['sendBacklog']))
|
||||
return y
|
||||
|
||||
def display_dht(self, y, data):
|
||||
y += 2
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("DHT:")
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("introset lookups")
|
||||
y = self.display_bucket(y, data["pendingIntrosetLookups"])
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("router lookups")
|
||||
return self.display_bucket(y, data["pendingRouterLookups"])
|
||||
|
||||
def display_bucket(self, y, data):
|
||||
txs = data['tx']
|
||||
self.win.addstr(" ({} lookups)".format(len(txs)))
|
||||
for tx in txs:
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr('search for {}'.format(tx['tx']['target']))
|
||||
return y
|
||||
|
||||
def display_data(self):
|
||||
"""draw main window"""
|
||||
if self.data is not None:
|
||||
self.win.clear()
|
||||
self.win.box()
|
||||
self.win.addstr(1,1, "lokinet online")
|
||||
#print(self.data)
|
||||
services = self.data["services"]
|
||||
y = 3
|
||||
for k in services:
|
||||
y = self.display_service(y, k, services[k])
|
||||
y = self.display_links(y, self.data["links"])
|
||||
y = self.display_dht(y, self.data["dht"])
|
||||
self.win.refresh()
|
||||
|
||||
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
self.update_data()
|
||||
self.display_data()
|
||||
time.sleep(0.5)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
c = Canvas()
|
||||
c.run()
|
@ -0,0 +1,266 @@
|
||||
#!/usr/bin/env python3
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
import curses
|
||||
import math
|
||||
import traceback
|
||||
|
||||
|
||||
class Monitor:
|
||||
|
||||
_speedSamples = 8
|
||||
_globalspeed = []
|
||||
|
||||
def __init__(self, url):
|
||||
self.data = dict()
|
||||
self.win = curses.initscr()
|
||||
self._url = url
|
||||
while len(self._globalspeed) < self._speedSamples:
|
||||
self._globalspeed.append((0, 0))
|
||||
|
||||
def __del__(self):
|
||||
curses.endwin()
|
||||
|
||||
def on_timer(self, event):
|
||||
"""called on timer event"""
|
||||
self.update_data()
|
||||
|
||||
def jsonrpc(self, meth, params):
|
||||
r = requests.post(
|
||||
self._url,
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"jsonrpc": "2.0",
|
||||
"id": "0",
|
||||
"method": "{}".format(meth),
|
||||
"params": params,
|
||||
},
|
||||
)
|
||||
return r.json()
|
||||
|
||||
def update_data(self):
|
||||
"""update data from lokinet"""
|
||||
try:
|
||||
j = self.jsonrpc("llarp.admin.dumpstate", {})
|
||||
self.data = j["result"]
|
||||
except Exception as ex:
|
||||
self.data = None
|
||||
|
||||
def _render_path(self, y, path, name):
|
||||
"""render a path at current position"""
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("({}) ".format(name))
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
y += 1
|
||||
self.win.addstr("me -> ")
|
||||
for hop in path["hops"]:
|
||||
self.win.addstr(" {} ->".format(hop["router"][:4]))
|
||||
self.win.addstr(" [{} ms latency]".format(path["intro"]["latency"]))
|
||||
self.win.addstr(" [{} until expire]".format(self.timeTo(path["expiresAt"])))
|
||||
if path["expiresSoon"]:
|
||||
self.win.addstr("(expiring)")
|
||||
elif path["expired"]:
|
||||
self.win.addstr("(expired)")
|
||||
return y
|
||||
|
||||
def timeTo(self, ts):
|
||||
""" return time until timestamp in seconds formatted"""
|
||||
now = time.time() * 1000
|
||||
return "{} seconds".format(int((ts - now) / 1000))
|
||||
|
||||
def speedOf(self, rate):
|
||||
"""turn int speed into string formatted"""
|
||||
units = ["B", "KB", "MB", "GB"]
|
||||
idx = 0
|
||||
while rate > 1000 and idx < len(units):
|
||||
rate /= 1000.0
|
||||
idx += 1
|
||||
return "{} {}ps".format("%.2f" % rate, units[idx])
|
||||
|
||||
def display_service(self, y, name, status):
|
||||
"""display a service at current position"""
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("service [{}]".format(name))
|
||||
build = status["buildStats"]
|
||||
ratio = build["success"] / (build["attempts"] or 1)
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("build success: {} %".format(int(100 * ratio)))
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
paths = status["paths"]
|
||||
self.win.addstr("paths: {}".format(len(paths)))
|
||||
for path in paths:
|
||||
y = self._render_path(y, path, "inbound")
|
||||
for session in status["remoteSessions"]:
|
||||
for path in session["paths"]:
|
||||
y = self._render_path(
|
||||
y, path, "[active] {}".format(session["currentConvoTag"])
|
||||
)
|
||||
for session in status["snodeSessions"]:
|
||||
for path in session["paths"]:
|
||||
y = self._render_path(y, path, "[snode]")
|
||||
return y
|
||||
|
||||
# for k in status:
|
||||
# self.win.move(y + 1, 1)
|
||||
# y += 1
|
||||
# self.win.addstr('{}: {}'.format(k, json.dumps(status[k])))
|
||||
|
||||
def display_links(self, y, data):
|
||||
self.txrate = 0
|
||||
self.rxrate = 0
|
||||
for link in data["outbound"]:
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("outbound sessions:")
|
||||
y = self.display_link(y, link)
|
||||
for link in data["inbound"]:
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("inbound sessions:")
|
||||
y = self.display_link(y, link)
|
||||
y += 2
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr(
|
||||
"global speed:\t\t[{}\ttx]\t[{}\trx]".format(
|
||||
self.speedOf(self.txrate), self.speedOf(self.rxrate)
|
||||
)
|
||||
)
|
||||
|
||||
self._globalspeed.append((self.txrate, self.rxrate))
|
||||
while len(self._globalspeed) > self._speedSamples:
|
||||
self._globalspeed.pop(0)
|
||||
return self.display_speedgraph(y + 2, self._globalspeed)
|
||||
|
||||
def display_speedgraph(self, y, samps, maxsz=20):
|
||||
""" display global speed graph """
|
||||
|
||||
def scale(x, n):
|
||||
while n > 0:
|
||||
x /= 2
|
||||
n -= 1
|
||||
return int(x)
|
||||
|
||||
txmax, rxmax = 1000, 1000
|
||||
for tx, rx in samps:
|
||||
if tx > txmax:
|
||||
txmax = tx
|
||||
if rx > rxmax:
|
||||
rxmax = rx
|
||||
|
||||
rxscale = 0
|
||||
while rxmax > maxsz:
|
||||
rxscale += 1
|
||||
rxmax /= 2
|
||||
|
||||
txscale = 0
|
||||
while txmax > maxsz:
|
||||
txscale += 1
|
||||
txmax /= 2
|
||||
|
||||
def makebar(samp, max):
|
||||
bar = "#" * samp
|
||||
pad = " " * (max - samp)
|
||||
return pad, bar
|
||||
|
||||
txlabelpad = int(txmax / 2) - 1
|
||||
rxlabelpad = int(rxmax / 2) - 1
|
||||
if txlabelpad <= 0:
|
||||
txlabelpad = 1
|
||||
if rxlabelpad <= 0:
|
||||
rxlabelpad = 1
|
||||
txlabelpad = " " * txlabelpad
|
||||
rxlabelpad = " " * rxlabelpad
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr(
|
||||
"{}tx{}{}rx{}".format(txlabelpad, txlabelpad, rxlabelpad, rxlabelpad)
|
||||
)
|
||||
for tx, rx in samps:
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
txpad, txbar = makebar(scale(tx, txscale), int(txmax))
|
||||
rxpad, rxbar = makebar(scale(rx, rxscale), int(rxmax))
|
||||
self.win.addstr("{}{}|{}{}".format(txpad, txbar, rxbar, rxpad))
|
||||
return y + 2
|
||||
|
||||
def display_link(self, y, link):
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
sessions = link["sessions"]["established"]
|
||||
for s in sessions:
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.txrate += s["tx"]
|
||||
self.rxrate += s["rx"]
|
||||
self.win.addstr(
|
||||
"{}\t[{}\ttx]\t[{}\trx]".format(
|
||||
s["remoteAddr"], self.speedOf(s["tx"]), self.speedOf(s["rx"])
|
||||
)
|
||||
)
|
||||
if s["sendBacklog"] > 0:
|
||||
self.win.addstr("[backlog {}]".format(s["sendBacklog"]))
|
||||
return y
|
||||
|
||||
def display_dht(self, y, data):
|
||||
y += 2
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("DHT:")
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("introset lookups")
|
||||
y = self.display_bucket(y, data["pendingIntrosetLookups"])
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("router lookups")
|
||||
return self.display_bucket(y, data["pendingRouterLookups"])
|
||||
|
||||
def display_bucket(self, y, data):
|
||||
txs = data["tx"]
|
||||
self.win.addstr(" ({} lookups)".format(len(txs)))
|
||||
for tx in txs:
|
||||
y += 1
|
||||
self.win.move(y, 1)
|
||||
self.win.addstr("search for {}".format(tx["tx"]["target"]))
|
||||
return y
|
||||
|
||||
def display_data(self):
|
||||
"""draw main window"""
|
||||
if self.data is not None:
|
||||
self.win.addstr(1, 1, "lokinet online")
|
||||
# print(self.data)
|
||||
services = self.data["services"] or {}
|
||||
y = 3
|
||||
try:
|
||||
y = self.display_links(y, self.data["links"])
|
||||
for k in services:
|
||||
y = self.display_service(y, k, services[k])
|
||||
y = self.display_dht(y, self.data["dht"])
|
||||
except Exception as exc:
|
||||
pass
|
||||
else:
|
||||
self.win.move(1, 1)
|
||||
self.win.addstr("lokinet offline")
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
self.win.clear()
|
||||
self.win.box()
|
||||
self.update_data()
|
||||
self.display_data()
|
||||
self.win.refresh()
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
mon = Monitor(
|
||||
"http://{}/jsonrpc".format(
|
||||
len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1:1190"
|
||||
)
|
||||
)
|
||||
mon.run()
|
Loading…
Reference in New Issue