Folder reorganize 1

This commit is contained in:
Rucus
2026-02-17 12:44:37 -06:00
parent ec99d85bc2
commit f0ae0ab905
17427 changed files with 2071 additions and 1059030 deletions

View File

@@ -0,0 +1,77 @@
<?php // phpcs:ignoreFile
require __DIR__ . '/../session.php';
require __DIR__ . '/../userAccess.php';
require __DIR__ . '/../includes/db.php';
header('Content-Type: application/json; charset=utf-8');
$deviceId = isset($_GET['device_id']) ? (int) $_GET['device_id'] : 0;
$minutes = isset($_GET['minutes']) ? (int) $_GET['minutes'] : 60;
$limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 0;
if ($deviceId <= 0) {
echo json_encode([
'success' => false,
'message' => 'device_id parameter is required.',
]);
exit;
}
$minutes = max(1, min(1440, $minutes));
$limit = $limit > 0 ? min($limit, 5000) : 1500;
try {
$connection = controls_db_connect();
} catch (Throwable $exception) {
echo json_encode([
'success' => false,
'message' => 'Database connection failed: ' . $exception->getMessage(),
]);
exit;
}
$startTime = (new DateTimeImmutable('now', new DateTimeZone('UTC')))
->modify(sprintf('-%d minutes', $minutes));
$startBound = $startTime->format('Y-m-d H:i:s');
try {
$statement = $connection->prepare(
'SELECT checked_at, latency_ms, status '
. 'FROM monitoring_latency_log '
. 'WHERE device_id = ? AND checked_at >= ? '
. 'ORDER BY checked_at ASC '
. 'LIMIT ?'
);
$statement->bind_param('isi', $deviceId, $startBound, $limit);
$statement->execute();
$result = $statement->get_result();
$series = [];
while ($row = $result->fetch_assoc()) {
$series[] = [
'x' => gmdate('c', strtotime($row['checked_at'])),
'y' => $row['latency_ms'] !== null ? (float) $row['latency_ms'] : null,
'status' => $row['status'],
];
}
$statement->close();
$connection->close();
echo json_encode([
'success' => true,
'data' => $series,
]);
} catch (Throwable $exception) {
if (isset($statement) && $statement instanceof mysqli_stmt) {
$statement->close();
}
$connection->close();
echo json_encode([
'success' => false,
'message' => 'Query failed: ' . $exception->getMessage(),
]);
}

View File

