diff --git a/asf-cloud-server/monitor/.env b/asf-cloud-server/monitor/.env new file mode 100644 index 0000000..ab22a92 --- /dev/null +++ b/asf-cloud-server/monitor/.env @@ -0,0 +1,2 @@ +PC_PASS=ASF +PI_PASS=ASF_TB diff --git a/asf-cloud-server/monitor/Dockerfile b/asf-cloud-server/monitor/Dockerfile new file mode 100644 index 0000000..f05258d --- /dev/null +++ b/asf-cloud-server/monitor/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.11-slim + +WORKDIR /app + +RUN apt-get update && apt-get install -y \ + iputils-ping \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 8000 + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/asf-cloud-server/monitor/docker-compose.yml b/asf-cloud-server/monitor/docker-compose.yml new file mode 100644 index 0000000..a8d42b7 --- /dev/null +++ b/asf-cloud-server/monitor/docker-compose.yml @@ -0,0 +1,19 @@ +version: '3.8' + +services: + monitor-app: + build: . + container_name: monitor-app + restart: always + environment: + - PC_PASS=ASF + - PI_PASS=ASF_TB + networks: + - app-network + - caddy_network + +networks: + app-network: + driver: bridge + caddy_network: + external: true diff --git a/asf-cloud-server/monitor/main.py b/asf-cloud-server/monitor/main.py new file mode 100644 index 0000000..2c5e8b3 --- /dev/null +++ b/asf-cloud-server/monitor/main.py @@ -0,0 +1,66 @@ +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +import os +from dotenv import load_dotenv +from monitor_logic import get_ssh_data, check_web_service +import asyncio + +load_dotenv() + +app = FastAPI() + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_methods=["*"], + allow_headers=["*"], +) + +# Configuration from environment variables +PC_HOST = "asf-server.duckdns.org" +PC_PORT = 49152 +PC_USER = "asf" +PC_PASS = os.getenv("PC_PASS", "ASF") + +PI_HOST = "rpi-asf-tb.duckdns.org" +PI_PORT = 2222 +PI_USER = "asf_tb" +PI_PASS = os.getenv("PI_PASS", "ASF_TB") + +WEB_SERVICES = [ + {"name": "Gitea", "url": "https://gitea.nabd-co.com/"}, + {"name": "OpenProject", "url": "https://openproject.nabd-co.com/"}, + {"name": "Draw.io", "url": "https://drawio.nabd-co.com/"}, + {"name": "TestArena", "url": "https://testarena.nabd-co.com/"}, + {"name": "TBM", "url": "https://tbm.asf.nabd-co.com/"}, + {"name": "Board", "url": "https://board.nabd-co.com/"}, +] + +@app.get("/api/status") +async def get_status(): + # Run SSH checks in parallel + pc_task = asyncio.to_thread(get_ssh_data, PC_HOST, PC_PORT, PC_USER, PC_PASS) + pi_task = asyncio.to_thread(get_ssh_data, PI_HOST, PI_PORT, PI_USER, PI_PASS) + + # Run web checks in parallel + web_tasks = [asyncio.to_thread(check_web_service, s["url"]) for s in WEB_SERVICES] + + pc_res, pi_res, *web_res = await asyncio.gather(pc_task, pi_task, *web_tasks) + + services = [] + for i, res in enumerate(web_res): + services.append({ + "name": WEB_SERVICES[i]["name"], + "url": WEB_SERVICES[i]["url"], + **res + }) + + return { + "pc": pc_res, + "pi": pi_res, + "services": services + } + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/asf-cloud-server/monitor/monitor.html b/asf-cloud-server/monitor/monitor.html new file mode 100644 index 0000000..a2d5774 --- /dev/null +++ b/asf-cloud-server/monitor/monitor.html @@ -0,0 +1,516 @@ + + + + + + + ASF Infrastructure Monitor + + + + + + + +
+ +
+ +
+

ASF Infrastructure Monitor

+

Real-time system health and service availability

+
+ +
+ +
+
+
+ +
+

Ubuntu Server PC

+ asf-server.duckdns.org +
+
+
+ Offline +
+
+
+
+
+
+ 0% + CPU +
+
+
+
+
+ 0% + RAM +
+
+
+
+
+ Temperature + --°C +
+
+ Disk Usage + --% +
+
+
+ + +
+
+
+ +
+

Raspberry Pi

+ rpi-asf-tb.duckdns.org +
+
+
+ Offline +
+
+
+
+
+
+ 0% + CPU +
+
+
+
+
+ 0% + RAM +
+
+
+
+
+ Temperature + --°C +
+
+ Disk Usage + --% +
+
+
+ + +
+
+
+ +

Web Services

+
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/asf-cloud-server/monitor/monitor_logic.py b/asf-cloud-server/monitor/monitor_logic.py new file mode 100644 index 0000000..42b1638 --- /dev/null +++ b/asf-cloud-server/monitor/monitor_logic.py @@ -0,0 +1,59 @@ +import paramiko +import requests +import time +import re + +def get_ssh_data(host, port, user, password): + try: + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect(host, port=port, username=user, password=password, timeout=10) + + # CPU Usage + stdin, stdout, stderr = ssh.exec_command("top -bn1 | grep 'Cpu(s)' | sed 's/.*, *\\([0-9.]*\\)%* id.*/\\1/' | awk '{print 100 - $1}'") + cpu_usage = stdout.read().decode().strip() + + # CPU Temp + # For Ubuntu Server + stdin, stdout, stderr = ssh.exec_command("cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null || vcgencmd measure_temp 2>/dev/null") + temp_raw = stdout.read().decode().strip() + if "temp=" in temp_raw: + cpu_temp = temp_raw.replace("temp=", "").replace("'C", "") + elif temp_raw: + cpu_temp = str(float(temp_raw) / 1000) + else: + cpu_temp = "N/A" + + # RAM Status + stdin, stdout, stderr = ssh.exec_command("free -m | awk 'NR==2{printf \"%.2f\", $3*100/$2 }'") + ram_usage = stdout.read().decode().strip() + + # Disk Space + stdin, stdout, stderr = ssh.exec_command("df -h / | awk 'NR==2{print $5}' | sed 's/%//'") + disk_usage = stdout.read().decode().strip() + + ssh.close() + return { + "status": "online", + "cpu": cpu_usage, + "temp": cpu_temp, + "ram": ram_usage, + "disk": disk_usage + } + except Exception as e: + return { + "status": "offline", + "error": str(e) + } + +def check_web_service(url): + try: + start_time = time.time() + response = requests.get(url, timeout=5) + latency = int((time.time() - start_time) * 1000) + if response.status_code == 200: + return {"status": "online", "latency": f"{latency}ms"} + else: + return {"status": "error", "code": response.status_code} + except Exception: + return {"status": "offline"} diff --git a/asf-cloud-server/monitor/requirements.txt b/asf-cloud-server/monitor/requirements.txt new file mode 100644 index 0000000..80fd53a --- /dev/null +++ b/asf-cloud-server/monitor/requirements.txt @@ -0,0 +1,5 @@ +fastapi +uvicorn +requests +paramiko +python-dotenv diff --git a/asf-cloud-server/monitor/tbm.ico b/asf-cloud-server/monitor/tbm.ico new file mode 100644 index 0000000..249f007 Binary files /dev/null and b/asf-cloud-server/monitor/tbm.ico differ diff --git a/asf-cloud-server/monitor/testarena.png b/asf-cloud-server/monitor/testarena.png new file mode 100644 index 0000000..73e6fb6 Binary files /dev/null and b/asf-cloud-server/monitor/testarena.png differ