Translate all Portuguese text to English

This commit is contained in:
2025-09-25 21:05:41 -03:00
parent f8279933d6
commit f38689d9dd
19 changed files with 509 additions and 509 deletions

View File

@@ -17,19 +17,19 @@ from app.services.historical_analysis import HistoricalAnalysisService
logger = logging.getLogger(__name__)
# Criar router
# Create router
api_router = APIRouter()
# Inicializar serviços
# Initialize services
validation_service = ValidationService()
report_service = ReportService()
def get_k8s_client(request: Request):
"""Dependency para obter cliente Kubernetes"""
"""Dependency to get Kubernetes client"""
return request.app.state.k8s_client
def get_prometheus_client(request: Request):
"""Dependency para obter cliente Prometheus"""
"""Dependency to get Prometheus client"""
return request.app.state.prometheus_client
@api_router.get("/cluster/status")
@@ -39,17 +39,17 @@ async def get_cluster_status(
):
"""Get overall cluster status"""
try:
# Coletar dados básicos
# Collect basic data
pods = await k8s_client.get_all_pods()
nodes_info = await k8s_client.get_nodes_info()
# Validar recursos
# Validate resources
all_validations = []
for pod in pods:
pod_validations = validation_service.validate_pod_resources(pod)
all_validations.extend(pod_validations)
# Obter informações de overcommit
# Get overcommit information
overcommit_info = await prometheus_client.get_cluster_overcommit()
# Get VPA recommendations
@@ -78,19 +78,19 @@ async def get_namespace_status(
):
"""Get status of a specific namespace"""
try:
# Coletar dados do namespace
# Collect namespace data
namespace_resources = await k8s_client.get_namespace_resources(namespace)
# Validar recursos
# Validate resources
all_validations = []
for pod in namespace_resources.pods:
pod_validations = validation_service.validate_pod_resources(pod)
all_validations.extend(pod_validations)
# Obter uso de recursos do Prometheus
# Get resource usage from Prometheus
resource_usage = await prometheus_client.get_namespace_resource_usage(namespace)
# Generate report do namespace
# Generate namespace report
report = report_service.generate_namespace_report(
namespace=namespace,
pods=namespace_resources.pods,
@@ -131,26 +131,26 @@ async def get_validations(
):
"""List resource validations with pagination"""
try:
# Coletar pods
# Collect pods
if namespace:
namespace_resources = await k8s_client.get_namespace_resources(namespace)
pods = namespace_resources.pods
else:
pods = await k8s_client.get_all_pods()
# Validar recursos
# Validate resources
all_validations = []
for pod in pods:
pod_validations = validation_service.validate_pod_resources(pod)
all_validations.extend(pod_validations)
# Filtrar por severidade se especificado
# Filter by severity if specified
if severity:
all_validations = [
v for v in all_validations if v.severity == severity
]
# Paginação
# Pagination
total = len(all_validations)
start = (page - 1) * page_size
end = start + page_size
@@ -180,10 +180,10 @@ async def get_validations_by_namespace(
):
"""List validations grouped by namespace with pagination"""
try:
# Coletar todos os pods com filtro de namespaces do sistema
# Collect all pods with system namespace filter
pods = await k8s_client.get_all_pods(include_system_namespaces=include_system_namespaces)
# Validar recursos e agrupar por namespace
# Validate resources and group by namespace
namespace_validations = {}
for pod in pods:
pod_validations = validation_service.validate_pod_resources(pod)
@@ -203,14 +203,14 @@ async def get_validations_by_namespace(
"validations": []
}
# Filtrar por severidade se especificado
# Filter by severity if specified
if severity:
pod_validations = [v for v in pod_validations if v.severity == severity]
namespace_validations[pod.namespace]["pods"][pod.name]["validations"] = pod_validations
namespace_validations[pod.namespace]["total_validations"] += len(pod_validations)
# Contar severidades
# Count severities
for validation in pod_validations:
namespace_validations[pod.namespace]["severity_breakdown"][validation.severity] += 1
@@ -218,7 +218,7 @@ async def get_validations_by_namespace(
namespace_list = list(namespace_validations.values())
namespace_list.sort(key=lambda x: x["total_validations"], reverse=True)
# Paginação
# Pagination
total = len(namespace_list)
start = (page - 1) * page_size
end = start + page_size
@@ -270,17 +270,17 @@ async def export_report(
pods = await k8s_client.get_all_pods()
nodes_info = await k8s_client.get_nodes_info()
# Filtrar por namespaces se especificado
# Filter by namespaces if specified
if export_request.namespaces:
pods = [p for p in pods if p.namespace in export_request.namespaces]
# Validar recursos
# Validate resources
all_validations = []
for pod in pods:
pod_validations = validation_service.validate_pod_resources(pod)
all_validations.extend(pod_validations)
# Obter informações adicionais
# Get additional information
overcommit_info = {}
vpa_recommendations = []
@@ -299,7 +299,7 @@ async def export_report(
nodes_info=nodes_info
)
# Exportar
# Export
filepath = await report_service.export_report(report, export_request)
return {
@@ -331,7 +331,7 @@ async def download_exported_file(filename: str):
file_info = next((f for f in files if f["filename"] == filename), None)
if not file_info:
raise HTTPException(status_code=404, detail="Arquivo não encontrado")
raise HTTPException(status_code=404, detail="File not found")
return FileResponse(
path=file_info["filepath"],
@@ -350,18 +350,18 @@ async def apply_recommendation(
):
"""Apply resource recommendation"""
try:
# TODO: Implementar aplicação de recomendações
# Por enquanto, apenas simular
# TODO: Implement recommendation application
# For now, just simulate
if recommendation.dry_run:
return {
"message": "Dry run - recomendação seria aplicada",
"message": "Dry run - recommendation would be applied",
"pod": recommendation.pod_name,
"namespace": recommendation.namespace,
"container": recommendation.container_name,
"action": f"{recommendation.action} {recommendation.resource_type} = {recommendation.value}"
}
else:
# Implementar aplicação real da recomendação
# Implement real recommendation application
raise HTTPException(status_code=501, detail="Recommendation application not implemented yet")
except Exception as e:
@@ -378,14 +378,14 @@ async def get_historical_validations(
try:
validation_service = ValidationService()
# Coletar pods
# Collect pods
if namespace:
namespace_resources = await k8s_client.get_namespace_resources(namespace)
pods = namespace_resources.pods
else:
pods = await k8s_client.get_all_pods()
# Validar com análise histórica
# Validate with historical analysis
all_validations = []
for pod in pods:
pod_validations = await validation_service.validate_pod_resources_with_historical_analysis(

View File

@@ -18,12 +18,12 @@ class Settings(BaseSettings):
prometheus_url: str = "http://prometheus.openshift-monitoring.svc.cluster.local:9090"
# Validation settings
cpu_limit_ratio: float = 3.0 # Ratio padrão limit:request para CPU
memory_limit_ratio: float = 3.0 # Ratio padrão limit:request para memória
min_cpu_request: str = "10m" # Mínimo de CPU request
min_memory_request: str = "32Mi" # Mínimo de memória request
cpu_limit_ratio: float = 3.0 # Default limit:request ratio for CPU
memory_limit_ratio: float = 3.0 # Default limit:request ratio for memory
min_cpu_request: str = "10m" # Minimum CPU request
min_memory_request: str = "32Mi" # Minimum memory request
# Namespaces críticos para VPA
# Critical namespaces for VPA
critical_namespaces: List[str] = [
"openshift-monitoring",
"openshift-ingress",

View File

@@ -1,5 +1,5 @@
"""
Cliente Kubernetes/OpenShift para coleta de dados
Kubernetes/OpenShift client for data collection
"""
import logging
from typing import List, Dict, Any, Optional
@@ -14,7 +14,7 @@ from app.models.resource_models import PodResource, NamespaceResources, VPARecom
logger = logging.getLogger(__name__)
class K8sClient:
"""Cliente para interação com Kubernetes/OpenShift"""
"""Client for interaction with Kubernetes/OpenShift"""
def __init__(self):
self.v1 = None
@@ -23,16 +23,16 @@ class K8sClient:
self.initialized = False
async def initialize(self):
"""Inicializar cliente Kubernetes"""
"""Initialize Kubernetes client"""
try:
# Tentar carregar configuração do cluster
# Try to load cluster configuration
if settings.kubeconfig_path:
config.load_kube_config(config_file=settings.kubeconfig_path)
else:
# Usar configuração in-cluster
# Use in-cluster configuration
config.load_incluster_config()
# Inicializar clientes da API
# Initialize API clients
self.v1 = client.CoreV1Api()
self.autoscaling_v1 = client.AutoscalingV1Api()
self.apps_v1 = client.AppsV1Api()
@@ -45,8 +45,8 @@ class K8sClient:
raise
def _is_system_namespace(self, namespace: str, include_system: bool = None) -> bool:
"""Verificar se um namespace é do sistema"""
# Usar parâmetro se fornecido, senão usar configuração global
"""Check if a namespace is a system namespace"""
# Use parameter if provided, otherwise use global configuration
should_include = include_system if include_system is not None else settings.include_system_namespaces
if should_include:
@@ -58,18 +58,18 @@ class K8sClient:
return False
async def get_all_pods(self, include_system_namespaces: bool = None) -> List[PodResource]:
"""Coletar informações de todos os pods do cluster"""
"""Collect information from all pods in the cluster"""
if not self.initialized:
raise RuntimeError("Kubernetes client not initialized")
pods_data = []
try:
# Listar todos os pods em todos os namespaces
# List all pods in all namespaces
pods = self.v1.list_pod_for_all_namespaces(watch=False)
for pod in pods.items:
# Filtrar namespaces do sistema
# Filter system namespaces
if self._is_system_namespace(pod.metadata.namespace, include_system_namespaces):
continue
pod_resource = PodResource(
@@ -80,7 +80,7 @@ class K8sClient:
containers=[]
)
# Processar containers do pod
# Process pod containers
for container in pod.spec.containers:
container_resource = {
"name": container.name,
@@ -91,7 +91,7 @@ class K8sClient:
}
}
# Extrair requests e limits
# Extract requests and limits
if container.resources:
if container.resources.requests:
container_resource["resources"]["requests"] = {
@@ -106,7 +106,7 @@ class K8sClient:
pods_data.append(pod_resource)
logger.info(f"Coletados {len(pods_data)} pods")
logger.info(f"Collected {len(pods_data)} pods")
return pods_data
except ApiException as e:
@@ -114,13 +114,13 @@ class K8sClient:
raise
async def get_namespace_resources(self, namespace: str) -> NamespaceResources:
"""Coletar recursos de um namespace específico"""
"""Collect resources from a specific namespace"""
if not self.initialized:
raise RuntimeError("Kubernetes client not initialized")
# Verificar se é namespace do sistema
# Check if it's a system namespace
if self._is_system_namespace(namespace):
logger.info(f"Namespace {namespace} é do sistema, retornando vazio")
logger.info(f"Namespace {namespace} is system, returning empty")
return NamespaceResources(
name=namespace,
pods=[],
@@ -131,7 +131,7 @@ class K8sClient:
)
try:
# Listar pods do namespace
# List namespace pods
pods = self.v1.list_namespaced_pod(namespace=namespace)
namespace_resource = NamespaceResources(
@@ -183,28 +183,28 @@ class K8sClient:
raise
async def get_vpa_recommendations(self) -> List[VPARecommendation]:
"""Coletar recomendações do VPA"""
"""Collect VPA recommendations"""
if not self.initialized:
raise RuntimeError("Kubernetes client not initialized")
recommendations = []
try:
# VPA não está disponível na API padrão do Kubernetes
# TODO: Implementar usando Custom Resource Definition (CRD)
logger.warning("VPA não está disponível na API padrão do Kubernetes")
# VPA is not available in the standard Kubernetes API
# TODO: Implement using Custom Resource Definition (CRD)
logger.warning("VPA is not available in the standard Kubernetes API")
return []
logger.info(f"Coletadas {len(recommendations)} recomendações VPA")
logger.info(f"Collected {len(recommendations)} VPA recommendations")
return recommendations
except ApiException as e:
logger.error(f"Error collecting VPA recommendations: {e}")
# VPA pode não estar instalado, retornar lista vazia
# VPA may not be installed, return empty list
return []
async def get_nodes_info(self) -> List[Dict[str, Any]]:
"""Coletar informações dos nós do cluster"""
"""Collect cluster node information"""
if not self.initialized:
raise RuntimeError("Kubernetes client not initialized")
@@ -221,19 +221,19 @@ class K8sClient:
"conditions": []
}
# Capacidade do nó
# Node capacity
if node.status.capacity:
node_info["capacity"] = {
k: v for k, v in node.status.capacity.items()
}
# Recursos alocáveis
# Allocatable resources
if node.status.allocatable:
node_info["allocatable"] = {
k: v for k, v in node.status.allocatable.items()
}
# Condições do nó
# Node conditions
if node.status.conditions:
node_info["conditions"] = [
{

View File

@@ -1,5 +1,5 @@
"""
Cliente Prometheus para coleta de métricas
Prometheus client for metrics collection
"""
import logging
import aiohttp
@@ -12,7 +12,7 @@ from app.core.config import settings
logger = logging.getLogger(__name__)
class PrometheusClient:
"""Cliente para interação com Prometheus"""
"""Client for Prometheus interaction"""
def __init__(self):
self.base_url = settings.prometheus_url
@@ -20,25 +20,25 @@ class PrometheusClient:
self.initialized = False
async def initialize(self):
"""Inicializar cliente Prometheus"""
"""Initialize Prometheus client"""
try:
self.session = aiohttp.ClientSession()
# Testar conexão
# Test connection
async with self.session.get(f"{self.base_url}/api/v1/query?query=up") as response:
if response.status == 200:
self.initialized = True
logger.info("Prometheus client initialized successfully")
else:
logger.warning(f"Prometheus retornou status {response.status}")
logger.warning(f"Prometheus returned status {response.status}")
except Exception as e:
logger.error(f"Error initializing Prometheus client: {e}")
# Prometheus pode não estar disponível, continuar sem ele
# Prometheus may not be available, continue without it
self.initialized = False
async def query(self, query: str, time: Optional[datetime] = None) -> Dict[str, Any]:
"""Executar query no Prometheus"""
"""Execute query in Prometheus"""
if not self.initialized or not self.session:
return {"status": "error", "message": "Prometheus not available"}
@@ -63,17 +63,17 @@ class PrometheusClient:
return {"status": "error", "message": str(e)}
async def get_pod_cpu_usage(self, namespace: str, pod_name: str) -> Dict[str, Any]:
"""Obter uso de CPU de um pod específico"""
"""Get CPU usage for a specific pod"""
query = f'rate(container_cpu_usage_seconds_total{{namespace="{namespace}", pod="{pod_name}"}}[5m])'
return await self.query(query)
async def get_pod_memory_usage(self, namespace: str, pod_name: str) -> Dict[str, Any]:
"""Obter uso de memória de um pod específico"""
"""Get memory usage for a specific pod"""
query = f'container_memory_working_set_bytes{{namespace="{namespace}", pod="{pod_name}"}}'
return await self.query(query)
async def get_namespace_resource_usage(self, namespace: str) -> Dict[str, Any]:
"""Obter uso de recursos de um namespace"""
"""Get resource usage of a namespace"""
cpu_query = f'sum(rate(container_cpu_usage_seconds_total{{namespace="{namespace}"}}[5m]))'
memory_query = f'sum(container_memory_working_set_bytes{{namespace="{namespace}"}})'
@@ -86,7 +86,7 @@ class PrometheusClient:
}
async def get_cluster_overcommit(self) -> Dict[str, Any]:
"""Verificar overcommit no cluster"""
"""Check overcommit in cluster"""
# CPU overcommit
cpu_capacity_query = 'sum(kube_node_status_capacity{resource="cpu"})'
cpu_requests_query = 'sum(kube_pod_container_resource_requests{resource="cpu"})'
@@ -112,7 +112,7 @@ class PrometheusClient:
}
async def get_node_resource_usage(self) -> List[Dict[str, Any]]:
"""Obter uso de recursos por nó"""
"""Get resource usage by node"""
query = '''
(
kube_node_status_capacity{resource="cpu"} or
@@ -126,6 +126,6 @@ class PrometheusClient:
return result
async def close(self):
"""Fechar sessão HTTP"""
"""Close HTTP session"""
if self.session:
await self.session.close()

View File

@@ -1,6 +1,6 @@
"""
OpenShift Resource Governance Tool
Aplicação para governança de recursos no cluster OpenShift
Application for resource governance in OpenShift cluster
"""
import os
import logging
@@ -14,7 +14,7 @@ from app.api.routes import api_router
from app.core.kubernetes_client import K8sClient
from app.core.prometheus_client import PrometheusClient
# Configuração de logging
# Logging configuration
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
@@ -23,10 +23,10 @@ logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Inicialização e cleanup da aplicação"""
logger.info("Iniciando OpenShift Resource Governance Tool")
"""Application initialization and cleanup"""
logger.info("Starting OpenShift Resource Governance Tool")
# Inicializar clientes
# Initialize clients
app.state.k8s_client = K8sClient()
app.state.prometheus_client = PrometheusClient()
@@ -40,25 +40,25 @@ async def lifespan(app: FastAPI):
yield
logger.info("Finalizando aplicação")
logger.info("Shutting down application")
# Criar aplicação FastAPI
# Create FastAPI application
app = FastAPI(
title="OpenShift Resource Governance Tool",
description="Ferramenta de governança de recursos para clusters OpenShift",
description="Resource governance tool for OpenShift clusters",
version="1.0.0",
lifespan=lifespan
)
# Incluir rotas da API
# Include API routes
app.include_router(api_router, prefix="/api/v1")
# Servir arquivos estáticos
# Serve static files
app.mount("/static", StaticFiles(directory="app/static"), name="static")
@app.get("/", response_class=HTMLResponse)
async def root():
"""Página principal da aplicação"""
"""Main application page"""
with open("app/static/index.html", "r") as f:
return HTMLResponse(content=f.read())

View File

@@ -1,17 +1,17 @@
"""
Modelos de dados para recursos Kubernetes
Data models for Kubernetes resources
"""
from typing import List, Dict, Any, Optional
from pydantic import BaseModel
class ContainerResource(BaseModel):
"""Recursos de um container"""
"""Container resources"""
name: str
image: str
resources: Dict[str, Dict[str, str]]
class PodResource(BaseModel):
"""Recursos de um pod"""
"""Pod resources"""
name: str
namespace: str
node_name: Optional[str] = None
@@ -19,7 +19,7 @@ class PodResource(BaseModel):
containers: List[ContainerResource]
class NamespaceResources(BaseModel):
"""Recursos de um namespace"""
"""Namespace resources"""
name: str
pods: List[PodResource]
total_cpu_requests: str = "0"
@@ -28,14 +28,14 @@ class NamespaceResources(BaseModel):
total_memory_limits: str = "0"
class VPARecommendation(BaseModel):
"""Recomendação do VPA"""
"""VPA recommendation"""
name: str
namespace: str
target_ref: Dict[str, str]
recommendations: Dict[str, Any]
class ResourceValidation(BaseModel):
"""Resultado de validação de recursos"""
"""Resource validation result"""
pod_name: str
namespace: str
container_name: str
@@ -72,7 +72,7 @@ class ExportRequest(BaseModel):
include_validations: bool = True
class ApplyRecommendationRequest(BaseModel):
"""Request para aplicar recomendação"""
"""Request to apply recommendation"""
pod_name: str
namespace: str
container_name: str

View File

@@ -1,5 +1,5 @@
"""
Serviço de análise histórica usando métricas do Prometheus
Historical analysis service using Prometheus metrics
"""
import logging
import asyncio
@@ -14,16 +14,16 @@ from app.core.config import settings
logger = logging.getLogger(__name__)
class HistoricalAnalysisService:
"""Serviço para análise histórica de recursos usando Prometheus"""
"""Service for historical resource analysis using Prometheus"""
def __init__(self):
self.prometheus_url = settings.prometheus_url
self.time_ranges = {
'1h': 3600, # 1 hora
'6h': 21600, # 6 horas
'24h': 86400, # 24 horas
'7d': 604800, # 7 dias
'30d': 2592000 # 30 dias
'1h': 3600, # 1 hour
'6h': 21600, # 6 hours
'24h': 86400, # 24 hours
'7d': 604800, # 7 days
'30d': 2592000 # 30 days
}
async def analyze_pod_historical_usage(
@@ -31,7 +31,7 @@ class HistoricalAnalysisService:
pod: PodResource,
time_range: str = '24h'
) -> List[ResourceValidation]:
"""Analisar uso histórico de um pod"""
"""Analyze historical usage of a pod"""
validations = []
if time_range not in self.time_ranges:
@@ -41,13 +41,13 @@ class HistoricalAnalysisService:
start_time = end_time - timedelta(seconds=self.time_ranges[time_range])
try:
# Analisar CPU
# Analyze CPU
cpu_analysis = await self._analyze_cpu_usage(
pod, start_time, end_time, time_range
)
validations.extend(cpu_analysis)
# Analisar memória
# Analyze memory
memory_analysis = await self._analyze_memory_usage(
pod, start_time, end_time, time_range
)
@@ -74,14 +74,14 @@ class HistoricalAnalysisService:
end_time: datetime,
time_range: str
) -> List[ResourceValidation]:
"""Analisar uso histórico de CPU"""
"""Analyze historical CPU usage"""
validations = []
for container in pod.containers:
container_name = container["name"]
try:
# Query para CPU usage rate
# Query for CPU usage rate
cpu_query = f'''
rate(container_cpu_usage_seconds_total{{
pod="{pod.name}",
@@ -92,7 +92,7 @@ class HistoricalAnalysisService:
}}[{time_range}])
'''
# Query para CPU requests
# Query for CPU requests
cpu_requests_query = f'''
kube_pod_container_resource_requests{{
pod="{pod.name}",
@@ -101,7 +101,7 @@ class HistoricalAnalysisService:
}}
'''
# Query para CPU limits
# Query for CPU limits
cpu_limits_query = f'''
kube_pod_container_resource_limits{{
pod="{pod.name}",
@@ -110,7 +110,7 @@ class HistoricalAnalysisService:
}}
'''
# Executar queries
# Execute queries
cpu_usage = await self._query_prometheus(cpu_query, start_time, end_time)
cpu_requests = await self._query_prometheus(cpu_requests_query, start_time, end_time)
cpu_limits = await self._query_prometheus(cpu_limits_query, start_time, end_time)
@@ -134,14 +134,14 @@ class HistoricalAnalysisService:
end_time: datetime,
time_range: str
) -> List[ResourceValidation]:
"""Analisar uso histórico de memória"""
"""Analyze historical memory usage"""
validations = []
for container in pod.containers:
container_name = container["name"]
try:
# Query para memória usage
# Query for memory usage
memory_query = f'''
container_memory_working_set_bytes{{
pod="{pod.name}",
@@ -152,7 +152,7 @@ class HistoricalAnalysisService:
}}
'''
# Query para memória requests
# Query for memory requests
memory_requests_query = f'''
kube_pod_container_resource_requests{{
pod="{pod.name}",
@@ -161,7 +161,7 @@ class HistoricalAnalysisService:
}}
'''
# Query para memória limits
# Query for memory limits
memory_limits_query = f'''
kube_pod_container_resource_limits{{
pod="{pod.name}",
@@ -170,7 +170,7 @@ class HistoricalAnalysisService:
}}
'''
# Executar queries
# Execute queries
memory_usage = await self._query_prometheus(memory_query, start_time, end_time)
memory_requests = await self._query_prometheus(memory_requests_query, start_time, end_time)
memory_limits = await self._query_prometheus(memory_limits_query, start_time, end_time)
@@ -197,22 +197,22 @@ class HistoricalAnalysisService:
limits_data: List[Dict],
time_range: str
) -> List[ResourceValidation]:
"""Analisar métricas de CPU"""
"""Analyze CPU metrics"""
validations = []
if not usage_data or not requests_data:
return validations
# Calcular estatísticas de uso
# Calculate usage statistics
usage_values = [float(point[1]) for point in usage_data if point[1] != 'NaN']
if not usage_values:
return validations
# Valores atuais de requests/limits
# Current values of requests/limits
current_requests = float(requests_data[0][1]) if requests_data else 0
current_limits = float(limits_data[0][1]) if limits_data else 0
# Estatísticas de uso
# Usage statistics
avg_usage = sum(usage_values) / len(usage_values)
max_usage = max(usage_values)
p95_usage = sorted(usage_values)[int(len(usage_values) * 0.95)]
@@ -282,28 +282,28 @@ class HistoricalAnalysisService:
limits_data: List[Dict],
time_range: str
) -> List[ResourceValidation]:
"""Analisar métricas de memória"""
"""Analyze memory metrics"""
validations = []
if not usage_data or not requests_data:
return validations
# Calcular estatísticas de uso
# Calculate usage statistics
usage_values = [float(point[1]) for point in usage_data if point[1] != 'NaN']
if not usage_values:
return validations
# Valores atuais de requests/limits (em bytes)
# Current values of requests/limits (in bytes)
current_requests = float(requests_data[0][1]) if requests_data else 0
current_limits = float(limits_data[0][1]) if limits_data else 0
# Estatísticas de uso
# Usage statistics
avg_usage = sum(usage_values) / len(usage_values)
max_usage = max(usage_values)
p95_usage = sorted(usage_values)[int(len(usage_values) * 0.95)]
p99_usage = sorted(usage_values)[int(len(usage_values) * 0.99)]
# Converter para MiB para melhor legibilidade
# Convert to MiB for better readability
def bytes_to_mib(bytes_value):
return bytes_value / (1024 * 1024)
@@ -362,14 +362,14 @@ class HistoricalAnalysisService:
return validations
async def _query_prometheus(self, query: str, start_time: datetime, end_time: datetime) -> List[Dict]:
"""Executar query no Prometheus"""
"""Execute query in Prometheus"""
try:
async with aiohttp.ClientSession() as session:
params = {
'query': query,
'start': start_time.timestamp(),
'end': end_time.timestamp(),
'step': '60s' # 1 minuto de resolução
'step': '60s' # 1 minute resolution
}
async with session.get(
@@ -389,9 +389,9 @@ class HistoricalAnalysisService:
return []
async def get_cluster_historical_summary(self, time_range: str = '24h') -> Dict[str, Any]:
"""Obter resumo histórico do cluster"""
"""Get cluster historical summary"""
try:
# Query para CPU total do cluster
# Query for total cluster CPU
cpu_query = f'''
sum(rate(container_cpu_usage_seconds_total{{
container!="POD",
@@ -399,7 +399,7 @@ class HistoricalAnalysisService:
}}[{time_range}]))
'''
# Query para memória total do cluster
# Query for total cluster memory
memory_query = f'''
sum(container_memory_working_set_bytes{{
container!="POD",
@@ -407,7 +407,7 @@ class HistoricalAnalysisService:
}})
'''
# Query para requests totais
# Query for total requests
cpu_requests_query = f'''
sum(kube_pod_container_resource_requests{{resource="cpu"}})
'''
@@ -416,7 +416,7 @@ class HistoricalAnalysisService:
sum(kube_pod_container_resource_requests{{resource="memory"}})
'''
# Executar queries
# Execute queries
cpu_usage = await self._query_prometheus(cpu_query,
datetime.now() - timedelta(seconds=self.time_ranges[time_range]),
datetime.now())

View File

@@ -181,7 +181,7 @@ class ReportService:
filename = f"cluster_report_{timestamp}.json"
filepath = os.path.join(self.export_path, filename)
# Converter para dict para serialização
# Convert to dict for serialization
report_dict = report.dict()
with open(filepath, 'w', encoding='utf-8') as f:
@@ -198,7 +198,7 @@ class ReportService:
with open(filepath, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
# Cabeçalho
# Header
writer.writerow([
"Pod Name", "Namespace", "Container Name",
"Validation Type", "Severity", "Message", "Recommendation"
@@ -234,12 +234,12 @@ class ReportService:
styles = getSampleStyleSheet()
story = []
# Título
# Title
title = Paragraph("OpenShift Resource Governance Report", styles['Title'])
story.append(title)
story.append(Spacer(1, 12))
# Resumo
# Summary
summary_text = f"""
<b>Cluster Summary:</b><br/>
Total Pods: {report.total_pods}<br/>
@@ -276,7 +276,7 @@ class ReportService:
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
story.append(Paragraph("<b>Validações:</b>", styles['Heading2']))
story.append(Paragraph("<b>Validations:</b>", styles['Heading2']))
story.append(table)
doc.build(story)

View File

@@ -40,10 +40,10 @@ class ValidationService:
time_range: str = '24h'
) -> List[ResourceValidation]:
"""Validate pod resources including historical analysis"""
# Validações estáticas
# Static validations
static_validations = self.validate_pod_resources(pod)
# Análise histórica
# Historical analysis
try:
historical_validations = await self.historical_analysis.analyze_pod_historical_usage(
pod, time_range
@@ -66,7 +66,7 @@ class ValidationService:
requests = resources.get("requests", {})
limits = resources.get("limits", {})
# 1. Verificar se requests estão definidos
# 1. Check if requests are defined
if not requests:
validations.append(ResourceValidation(
pod_name=pod_name,
@@ -78,7 +78,7 @@ class ValidationService:
recommendation="Define CPU and memory requests to guarantee QoS"
))
# 2. Verificar se limits estão definidos
# 2. Check if limits are defined
if not limits:
validations.append(ResourceValidation(
pod_name=pod_name,
@@ -213,7 +213,7 @@ class ValidationService:
"""Validate minimum request values"""
validations = []
# Validar CPU mínima
# Validate minimum CPU
if "cpu" in requests:
try:
request_value = self._parse_cpu_value(requests["cpu"])
@@ -232,7 +232,7 @@ class ValidationService:
except (ValueError, InvalidOperation):
pass
# Validar memória mínima
# Validate minimum memory
if "memory" in requests:
try:
request_value = self._parse_memory_value(requests["memory"])
@@ -254,7 +254,7 @@ class ValidationService:
return validations
def _parse_cpu_value(self, value: str) -> float:
"""Converter valor de CPU para float (cores)"""
"""Convert CPU value to float (cores)"""
if value.endswith('m'):
return float(value[:-1]) / 1000
elif value.endswith('n'):
@@ -263,7 +263,7 @@ class ValidationService:
return float(value)
def _parse_memory_value(self, value: str) -> int:
"""Converter valor de memória para bytes"""
"""Convert memory value to bytes"""
value = value.upper()
if value.endswith('KI'):
@@ -289,15 +289,15 @@ class ValidationService:
"""Validate overcommit in a namespace"""
validations = []
# Calcular total de requests do namespace
# Calculate total namespace requests
total_cpu_requests = self._parse_cpu_value(namespace_resources.total_cpu_requests)
total_memory_requests = self._parse_memory_value(namespace_resources.total_memory_requests)
# Calcular capacidade total dos nós
# Calculate total node capacity
total_cpu_capacity = self._parse_cpu_value(node_capacity.get("cpu", "0"))
total_memory_capacity = self._parse_memory_value(node_capacity.get("memory", "0"))
# Verificar overcommit de CPU
# Check CPU overcommit
if total_cpu_capacity > 0:
cpu_utilization = (total_cpu_requests / total_cpu_capacity) * 100
if cpu_utilization > 100:
@@ -311,7 +311,7 @@ class ValidationService:
recommendation="Reduce CPU requests or add more nodes to the cluster"
))
# Verificar overcommit de memória
# Check memory overcommit
if total_memory_capacity > 0:
memory_utilization = (total_memory_requests / total_memory_capacity) * 100
if memory_utilization > 100:
@@ -331,7 +331,7 @@ class ValidationService:
"""Generate recommendations based on validations"""
recommendations = []
# Agrupar validações por tipo
# Group validations by type
validation_counts = {}
for validation in validations:
validation_type = validation.validation_type
@@ -339,7 +339,7 @@ class ValidationService:
validation_counts[validation_type] = 0
validation_counts[validation_type] += 1
# Gerar recomendações baseadas nos problemas encontrados
# Generate recommendations based on found issues
if validation_counts.get("missing_requests", 0) > 0:
recommendations.append(
f"Implement LimitRange in namespace to define default requests "