runbooks 0.7.7__py3-none-any.whl β 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- runbooks/__init__.py +1 -1
- runbooks/base.py +2 -2
- runbooks/cfat/README.md +12 -1
- runbooks/cfat/__init__.py +8 -4
- runbooks/cfat/assessment/collectors.py +171 -14
- runbooks/cfat/assessment/compliance.py +546 -522
- runbooks/cfat/assessment/runner.py +129 -10
- runbooks/cfat/models.py +6 -2
- runbooks/common/__init__.py +152 -0
- runbooks/common/accuracy_validator.py +1039 -0
- runbooks/common/context_logger.py +440 -0
- runbooks/common/cross_module_integration.py +594 -0
- runbooks/common/enhanced_exception_handler.py +1108 -0
- runbooks/common/enterprise_audit_integration.py +634 -0
- runbooks/common/logger.py +14 -0
- runbooks/common/mcp_integration.py +539 -0
- runbooks/common/performance_monitor.py +387 -0
- runbooks/common/profile_utils.py +216 -0
- runbooks/common/rich_utils.py +622 -0
- runbooks/enterprise/__init__.py +68 -0
- runbooks/enterprise/error_handling.py +411 -0
- runbooks/enterprise/logging.py +439 -0
- runbooks/enterprise/multi_tenant.py +583 -0
- runbooks/feedback/user_feedback_collector.py +440 -0
- runbooks/finops/README.md +129 -14
- runbooks/finops/__init__.py +22 -3
- runbooks/finops/account_resolver.py +279 -0
- runbooks/finops/accuracy_cross_validator.py +638 -0
- runbooks/finops/aws_client.py +721 -36
- runbooks/finops/budget_integration.py +313 -0
- runbooks/finops/cli.py +90 -33
- runbooks/finops/cost_processor.py +211 -37
- runbooks/finops/dashboard_router.py +900 -0
- runbooks/finops/dashboard_runner.py +1334 -399
- runbooks/finops/embedded_mcp_validator.py +288 -0
- runbooks/finops/enhanced_dashboard_runner.py +526 -0
- runbooks/finops/enhanced_progress.py +327 -0
- runbooks/finops/enhanced_trend_visualization.py +423 -0
- runbooks/finops/finops_dashboard.py +41 -0
- runbooks/finops/helpers.py +639 -323
- runbooks/finops/iam_guidance.py +400 -0
- runbooks/finops/markdown_exporter.py +466 -0
- runbooks/finops/multi_dashboard.py +1502 -0
- runbooks/finops/optimizer.py +396 -395
- runbooks/finops/profile_processor.py +2 -2
- runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
- runbooks/finops/runbooks.security.report_generator.log +0 -0
- runbooks/finops/runbooks.security.run_script.log +0 -0
- runbooks/finops/runbooks.security.security_export.log +0 -0
- runbooks/finops/service_mapping.py +195 -0
- runbooks/finops/single_dashboard.py +710 -0
- runbooks/finops/tests/__init__.py +19 -0
- runbooks/finops/tests/results_test_finops_dashboard.xml +1 -0
- runbooks/finops/tests/run_comprehensive_tests.py +421 -0
- runbooks/finops/tests/run_tests.py +305 -0
- runbooks/finops/tests/test_finops_dashboard.py +705 -0
- runbooks/finops/tests/test_integration.py +477 -0
- runbooks/finops/tests/test_performance.py +380 -0
- runbooks/finops/tests/test_performance_benchmarks.py +500 -0
- runbooks/finops/tests/test_reference_images_validation.py +867 -0
- runbooks/finops/tests/test_single_account_features.py +715 -0
- runbooks/finops/tests/validate_test_suite.py +220 -0
- runbooks/finops/types.py +1 -1
- runbooks/hitl/enhanced_workflow_engine.py +725 -0
- runbooks/inventory/README.md +12 -1
- runbooks/inventory/artifacts/scale-optimize-status.txt +12 -0
- runbooks/inventory/collectors/aws_comprehensive.py +192 -185
- runbooks/inventory/collectors/enterprise_scale.py +281 -0
- runbooks/inventory/core/collector.py +299 -12
- runbooks/inventory/list_ec2_instances.py +21 -20
- runbooks/inventory/list_ssm_parameters.py +31 -3
- runbooks/inventory/organizations_discovery.py +1315 -0
- runbooks/inventory/rich_inventory_display.py +360 -0
- runbooks/inventory/run_on_multi_accounts.py +32 -16
- runbooks/inventory/runbooks.security.report_generator.log +0 -0
- runbooks/inventory/runbooks.security.run_script.log +0 -0
- runbooks/inventory/vpc_flow_analyzer.py +1030 -0
- runbooks/main.py +4171 -1615
- runbooks/metrics/dora_metrics_engine.py +1293 -0
- runbooks/monitoring/performance_monitor.py +433 -0
- runbooks/operate/README.md +394 -0
- runbooks/operate/__init__.py +2 -2
- runbooks/operate/base.py +291 -11
- runbooks/operate/deployment_framework.py +1032 -0
- runbooks/operate/deployment_validator.py +853 -0
- runbooks/operate/dynamodb_operations.py +10 -6
- runbooks/operate/ec2_operations.py +321 -11
- runbooks/operate/executive_dashboard.py +779 -0
- runbooks/operate/mcp_integration.py +750 -0
- runbooks/operate/nat_gateway_operations.py +1120 -0
- runbooks/operate/networking_cost_heatmap.py +685 -0
- runbooks/operate/privatelink_operations.py +940 -0
- runbooks/operate/s3_operations.py +10 -6
- runbooks/operate/vpc_endpoints.py +644 -0
- runbooks/operate/vpc_operations.py +1038 -0
- runbooks/remediation/README.md +489 -13
- runbooks/remediation/__init__.py +2 -2
- runbooks/remediation/acm_remediation.py +1 -1
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/cloudtrail_remediation.py +1 -1
- runbooks/remediation/cognito_remediation.py +1 -1
- runbooks/remediation/commons.py +8 -4
- runbooks/remediation/dynamodb_remediation.py +1 -1
- runbooks/remediation/ec2_remediation.py +1 -1
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -1
- runbooks/remediation/kms_enable_key_rotation.py +1 -1
- runbooks/remediation/kms_remediation.py +1 -1
- runbooks/remediation/lambda_remediation.py +1 -1
- runbooks/remediation/multi_account.py +1 -1
- runbooks/remediation/rds_remediation.py +1 -1
- runbooks/remediation/s3_block_public_access.py +1 -1
- runbooks/remediation/s3_enable_access_logging.py +1 -1
- runbooks/remediation/s3_encryption.py +1 -1
- runbooks/remediation/s3_remediation.py +1 -1
- runbooks/remediation/vpc_remediation.py +475 -0
- runbooks/security/ENTERPRISE_SECURITY_FRAMEWORK.md +506 -0
- runbooks/security/README.md +12 -1
- runbooks/security/__init__.py +166 -33
- runbooks/security/compliance_automation.py +634 -0
- runbooks/security/compliance_automation_engine.py +1021 -0
- runbooks/security/enterprise_security_framework.py +931 -0
- runbooks/security/enterprise_security_policies.json +293 -0
- runbooks/security/integration_test_enterprise_security.py +879 -0
- runbooks/security/module_security_integrator.py +641 -0
- runbooks/security/report_generator.py +10 -0
- runbooks/security/run_script.py +27 -5
- runbooks/security/security_baseline_tester.py +153 -27
- runbooks/security/security_export.py +456 -0
- runbooks/sre/README.md +472 -0
- runbooks/sre/__init__.py +33 -0
- runbooks/sre/mcp_reliability_engine.py +1049 -0
- runbooks/sre/performance_optimization_engine.py +1032 -0
- runbooks/sre/reliability_monitoring_framework.py +1011 -0
- runbooks/validation/__init__.py +10 -0
- runbooks/validation/benchmark.py +489 -0
- runbooks/validation/cli.py +368 -0
- runbooks/validation/mcp_validator.py +797 -0
- runbooks/vpc/README.md +478 -0
- runbooks/vpc/__init__.py +38 -0
- runbooks/vpc/config.py +212 -0
- runbooks/vpc/cost_engine.py +347 -0
- runbooks/vpc/heatmap_engine.py +605 -0
- runbooks/vpc/manager_interface.py +649 -0
- runbooks/vpc/networking_wrapper.py +1289 -0
- runbooks/vpc/rich_formatters.py +693 -0
- runbooks/vpc/tests/__init__.py +5 -0
- runbooks/vpc/tests/conftest.py +356 -0
- runbooks/vpc/tests/test_cli_integration.py +530 -0
- runbooks/vpc/tests/test_config.py +458 -0
- runbooks/vpc/tests/test_cost_engine.py +479 -0
- runbooks/vpc/tests/test_networking_wrapper.py +512 -0
- {runbooks-0.7.7.dist-info β runbooks-0.9.0.dist-info}/METADATA +175 -65
- {runbooks-0.7.7.dist-info β runbooks-0.9.0.dist-info}/RECORD +157 -60
- {runbooks-0.7.7.dist-info β runbooks-0.9.0.dist-info}/entry_points.txt +1 -1
- {runbooks-0.7.7.dist-info β runbooks-0.9.0.dist-info}/WHEEL +0 -0
- {runbooks-0.7.7.dist-info β runbooks-0.9.0.dist-info}/licenses/LICENSE +0 -0
- {runbooks-0.7.7.dist-info β runbooks-0.9.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,313 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Enhanced Budget Integration - Real AWS Budgets API Integration
|
4
|
+
|
5
|
+
This module provides comprehensive AWS Budgets API integration for enhanced
|
6
|
+
Budget Status column values, replacing placeholder data with real budget
|
7
|
+
information, alerts, and utilization tracking.
|
8
|
+
|
9
|
+
Features:
|
10
|
+
- Real AWS Budgets API integration
|
11
|
+
- Budget utilization calculations with visual indicators
|
12
|
+
- Budget alert thresholds and notifications
|
13
|
+
- Forecast vs actual spend analysis
|
14
|
+
- Rich CLI formatting for budget status
|
15
|
+
- Performance optimized for multi-account operations
|
16
|
+
|
17
|
+
Author: CloudOps Runbooks Team
|
18
|
+
Version: 0.8.0
|
19
|
+
"""
|
20
|
+
|
21
|
+
import json
|
22
|
+
from datetime import datetime, timedelta
|
23
|
+
from typing import Any, Dict, List, Optional, Tuple
|
24
|
+
|
25
|
+
import boto3
|
26
|
+
from botocore.exceptions import ClientError
|
27
|
+
from rich.console import Console
|
28
|
+
|
29
|
+
from ..common.rich_utils import (
|
30
|
+
STATUS_INDICATORS,
|
31
|
+
format_cost,
|
32
|
+
print_info,
|
33
|
+
print_warning,
|
34
|
+
)
|
35
|
+
from ..common.rich_utils import (
|
36
|
+
console as rich_console,
|
37
|
+
)
|
38
|
+
|
39
|
+
|
40
|
+
class EnhancedBudgetAnalyzer:
|
41
|
+
"""
|
42
|
+
Enhanced AWS Budgets API integration for real-time budget analysis.
|
43
|
+
|
44
|
+
Provides comprehensive budget status, utilization tracking, and alert
|
45
|
+
management across single and multi-account AWS environments.
|
46
|
+
"""
|
47
|
+
|
48
|
+
def __init__(self, console: Optional[Console] = None):
|
49
|
+
self.console = console or rich_console
|
50
|
+
|
51
|
+
def get_enhanced_budget_status(
|
52
|
+
self, session: boto3.Session, current_cost: float, account_id: str
|
53
|
+
) -> Dict[str, Any]:
|
54
|
+
"""
|
55
|
+
Get enhanced budget status with real AWS Budgets API data.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
session: AWS session with budget access
|
59
|
+
current_cost: Current month cost for comparison
|
60
|
+
account_id: AWS account ID for budget identification
|
61
|
+
|
62
|
+
Returns:
|
63
|
+
Dict containing enhanced budget information with Rich formatting
|
64
|
+
"""
|
65
|
+
try:
|
66
|
+
budgets_client = session.client("budgets")
|
67
|
+
|
68
|
+
# Get all budgets for the account
|
69
|
+
budgets_response = budgets_client.describe_budgets(AccountId=account_id)
|
70
|
+
budgets = budgets_response.get("Budgets", [])
|
71
|
+
|
72
|
+
if not budgets:
|
73
|
+
return {
|
74
|
+
"status": "no_budget",
|
75
|
+
"display": "[dim]No Budget Set[/]",
|
76
|
+
"utilization": 0,
|
77
|
+
"details": "No budgets configured for this account",
|
78
|
+
"recommendation": "Consider setting up budget alerts",
|
79
|
+
}
|
80
|
+
|
81
|
+
# Analyze primary budget (or cost budget if multiple exist)
|
82
|
+
primary_budget = self._select_primary_budget(budgets)
|
83
|
+
|
84
|
+
if not primary_budget:
|
85
|
+
return {
|
86
|
+
"status": "no_budget",
|
87
|
+
"display": "[dim]No Cost Budget[/]",
|
88
|
+
"utilization": 0,
|
89
|
+
"details": "No cost-based budgets found",
|
90
|
+
"recommendation": "Create monthly cost budget",
|
91
|
+
}
|
92
|
+
|
93
|
+
# Get budget utilization data
|
94
|
+
utilization_response = budgets_client.describe_budget_performance_history(
|
95
|
+
AccountId=account_id, BudgetName=primary_budget["BudgetName"]
|
96
|
+
)
|
97
|
+
|
98
|
+
# Calculate enhanced budget status
|
99
|
+
budget_analysis = self._analyze_budget_performance(primary_budget, utilization_response, current_cost)
|
100
|
+
|
101
|
+
return budget_analysis
|
102
|
+
|
103
|
+
except ClientError as e:
|
104
|
+
error_code = e.response.get("Error", {}).get("Code", "Unknown")
|
105
|
+
if error_code == "AccessDeniedException":
|
106
|
+
return {
|
107
|
+
"status": "access_denied",
|
108
|
+
"display": "[yellow]β οΈ Access Denied[/]",
|
109
|
+
"utilization": 0,
|
110
|
+
"details": "No budget read permissions",
|
111
|
+
"recommendation": "Grant budgets:ViewBudget permission",
|
112
|
+
}
|
113
|
+
else:
|
114
|
+
print_warning(f"Budget API error: {error_code}")
|
115
|
+
return self._create_estimated_budget_status(current_cost)
|
116
|
+
|
117
|
+
except Exception as e:
|
118
|
+
print_warning(f"Budget analysis failed: {str(e)[:50]}")
|
119
|
+
return self._create_estimated_budget_status(current_cost)
|
120
|
+
|
121
|
+
def _select_primary_budget(self, budgets: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
122
|
+
"""Select the primary budget for analysis (cost budget preferred)."""
|
123
|
+
|
124
|
+
# Prefer cost budgets over usage budgets
|
125
|
+
cost_budgets = [b for b in budgets if b.get("BudgetType") == "COST"]
|
126
|
+
if cost_budgets:
|
127
|
+
# Prefer monthly budgets
|
128
|
+
monthly_budgets = [b for b in cost_budgets if b.get("TimeUnit") == "MONTHLY"]
|
129
|
+
if monthly_budgets:
|
130
|
+
return monthly_budgets[0] # First monthly cost budget
|
131
|
+
return cost_budgets[0] # First cost budget
|
132
|
+
|
133
|
+
# Fallback to any budget
|
134
|
+
return budgets[0] if budgets else None
|
135
|
+
|
136
|
+
def _analyze_budget_performance(
|
137
|
+
self, budget: Dict[str, Any], performance_history: Dict[str, Any], current_cost: float
|
138
|
+
) -> Dict[str, Any]:
|
139
|
+
"""Analyze budget performance and generate enhanced status."""
|
140
|
+
|
141
|
+
budget_limit = float(budget.get("BudgetLimit", {}).get("Amount", 0))
|
142
|
+
budget_name = budget.get("BudgetName", "Unknown")
|
143
|
+
|
144
|
+
if budget_limit == 0:
|
145
|
+
return {
|
146
|
+
"status": "no_limit",
|
147
|
+
"display": "[dim]Unlimited Budget[/]",
|
148
|
+
"utilization": 0,
|
149
|
+
"details": f'Budget "{budget_name}" has no spending limit',
|
150
|
+
"recommendation": "Set budget limit for cost control",
|
151
|
+
}
|
152
|
+
|
153
|
+
# Calculate utilization
|
154
|
+
utilization_percent = (current_cost / budget_limit) * 100
|
155
|
+
|
156
|
+
# Get alerts configuration if available
|
157
|
+
alerts_info = self._analyze_budget_alerts(budget)
|
158
|
+
|
159
|
+
# Generate status based on utilization
|
160
|
+
if utilization_percent >= 100:
|
161
|
+
return {
|
162
|
+
"status": "over_budget",
|
163
|
+
"display": f"[red]π¨ Over Budget[/]\n[red]{utilization_percent:.0f}% ({format_cost(current_cost)}/{format_cost(budget_limit)})[/]",
|
164
|
+
"utilization": utilization_percent,
|
165
|
+
"details": f"Exceeded budget by {format_cost(current_cost - budget_limit)}",
|
166
|
+
"recommendation": "Immediate cost review required",
|
167
|
+
"alerts": alerts_info,
|
168
|
+
}
|
169
|
+
elif utilization_percent >= 90:
|
170
|
+
return {
|
171
|
+
"status": "critical",
|
172
|
+
"display": f"[red]β οΈ Critical[/]\n[red]{utilization_percent:.0f}% ({format_cost(current_cost)}/{format_cost(budget_limit)})[/]",
|
173
|
+
"utilization": utilization_percent,
|
174
|
+
"details": f"Approaching budget limit - {format_cost(budget_limit - current_cost)} remaining",
|
175
|
+
"recommendation": "Review and optimize high-cost services",
|
176
|
+
"alerts": alerts_info,
|
177
|
+
}
|
178
|
+
elif utilization_percent >= 75:
|
179
|
+
return {
|
180
|
+
"status": "warning",
|
181
|
+
"display": f"[yellow]β οΈ Warning[/]\n[yellow]{utilization_percent:.0f}% ({format_cost(current_cost)}/{format_cost(budget_limit)})[/]",
|
182
|
+
"utilization": utilization_percent,
|
183
|
+
"details": f"75% of budget used - {format_cost(budget_limit - current_cost)} remaining",
|
184
|
+
"recommendation": "Monitor spending closely",
|
185
|
+
"alerts": alerts_info,
|
186
|
+
}
|
187
|
+
elif utilization_percent >= 50:
|
188
|
+
return {
|
189
|
+
"status": "moderate",
|
190
|
+
"display": f"[cyan]π On Track[/]\n[cyan]{utilization_percent:.0f}% ({format_cost(current_cost)}/{format_cost(budget_limit)})[/]",
|
191
|
+
"utilization": utilization_percent,
|
192
|
+
"details": f"Moderate usage - {format_cost(budget_limit - current_cost)} remaining",
|
193
|
+
"recommendation": "Continue monitoring",
|
194
|
+
"alerts": alerts_info,
|
195
|
+
}
|
196
|
+
else:
|
197
|
+
return {
|
198
|
+
"status": "under_budget",
|
199
|
+
"display": f"[green]β
Under Budget[/]\n[green]{utilization_percent:.0f}% ({format_cost(current_cost)}/{format_cost(budget_limit)})[/]",
|
200
|
+
"utilization": utilization_percent,
|
201
|
+
"details": f"Low utilization - {format_cost(budget_limit - current_cost)} available",
|
202
|
+
"recommendation": "Budget utilization is low",
|
203
|
+
"alerts": alerts_info,
|
204
|
+
}
|
205
|
+
|
206
|
+
def _analyze_budget_alerts(self, budget: Dict[str, Any]) -> Dict[str, Any]:
|
207
|
+
"""Analyze budget alert configuration."""
|
208
|
+
# This would analyze budget alerts if available in the budget configuration
|
209
|
+
# For now, return basic alert info
|
210
|
+
return {"configured": False, "thresholds": [], "notification_methods": []}
|
211
|
+
|
212
|
+
def _create_estimated_budget_status(self, current_cost: float) -> Dict[str, Any]:
|
213
|
+
"""Create estimated budget status when real budget data is unavailable."""
|
214
|
+
|
215
|
+
# Simple heuristic-based budget estimation
|
216
|
+
if current_cost == 0:
|
217
|
+
return {
|
218
|
+
"status": "no_usage",
|
219
|
+
"display": "[dim]No Usage[/]",
|
220
|
+
"utilization": 0,
|
221
|
+
"details": "No current usage detected",
|
222
|
+
"recommendation": "Account appears inactive",
|
223
|
+
}
|
224
|
+
elif current_cost < 100:
|
225
|
+
return {
|
226
|
+
"status": "low_cost",
|
227
|
+
"display": f"[green]π° Low Cost[/]\n[green]{format_cost(current_cost)}/month[/]",
|
228
|
+
"utilization": 0,
|
229
|
+
"details": "Low monthly spend detected",
|
230
|
+
"recommendation": "Consider setting budget alerts",
|
231
|
+
}
|
232
|
+
elif current_cost < 1000:
|
233
|
+
return {
|
234
|
+
"status": "moderate_cost",
|
235
|
+
"display": f"[yellow]π Moderate[/]\n[yellow]{format_cost(current_cost)}/month[/]",
|
236
|
+
"utilization": 0,
|
237
|
+
"details": "Moderate monthly spend",
|
238
|
+
"recommendation": "Set budget limits for cost control",
|
239
|
+
}
|
240
|
+
else:
|
241
|
+
return {
|
242
|
+
"status": "high_cost",
|
243
|
+
"display": f"[red]πΈ High Cost[/]\n[red]{format_cost(current_cost)}/month[/]",
|
244
|
+
"utilization": 0,
|
245
|
+
"details": "High monthly spend detected",
|
246
|
+
"recommendation": "Budget management recommended",
|
247
|
+
}
|
248
|
+
|
249
|
+
def get_budget_forecast(
|
250
|
+
self, session: boto3.Session, account_id: str, budget_name: str
|
251
|
+
) -> Optional[Dict[str, Any]]:
|
252
|
+
"""Get budget forecast information for trend analysis."""
|
253
|
+
try:
|
254
|
+
budgets_client = session.client("budgets")
|
255
|
+
|
256
|
+
# Get budget forecast (if available)
|
257
|
+
forecast_response = budgets_client.describe_budget_performance_history(
|
258
|
+
AccountId=account_id,
|
259
|
+
BudgetName=budget_name,
|
260
|
+
TimePeriod={"Start": datetime.now() - timedelta(days=90), "End": datetime.now()},
|
261
|
+
)
|
262
|
+
|
263
|
+
# Process forecast data
|
264
|
+
performance_history = forecast_response.get("BudgetPerformanceHistory", {})
|
265
|
+
|
266
|
+
return {
|
267
|
+
"forecast_available": True,
|
268
|
+
"performance_history": performance_history,
|
269
|
+
"trend_analysis": self._analyze_spending_trends(performance_history),
|
270
|
+
}
|
271
|
+
|
272
|
+
except Exception as e:
|
273
|
+
print_warning(f"Budget forecast failed: {str(e)[:50]}")
|
274
|
+
return None
|
275
|
+
|
276
|
+
def _analyze_spending_trends(self, performance_history: Dict[str, Any]) -> Dict[str, Any]:
|
277
|
+
"""Analyze spending trends from budget performance history."""
|
278
|
+
# Placeholder for trend analysis logic
|
279
|
+
return {"trend_direction": "stable", "trend_confidence": "medium", "forecast_accuracy": "unknown"}
|
280
|
+
|
281
|
+
|
282
|
+
def create_budget_analyzer(console: Optional[Console] = None) -> EnhancedBudgetAnalyzer:
|
283
|
+
"""Factory function to create enhanced budget analyzer."""
|
284
|
+
return EnhancedBudgetAnalyzer(console=console)
|
285
|
+
|
286
|
+
|
287
|
+
def get_enhanced_budget_status_for_profile(profile: str, current_cost: float) -> Dict[str, Any]:
|
288
|
+
"""
|
289
|
+
Convenience function to get budget status for a specific profile.
|
290
|
+
|
291
|
+
Args:
|
292
|
+
profile: AWS profile name
|
293
|
+
current_cost: Current month cost
|
294
|
+
|
295
|
+
Returns:
|
296
|
+
Enhanced budget status dictionary
|
297
|
+
"""
|
298
|
+
try:
|
299
|
+
session = boto3.Session(profile_name=profile)
|
300
|
+
sts = session.client("sts")
|
301
|
+
account_id = sts.get_caller_identity()["Account"]
|
302
|
+
|
303
|
+
analyzer = create_budget_analyzer()
|
304
|
+
return analyzer.get_enhanced_budget_status(session, current_cost, account_id)
|
305
|
+
|
306
|
+
except Exception as e:
|
307
|
+
return {
|
308
|
+
"status": "error",
|
309
|
+
"display": "[red]Error[/]",
|
310
|
+
"utilization": 0,
|
311
|
+
"details": f"Budget analysis failed: {str(e)[:50]}",
|
312
|
+
"recommendation": "Check AWS credentials and permissions",
|
313
|
+
}
|
runbooks/finops/cli.py
CHANGED
@@ -10,38 +10,38 @@ from runbooks.finops.helpers import load_config_file
|
|
10
10
|
|
11
11
|
console = Console()
|
12
12
|
|
13
|
-
__version__ = "0.7.
|
13
|
+
__version__ = "0.7.8"
|
14
14
|
|
15
15
|
|
16
16
|
def welcome_banner() -> None:
|
17
17
|
banner = rf"""
|
18
18
|
[bold red]
|
19
|
-
/$$$$$$ /$$
|
20
|
-
/$$__ $$| $$
|
21
|
-
| $$ \
|
22
|
-
|
|
23
|
-
| $$
|
24
|
-
| $$ | $$|
|
25
|
-
|
|
26
|
-
|__/
|
27
|
-
|
28
|
-
|
29
|
-
|
19
|
+
/$$$$$$ /$$ /$$ /$$$$$$ /$$$$$$$ /$$ /$$
|
20
|
+
/$$__ $$| $$ | $$ /$$__ $$ | $$__ $$ | $$ | $$
|
21
|
+
| $$ \__/| $$ /$$$$$$ /$$ /$$ /$$$$$$$| $$ \ $$ /$$$$$$ /$$$$$$$ | $$ \ $$ /$$ /$$ /$$$$$$$ | $$$$$$$ /$$$$$$ /$$$$$$ | $$ /$$ /$$
|
22
|
+
| $$ | $$ /$$__ $$| $$ | $$ /$$__ $$| $$ | $$ /$$__ $$ /$$_____/ | $$$$$$$/ | $$ | $$| $$__ $$ | $$__ $$ /$$__ $$ /$$__ $$| $$ | $$ | $$
|
23
|
+
| $$ | $$| $$ \ $$| $$ | $$| $$ | $$| $$ | $$| $$ \ $$| $$$$$$ | $$__ $$ | $$ | $$| $$ \ $$ | $$ \ $$| $$ \ $$| $$ \ $$| $$ | $$ | $$
|
24
|
+
| $$ $$| $$| $$ | $$| $$ | $$| $$ | $$| $$ | $$| $$ | $$ \____ $$ | $$ \ $$ | $$ | $$| $$ | $$ | $$ | $$| $$ | $$| $$ | $$| $$ | $$ | $$
|
25
|
+
| $$$$$$/| $$| $$$$$$/| $$$$$$/| $$$$$$$| $$$$$$/| $$$$$$$/ /$$$$$$$/ | $$ | $$ | $$$$$$/| $$ | $$ | $$$$$$$/| $$$$$$/| $$$$$$/| $$ | $$$$$$/
|
26
|
+
\______/ |__/ \______/ \______/ \_______/ \______/ | $$____/ |_______/ |__/ |__/ \______/ |__/ |__/ |_______/ \______/ \______/ |__/ \______/
|
27
|
+
| $$
|
28
|
+
| $$
|
29
|
+
|__/
|
30
30
|
[/]
|
31
|
-
[bold bright_blue]
|
31
|
+
[bold bright_blue]CloudOps Runbooks FinOps Platform (v{__version__})[/]
|
32
32
|
"""
|
33
33
|
console.print(banner)
|
34
34
|
|
35
35
|
|
36
36
|
def check_latest_version() -> None:
|
37
|
-
"""Check for the latest version of the
|
37
|
+
"""Check for the latest version of the CloudOps Runbooks package."""
|
38
38
|
try:
|
39
|
-
response = requests.get("https://pypi.org/pypi/
|
39
|
+
response = requests.get("https://pypi.org/pypi/runbooks/json", timeout=3)
|
40
40
|
latest = response.json()["info"]["version"]
|
41
41
|
if version.parse(latest) > version.parse(__version__):
|
42
|
-
console.print(f"[bold red]A new version of
|
42
|
+
console.print(f"[bold red]A new version of CloudOps Runbooks is available: {latest}[/]")
|
43
43
|
console.print(
|
44
|
-
"[bold bright_yellow]Please update using:\
|
44
|
+
"[bold bright_yellow]Please update using:\npip install --upgrade runbooks\nor\nuv add runbooks@latest\n[/]"
|
45
45
|
)
|
46
46
|
except Exception:
|
47
47
|
pass
|
@@ -54,7 +54,9 @@ def main() -> int:
|
|
54
54
|
from runbooks.finops.dashboard_runner import run_dashboard
|
55
55
|
|
56
56
|
# Create the parser instance to be accessible for get_default
|
57
|
-
parser = argparse.ArgumentParser(
|
57
|
+
parser = argparse.ArgumentParser(
|
58
|
+
description="CloudOps Runbooks FinOps Platform - Enterprise Multi-Account Cost Optimization"
|
59
|
+
)
|
58
60
|
|
59
61
|
parser.add_argument(
|
60
62
|
"--config-file",
|
@@ -94,10 +96,10 @@ def main() -> int:
|
|
94
96
|
"--report-type",
|
95
97
|
"-y",
|
96
98
|
nargs="+",
|
97
|
-
choices=["csv", "json", "pdf"],
|
98
|
-
help="Specify one or more report types: csv and/or json and/or pdf (space-separated)",
|
99
|
+
choices=["csv", "json", "pdf", "markdown"],
|
100
|
+
help="Specify one or more report types: csv and/or json and/or pdf and/or markdown (space-separated)",
|
99
101
|
type=str,
|
100
|
-
default=["
|
102
|
+
default=["markdown"],
|
101
103
|
)
|
102
104
|
parser.add_argument(
|
103
105
|
"--dir",
|
@@ -145,6 +147,44 @@ def main() -> int:
|
|
145
147
|
help="Run PDCA in continuous mode (until manually stopped)",
|
146
148
|
)
|
147
149
|
|
150
|
+
# Enhanced Dashboard Configuration Parameters
|
151
|
+
parser.add_argument(
|
152
|
+
"--mode",
|
153
|
+
choices=["single_account", "multi_account"],
|
154
|
+
help="Explicit dashboard mode selection (overrides auto-detection)",
|
155
|
+
type=str,
|
156
|
+
)
|
157
|
+
parser.add_argument(
|
158
|
+
"--top-services",
|
159
|
+
help="Number of top services to display in single-account mode (default: 10)",
|
160
|
+
type=int,
|
161
|
+
default=10,
|
162
|
+
)
|
163
|
+
parser.add_argument(
|
164
|
+
"--top-accounts",
|
165
|
+
help="Number of top accounts to display in multi-account mode (default: 5)",
|
166
|
+
type=int,
|
167
|
+
default=5,
|
168
|
+
)
|
169
|
+
parser.add_argument(
|
170
|
+
"--services-per-account",
|
171
|
+
help="Number of services to show per account in multi-account mode (default: 3)",
|
172
|
+
type=int,
|
173
|
+
default=3,
|
174
|
+
)
|
175
|
+
parser.add_argument(
|
176
|
+
"--format",
|
177
|
+
choices=["table", "json", "csv", "markdown"],
|
178
|
+
help="Output format for dashboard display (default: markdown)",
|
179
|
+
type=str,
|
180
|
+
default="markdown",
|
181
|
+
)
|
182
|
+
parser.add_argument(
|
183
|
+
"--no-enhanced-routing",
|
184
|
+
action="store_true",
|
185
|
+
help="Disable enhanced service-focused routing (use legacy account-per-row layout)",
|
186
|
+
)
|
187
|
+
|
148
188
|
args = parser.parse_args()
|
149
189
|
|
150
190
|
config_data: Optional[Dict[str, Any]] = None
|
@@ -162,39 +202,40 @@ def main() -> int:
|
|
162
202
|
# Handle PDCA mode
|
163
203
|
if args.pdca or args.pdca_continuous:
|
164
204
|
import asyncio
|
205
|
+
|
165
206
|
from runbooks.finops.pdca_engine import AutonomousPDCAEngine, PDCAThresholds
|
166
|
-
|
207
|
+
|
167
208
|
console.print("[bold bright_cyan]π€ Launching Autonomous PDCA Engine...[/]")
|
168
|
-
|
209
|
+
|
169
210
|
# Configure PDCA thresholds
|
170
211
|
thresholds = PDCAThresholds(
|
171
212
|
max_risk_score=25,
|
172
213
|
max_cost_increase=10.0,
|
173
214
|
max_untagged_resources=50,
|
174
215
|
max_unused_eips=5,
|
175
|
-
max_budget_overruns=1
|
216
|
+
max_budget_overruns=1,
|
176
217
|
)
|
177
|
-
|
218
|
+
|
178
219
|
# Initialize PDCA engine
|
179
220
|
artifacts_dir = args.dir or "artifacts"
|
180
221
|
engine = AutonomousPDCAEngine(thresholds=thresholds, artifacts_dir=artifacts_dir)
|
181
|
-
|
222
|
+
|
182
223
|
try:
|
183
224
|
# Determine execution mode
|
184
225
|
continuous_mode = args.pdca_continuous
|
185
226
|
max_cycles = 0 if continuous_mode else args.pdca_cycles
|
186
|
-
|
227
|
+
|
187
228
|
# Run PDCA cycles
|
188
229
|
metrics_history = asyncio.run(engine.run_autonomous_cycles(max_cycles, continuous_mode))
|
189
|
-
|
230
|
+
|
190
231
|
# Generate summary report
|
191
232
|
engine.generate_cycle_summary_report()
|
192
|
-
|
233
|
+
|
193
234
|
console.print(f"\n[bold bright_green]π PDCA Engine completed successfully![/]")
|
194
235
|
console.print(f"[cyan]Generated {len(metrics_history)} cycle reports in: {engine.pdca_dir}[/]")
|
195
|
-
|
236
|
+
|
196
237
|
return 0
|
197
|
-
|
238
|
+
|
198
239
|
except KeyboardInterrupt:
|
199
240
|
console.print(f"\n[yellow]βΈοΈ PDCA Engine stopped by user[/]")
|
200
241
|
if engine.cycle_history:
|
@@ -204,8 +245,24 @@ def main() -> int:
|
|
204
245
|
console.print(f"\n[red]β PDCA Engine failed: {str(e)}[/]")
|
205
246
|
return 1
|
206
247
|
|
207
|
-
#
|
208
|
-
|
248
|
+
# Enhanced routing is now the default (service-per-row layout)
|
249
|
+
# Maintain backward compatibility with explicit --no-enhanced-routing flag
|
250
|
+
use_enhanced_routing = not getattr(args, "no_enhanced_routing", False)
|
251
|
+
|
252
|
+
if use_enhanced_routing:
|
253
|
+
try:
|
254
|
+
from runbooks.finops.dashboard_router import route_finops_request
|
255
|
+
|
256
|
+
console.print("[bold bright_cyan]π Using Enhanced Service-Focused Dashboard[/]")
|
257
|
+
result = route_finops_request(args)
|
258
|
+
except Exception as e:
|
259
|
+
console.print(f"[yellow]β οΈ Enhanced routing failed ({str(e)[:50]}), falling back to legacy mode[/]")
|
260
|
+
result = run_dashboard(args)
|
261
|
+
else:
|
262
|
+
# Legacy dashboard mode (backward compatibility)
|
263
|
+
console.print("[dim]Using legacy dashboard mode[/]")
|
264
|
+
result = run_dashboard(args)
|
265
|
+
|
209
266
|
return 0 if result == 0 else 1
|
210
267
|
|
211
268
|
|