Add server metrics graphing feature
All checks were successful
Docker Build and Push (Multi-architecture) / build-and-push (push) Successful in 5m25s
All checks were successful
Docker Build and Push (Multi-architecture) / build-and-push (push) Successful in 5m25s
This commit is contained in:
@@ -30,7 +30,13 @@ import configparser
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
from pathlib import Path
|
||||
import generate_config
|
||||
import matplotlib
|
||||
matplotlib.use('Agg') # Use non-interactive backend for server environments
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.dates as mdates
|
||||
from collections import deque
|
||||
import io
|
||||
from server_metrics_graphs import ServerMetricsGraphs, ServerMetricsManager
|
||||
|
||||
# ==============================================
|
||||
# LOGGING SETUP
|
||||
@@ -549,10 +555,11 @@ class PterodactylBot(commands.Bot):
|
||||
self.server_cache: Dict[str, dict] = {} # Cache of server data from Pterodactyl
|
||||
self.embed_locations: Dict[str, Dict[str, int]] = {} # Tracks where embeds are posted
|
||||
self.update_lock = asyncio.Lock() # Prevents concurrent updates
|
||||
self.embed_storage_path = Path(EMBED_LOCATIONS_FILE) # File to store embed locations
|
||||
self.embed_storage_path = Path(EMBED_LOCATIONS_FILE) # File to store embed
|
||||
self.metrics_manager = ServerMetricsManager() # Data manager for metrics graphing system
|
||||
# Track previous server states and CPU usage to detect changes
|
||||
# Format: {server_id: (state, cpu_usage, last_force_update)}
|
||||
self.previous_states: Dict[str, Tuple[str, float, Optional[float]]] = {}
|
||||
self.previous_states: Dict[str, Tuple[str, float, Optional[float]]] = {}
|
||||
logger.info("Initialized PterodactylBot instance with state tracking")
|
||||
|
||||
async def setup_hook(self):
|
||||
@@ -753,9 +760,9 @@ class PterodactylBot(commands.Bot):
|
||||
embed.add_field(name="🆔 Server ID", value=f"`{identifier}`", inline=True)
|
||||
|
||||
if is_suspended:
|
||||
embed.add_field(name="📊 Status", value="⛔ Suspended", inline=True)
|
||||
embed.add_field(name="ℹ️ Status", value="⛔ Suspended", inline=True)
|
||||
else:
|
||||
embed.add_field(name="📊 Status", value="✅ Active", inline=True)
|
||||
embed.add_field(name="ℹ️ Status", value="✅ Active", inline=True)
|
||||
|
||||
# Add resource usage if server is running
|
||||
if current_state.lower() == "running":
|
||||
@@ -806,22 +813,37 @@ class PterodactylBot(commands.Bot):
|
||||
usage_text = (
|
||||
f"```properties\n"
|
||||
f"CPU: {cpu_usage:>8} / {format_limit(cpu_limit, ' %')}\n"
|
||||
f"Memory: {memory_usage:>8} / {format_limit(memory_limit, ' MB')}\n"
|
||||
f"Disk: {disk_usage:>8} / {format_limit(disk_limit, ' MB')}\n"
|
||||
f"Memory: {memory_usage:>8} / {format_limit(memory_limit, ' MiB')}\n"
|
||||
f"Disk: {disk_usage:>8} / {format_limit(disk_limit, ' MiB')}\n"
|
||||
f"```"
|
||||
)
|
||||
|
||||
embed.add_field(
|
||||
name="📈 Resource Usage",
|
||||
name="📊 Resource Usage",
|
||||
value=usage_text,
|
||||
inline=False
|
||||
)
|
||||
|
||||
embed.add_field(
|
||||
name="🌐 Network",
|
||||
value=f"⬇️ {network_rx} MB / ⬆️ {network_tx} MB",
|
||||
value=f"⬇️ {network_rx} MiB / ⬆️ {network_tx} MiB",
|
||||
inline=False
|
||||
)
|
||||
|
||||
# Add graph images if available
|
||||
server_graphs = self.metrics_manager.get_server_graphs(identifier)
|
||||
if server_graphs and server_graphs.has_sufficient_data:
|
||||
summary = server_graphs.get_data_summary()
|
||||
|
||||
# Add a field explaining the graphs
|
||||
embed.add_field(
|
||||
name="📈 Usage Trends (Last Minute)",
|
||||
value=f"Data points: {summary['point_count']}/6 • CPU trend: {summary['cpu_trend']} • Memory trend: {summary['memory_trend']}",
|
||||
inline=False
|
||||
)
|
||||
|
||||
# Set graph images (these will be attached as files in the update_status method)
|
||||
embed.set_image(url=f"attachment://metrics_graph_{identifier}.png")
|
||||
|
||||
embed.set_footer(text="Last updated")
|
||||
|
||||
@@ -860,6 +882,10 @@ class PterodactylBot(commands.Bot):
|
||||
# Update our local cache with fresh server data
|
||||
self.server_cache = {server['attributes']['identifier']: server for server in servers}
|
||||
logger.debug(f"Updated server cache with {len(servers)} servers")
|
||||
|
||||
# Clean up metrics for servers that no longer exist
|
||||
active_server_ids = list(self.server_cache.keys())
|
||||
self.metrics_manager.cleanup_old_servers(active_server_ids)
|
||||
|
||||
# Variables to track our update statistics
|
||||
update_count = 0 # Successful updates
|
||||
@@ -885,7 +911,13 @@ class PterodactylBot(commands.Bot):
|
||||
resources = await self.pterodactyl_api.get_server_resources(server_id)
|
||||
current_state = resources.get('attributes', {}).get('current_state', 'offline')
|
||||
cpu_usage = round(resources.get('attributes', {}).get('resources', {}).get('cpu_absolute', 0), 2)
|
||||
|
||||
|
||||
# Collect metrics data for running servers
|
||||
if current_state == 'running':
|
||||
memory_usage = round(resources.get('attributes', {}).get('resources', {}).get('memory_bytes', 0) / (1024 ** 2), 2)
|
||||
self.metrics_manager.add_server_data(server_id, server_name, cpu_usage, memory_usage)
|
||||
logger.debug(f"Added metrics data for {server_name}: CPU={cpu_usage}%, Memory={memory_usage}MB")
|
||||
|
||||
# Retrieve previous recorded state, CPU usage, and last force update time
|
||||
prev_state, prev_cpu, last_force_update = self.previous_states.get(server_id, (None, 0, None))
|
||||
|
||||
@@ -929,7 +961,20 @@ class PterodactylBot(commands.Bot):
|
||||
|
||||
# Fetch and update the existing message
|
||||
message = await channel.fetch_message(int(location['message_id']))
|
||||
await message.edit(embed=embed, view=view)
|
||||
# Generate and attach graph images if available
|
||||
files = []
|
||||
server_graphs = self.metrics_manager.get_server_graphs(server_id)
|
||||
if server_graphs and server_graphs.has_sufficient_data:
|
||||
# Generate CPU graph
|
||||
combined_graph = server_graphs.generate_combined_graph()
|
||||
if combined_graph:
|
||||
files.append(discord.File(combined_graph, filename=f"metrics_graph_{server_id}.png"))
|
||||
|
||||
# Update message with embed, view, and graph files
|
||||
if files:
|
||||
await message.edit(embed=embed, view=view, attachments=files)
|
||||
else:
|
||||
await message.edit(embed=embed, view=view)
|
||||
update_count += 1
|
||||
logger.debug(f"Updated status for {server_name}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user