diff --git a/cowmesh_helpers.py b/cowmesh_helpers.py new file mode 100644 index 0000000..fec847e --- /dev/null +++ b/cowmesh_helpers.py @@ -0,0 +1,17 @@ +import paramiko + + +async def cleanup_iperf_server(node, ip, username, password, log): + await log("++ stopping iperf server on {}".format(node)) + + myconn = paramiko.SSHClient() + myconn.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + myconn.connect(ip, username=username, password=password) + + remote_cmd = 'pgrep iperf | xargs kill' + (stdin, stdout, stderr) = myconn.exec_command(remote_cmd) + await log("{}".format(stdout.read())) + await log("{}".format(type(myconn))) + await log("Options available to deal with the connections are many like\n{}".format(dir(myconn))) + myconn.close() \ No newline at end of file diff --git a/cowmesh_pi_iperf_test.py b/cowmesh_pi_iperf_test.py index 2e25108..94298cc 100644 --- a/cowmesh_pi_iperf_test.py +++ b/cowmesh_pi_iperf_test.py @@ -11,6 +11,7 @@ import asyncio, asyncssh, sys import paramiko import subprocess import json +from cowmesh_helpers import cleanup_iperf_server PROJECT_PATH = os.path.abspath(os.path.dirname(__file__)) SECRETS_PATH = os.path.join(PROJECT_PATH, "secrets.json") @@ -25,8 +26,6 @@ nodes = [ "writer", # pi in new-gazebo ] -results = {} - class CowmeshPiIperfTester: @@ -35,6 +34,7 @@ class CowmeshPiIperfTester: self.log = log self.debug = debug self.time = time + self.results = {} async def log(self, msg): print(msg) @@ -89,6 +89,14 @@ class CowmeshPiIperfTester: await self.debug_log("Options available to deal with the connections are many like\n{}".format(dir(myconn))) myconn.close() + async def cleanup_iperf_servers(self): + await self.log("++ shutting down iperf servers") + for node in nodes: + user = SECRETS["HOST_INFO"][node]["user"] + password = SECRETS["HOST_INFO"][node]["password"] + ip = SECRETS["HOST_INFO"][node]["ip"] + await cleanup_iperf_server(node=node, ip=ip, username=user, password=password, log=self.log) + async def run_test(self): await self.start_iperf_servers() @@ -101,7 +109,12 @@ class CowmeshPiIperfTester: r = await self.test_between_two_nodes(node_a, node_b) result_key = "{} -> {}".format(node_a, node_b) - results[result_key] = r + self.results[result_key] = r + + # try: + # await self.cleanup_iperf_servers() + # except: + # await self.log("error shutting down iperf servers") async def output_results(self): results_str = "" @@ -109,7 +122,7 @@ class CowmeshPiIperfTester: date = now.date() time = now.time() results_str += "**** computer-to-computer iperf results on {date:%m-%d-%Y} at {time:%H:%M}:\n\n".format(date=date, time=time) - for test_name, result in results.items(): + for test_name, result in self.results.items(): results_str += "{}: {} mbps\n".format(test_name, result) await self.log(results_str) diff --git a/cowmesh_pi_speedtest.py b/cowmesh_pi_speedtest.py new file mode 100644 index 0000000..c114188 --- /dev/null +++ b/cowmesh_pi_speedtest.py @@ -0,0 +1,97 @@ +""" +run speedtest-cli on every computer in mesh +""" +import datetime +import os +import re +import time +import asyncio, asyncssh, sys +import paramiko +import subprocess +import json +from cowmesh_helpers import cleanup_iperf_server + +PROJECT_PATH = os.path.abspath(os.path.dirname(__file__)) +SECRETS_PATH = os.path.join(PROJECT_PATH, "secrets.json") +with open(SECRETS_PATH, 'r') as f: + SECRETS = json.loads(f.read()) + +base_node = "janastunuc" # nuc in jaaga + +nodes = [ + "janastunuc", # nuc in jaaga + "solipi", # pi in guard + "writer", # pi in new-gazebo +] + + +class CowmeshPiSpeedtestTester: + + def __init__(self, log=None, debug=False): + if log: + self.log = log + self.debug = debug + self.results = {} + + async def log(self, msg): + print(msg) + + async def debug_log(self, msg): + if self.debug: + await self.log(msg) + + async def speedtest_node(self, node): + await self.log("++ running speedtest on {}".format(node)) + user = SECRETS["HOST_INFO"][node]["user"] + password = SECRETS["HOST_INFO"][node]["password"] + + myconn = paramiko.SSHClient() + myconn.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + ip = SECRETS["HOST_INFO"][node]["ip"] + myconn.connect(ip, username=user, password=password) + + remote_cmd = 'speedtest-cli --simple' + (stdin, stdout, stderr) = myconn.exec_command(remote_cmd) + output = str(stdout.read()) + await self.debug_log("output: {}".format(output)) + await self.debug_log("errors: {}".format(stderr.read())) + myconn.close() + + return output + + async def run_test(self): + + for node in nodes: + r = await self.speedtest_node(node) + result_key = "{}".format(node) + self.results[result_key] = r + + async def output_results(self): + results_str = "" + now = datetime.datetime.now() + date = now.date() + time = now.time() + results_str += "**** computer speedtests on {date:%m-%d-%Y} at {time:%H:%M}:\n\n".format(date=date, time=time) + for test_name, result in self.results.items(): + result = str(result) + result = result.replace("b'", "") + result = result.replace("'", "") + result = result.replace("\\n", " | ") + results_str += "{}: {}\n".format(test_name, result) + await self.log(results_str) + + +if __name__ == "__main__": + try: + tester = CowmeshPiSpeedtestTester() + + async def main_fun(): + await tester.run_test() + await tester.output_results() + + asyncio.get_event_loop().run_until_complete(main_fun()) + + + except (OSError, asyncssh.Error) as exc: + sys.exit('SSH connection failed: ' + str(exc)) \ No newline at end of file diff --git a/cowmesh_router_iperf_test.py b/cowmesh_router_iperf_test.py index 80f11e3..0fa7344 100644 --- a/cowmesh_router_iperf_test.py +++ b/cowmesh_router_iperf_test.py @@ -12,6 +12,8 @@ import paramiko import subprocess import json +from cowmesh_helpers import cleanup_iperf_server + PROJECT_PATH = os.path.abspath(os.path.dirname(__file__)) SECRETS_PATH = os.path.join(PROJECT_PATH, "secrets.json") with open(SECRETS_PATH, 'r') as f: @@ -35,8 +37,6 @@ host_to_ip = { "kotemanetp": "10.56.40.113" } -results = {} - class CowmeshRouterIperfTester: @@ -45,6 +45,7 @@ class CowmeshRouterIperfTester: self.log = log self.debug = debug self.time = seconds + self.results = {} async def log(self, msg): print(msg) @@ -98,6 +99,14 @@ class CowmeshRouterIperfTester: await self.debug_log("Options available to deal with the connections are many like\n{}".format(dir(myconn))) myconn.close() + async def cleanup_iperf_servers(self): + await self.log("shutting down iperf servers") + for node in nodes: + user = "root" + password = SECRETS["ROUTER_PASSWORD"] + ip = host_to_ip[node] + await cleanup_iperf_server(node=node, ip=ip, username=user, password=password, log=self.log) + async def run_test(self): await self.start_iperf_servers() @@ -110,7 +119,12 @@ class CowmeshRouterIperfTester: r = await self.test_between_two_nodes(node_a, node_b) result_key = "{} -> {}".format(node_a, node_b) - results[result_key] = r + self.results[result_key] = r + + # try: + # await self.cleanup_iperf_servers() + # except: + # await self.log("error shutting down iperf servers") async def output_results(self): results_str = "" @@ -118,7 +132,7 @@ class CowmeshRouterIperfTester: date = now.date() time = now.time() results_str += "**** iperf results on {date:%m-%d-%Y} at {time:%H:%M}:\n\n".format(date=date, time=time) - for test_name, result in results.items(): + for test_name, result in self.results.items(): results_str += "{}: {} mbps\n".format(test_name, result) await self.log(results_str) diff --git a/moonlight_analytics.py b/moonlight_analytics.py index 922dd1c..61c7c9e 100644 --- a/moonlight_analytics.py +++ b/moonlight_analytics.py @@ -6,6 +6,7 @@ from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, Messa import os import json +from cowmesh_pi_speedtest import CowmeshPiSpeedtestTester from cowmesh_router_iperf_test import CowmeshRouterIperfTester from cowmesh_pi_iperf_test import CowmeshPiIperfTester @@ -70,6 +71,21 @@ async def pi_iperf(update: Update, context: ContextTypes.DEFAULT_TYPE): await tester.output_results() +async def speedtest(update: Update, context: ContextTypes.DEFAULT_TYPE): + + async def log(msg): + await context.bot.send_message(chat_id=update.effective_chat.id, text=msg, message_thread_id=update.message.message_thread_id) + + if update.effective_chat.id != int(SECRETS["TELEGRAM_LOG_CHAT_ID"]): + await log("++ can only start test from Moonlight Bot group") + return + + await log("++ starting pi speedtest-cli speedtest") + tester = CowmeshPiSpeedtestTester(log=log) + await tester.run_test() + await tester.output_results() + + async def unknown(update: Update, context: ContextTypes.DEFAULT_TYPE): await context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand that, please run /help for a list of available commands.", message_thread_id=update.message.message_thread_id) @@ -137,6 +153,22 @@ async def nightly_pi_iperf(time=10): await tester.output_results() +async def nightly_pi_speedtest(): + token = SECRETS["TELEGRAM_TOKEN"] + application = ApplicationBuilder().token(token).build() + chat_id = SECRETS["TELEGRAM_LOG_CHAT_ID"] + message_thread_id = SECRETS.get("TELEGRAM_LOG_MESSAGE_THREAD_ID") + bot = application.bot + + async def log(msg): + await bot.send_message(chat_id=chat_id, text=msg, message_thread_id=message_thread_id) + + await log("☾☾ starting nightly speedtest-cli speedtest") + tester = CowmeshPiSpeedtestTester(log=log) + await tester.run_test() + await tester.output_results() + + def init_bot_listener(): token = SECRETS["TELEGRAM_TOKEN"] application = ApplicationBuilder().token(token).build() @@ -159,6 +191,10 @@ def init_bot_listener(): pi_iperf_handler = CommandHandler('pi_iperf', pi_iperf) application.add_handler(pi_iperf_handler) + speedtest_handler = CommandHandler('speedtest', speedtest) + application.add_handler(speedtest_handler) + + unknown_handler = MessageHandler(filters.COMMAND, unknown) application.add_handler(unknown_handler) diff --git a/nightly_test.py b/nightly_test.py index a87919e..72f0b15 100644 --- a/nightly_test.py +++ b/nightly_test.py @@ -1,5 +1,5 @@ import argparse -from moonlight_analytics import nightly_router_iperf, nightly_pi_iperf +from moonlight_analytics import nightly_router_iperf, nightly_pi_iperf, nightly_pi_speedtest import asyncio import sys @@ -9,4 +9,6 @@ if __name__ == '__main__': if sys.argv[1] == "router": asyncio.get_event_loop().run_until_complete(nightly_router_iperf(time=time)) elif sys.argv[1] == "pi": - asyncio.get_event_loop().run_until_complete(nightly_pi_iperf(time=time)) \ No newline at end of file + asyncio.get_event_loop().run_until_complete(nightly_pi_iperf(time=time)) + elif sys.argv[1] == "speedtest": + asyncio.get_event_loop().run_until_complete(nightly_pi_speedtest()) \ No newline at end of file