Initial Commit
This commit is contained in:
17
reporter/Dockerfile
Normal file
17
reporter/Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
# Use a slim Python base
|
||||
FROM python:3.10-slim
|
||||
|
||||
# Install chrony (we only need the 'chronyc' client)
|
||||
RUN apt-get update && apt-get install -y chrony && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Python dependencies
|
||||
RUN pip install Flask
|
||||
|
||||
# Copy the app
|
||||
WORKDIR /app
|
||||
COPY main.py .
|
||||
|
||||
# Expose the port and run the app
|
||||
EXPOSE 8080
|
||||
CMD ["python", "main.py"]
|
||||
0
reporter/README.md
Normal file
0
reporter/README.md
Normal file
72
reporter/main.py
Normal file
72
reporter/main.py
Normal file
@ -0,0 +1,72 @@
|
||||
import os
|
||||
import subprocess
|
||||
import csv
|
||||
import io
|
||||
from flask import Flask, jsonify
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Config
|
||||
NODE_ID = os.environ.get("NODE_ID", "unknown-node")
|
||||
PUBLIC_IP = os.environ.get("PUBLIC_IP", "unknown-ip")
|
||||
BIND_IP = os.environ.get("BIND_IP", "127.0.0.1")
|
||||
CHRONY_HOST = "127.0.0.1"
|
||||
|
||||
def run_chronyc(command):
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["chronyc", "-h", CHRONY_HOST, "-c"] + command,
|
||||
capture_output=True, text=True, timeout=2, check=True
|
||||
)
|
||||
return result.stdout.strip()
|
||||
except Exception as e:
|
||||
print(f"Error running 'chronyc {command}': {e}")
|
||||
return None
|
||||
|
||||
def parse_tracking():
|
||||
raw_csv = run_chronyc(["tracking"])
|
||||
if not raw_csv: return {"Error": "Could not run tracking command"}
|
||||
try:
|
||||
reader = csv.reader(io.StringIO(raw_csv))
|
||||
headers = [
|
||||
"Reference ID", "Ref Source IP", "Stratum", "Ref time (UTC)", "System time",
|
||||
"Last offset", "RMS offset", "Frequency", "Residual freq",
|
||||
"Skew", "Root delay", "Root dispersion", "Update interval", "Leap status"
|
||||
]
|
||||
values = next(reader)
|
||||
return dict(zip(headers, values))
|
||||
except Exception as e:
|
||||
return {"Error": f"Failed to parse tracking CSV: {e}", "RawData": raw_csv}
|
||||
|
||||
def parse_sources():
|
||||
raw_csv = run_chronyc(["sources"])
|
||||
if not raw_csv: return []
|
||||
try:
|
||||
reader = csv.reader(io.StringIO(raw_csv))
|
||||
# --- FIX: Add 10th header 'Std Dev' ---
|
||||
headers = [
|
||||
"Mode", "State", "Name/IP address", "Stratum", "Poll", "Reach",
|
||||
"LastRx", "Last sample", "Last sample original", "Std Dev" # Was: Last sample error
|
||||
]
|
||||
sources_list = []
|
||||
for row in reader:
|
||||
if row: sources_list.append(dict(zip(headers, row)))
|
||||
return sources_list
|
||||
except Exception as e:
|
||||
print(f"Error parsing sources: {e}")
|
||||
return []
|
||||
|
||||
@app.route('/fragment.json')
|
||||
def get_fragment():
|
||||
tracking_data = parse_tracking()
|
||||
sources_data = parse_sources()
|
||||
return jsonify({
|
||||
"node_id": NODE_ID,
|
||||
"public_ip": PUBLIC_IP,
|
||||
"report_generated_time": subprocess.run(["date", "-u", "+%Y-%m-%dT%H:%M:%SZ"], capture_output=True, text=True).stdout.strip(),
|
||||
"tracking": tracking_data,
|
||||
"sources": sources_data
|
||||
})
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host=BIND_IP, port=9898)
|
||||
9
reporter/pyproject.toml
Normal file
9
reporter/pyproject.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[project]
|
||||
name = "reporter"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"flask>=3.1.2",
|
||||
]
|
||||
Reference in New Issue
Block a user