From ab5fcd7a5bf293a08ce8cacdfcad3d95f91966e3 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 11 Jan 2025 19:30:00 +0100 Subject: [PATCH] Added live traffic stats counting and output to rnstatus --- RNS/Reticulum.py | 20 ++++++++++++++++-- RNS/Transport.py | 43 +++++++++++++++++++++++++++++++++++++++ RNS/Utilities/rnstatus.py | 43 ++++++++++++++++++++++++++++++++++++--- RNS/__init__.py | 10 ++++----- 4 files changed, 106 insertions(+), 10 deletions(-) diff --git a/RNS/Reticulum.py b/RNS/Reticulum.py index 3426684a..6d902e9c 100755 --- a/RNS/Reticulum.py +++ b/RNS/Reticulum.py @@ -172,8 +172,8 @@ def exit_handler(): RNS.Transport.exit_handler() RNS.Identity.exit_handler() - if RNS.Profiler.ran(): - RNS.Profiler.results() + # if RNS.Profiler.ran(): + # RNS.Profiler.results() @staticmethod def sigint_handler(signal, frame): @@ -965,6 +965,18 @@ def get_interface_stats(self): else: ifstats["bitrate"] = None + if hasattr(interface, "current_rx_speed"): + if interface.current_rx_speed != None: + ifstats["rxs"] = interface.current_rx_speed + else: + ifstats["rxs"] = 0 + + if hasattr(interface, "current_tx_speed"): + if interface.current_tx_speed != None: + ifstats["txs"] = interface.current_tx_speed + else: + ifstats["txs"] = 0 + if hasattr(interface, "peers"): if interface.peers != None: ifstats["peers"] = len(interface.peers) @@ -1002,6 +1014,10 @@ def get_interface_stats(self): stats = {} stats["interfaces"] = interfaces + stats["rxb"] = RNS.Transport.traffic_rxb + stats["txb"] = RNS.Transport.traffic_txb + stats["rxs"] = RNS.Transport.speed_rx + stats["txs"] = RNS.Transport.speed_tx if Reticulum.transport_enabled(): stats["transport_id"] = RNS.Transport.identity.hash stats["transport_uptime"] = time.time()-RNS.Transport.start_time diff --git a/RNS/Transport.py b/RNS/Transport.py index 48546e69..522744ef 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -142,6 +142,12 @@ class Transport: interface_last_jobs = 0.0 interface_jobs_interval = 5.0 + traffic_rxb = 0 + traffic_txb = 0 + speed_rx = 0 + speed_tx = 0 + traffic_captured = None + identity = None @staticmethod @@ -195,6 +201,9 @@ def start(reticulum_instance): thread = threading.Thread(target=Transport.jobloop, daemon=True) thread.start() + thread = threading.Thread(target=Transport.count_traffic_loop, daemon=True) + thread.start() + if RNS.Reticulum.transport_enabled(): destination_table_path = RNS.Reticulum.storagepath+"/destination_table" tunnel_table_path = RNS.Reticulum.storagepath+"/tunnels" @@ -321,6 +330,40 @@ def prioritize_interfaces(): except Exception as e: RNS.log(f"Could not prioritize interfaces according to bitrate. The contained exception was: {e}", RNS.LOG_ERROR) + @staticmethod + def count_traffic_loop(): + while True: + time.sleep(1) + try: + rxb = 0; txb = 0; + rxs = 0; txs = 0; + for interface in Transport.interfaces: + if not hasattr(interface, "parent_interface") or interface.parent_interface == None: + if hasattr(interface, "transport_traffic_counter"): + now = time.time(); irxb = interface.rxb; itxb = interface.txb + tc = interface.transport_traffic_counter + rx_diff = irxb - tc["rxb"] + tx_diff = itxb - tc["txb"] + ts_diff = now - tc["ts"] + rxb += rx_diff; crxs = (rx_diff*8)/ts_diff + txb += tx_diff; ctxs = (tx_diff*8)/ts_diff + interface.current_rx_speed = crxs; rxs += crxs + interface.current_tx_speed = ctxs; txs += ctxs + tc["rxb"] = irxb; + tc["txb"] = itxb; + tc["ts"] = now; + + else: + interface.transport_traffic_counter = {"ts": time.time(), "rxb": interface.rxb, "txb": interface.txb} + + Transport.traffic_rxb += rxb + Transport.traffic_txb += txb + Transport.speed_rx = rxs + Transport.speed_tx = txs + + except Exception as e: + RNS.log(f"An error occurred while counting interface traffic: {e}", RNS.LOG_ERROR) + @staticmethod def jobloop(): while (True): diff --git a/RNS/Utilities/rnstatus.py b/RNS/Utilities/rnstatus.py index f22ae421..77b89994 100644 --- a/RNS/Utilities/rnstatus.py +++ b/RNS/Utilities/rnstatus.py @@ -135,7 +135,7 @@ def remote_link_established(link): def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=False, astats=False, lstats=False, sorting=None, sort_reverse=False, remote=None, management_identity=None, - remote_timeout=RNS.Transport.PATH_REQUEST_TIMEOUT, must_exit=True, rns_instance=None): + remote_timeout=RNS.Transport.PATH_REQUEST_TIMEOUT, must_exit=True, rns_instance=None, traffic_totals=False): if remote: require_shared = False @@ -228,6 +228,10 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json= interfaces.sort(key=lambda i: i["rxb"], reverse=not sort_reverse) if sorting == "tx": interfaces.sort(key=lambda i: i["txb"], reverse=not sort_reverse) + if sorting == "rxs": + interfaces.sort(key=lambda i: i["rxs"], reverse=not sort_reverse) + if sorting == "txs": + interfaces.sort(key=lambda i: i["txs"], reverse=not sort_reverse) if sorting == "traffic": interfaces.sort(key=lambda i: i["rxb"]+i["txb"], reverse=not sort_reverse) if sorting == "announces" or sorting == "announce": @@ -364,7 +368,18 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json= print(" Announces : {iaf}↑".format(iaf=RNS.prettyfrequency(ifstat["outgoing_announce_frequency"]))) print(" {iaf}↓".format(iaf=RNS.prettyfrequency(ifstat["incoming_announce_frequency"]))) - print(" Traffic : {txb}↑\n {rxb}↓".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"]))) + rxb_str = "↓"+RNS.prettysize(ifstat["rxb"]) + txb_str = "↑"+RNS.prettysize(ifstat["txb"]) + strdiff = len(rxb_str)-len(txb_str) + if strdiff > 0: + txb_str += " "*strdiff + elif strdiff < 0: + rxb_str += " "*-strdiff + + rxstat = rxb_str+" "+RNS.prettyspeed(ifstat["rxs"]) + txstat = txb_str+" "+RNS.prettyspeed(ifstat["txs"]) + + print(f" Traffic : {txstat}\n {rxstat}") lstr = "" if link_count != None and lstats: @@ -374,6 +389,19 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json= else: lstr = f" {link_count} entr{ms} in link table" + if traffic_totals: + rxb_str = "↓"+RNS.prettysize(stats["rxb"]) + txb_str = "↑"+RNS.prettysize(stats["txb"]) + strdiff = len(rxb_str)-len(txb_str) + if strdiff > 0: + txb_str += " "*strdiff + elif strdiff < 0: + rxb_str += " "*-strdiff + + rxstat = rxb_str+" "+RNS.prettyspeed(stats["rxs"]) + txstat = txb_str+" "+RNS.prettyspeed(stats["txs"]) + print(f"\n Totals : {txstat}\n {rxstat}") + if "transport_id" in stats and stats["transport_id"] != None: print("\n Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" running") if "probe_responder" in stats and stats["probe_responder"] != None: @@ -426,11 +454,19 @@ def main(must_exit=True, rns_instance=None): default=False, ) + parser.add_argument( + "-t", + "--totals", + action="store_true", + help="display traffic totals", + default=False, + ) + parser.add_argument( "-s", "--sort", action="store", - help="sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]", + help="sort interfaces by [rate, traffic, rx, tx, rxs, txs, announces, arx, atx, held]", default=None, type=str ) @@ -504,6 +540,7 @@ def main(must_exit=True, rns_instance=None): remote_timeout=args.w, must_exit=must_exit, rns_instance=rns_instance, + traffic_totals=args.totals, ) except KeyboardInterrupt: diff --git a/RNS/__init__.py b/RNS/__init__.py index 70a77e4e..7b52e795 100755 --- a/RNS/__init__.py +++ b/RNS/__init__.py @@ -481,10 +481,10 @@ def print_tag_results(tag, level): print(f"{ind} St.dev. : {prettyshorttime(stdev)}") print("") - print("\nProfiler results:\n") - for tag_name in results: - tag = results[tag_name] - if tag["super"] == None: - print_results_recursive(tag, results) + print("\nProfiler results:\n") + for tag_name in results: + tag = results[tag_name] + if tag["super"] == None: + print_results_recursive(tag, results) profile = Profiler.get_profiler \ No newline at end of file