@@ -0,0 +1,195 @@
"""Periodic latency monitor for controls network devices."""
from __future__ import annotations
import os
import re
import signal
import subprocess
import sys
import time
from contextlib import contextmanager
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Iterable, Optional
import mysql.connector
PING_TIMEOUT_MS = int(os.environ.get("LATENCY_PING_TIMEOUT_MS", "2000"))
POLL_INTERVAL_SECONDS = int(os.environ.get("LATENCY_POLL_INTERVAL", "15"))
DB_CONFIG = {
"host": os.environ.get("CONTROLS_DB_HOST", "localhost"),
"user": os.environ.get("CONTROLS_DB_USER", "corey"),
"password": os.environ.get("CONTROLS_DB_PASSWORD", "41945549"),
"database": os.environ.get("CONTROLS_DB_NAME", "controls"),
"port": int(os.environ.get("CONTROLS_DB_PORT", "3306")),
"autocommit": False,
"connection_timeout": 5, # Add this line
}
STATUS_UP = "up"
STATUS_DOWN = "down"
STATUS_UNKNOWN = "unknown"
@dataclass
class Device:
device_id: int
host: str
label: str
@dataclass
class PingResult:
device: Device
status: str
latency_ms: Optional[int]
checked_at: datetime
@contextmanager
def mysql_connection():
print("About to connect to MySQL...") # Debug
try:
connection = mysql.connector.connect(**DB_CONFIG)
print("Connected to MySQL!") # Debug
yield connection
except Exception as e:
print(f"Error connecting to MySQL: {e}", file=sys.stderr)
raise
finally:
try:
connection.close()
except Exception:
pass
def fetch_devices(connection) -> Iterable[Device]:
cursor = connection.cursor(dictionary=True)
cursor.execute(
"SELECT id, host, label FROM monitoring_devices WHERE is_active = 1 ORDER BY id"
)
for row in cursor:
yield Device(device_id=row["id"], host=row["host"], label=row["label"])
cursor.close()
def ping_device(host: str) -> tuple[str, Optional[int]]:
command = [
"ping",
"-n",
"1",
"-w",
str(PING_TIMEOUT_MS),
host,
]
completed = subprocess.run(
command,
capture_output=True,
text=True,
check=False,
)
output = completed.stdout + completed.stderr
if completed.returncode != 0:
return STATUS_DOWN, None
match = re.search(r"time[=<]([0-9]+)ms", output)
if match:
return STATUS_UP, int(match.group(1))
return STATUS_UNKNOWN, None
def record_latency(connection, result: PingResult) -> None:
cursor = connection.cursor()
cursor.execute(
(
"INSERT INTO monitoring_latency_log"
" (device_id, latency_ms, status, checked_at)"
" VALUES (%s, %s, %s, %s)"
),
(
result.device.device_id,
result.latency_ms,
result.status,
result.checked_at,
),
)
connection.commit()
cursor.close()
def monitor_once() -> None:
print("monitor_once() called") # Debug
try:
print("Opening DB connection...")
with mysql_connection() as connection:
print("DB connection opened.")
try:
devices = list(fetch_devices(connection))
print(f"Fetched {len(devices)} devices") # Add this line
except Exception as fetch_err:
print(f"Error fetching devices: {fetch_err}", file=sys.stderr)
return
if not devices:
print("No active devices found in monitoring_devices table.")
return
for device in devices:
print(f"Pinging device: {device.label} ({device.host})") # Debug
status, latency = ping_device(device.host)
result = PingResult(
device=device,
status=status,
latency_ms=latency,
checked_at=datetime.now(timezone.utc),
)
try:
record_latency(connection, result)
except Exception as log_err:
print(f"Error logging latency: {log_err}", file=sys.stderr)
latency_display = f"{latency} ms" if latency is not None else "n/a"
print(
f"[{result.checked_at.isoformat()}] {device.label}"
f" ({device.host}) -> {status} ({latency_display})"
)
except mysql.connector.Error as error:
print(f"MySQL error: {error}", file=sys.stderr)
except Exception as error: # pylint: disable=broad-except
print(f"Unexpected error: {error}", file=sys.stderr)
def run_forever() -> None:
stop_requested = False
def _signal_handler(signum, frame): # noqa: D401, ANN001, ANN202
nonlocal stop_requested
stop_requested = True
print(f"Received signal {signum}; shutting down...")
signal.signal(signal.SIGINT, _signal_handler)
signal.signal(signal.SIGTERM, _signal_handler)
while not stop_requested:
monitor_once()
if stop_requested:
break
time.sleep(POLL_INTERVAL_SECONDS)
if __name__ == "__main__":
print("Script started") # Debug
if not DB_CONFIG.get("user") or DB_CONFIG.get("password") is None:
print(
"Database credentials are not set."
" Provide CONTROLS_DB_USER and CONTROLS_DB_PASSWORD env vars.",
file=sys.stderr,
)
sys.exit(1)
if os.environ.get("LATENCY_RUN_ONCE") == "1":
monitor_once()
else:
run_forever()

View File

@@ -0,0 +1 @@
mysql-connector-python>=9.0.0

24
monitoring/schema.sql Normal file
View File

@@ -0,0 +1,24 @@
-- Devices being monitored for latency
CREATE TABLE monitoring_devices (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
host VARCHAR(255) NOT NULL,
label VARCHAR(255) NOT NULL,
is_active TINYINT(1) NOT NULL DEFAULT 1,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_monitoring_devices_host (host)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Individual latency samples captured by the watcher
CREATE TABLE monitoring_latency_log (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
device_id INT UNSIGNED NOT NULL,
latency_ms INT UNSIGNED NULL,
status ENUM('up','down','unknown') NOT NULL,
checked_at DATETIME NOT NULL,
PRIMARY KEY (id),
KEY idx_monitoring_latency_device_checked (device_id, checked_at),
CONSTRAINT fk_monitoring_latency_device
FOREIGN KEY (device_id) REFERENCES monitoring_devices(id)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

15
monitoring/test_mysql.py Normal file
View File

@@ -0,0 +1,15 @@
import mysql.connector
try:
conn = mysql.connector.connect(
host="localhost",
user="corey",
password="41945549",
database="controls",
port=3306,
connection_timeout=5,
)
print("Connected!")
conn.close()
except Exception as e:
print(f"Connection failed: {e}")