Files
leox-gpon-monitoring/app.py
Mateusz Gruszczyński 69711b46bc release
2026-01-02 22:31:35 +01:00

180 lines
7.1 KiB
Python

from flask import Flask, render_template, jsonify, Response
import logging
from datetime import datetime
import json
from config import Config
from collector import GPONCollector
from rrd_manager import RRDManager
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = Flask(__name__)
app.config.from_object(Config)
collector = GPONCollector(Config)
rrd_manager = RRDManager(Config.RRD_DIR)
@app.route('/')
def index():
return render_template('index.html', config=Config)
@app.route('/api/current')
def api_current():
data = collector.get_data()
return jsonify(data)
@app.route('/api/history/<metric>/<period>')
def history(metric, period):
valid_metrics = ['optical', 'traffic', 'fec']
valid_periods = ['1h', '6h', '12h', '24h', '3d', '7d', '14d', '30d',
'60d', '90d', '120d', '1y', '2y', '5y']
if metric not in valid_metrics:
return jsonify({'error': 'Invalid metric'}), 400
if period not in valid_periods:
return jsonify({'error': f'Invalid period. Valid: {valid_periods}'}), 400
data = rrd_manager.fetch(metric, period)
if data:
return jsonify(data)
return jsonify({'error': 'No data'}), 404
@app.route('/api/alerts')
def api_alerts():
alerts = collector.get_alerts()
return jsonify(alerts)
@app.route('/metrics')
def prometheus_metrics():
data = collector.get_data()
metrics = []
host = Config.GPON_HOST
if data.get('rx_power') is not None:
metrics.append(f'# HELP gpon_rx_power_dbm GPON RX optical power in dBm')
metrics.append(f'# TYPE gpon_rx_power_dbm gauge')
metrics.append(f'gpon_rx_power_dbm{{host="{host}"}} {data["rx_power"]:.3f}')
if data.get('tx_power') is not None:
metrics.append(f'# HELP gpon_tx_power_dbm GPON TX optical power in dBm')
metrics.append(f'# TYPE gpon_tx_power_dbm gauge')
metrics.append(f'gpon_tx_power_dbm{{host="{host}"}} {data["tx_power"]:.3f}')
if data.get('voltage') is not None:
metrics.append(f'# HELP gpon_voltage_volts GPON supply voltage in volts')
metrics.append(f'# TYPE gpon_voltage_volts gauge')
metrics.append(f'gpon_voltage_volts{{host="{host}"}} {data["voltage"]:.3f}')
if data.get('tx_bias_current') is not None:
metrics.append(f'# HELP gpon_tx_bias_ma GPON TX bias current in mA')
metrics.append(f'# TYPE gpon_tx_bias_ma gauge')
metrics.append(f'gpon_tx_bias_ma{{host="{host}"}} {data["tx_bias_current"]:.2f}')
if data.get('temperature') is not None:
metrics.append(f'# HELP gpon_temperature_celsius GPON temperature in Celsius')
metrics.append(f'# TYPE gpon_temperature_celsius gauge')
metrics.append(f'gpon_temperature_celsius{{host="{host}"}} {data["temperature"]:.2f}')
if data.get('uptime') is not None:
metrics.append(f'# HELP gpon_uptime_seconds GPON uptime in seconds')
metrics.append(f'# TYPE gpon_uptime_seconds gauge')
metrics.append(f'gpon_uptime_seconds{{host="{host}"}} {data["uptime"]}')
status_value = 1 if data.get('status') == 'online' else 0
metrics.append(f'# HELP gpon_status GPON status (1=online, 0=offline)')
metrics.append(f'# TYPE gpon_status gauge')
metrics.append(f'gpon_status{{host="{host}"}} {status_value}')
if data.get('rx_packets') is not None:
metrics.append(f'# HELP gpon_rx_packets_total Total RX packets')
metrics.append(f'# TYPE gpon_rx_packets_total counter')
metrics.append(f'gpon_rx_packets_total{{host="{host}"}} {data["rx_packets"]}')
if data.get('tx_packets') is not None:
metrics.append(f'# HELP gpon_tx_packets_total Total TX packets')
metrics.append(f'# TYPE gpon_tx_packets_total counter')
metrics.append(f'gpon_tx_packets_total{{host="{host}"}} {data["tx_packets"]}')
if data.get('rx_bytes') is not None:
metrics.append(f'# HELP gpon_rx_bytes_total Total RX bytes')
metrics.append(f'# TYPE gpon_rx_bytes_total counter')
metrics.append(f'gpon_rx_bytes_total{{host="{host}"}} {data["rx_bytes"]}')
if data.get('tx_bytes') is not None:
metrics.append(f'# HELP gpon_tx_bytes_total Total TX bytes')
metrics.append(f'# TYPE gpon_tx_bytes_total counter')
metrics.append(f'gpon_tx_bytes_total{{host="{host}"}} {data["tx_bytes"]}')
if data.get('fec_corrected') is not None:
metrics.append(f'# HELP gpon_fec_corrected_total Total FEC corrected errors')
metrics.append(f'# TYPE gpon_fec_corrected_total counter')
metrics.append(f'gpon_fec_corrected_total{{host="{host}"}} {data["fec_corrected"]}')
if data.get('fec_uncorrected') is not None:
metrics.append(f'# HELP gpon_fec_uncorrected_total Total FEC uncorrected errors')
metrics.append(f'# TYPE gpon_fec_uncorrected_total counter')
metrics.append(f'gpon_fec_uncorrected_total{{host="{host}"}} {data["fec_uncorrected"]}')
if data.get('fec_total_codewords') is not None:
metrics.append(f'# HELP gpon_fec_codewords_total Total FEC codewords')
metrics.append(f'# TYPE gpon_fec_codewords_total counter')
metrics.append(f'gpon_fec_codewords_total{{host="{host}"}} {data["fec_total_codewords"]}')
vendor = data.get('vendor_id', 'unknown').replace('"', '\\"')
serial = data.get('serial_number', 'unknown').replace('"', '\\"')
version = data.get('version', 'unknown').replace('"', '\\"')
mac = data.get('mac_address', 'unknown').replace('"', '\\"')
metrics.append(f'# HELP gpon_device_info GPON device information')
metrics.append(f'# TYPE gpon_device_info gauge')
metrics.append(f'gpon_device_info{{host="{host}",vendor="{vendor}",serial="{serial}",version="{version}",mac="{mac}"}} 1')
olt_vendor = data.get('olt_vendor_info', 'unknown').replace('"', '\\"')
olt_version = data.get('olt_version_info', 'unknown').replace('"', '\\"')
metrics.append(f'# HELP gpon_olt_info OLT information')
metrics.append(f'# TYPE gpon_olt_info gauge')
metrics.append(f'gpon_olt_info{{host="{host}",olt_vendor="{olt_vendor}",olt_version="{olt_version}"}} 1')
return Response('\n'.join(metrics) + '\n', mimetype='text/plain')
def update_rrd_loop():
import time
while True:
try:
data = collector.get_data()
if data:
rrd_manager.update(data)
except Exception as e:
logger.error(f"RRD update error: {e}")
time.sleep(Config.POLL_INTERVAL)
if __name__ == '__main__':
logger.info("=" * 70)
logger.info("GPON Monitor - Starting")
logger.info(f"Host: {Config.GPON_HOST}:{Config.GPON_PORT}")
logger.info(f"Web: http://{Config.LISTEN_HOST}:{Config.LISTEN_PORT}")
logger.info(f"RRD Directory: {Config.RRD_DIR}")
logger.info("=" * 70)
collector.start()
from threading import Thread
rrd_thread = Thread(target=update_rrd_loop, daemon=True)
rrd_thread.start()
app.run(
host=Config.LISTEN_HOST,
port=Config.LISTEN_PORT,
debug=False,
threaded=True
)