diff --git a/miscellaneous/python/einstimer.py b/miscellaneous/python/einstimer.py new file mode 100644 index 0000000..509fe5d --- /dev/null +++ b/miscellaneous/python/einstimer.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 + +import os +import subprocess +import hashlib +import time +from datetime import datetime +import requests + +# === Configuration === +DEST_USER = "doc" +DEST_HOST = "chatwithus.live" +DEST_PATH = f"/home/{DEST_USER}/genesis_timer/" +DEST_FULL = f"{DEST_USER}@{DEST_HOST}" +LOCAL_TESTFILE = f"/home/{DEST_USER}/genesis_testfile_10mb" +REMOTE_TESTFILE = f"{DEST_PATH}genesis_testfile_10mb" +LOGFILE = f"/home/{DEST_USER}/genesis_timer.log" + +# Mastodon alert config +MASTODON_INSTANCE = "https://chatwithus.live" +MASTODON_TOKEN = "rimxBLi-eaJAcwagkmoj6UoW7Lc473tQY0cOM041Euw" # Replace with real token +ALERT_THRESHOLD_MS = 2000 # Alert if transfer takes longer than this + +# === Helpers === + +def create_test_file(path): + if not os.path.exists(path): + with open(path, "wb") as f: + f.write(os.urandom(10 * 1024 * 1024)) + +def sha256sum(filename): + h = hashlib.sha256() + with open(filename, 'rb') as f: + while chunk := f.read(8192): + h.update(chunk) + return h.hexdigest() + +def send_masto_alert(message): + headers = { + "Authorization": f"Bearer {MASTODON_TOKEN}" + } + payload = { + "status": message, + "visibility": "unlisted" + } + try: + r = requests.post(f"{MASTODON_INSTANCE}/api/v1/statuses", headers=headers, data=payload) + r.raise_for_status() + except Exception as e: + print(f"[{datetime.now()}] Mastodon alert failed: {e}") + +# === Core Function === + +def run_transfer(): + create_test_file(LOCAL_TESTFILE) + local_hash = sha256sum(LOCAL_TESTFILE) + + # Ensure remote directory exists + subprocess.run( + ["ssh", DEST_FULL, f"mkdir -p {DEST_PATH}"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + # Start transfer + start = time.time() + try: + subprocess.run( + ["scp", LOCAL_TESTFILE, f"{DEST_FULL}:{DEST_PATH}"], + check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + except subprocess.CalledProcessError as e: + error_msg = f"[{datetime.now()}] Transfer failed: {e.stderr.decode()}" + send_masto_alert("🚨 GenesisTimer Alert: Transfer failed.") + return error_msg + + end = time.time() + duration_ms = int((end - start) * 1000) + + # Check hash remotely + try: + result = subprocess.run( + ["ssh", DEST_FULL, f"sha256sum {REMOTE_TESTFILE}"], + check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + remote_hash = result.stdout.decode().split()[0] + except subprocess.CalledProcessError as e: + error_msg = f"[{datetime.now()}] Remote hash check failed: {e.stderr.decode()}" + send_masto_alert("🚨 GenesisTimer Alert: Remote hash check failed.") + return error_msg + + # Cleanup + subprocess.run(["ssh", DEST_FULL, f"rm -f {REMOTE_TESTFILE}"]) + if os.path.exists(LOCAL_TESTFILE): + os.remove(LOCAL_TESTFILE) + + match = "MATCH" if remote_hash == local_hash else "MISMATCH" + + log_entry = ( + f"[{datetime.now()}] Transfer to {DEST_FULL} took {duration_ms} ms | " + f"SHA256: {match}\n" + ) + + with open(LOGFILE, "a") as log: + log.write(log_entry) + + # 🚨 Alert if needed + if match != "MATCH" or duration_ms > ALERT_THRESHOLD_MS: + alert_msg = ( + f"🚨 GenesisTimer Alert:\n" + f"Transfer to {DEST_HOST} took {duration_ms} ms\n" + f"Hash check: {match}" + ) + send_masto_alert(alert_msg) + + return log_entry + +# === Run === +if __name__ == "__main__": + print(run_transfer()) diff --git a/miscellaneous/vps/genesisctl.sh b/miscellaneous/vps/genesisctl.sh new file mode 100755 index 0000000..1aaa532 --- /dev/null +++ b/miscellaneous/vps/genesisctl.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +# genesisctl - Genesis VPS Provisioning and Reboot CLI +# Usage: +# genesisctl provision