Files
Server_Monitorizare/server.py
ske087 42989aa8df Initial server monitoring system with port 80 support
Features:
- Device management and monitoring dashboard
- Remote command execution on devices via port 80
- Auto-update coordination for multiple devices
- Database reset functionality with safety confirmations
- Server logs filtering and dedicated logging interface
- Device status monitoring and management
- SQLite database for comprehensive logging
- Web interface with Bootstrap styling
- Comprehensive error handling and logging

Key components:
- server.py: Main Flask application with all routes
- templates/: Complete web interface templates
- data/database.db: SQLite database for device logs
- UPDATE_SUMMARY.md: Development progress documentation
2025-08-14 15:53:00 +03:00

462 lines
17 KiB
Python

from flask import Flask, request, render_template, jsonify, redirect, url_for
import sqlite3
from datetime import datetime
from urllib.parse import unquote
import requests
import threading
app = Flask(__name__)
DATABASE = 'data/database.db' # Updated path for the database
# Route to handle log submissions
@app.route('/logs', methods=['POST'])
@app.route('/log', methods=['POST'])
def log_event():
try:
#print(f"Connecting to database at: {DATABASE}")
# Get the JSON payload
data = request.json
if not data:
return {"error": "Invalid or missing JSON payload"}, 400
#print(f"Received request data: {data}")
# Extract fields from the JSON payload
hostname = data.get('hostname')
device_ip = data.get('device_ip')
nume_masa = data.get('nume_masa')
log_message = data.get('log_message')
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# Validate required fields
if not hostname or not device_ip or not nume_masa or not log_message:
print("Validation failed: Missing required fields")
return {"error": "Missing required fields"}, 400
# Save the log to the database
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
cursor.execute('''
INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description)
VALUES (?, ?, ?, ?, ?)
''', (hostname, device_ip, nume_masa, timestamp, log_message))
conn.commit()
print("Log saved successfully")
return {"message": "Log saved successfully"}, 201
except sqlite3.Error as e:
print(f"Database error: {e}")
return {"error": f"Database connection failed: {e}"}, 500
except Exception as e:
print(f"Unexpected error: {e}")
return {"error": "An unexpected error occurred"}, 500
# Route to display the dashboard (excluding server logs)
@app.route('/dashboard', methods=['GET'])
def dashboard():
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
# Fetch the last 60 logs excluding server logs, ordered by timestamp in descending order
cursor.execute('''
SELECT hostname, device_ip, nume_masa, timestamp, event_description
FROM logs
WHERE hostname != 'SERVER'
ORDER BY timestamp DESC
LIMIT 60
''')
logs = cursor.fetchall()
return render_template('dashboard.html', logs=logs)
# Route to display logs for a specific device (excluding server logs)
@app.route('/device_logs/<nume_masa>', methods=['GET'])
def device_logs(nume_masa):
nume_masa = unquote(nume_masa) # Decode URL-encoded value
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
# Order logs by timestamp in descending order, excluding server logs
cursor.execute('''
SELECT hostname, nume_masa, timestamp, event_description
FROM logs
WHERE nume_masa = ? AND hostname != 'SERVER'
ORDER BY timestamp DESC
''', (nume_masa,))
logs = cursor.fetchall()
return render_template('device_logs.html', logs=logs, nume_masa=nume_masa)
@app.route('/unique_devices', methods=['GET'])
def unique_devices():
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
# Query to get unique devices with their most recent log (excluding server logs)
cursor.execute('''
SELECT hostname, device_ip, MAX(timestamp) AS last_log, event_description
FROM logs
WHERE hostname != 'SERVER'
GROUP BY hostname, device_ip
ORDER BY last_log DESC
''')
devices = cursor.fetchall()
return render_template('unique_devices.html', devices=devices)
@app.route('/hostname_logs/<hostname>', methods=['GET'])
def hostname_logs(hostname):
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
# Fetch logs for the specified hostname (excluding server logs)
cursor.execute('''
SELECT hostname, nume_masa, timestamp, event_description
FROM logs
WHERE hostname = ? AND hostname != 'SERVER'
ORDER BY timestamp DESC
''', (hostname,))
logs = cursor.fetchall()
return render_template('hostname_logs.html', logs=logs, hostname=hostname)
# Route to display server logs only
@app.route('/server_logs', methods=['GET'])
def server_logs():
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
# Fetch only server logs, ordered by timestamp in descending order
cursor.execute('''
SELECT hostname, device_ip, nume_masa, timestamp, event_description
FROM logs
WHERE hostname = 'SERVER'
ORDER BY timestamp DESC
''')
logs = cursor.fetchall()
return render_template('server_logs.html', logs=logs)
# Function to execute command on a remote device
def execute_command_on_device(device_ip, command):
"""
Send command to a device for execution
"""
try:
url = f"http://{device_ip}:80/execute_command"
payload = {"command": command}
response = requests.post(url, json=payload, timeout=30)
if response.status_code == 200:
result = response.json()
return {"success": True, "result": result}
else:
error_data = response.json() if response.content else {"error": "Unknown error"}
return {"success": False, "error": error_data}
except requests.exceptions.RequestException as e:
return {"success": False, "error": f"Connection error: {str(e)}"}
except Exception as e:
return {"success": False, "error": f"Unexpected error: {str(e)}"}
# Function to get device status
def get_device_status(device_ip):
"""
Get status information from a device
"""
try:
url = f"http://{device_ip}:80/status"
response = requests.get(url, timeout=10)
if response.status_code == 200:
return {"success": True, "status": response.json()}
else:
return {"success": False, "error": "Failed to get device status"}
except requests.exceptions.RequestException as e:
return {"success": False, "error": f"Connection error: {str(e)}"}
except Exception as e:
return {"success": False, "error": f"Unexpected error: {str(e)}"}
# Route to display device management page (excluding server)
@app.route('/device_management', methods=['GET'])
def device_management():
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
# Get unique devices excluding server
cursor.execute('''
SELECT hostname, device_ip, MAX(timestamp) AS last_log
FROM logs
WHERE hostname != 'SERVER'
GROUP BY hostname, device_ip
ORDER BY last_log DESC
''')
devices = cursor.fetchall()
return render_template('device_management.html', devices=devices)
# Route to execute command on a specific device
@app.route('/execute_command', methods=['POST'])
def execute_command():
try:
data = request.json
device_ip = data.get('device_ip')
command = data.get('command')
if not device_ip or not command:
return jsonify({"error": "device_ip and command are required"}), 400
# Execute command on device
result = execute_command_on_device(device_ip, command)
# Log the command execution attempt
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
status = "SUCCESS" if result['success'] else "FAILED"
log_message = f"Command '{command}' {status}"
if not result['success']:
log_message += f" - Error: {result['error']}"
cursor.execute('''
INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description)
VALUES (?, ?, ?, ?, ?)
''', ("SERVER", device_ip, "COMMAND", datetime.now().strftime('%Y-%m-%d %H:%M:%S'), log_message))
conn.commit()
return jsonify(result), 200 if result['success'] else 400
except Exception as e:
return jsonify({"error": f"Server error: {str(e)}"}), 500
# Route to get device status
@app.route('/device_status/<device_ip>', methods=['GET'])
def device_status(device_ip):
result = get_device_status(device_ip)
return jsonify(result), 200 if result['success'] else 400
# Route to execute command on multiple devices
@app.route('/execute_command_bulk', methods=['POST'])
def execute_command_bulk():
try:
data = request.json
device_ips = data.get('device_ips', [])
command = data.get('command')
if not device_ips or not command:
return jsonify({"error": "device_ips and command are required"}), 400
results = {}
threads = []
def execute_on_device(ip):
results[ip] = execute_command_on_device(ip, command)
# Execute commands in parallel
for ip in device_ips:
thread = threading.Thread(target=execute_on_device, args=(ip,))
threads.append(thread)
thread.start()
# Wait for all threads to complete
for thread in threads:
thread.join()
# Log bulk command execution
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
for ip, result in results.items():
status = "SUCCESS" if result['success'] else "FAILED"
log_message = f"Bulk command '{command}' {status}"
if not result['success']:
log_message += f" - Error: {result['error']}"
cursor.execute('''
INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description)
VALUES (?, ?, ?, ?, ?)
''', ("SERVER", ip, "BULK_COMMAND", datetime.now().strftime('%Y-%m-%d %H:%M:%S'), log_message))
conn.commit()
return jsonify({"results": results}), 200
except Exception as e:
return jsonify({"error": f"Server error: {str(e)}"}), 500
@app.route('/auto_update_devices', methods=['POST'])
def auto_update_devices():
"""
Trigger auto-update on selected devices
"""
try:
data = request.json
device_ips = data.get('device_ips', [])
if not device_ips:
return jsonify({"error": "device_ips list is required"}), 400
results = []
for ip in device_ips:
try:
# Send auto-update command to device
response = requests.post(
f'http://{ip}:80/auto_update',
json={},
timeout=10
)
if response.status_code == 200:
result_data = response.json()
results.append({
"device_ip": ip,
"success": True,
"status": result_data.get('status'),
"message": result_data.get('message'),
"old_version": result_data.get('old_version'),
"new_version": result_data.get('new_version')
})
# Log successful update
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
log_message = f"Auto-update: {result_data.get('message', 'Update initiated')}"
cursor.execute('''
INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description)
VALUES (?, ?, ?, ?, ?)
''', ("SERVER", ip, "AUTO_UPDATE", datetime.now().strftime('%Y-%m-%d %H:%M:%S'), log_message))
conn.commit()
else:
error_msg = f"HTTP {response.status_code}"
try:
error_data = response.json()
error_msg = error_data.get('error', error_msg)
except:
pass
results.append({
"device_ip": ip,
"success": False,
"error": error_msg
})
except requests.exceptions.Timeout:
results.append({
"device_ip": ip,
"success": False,
"error": "Request timeout"
})
except requests.exceptions.ConnectionError:
results.append({
"device_ip": ip,
"success": False,
"error": "Connection failed - device may be offline"
})
except Exception as e:
results.append({
"device_ip": ip,
"success": False,
"error": str(e)
})
# Log failed update attempt
if not results[-1]['success']:
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
log_message = f"Auto-update failed: {results[-1]['error']}"
cursor.execute('''
INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description)
VALUES (?, ?, ?, ?, ?)
''', ("SERVER", ip, "AUTO_UPDATE", datetime.now().strftime('%Y-%m-%d %H:%M:%S'), log_message))
conn.commit()
return jsonify({"results": results}), 200
except Exception as e:
return jsonify({"error": f"Server error: {str(e)}"}), 500
# Route to clear and reset the database
@app.route('/reset_database', methods=['POST'])
def reset_database():
"""
Clear all data from the database and reinitialize with fresh schema
"""
try:
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
# Get the count of logs before deletion for logging
cursor.execute('SELECT COUNT(*) FROM logs')
log_count = cursor.fetchone()[0]
# Drop the existing logs table
cursor.execute('DROP TABLE IF EXISTS logs')
# Recreate the logs table with fresh schema
cursor.execute('''
CREATE TABLE logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hostname TEXT NOT NULL,
device_ip TEXT NOT NULL,
nume_masa TEXT NOT NULL,
timestamp TEXT NOT NULL,
event_description TEXT NOT NULL
)
''')
# Insert a system log entry to mark the database reset
reset_timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
cursor.execute('''
INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description)
VALUES (?, ?, ?, ?, ?)
''', ("SERVER", "127.0.0.1", "SYSTEM", reset_timestamp, f"Database cleared and reinitialized - {log_count} logs deleted"))
conn.commit()
return jsonify({
"success": True,
"message": "Database successfully cleared and reinitialized",
"timestamp": reset_timestamp,
"deleted_count": log_count
}), 200
except sqlite3.Error as e:
return jsonify({
"success": False,
"error": f"Database error: {str(e)}"
}), 500
except Exception as e:
return jsonify({
"success": False,
"error": f"Unexpected error: {str(e)}"
}), 500
# Route to get database statistics
@app.route('/database_stats', methods=['GET'])
def database_stats():
"""
Get database statistics including log count
"""
try:
with sqlite3.connect(DATABASE) as conn:
cursor = conn.cursor()
cursor.execute('SELECT COUNT(*) FROM logs')
total_logs = cursor.fetchone()[0]
cursor.execute('SELECT COUNT(DISTINCT hostname) FROM logs')
unique_devices = cursor.fetchone()[0]
cursor.execute('SELECT MIN(timestamp), MAX(timestamp) FROM logs')
date_range = cursor.fetchone()
return jsonify({
"success": True,
"total_logs": total_logs,
"unique_devices": unique_devices,
"earliest_log": date_range[0],
"latest_log": date_range[1]
}), 200
except sqlite3.Error as e:
return jsonify({
"success": False,
"error": f"Database error: {str(e)}"
}), 500
except Exception as e:
return jsonify({
"success": False,
"error": f"Unexpected error: {str(e)}"
}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)