180 lines
7.1 KiB
Python
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
|
|
)
|