Update: Code formatting
All checks were successful
CI/CD Pipeline / Unit Tests (Python 3.10) (push) Successful in 9m23s
CI/CD Pipeline / Unit Tests (Python 3.11) (push) Successful in 9m19s
CI/CD Pipeline / Unit Tests (Python 3.9) (push) Successful in 9m21s
CI/CD Pipeline / Security Scanning (push) Successful in 16s
CI/CD Pipeline / Code Quality & Linting (push) Successful in 44s
CI/CD Pipeline / Integration Tests (push) Successful in 9m14s
CI/CD Pipeline / Build Docker Image (push) Successful in 35s
CI/CD Pipeline / Generate Test Report (push) Successful in 4s
CI/CD Pipeline / CI/CD Pipeline Status (push) Successful in 2s
All checks were successful
CI/CD Pipeline / Unit Tests (Python 3.10) (push) Successful in 9m23s
CI/CD Pipeline / Unit Tests (Python 3.11) (push) Successful in 9m19s
CI/CD Pipeline / Unit Tests (Python 3.9) (push) Successful in 9m21s
CI/CD Pipeline / Security Scanning (push) Successful in 16s
CI/CD Pipeline / Code Quality & Linting (push) Successful in 44s
CI/CD Pipeline / Integration Tests (push) Successful in 9m14s
CI/CD Pipeline / Build Docker Image (push) Successful in 35s
CI/CD Pipeline / Generate Test Report (push) Successful in 4s
CI/CD Pipeline / CI/CD Pipeline Status (push) Successful in 2s
This commit is contained in:
@@ -113,7 +113,7 @@ jobs:
|
||||
- name: Run flake8
|
||||
run: |
|
||||
flake8 pterodisbot.py server_metrics_graphs.py \
|
||||
--max-line-length=120 \
|
||||
--max-line-length=140 \
|
||||
--ignore=E501,W503,E203 \
|
||||
--exclude=venv,__pycache__,build,dist \
|
||||
--statistics \
|
||||
@@ -124,19 +124,19 @@ jobs:
|
||||
run: |
|
||||
pylint pterodisbot.py server_metrics_graphs.py \
|
||||
--disable=C0111,C0103,R0913,R0914,R0915,W0718 \
|
||||
--max-line-length=120 \
|
||||
--max-line-length=140 \
|
||||
--output-format=text \
|
||||
--reports=y > pylint-report.txt || true
|
||||
continue-on-error: true
|
||||
|
||||
- name: Check code formatting with black
|
||||
run: |
|
||||
black --check --line-length=120 --diff pterodisbot.py server_metrics_graphs.py | tee black-report.txt
|
||||
black --check --line-length=140 --diff pterodisbot.py server_metrics_graphs.py | tee black-report.txt
|
||||
continue-on-error: true
|
||||
|
||||
- name: Check import ordering
|
||||
run: |
|
||||
isort --check-only --profile black --line-length=120 pterodisbot.py server_metrics_graphs.py
|
||||
isort --check-only --profile black --line-length=140 pterodisbot.py server_metrics_graphs.py
|
||||
continue-on-error: true
|
||||
|
||||
- name: Type checking with mypy
|
||||
|
||||
876
pterodisbot.py
876
pterodisbot.py
File diff suppressed because it is too large
Load Diff
@@ -6,18 +6,20 @@ Generates line graphs as PNG images for embedding in Discord messages.
|
||||
"""
|
||||
|
||||
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
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, Tuple, Optional
|
||||
from datetime import datetime
|
||||
from typing import Dict, Optional
|
||||
import io
|
||||
import logging
|
||||
import math
|
||||
|
||||
matplotlib.use("Agg") # Use non-interactive backend for server environments
|
||||
|
||||
# Get the logger from the main bot module
|
||||
logger = logging.getLogger('pterodisbot')
|
||||
logger = logging.getLogger("pterodisbot")
|
||||
|
||||
|
||||
class ServerMetricsGraphs:
|
||||
"""
|
||||
@@ -50,9 +52,13 @@ class ServerMetricsGraphs:
|
||||
# Track if we have enough data for meaningful graphs (at least 2 points)
|
||||
self.has_sufficient_data = False
|
||||
|
||||
logger.debug(f"Initialized metrics tracking for server {server_name} ({server_id})")
|
||||
logger.debug(
|
||||
f"Initialized metrics tracking for server {server_name} ({server_id})"
|
||||
)
|
||||
|
||||
def add_data_point(self, cpu_percent: float, memory_mb: float, timestamp: Optional[datetime] = None):
|
||||
def add_data_point(
|
||||
self, cpu_percent: float, memory_mb: float, timestamp: Optional[datetime] = None
|
||||
):
|
||||
"""
|
||||
Add a new data point to the metrics history.
|
||||
|
||||
@@ -70,7 +76,9 @@ class ServerMetricsGraphs:
|
||||
# Update sufficient data flag
|
||||
self.has_sufficient_data = len(self.data_points) >= 2
|
||||
|
||||
logger.debug(f"Added metrics data point for {self.server_name}: CPU={cpu_percent}%, Memory={memory_mb}MB")
|
||||
logger.debug(
|
||||
f"Added metrics data point for {self.server_name}: CPU={cpu_percent}%, Memory={memory_mb}MB"
|
||||
)
|
||||
|
||||
def _calculate_cpu_scale_limit(self, max_cpu_value: float) -> int:
|
||||
"""
|
||||
@@ -97,7 +105,9 @@ class ServerMetricsGraphs:
|
||||
BytesIO object containing PNG image data, or None if insufficient data
|
||||
"""
|
||||
if not self.has_sufficient_data:
|
||||
logger.debug(f"Insufficient data for CPU graph generation: {self.server_name}")
|
||||
logger.debug(
|
||||
f"Insufficient data for CPU graph generation: {self.server_name}"
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -110,61 +120,78 @@ class ServerMetricsGraphs:
|
||||
cpu_scale_limit = self._calculate_cpu_scale_limit(max_cpu)
|
||||
|
||||
# Create figure with dark theme styling
|
||||
plt.style.use('dark_background')
|
||||
plt.style.use("dark_background")
|
||||
fig, ax = plt.subplots(figsize=(8, 4), dpi=100)
|
||||
fig.patch.set_facecolor('#2f3136') # Discord dark theme background
|
||||
ax.set_facecolor('#36393f') # Slightly lighter for graph area
|
||||
fig.patch.set_facecolor("#2f3136") # Discord dark theme background
|
||||
ax.set_facecolor("#36393f") # Slightly lighter for graph area
|
||||
|
||||
# Plot CPU line with gradient fill
|
||||
line = ax.plot(timestamps, cpu_values, color='#7289da', linewidth=2.5, marker='o', markersize=4)
|
||||
ax.fill_between(timestamps, cpu_values, alpha=0.3, color='#7289da')
|
||||
ax.fill_between(timestamps, cpu_values, alpha=0.3, color="#7289da")
|
||||
|
||||
# Customize axes with dynamic scaling
|
||||
ax.set_ylabel('CPU Usage (%)', color='#ffffff', fontsize=10)
|
||||
ax.set_ylabel("CPU Usage (%)", color="#ffffff", fontsize=10)
|
||||
ax.set_ylim(0, cpu_scale_limit)
|
||||
|
||||
# Add horizontal grid lines at 100% increments for better readability
|
||||
for i in range(100, cpu_scale_limit + 1, 100):
|
||||
ax.axhline(y=i, color='#ffffff', alpha=0.2, linestyle='--', linewidth=0.8)
|
||||
ax.axhline(
|
||||
y=i, color="#ffffff", alpha=0.2, linestyle="--", linewidth=0.8
|
||||
)
|
||||
|
||||
# Format time axis
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S"))
|
||||
ax.xaxis.set_major_locator(mdates.SecondLocator(interval=20))
|
||||
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right', color='#ffffff', fontsize=8)
|
||||
plt.setp(
|
||||
ax.xaxis.get_majorticklabels(),
|
||||
rotation=45,
|
||||
ha="right",
|
||||
color="#ffffff",
|
||||
fontsize=8,
|
||||
)
|
||||
|
||||
# Style the graph
|
||||
ax.tick_params(colors='#ffffff', labelsize=8)
|
||||
ax.grid(True, alpha=0.3, color='#ffffff')
|
||||
ax.spines['bottom'].set_color('#ffffff')
|
||||
ax.spines['left'].set_color('#ffffff')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.tick_params(colors="#ffffff", labelsize=8)
|
||||
ax.grid(True, alpha=0.3, color="#ffffff")
|
||||
ax.spines["bottom"].set_color("#ffffff")
|
||||
ax.spines["left"].set_color("#ffffff")
|
||||
ax.spines["top"].set_visible(False)
|
||||
ax.spines["right"].set_visible(False)
|
||||
|
||||
# Add title with scale info for multi-vCPU servers
|
||||
title = f'{self.server_name} - CPU Usage'
|
||||
title = f"{self.server_name} - CPU Usage"
|
||||
if cpu_scale_limit > 100:
|
||||
estimated_vcpus = cpu_scale_limit // 100
|
||||
title += f' (~{estimated_vcpus} vCPU cores)'
|
||||
ax.set_title(title, color='#ffffff', fontsize=12, pad=20)
|
||||
title += f" (~{estimated_vcpus} vCPU cores)"
|
||||
ax.set_title(title, color="#ffffff", fontsize=12, pad=20)
|
||||
|
||||
# Tight layout to prevent label cutoff
|
||||
plt.tight_layout()
|
||||
|
||||
# Save to BytesIO
|
||||
img_buffer = io.BytesIO()
|
||||
plt.savefig(img_buffer, format='png', facecolor='#2f3136', edgecolor='none',
|
||||
bbox_inches='tight', dpi=100)
|
||||
plt.savefig(
|
||||
img_buffer,
|
||||
format="png",
|
||||
facecolor="#2f3136",
|
||||
edgecolor="none",
|
||||
bbox_inches="tight",
|
||||
dpi=100,
|
||||
)
|
||||
img_buffer.seek(0)
|
||||
|
||||
# Clean up matplotlib resources
|
||||
plt.close(fig)
|
||||
|
||||
logger.debug(f"Generated CPU graph for {self.server_name} (scale: 0-{cpu_scale_limit}%)")
|
||||
logger.debug(
|
||||
f"Generated CPU graph for {self.server_name} (scale: 0-{cpu_scale_limit}%)"
|
||||
)
|
||||
return img_buffer
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to generate CPU graph for {self.server_name}: {str(e)}")
|
||||
plt.close('all') # Clean up any remaining figures
|
||||
logger.error(
|
||||
f"Failed to generate CPU graph for {self.server_name}: {str(e)}"
|
||||
)
|
||||
plt.close("all") # Clean up any remaining figures
|
||||
return None
|
||||
|
||||
def generate_memory_graph(self) -> Optional[io.BytesIO]:
|
||||
@@ -175,7 +202,9 @@ class ServerMetricsGraphs:
|
||||
BytesIO object containing PNG image data, or None if insufficient data
|
||||
"""
|
||||
if not self.has_sufficient_data:
|
||||
logger.debug(f"Insufficient data for memory graph generation: {self.server_name}")
|
||||
logger.debug(
|
||||
f"Insufficient data for memory graph generation: {self.server_name}"
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -184,42 +213,58 @@ class ServerMetricsGraphs:
|
||||
memory_values = [point[2] for point in self.data_points]
|
||||
|
||||
# Create figure with dark theme styling
|
||||
plt.style.use('dark_background')
|
||||
plt.style.use("dark_background")
|
||||
fig, ax = plt.subplots(figsize=(8, 4), dpi=100)
|
||||
fig.patch.set_facecolor('#2f3136') # Discord dark theme background
|
||||
ax.set_facecolor('#36393f') # Slightly lighter for graph area
|
||||
fig.patch.set_facecolor("#2f3136") # Discord dark theme background
|
||||
ax.set_facecolor("#36393f") # Slightly lighter for graph area
|
||||
|
||||
# Plot memory line with gradient fill
|
||||
line = ax.plot(timestamps, memory_values, color='#43b581', linewidth=2.5, marker='o', markersize=4)
|
||||
ax.fill_between(timestamps, memory_values, alpha=0.3, color='#43b581')
|
||||
ax.fill_between(timestamps, memory_values, alpha=0.3, color="#43b581")
|
||||
|
||||
# Customize axes
|
||||
ax.set_ylabel('Memory Usage (MB)', color='#ffffff', fontsize=10)
|
||||
ax.set_ylabel("Memory Usage (MB)", color="#ffffff", fontsize=10)
|
||||
ax.set_ylim(0, max(memory_values) * 1.1) # Dynamic scaling with 10% padding
|
||||
|
||||
# Format time axis
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S"))
|
||||
ax.xaxis.set_major_locator(mdates.SecondLocator(interval=20))
|
||||
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right', color='#ffffff', fontsize=8)
|
||||
plt.setp(
|
||||
ax.xaxis.get_majorticklabels(),
|
||||
rotation=45,
|
||||
ha="right",
|
||||
color="#ffffff",
|
||||
fontsize=8,
|
||||
)
|
||||
|
||||
# Style the graph
|
||||
ax.tick_params(colors='#ffffff', labelsize=8)
|
||||
ax.grid(True, alpha=0.3, color='#ffffff')
|
||||
ax.spines['bottom'].set_color('#ffffff')
|
||||
ax.spines['left'].set_color('#ffffff')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.tick_params(colors="#ffffff", labelsize=8)
|
||||
ax.grid(True, alpha=0.3, color="#ffffff")
|
||||
ax.spines["bottom"].set_color("#ffffff")
|
||||
ax.spines["left"].set_color("#ffffff")
|
||||
ax.spines["top"].set_visible(False)
|
||||
ax.spines["right"].set_visible(False)
|
||||
|
||||
# Add title
|
||||
ax.set_title(f'{self.server_name} - Memory Usage', color='#ffffff', fontsize=12, pad=20)
|
||||
ax.set_title(
|
||||
f"{self.server_name} - Memory Usage",
|
||||
color="#ffffff",
|
||||
fontsize=12,
|
||||
pad=20,
|
||||
)
|
||||
|
||||
# Tight layout to prevent label cutoff
|
||||
plt.tight_layout()
|
||||
|
||||
# Save to BytesIO
|
||||
img_buffer = io.BytesIO()
|
||||
plt.savefig(img_buffer, format='png', facecolor='#2f3136', edgecolor='none',
|
||||
bbox_inches='tight', dpi=100)
|
||||
plt.savefig(
|
||||
img_buffer,
|
||||
format="png",
|
||||
facecolor="#2f3136",
|
||||
edgecolor="none",
|
||||
bbox_inches="tight",
|
||||
dpi=100,
|
||||
)
|
||||
img_buffer.seek(0)
|
||||
|
||||
# Clean up matplotlib resources
|
||||
@@ -229,8 +274,10 @@ class ServerMetricsGraphs:
|
||||
return img_buffer
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to generate memory graph for {self.server_name}: {str(e)}")
|
||||
plt.close('all') # Clean up any remaining figures
|
||||
logger.error(
|
||||
f"Failed to generate memory graph for {self.server_name}: {str(e)}"
|
||||
)
|
||||
plt.close("all") # Clean up any remaining figures
|
||||
return None
|
||||
|
||||
def generate_combined_graph(self) -> Optional[io.BytesIO]:
|
||||
@@ -241,7 +288,9 @@ class ServerMetricsGraphs:
|
||||
BytesIO object containing PNG image data, or None if insufficient data
|
||||
"""
|
||||
if not self.has_sufficient_data:
|
||||
logger.debug(f"Insufficient data for combined graph generation: {self.server_name}")
|
||||
logger.debug(
|
||||
f"Insufficient data for combined graph generation: {self.server_name}"
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -255,67 +304,99 @@ class ServerMetricsGraphs:
|
||||
cpu_scale_limit = self._calculate_cpu_scale_limit(max_cpu)
|
||||
|
||||
# Create figure with two subplots
|
||||
plt.style.use('dark_background')
|
||||
plt.style.use("dark_background")
|
||||
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6), dpi=100, sharex=True)
|
||||
fig.patch.set_facecolor('#2f3136')
|
||||
fig.patch.set_facecolor("#2f3136")
|
||||
|
||||
# CPU subplot
|
||||
ax1.set_facecolor('#36393f')
|
||||
ax1.plot(timestamps, cpu_values, color='#7289da', linewidth=2.5, marker='o', markersize=4)
|
||||
ax1.fill_between(timestamps, cpu_values, alpha=0.3, color='#7289da')
|
||||
ax1.set_ylabel('CPU Usage (%)', color='#ffffff', fontsize=10)
|
||||
ax1.set_facecolor("#36393f")
|
||||
ax1.plot(
|
||||
timestamps,
|
||||
cpu_values,
|
||||
color="#7289da",
|
||||
linewidth=2.5,
|
||||
marker="o",
|
||||
markersize=4,
|
||||
)
|
||||
ax1.fill_between(timestamps, cpu_values, alpha=0.3, color="#7289da")
|
||||
ax1.set_ylabel("CPU Usage (%)", color="#ffffff", fontsize=10)
|
||||
ax1.set_ylim(0, cpu_scale_limit)
|
||||
ax1.tick_params(colors='#ffffff', labelsize=8)
|
||||
ax1.grid(True, alpha=0.3, color='#ffffff')
|
||||
ax1.tick_params(colors="#ffffff", labelsize=8)
|
||||
ax1.grid(True, alpha=0.3, color="#ffffff")
|
||||
|
||||
# Add horizontal grid lines at 100% increments for CPU subplot
|
||||
for i in range(100, cpu_scale_limit + 1, 100):
|
||||
ax1.axhline(y=i, color='#ffffff', alpha=0.2, linestyle='--', linewidth=0.8)
|
||||
ax1.axhline(
|
||||
y=i, color="#ffffff", alpha=0.2, linestyle="--", linewidth=0.8
|
||||
)
|
||||
|
||||
# Title with vCPU info if applicable
|
||||
title = f'{self.server_name} - Resource Usage'
|
||||
title = f"{self.server_name} - Resource Usage"
|
||||
if cpu_scale_limit > 100:
|
||||
estimated_vcpus = cpu_scale_limit // 100
|
||||
title += f' (~{estimated_vcpus} vCPU cores)'
|
||||
ax1.set_title(title, color='#ffffff', fontsize=12)
|
||||
title += f" (~{estimated_vcpus} vCPU cores)"
|
||||
ax1.set_title(title, color="#ffffff", fontsize=12)
|
||||
|
||||
# Memory subplot
|
||||
ax2.set_facecolor('#36393f')
|
||||
ax2.plot(timestamps, memory_values, color='#43b581', linewidth=2.5, marker='o', markersize=4)
|
||||
ax2.fill_between(timestamps, memory_values, alpha=0.3, color='#43b581')
|
||||
ax2.set_ylabel('Memory (MB)', color='#ffffff', fontsize=10)
|
||||
ax2.set_facecolor("#36393f")
|
||||
ax2.plot(
|
||||
timestamps,
|
||||
memory_values,
|
||||
color="#43b581",
|
||||
linewidth=2.5,
|
||||
marker="o",
|
||||
markersize=4,
|
||||
)
|
||||
ax2.fill_between(timestamps, memory_values, alpha=0.3, color="#43b581")
|
||||
ax2.set_ylabel("Memory (MB)", color="#ffffff", fontsize=10)
|
||||
ax2.set_ylim(0, max(memory_values) * 1.1)
|
||||
ax2.tick_params(colors='#ffffff', labelsize=8)
|
||||
ax2.grid(True, alpha=0.3, color='#ffffff')
|
||||
ax2.tick_params(colors="#ffffff", labelsize=8)
|
||||
ax2.grid(True, alpha=0.3, color="#ffffff")
|
||||
|
||||
# Format time axis (only on bottom subplot)
|
||||
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
|
||||
ax2.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S"))
|
||||
ax2.xaxis.set_major_locator(mdates.SecondLocator(interval=20))
|
||||
plt.setp(ax2.xaxis.get_majorticklabels(), rotation=45, ha='right', color='#ffffff', fontsize=8)
|
||||
plt.setp(
|
||||
ax2.xaxis.get_majorticklabels(),
|
||||
rotation=45,
|
||||
ha="right",
|
||||
color="#ffffff",
|
||||
fontsize=8,
|
||||
)
|
||||
|
||||
# Style both subplots
|
||||
for ax in [ax1, ax2]:
|
||||
ax.spines['bottom'].set_color('#ffffff')
|
||||
ax.spines['left'].set_color('#ffffff')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.spines["bottom"].set_color("#ffffff")
|
||||
ax.spines["left"].set_color("#ffffff")
|
||||
ax.spines["top"].set_visible(False)
|
||||
ax.spines["right"].set_visible(False)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
# Save to BytesIO
|
||||
img_buffer = io.BytesIO()
|
||||
plt.savefig(img_buffer, format='png', facecolor='#2f3136', edgecolor='none',
|
||||
bbox_inches='tight', dpi=100)
|
||||
plt.savefig(
|
||||
img_buffer,
|
||||
format="png",
|
||||
facecolor="#2f3136",
|
||||
edgecolor="none",
|
||||
bbox_inches="tight",
|
||||
dpi=100,
|
||||
)
|
||||
img_buffer.seek(0)
|
||||
|
||||
plt.close(fig)
|
||||
|
||||
logger.debug(f"Generated combined graph for {self.server_name} (CPU scale: 0-{cpu_scale_limit}%)")
|
||||
logger.debug(
|
||||
f"Generated combined graph for {self.server_name} (CPU scale: 0-{cpu_scale_limit}%)"
|
||||
)
|
||||
return img_buffer
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to generate combined graph for {self.server_name}: {str(e)}")
|
||||
plt.close('all')
|
||||
logger.error(
|
||||
f"Failed to generate combined graph for {self.server_name}: {str(e)}"
|
||||
)
|
||||
plt.close("all")
|
||||
return None
|
||||
|
||||
def get_data_summary(self) -> Dict[str, any]:
|
||||
@@ -327,10 +408,10 @@ class ServerMetricsGraphs:
|
||||
"""
|
||||
if not self.data_points:
|
||||
return {
|
||||
'point_count': 0,
|
||||
'has_data': False,
|
||||
'latest_cpu': 0,
|
||||
'latest_memory': 0
|
||||
"point_count": 0,
|
||||
"has_data": False,
|
||||
"latest_cpu": 0,
|
||||
"latest_memory": 0,
|
||||
}
|
||||
|
||||
# Get latest values
|
||||
@@ -344,8 +425,8 @@ class ServerMetricsGraphs:
|
||||
estimated_vcpus = cpu_scale_limit // 100
|
||||
|
||||
# Calculate trends if we have multiple points
|
||||
cpu_trend = 'stable'
|
||||
memory_trend = 'stable'
|
||||
cpu_trend = "stable"
|
||||
memory_trend = "stable"
|
||||
|
||||
if len(self.data_points) >= 2:
|
||||
first_point = self.data_points[0]
|
||||
@@ -354,21 +435,21 @@ class ServerMetricsGraphs:
|
||||
|
||||
# Determine trends (>5% change considered significant)
|
||||
if abs(cpu_change) > 5:
|
||||
cpu_trend = 'increasing' if cpu_change > 0 else 'decreasing'
|
||||
cpu_trend = "increasing" if cpu_change > 0 else "decreasing"
|
||||
|
||||
if abs(memory_change) > 50: # 50MB change threshold
|
||||
memory_trend = 'increasing' if memory_change > 0 else 'decreasing'
|
||||
memory_trend = "increasing" if memory_change > 0 else "decreasing"
|
||||
|
||||
return {
|
||||
'point_count': len(self.data_points),
|
||||
'has_data': self.has_sufficient_data,
|
||||
'latest_cpu': latest_cpu,
|
||||
'latest_memory': latest_memory,
|
||||
'cpu_trend': cpu_trend,
|
||||
'memory_trend': memory_trend,
|
||||
'cpu_scale_limit': cpu_scale_limit,
|
||||
'estimated_vcpus': estimated_vcpus,
|
||||
'time_span_minutes': len(self.data_points) * 10 / 60 # Convert to minutes
|
||||
"point_count": len(self.data_points),
|
||||
"has_data": self.has_sufficient_data,
|
||||
"latest_cpu": latest_cpu,
|
||||
"latest_memory": latest_memory,
|
||||
"cpu_trend": cpu_trend,
|
||||
"memory_trend": memory_trend,
|
||||
"cpu_scale_limit": cpu_scale_limit,
|
||||
"estimated_vcpus": estimated_vcpus,
|
||||
"time_span_minutes": len(self.data_points) * 10 / 60, # Convert to minutes
|
||||
}
|
||||
|
||||
|
||||
@@ -387,7 +468,9 @@ class ServerMetricsManager:
|
||||
self.server_graphs: Dict[str, ServerMetricsGraphs] = {}
|
||||
logger.info("Initialized ServerMetricsManager")
|
||||
|
||||
def get_or_create_server_graphs(self, server_id: str, server_name: str) -> ServerMetricsGraphs:
|
||||
def get_or_create_server_graphs(
|
||||
self, server_id: str, server_name: str
|
||||
) -> ServerMetricsGraphs:
|
||||
"""
|
||||
Get existing ServerMetricsGraphs instance or create a new one.
|
||||
|
||||
@@ -404,7 +487,9 @@ class ServerMetricsManager:
|
||||
|
||||
return self.server_graphs[server_id]
|
||||
|
||||
def add_server_data(self, server_id: str, server_name: str, cpu_percent: float, memory_mb: float):
|
||||
def add_server_data(
|
||||
self, server_id: str, server_name: str, cpu_percent: float, memory_mb: float
|
||||
):
|
||||
"""
|
||||
Add data point to a server's metrics tracking.
|
||||
|
||||
@@ -456,7 +541,9 @@ class ServerMetricsManager:
|
||||
self.remove_server(server_id)
|
||||
|
||||
if servers_to_remove:
|
||||
logger.info(f"Cleaned up metrics for {len(servers_to_remove)} inactive servers")
|
||||
logger.info(
|
||||
f"Cleaned up metrics for {len(servers_to_remove)} inactive servers"
|
||||
)
|
||||
|
||||
def get_summary(self) -> Dict[str, any]:
|
||||
"""
|
||||
@@ -466,7 +553,13 @@ class ServerMetricsManager:
|
||||
Dictionary with tracking statistics
|
||||
"""
|
||||
return {
|
||||
'total_servers': len(self.server_graphs),
|
||||
'servers_with_data': sum(1 for graphs in self.server_graphs.values() if graphs.has_sufficient_data),
|
||||
'total_data_points': sum(len(graphs.data_points) for graphs in self.server_graphs.values())
|
||||
"total_servers": len(self.server_graphs),
|
||||
"servers_with_data": sum(
|
||||
1
|
||||
for graphs in self.server_graphs.values()
|
||||
if graphs.has_sufficient_data
|
||||
),
|
||||
"total_data_points": sum(
|
||||
len(graphs.data_points) for graphs in self.server_graphs.values()
|
||||
),
|
||||
}
|
||||
Reference in New Issue
Block a user