MINI Sh3ll
#!/usr/bin/env python3
"""SWPL host metrics collector — stdlib only, no pip installs.
Collects local IP, CPU%, memory%, disk% and POSTs JSON to the central host.
Run once (for cron, recommended): python3 collector.py
Run as a loop (every 60s): python3 collector.py --loop
"""
import json, os, platform, socket, sys, time, urllib.request
# ---- settings (edit these) -------------------------------------------------
CENTRAL_URL = "https://swplapps.in/tracker/ingest.php"
TOKEN = "d0669e15e5045a9949a6aa33904a6a53435aac25843e39cca3fac19f6508153f" # must match server/config.php
DISK_PATH = "/" # filesystem to report
# Hostname is detected automatically (see detect_hostname). Optionally override
# with SWPL_HOST=... if a box's OS name doesn't match the sheet.
# ---------------------------------------------------------------------------
def detect_hostname():
"""Figure out this host's name on its own, with sensible fallbacks."""
override = os.environ.get("SWPL_HOST")
if override and override.strip():
return override.strip().split(".")[0]
name = ""
try:
name = socket.gethostname().strip()
except Exception:
pass
if not name or name.startswith("localhost"):
try:
with open("/etc/hostname") as f: # set by hostnamectl
name = f.read().strip()
except Exception:
pass
if not name or name.startswith("localhost"):
name = (platform.node() or "").strip()
if not name or name.startswith("localhost"):
# last resort: derive a stable name from the primary IP
name = "host-" + local_ip().replace(".", "-")
return name.split(".")[0] # short name, no domain
def local_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect(("8.8.8.8", 80)) # no packet sent; just picks the iface
return s.getsockname()[0]
except Exception:
return "127.0.0.1"
finally:
s.close()
def cpu_percent(interval=1.0):
def snap():
with open("/proc/stat") as f:
v = list(map(int, f.readline().split()[1:]))
idle = v[3] + v[4] # idle + iowait
return idle, sum(v)
i1, t1 = snap(); time.sleep(interval); i2, t2 = snap()
dt, di = t2 - t1, i2 - i1
return round(100 * (1 - di / dt), 1) if dt else 0.0
def mem_percent():
info = {}
with open("/proc/meminfo") as f:
for line in f:
k, _, rest = line.partition(":")
info[k] = int(rest.split()[0]) # kB
total = info["MemTotal"]
avail = info.get("MemAvailable", info.get("MemFree", 0))
return round(100 * (1 - avail / total), 1) if total else 0.0
def disk_percent(path=DISK_PATH):
st = os.statvfs(path)
total = st.f_blocks * st.f_frsize
free = st.f_bavail * st.f_frsize
return round(100 * (1 - free / total), 1) if total else 0.0
def collect():
return {
"hostname": detect_hostname(),
"ip": local_ip(),
"cpu": cpu_percent(),
"mem": mem_percent(),
"disk": disk_percent(),
}
def send(data):
body = json.dumps(data).encode()
req = urllib.request.Request(
CENTRAL_URL, data=body, method="POST",
headers={"Content-Type": "application/json", "X-Token": TOKEN},
)
with urllib.request.urlopen(req, timeout=10) as r:
return r.status
def main():
loop = "--loop" in sys.argv
while True:
try:
d = collect()
code = send(d)
print(f"{d['hostname']} cpu={d['cpu']}% mem={d['mem']}% disk={d['disk']}% -> {code}")
except Exception as e:
print("error:", e, file=sys.stderr)
if not loop:
break
time.sleep(60)
if __name__ == "__main__":
main()
OHA YOOOO