changed speedtest service to ookla
This commit is contained in:
@@ -1,14 +1,20 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
RUN apt-get update && apt-get install -y cron git && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
COPY requirements.txt .
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
# Install cron, curl, and the official Ookla Speedtest repository
|
||||
RUN apt-get update && apt-get install -y cron curl \
|
||||
&& curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash \
|
||||
&& apt-get install -y speedtest \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY . .
|
||||
COPY crontab /etc/cron.d/speedtest
|
||||
|
||||
RUN chmod 0644 /etc/cron.d/speedtest && crontab /etc/cron.d/speedtest
|
||||
# Set up the cron job
|
||||
COPY crontab /etc/cron.d/speedtest-cron
|
||||
RUN chmod 0644 /etc/cron.d/speedtest-cron
|
||||
RUN crontab /etc/cron.d/speedtest-cron
|
||||
|
||||
ENV DB_PATH=/app/speedtests.db
|
||||
|
||||
# Dump env vars for cron and start the daemon
|
||||
CMD printenv > /etc/environment && cron -f
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import os
|
||||
|
||||
DB_PATH = os.getenv("DB_PATH")
|
||||
DB_PATH = os.getenv("DB_PATH") or "speedtest.db"
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
*/15 * * * * cd /app && /usr/local/bin/python3 run_speedtest.py >> /proc/1/fd/1 2>> /proc/1/fd/2
|
||||
# Empty line required at the end!
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import sqlite3
|
||||
import time
|
||||
from config import DB_PATH
|
||||
|
||||
|
||||
def init_db(db_path=DB_PATH):
|
||||
conn = sqlite3.connect(db_path)
|
||||
c = conn.cursor()
|
||||
@@ -17,73 +17,48 @@ def init_db(db_path=DB_PATH):
|
||||
location_region TEXT,
|
||||
latency REAL,
|
||||
jitter REAL,
|
||||
down_100kB REAL,
|
||||
down_1MB REAL,
|
||||
down_10MB REAL,
|
||||
down_25MB REAL,
|
||||
down_90th REAL,
|
||||
up_100kB REAL,
|
||||
up_1MB REAL,
|
||||
up_10MB REAL,
|
||||
up_90th REAL
|
||||
down_100kB REAL, down_1MB REAL, down_10MB REAL, down_25MB REAL, down_90th REAL,
|
||||
up_100kB REAL, up_1MB REAL, up_10MB REAL, up_90th REAL
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def insert_result(results: dict|None, db_path=DB_PATH):
|
||||
def insert_result(data: dict|None, db_path=DB_PATH):
|
||||
conn = sqlite3.connect(db_path)
|
||||
c = conn.cursor()
|
||||
|
||||
# If the test failed entirely, store it as a failure with timestamp now
|
||||
if results is None or "tests" not in results:
|
||||
from time import time
|
||||
c.execute("INSERT INTO speed_tests (timestamp, failed) VALUES (?, ?)", (time(), True))
|
||||
if data is None:
|
||||
c.execute("INSERT INTO speed_tests (timestamp, failed) VALUES (?, ?)", (time.time(), True))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
|
||||
tests = results.get("tests", {})
|
||||
meta = results.get("meta", {})
|
||||
# Convert bytes per second to Megabits per second (Mbps)
|
||||
def to_mbps(bytes_per_sec):
|
||||
return (bytes_per_sec * 8) / 1000000 if bytes_per_sec else None
|
||||
|
||||
# Get a consistent timestamp from any TestResult (or fallback to now)
|
||||
from time import time as now
|
||||
sample_test = next(iter(tests.values()), None)
|
||||
timestamp = sample_test.time if sample_test else now()
|
||||
|
||||
print(tests)
|
||||
print(meta)
|
||||
|
||||
def get(tests, key):
|
||||
return tests[key].value if key in tests else None
|
||||
# Ookla provides a UTC ISO timestamp, but we'll stick to local Unix time for consistency with your old data
|
||||
current_time = time.time()
|
||||
|
||||
c.execute('''
|
||||
INSERT INTO speed_tests (
|
||||
timestamp, failed, isp, ip, location_code, location_city, location_region,
|
||||
latency, jitter,
|
||||
down_100kB, down_1MB, down_10MB, down_25MB, down_90th,
|
||||
up_100kB, up_1MB, up_10MB, up_90th
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
latency, jitter, down_90th, up_90th
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''', (
|
||||
timestamp, False,
|
||||
get(tests, "isp"),
|
||||
get(meta, "ip"),
|
||||
get(meta, "location_code"),
|
||||
get(meta, "location_city"),
|
||||
get(meta, "location_region"),
|
||||
get(tests, "latency"),
|
||||
get(tests, "jitter"),
|
||||
get(tests, "100kB_down_mbps"),
|
||||
get(tests, "1MB_down_mbps"),
|
||||
get(tests, "10MB_down_mbps"),
|
||||
get(tests, "25MB_down_mbps"),
|
||||
get(tests, "90th_percentile_down_mbps"),
|
||||
get(tests, "100kB_up_mbps"),
|
||||
get(tests, "1MB_up_mbps"),
|
||||
get(tests, "10MB_up_mbps"),
|
||||
get(tests, "90th_percentile_up_mbps")
|
||||
current_time,
|
||||
False,
|
||||
data.get("isp"),
|
||||
data.get("interface", {}).get("externalIp"),
|
||||
data.get("server", {}).get("name"), # Ookla Server Name
|
||||
data.get("server", {}).get("location"), # Ookla Server City
|
||||
data.get("server", {}).get("country"), # Ookla Server Country
|
||||
data.get("ping", {}).get("latency"),
|
||||
data.get("ping", {}).get("jitter"),
|
||||
to_mbps(data.get("download", {}).get("bandwidth")),
|
||||
to_mbps(data.get("upload", {}).get("bandwidth"))
|
||||
))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
cloudflarepycli @ git+https://github.com/cato447/cloudflarepycli.git
|
||||
@@ -1,16 +1,53 @@
|
||||
from cfspeedtest import CloudflareSpeedtest
|
||||
import subprocess
|
||||
import json
|
||||
import traceback
|
||||
from db import init_db, insert_result
|
||||
|
||||
def run_test_and_save():
|
||||
try:
|
||||
tester = CloudflareSpeedtest()
|
||||
results = tester.run_all(megabits=True) # returns SuiteResults
|
||||
except Exception:
|
||||
results = None # Trigger a failed test record
|
||||
|
||||
print("==== Running Ookla Speedtest ====")
|
||||
init_db()
|
||||
insert_result(results)
|
||||
|
||||
try:
|
||||
# Corrected the typo in --accept-license
|
||||
result = subprocess.run(
|
||||
["speedtest", "--accept-license", "--accept-gdpr", "-f", "json"],
|
||||
capture_output=True, text=True, check=True
|
||||
)
|
||||
|
||||
# Ookla sometimes outputs logs and results in the same stream.
|
||||
# We need to isolate the actual result JSON.
|
||||
output_str = result.stdout.strip()
|
||||
final_data = None
|
||||
|
||||
# Split by newline in case they are separated, or scan the string
|
||||
for line in output_str.split('\n'):
|
||||
if '{"type":"result"' in line:
|
||||
# Isolate the valid JSON starting from {"type":"result"
|
||||
start_idx = line.find('{"type":"result"')
|
||||
valid_json_str = line[start_idx:]
|
||||
|
||||
try:
|
||||
final_data = json.loads(valid_json_str)
|
||||
break
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
if final_data and "download" in final_data:
|
||||
insert_result(final_data)
|
||||
print(f"Results successfully saved. Down: {final_data['download']['bandwidth'] * 8 / 1000000:.2f} Mbps")
|
||||
else:
|
||||
print("Could not find valid result JSON in output:", output_str)
|
||||
insert_result(None)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("Speedtest failed execution:")
|
||||
print("STDOUT:", e.stdout)
|
||||
print("STDERR:", e.stderr)
|
||||
insert_result(None)
|
||||
except Exception as e:
|
||||
print("Error processing results:")
|
||||
print(traceback.format_exc())
|
||||
insert_result(None)
|
||||
|
||||
print("==== Running Speedtest ====")
|
||||
run_test_and_save()
|
||||
print("==== Speedtest ended ====")
|
||||
if __name__ == "__main__":
|
||||
run_test_and_save()
|
||||
|
||||
Reference in New Issue
Block a user