runbooks 0.7.9__py3-none-any.whl → 0.9.1__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/cfat/README.md +12 -1
- runbooks/cfat/__init__.py +1 -1
- runbooks/cfat/assessment/compliance.py +4 -1
- runbooks/cfat/assessment/runner.py +42 -34
- runbooks/cfat/models.py +1 -1
- runbooks/cloudops/__init__.py +123 -0
- runbooks/cloudops/base.py +385 -0
- runbooks/cloudops/cost_optimizer.py +811 -0
- runbooks/cloudops/infrastructure_optimizer.py +29 -0
- runbooks/cloudops/interfaces.py +828 -0
- runbooks/cloudops/lifecycle_manager.py +29 -0
- runbooks/cloudops/mcp_cost_validation.py +678 -0
- runbooks/cloudops/models.py +251 -0
- runbooks/cloudops/monitoring_automation.py +29 -0
- runbooks/cloudops/notebook_framework.py +676 -0
- runbooks/cloudops/security_enforcer.py +449 -0
- 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/mcp_cost_explorer_integration.py +900 -0
- runbooks/common/mcp_integration.py +548 -0
- runbooks/common/performance_monitor.py +387 -0
- runbooks/common/profile_utils.py +216 -0
- runbooks/common/rich_utils.py +172 -1
- runbooks/feedback/user_feedback_collector.py +440 -0
- runbooks/finops/README.md +377 -458
- runbooks/finops/__init__.py +4 -21
- 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 +59 -5
- runbooks/finops/cost_optimizer.py +1340 -0
- runbooks/finops/cost_processor.py +211 -37
- runbooks/finops/dashboard_router.py +900 -0
- runbooks/finops/dashboard_runner.py +990 -232
- runbooks/finops/embedded_mcp_validator.py +288 -0
- runbooks/finops/enhanced_dashboard_runner.py +8 -7
- runbooks/finops/enhanced_progress.py +327 -0
- runbooks/finops/enhanced_trend_visualization.py +423 -0
- runbooks/finops/finops_dashboard.py +184 -1829
- runbooks/finops/helpers.py +509 -196
- 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 +15 -15
- 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/schemas.py +589 -0
- runbooks/finops/service_mapping.py +195 -0
- runbooks/finops/single_dashboard.py +710 -0
- runbooks/finops/tests/test_reference_images_validation.py +1 -1
- runbooks/inventory/README.md +12 -1
- runbooks/inventory/core/collector.py +157 -29
- runbooks/inventory/list_ec2_instances.py +9 -6
- runbooks/inventory/list_ssm_parameters.py +10 -10
- runbooks/inventory/organizations_discovery.py +210 -164
- runbooks/inventory/rich_inventory_display.py +74 -107
- runbooks/inventory/run_on_multi_accounts.py +13 -13
- runbooks/inventory/runbooks.inventory.organizations_discovery.log +0 -0
- runbooks/inventory/runbooks.security.security_export.log +0 -0
- runbooks/main.py +1371 -240
- runbooks/metrics/dora_metrics_engine.py +711 -17
- runbooks/monitoring/performance_monitor.py +433 -0
- runbooks/operate/README.md +394 -0
- runbooks/operate/base.py +215 -47
- runbooks/operate/ec2_operations.py +435 -5
- runbooks/operate/iam_operations.py +598 -3
- runbooks/operate/privatelink_operations.py +1 -1
- runbooks/operate/rds_operations.py +508 -0
- runbooks/operate/s3_operations.py +508 -0
- runbooks/operate/vpc_endpoints.py +1 -1
- runbooks/remediation/README.md +489 -13
- runbooks/remediation/base.py +5 -3
- runbooks/remediation/commons.py +8 -4
- runbooks/security/ENTERPRISE_SECURITY_FRAMEWORK.md +506 -0
- runbooks/security/README.md +12 -1
- runbooks/security/__init__.py +265 -33
- runbooks/security/cloudops_automation_security_validator.py +1164 -0
- runbooks/security/compliance_automation.py +12 -10
- runbooks/security/compliance_automation_engine.py +1021 -0
- runbooks/security/enterprise_security_framework.py +930 -0
- runbooks/security/enterprise_security_policies.json +293 -0
- runbooks/security/executive_security_dashboard.py +1247 -0
- runbooks/security/integration_test_enterprise_security.py +879 -0
- runbooks/security/module_security_integrator.py +641 -0
- runbooks/security/multi_account_security_controls.py +2254 -0
- runbooks/security/real_time_security_monitor.py +1196 -0
- runbooks/security/report_generator.py +1 -1
- runbooks/security/run_script.py +4 -8
- runbooks/security/security_baseline_tester.py +39 -52
- runbooks/security/security_export.py +99 -120
- 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/production_monitoring_framework.py +584 -0
- runbooks/sre/reliability_monitoring_framework.py +1011 -0
- runbooks/validation/__init__.py +2 -2
- runbooks/validation/benchmark.py +154 -149
- runbooks/validation/cli.py +159 -147
- runbooks/validation/mcp_validator.py +291 -248
- runbooks/vpc/README.md +478 -0
- runbooks/vpc/__init__.py +2 -2
- runbooks/vpc/manager_interface.py +366 -351
- runbooks/vpc/networking_wrapper.py +68 -36
- runbooks/vpc/rich_formatters.py +22 -8
- runbooks-0.9.1.dist-info/METADATA +308 -0
- {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/RECORD +120 -59
- {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/entry_points.txt +1 -1
- runbooks/finops/cross_validation.py +0 -375
- runbooks-0.7.9.dist-info/METADATA +0 -636
- {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/WHEEL +0 -0
- {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/licenses/LICENSE +0 -0
- {runbooks-0.7.9.dist-info → runbooks-0.9.1.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
@@ -96,10 +96,10 @@ def main() -> int:
|
|
96
96
|
"--report-type",
|
97
97
|
"-y",
|
98
98
|
nargs="+",
|
99
|
-
choices=["csv", "json", "pdf"],
|
100
|
-
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)",
|
101
101
|
type=str,
|
102
|
-
default=["
|
102
|
+
default=["markdown"],
|
103
103
|
)
|
104
104
|
parser.add_argument(
|
105
105
|
"--dir",
|
@@ -147,6 +147,44 @@ def main() -> int:
|
|
147
147
|
help="Run PDCA in continuous mode (until manually stopped)",
|
148
148
|
)
|
149
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
|
+
|
150
188
|
args = parser.parse_args()
|
151
189
|
|
152
190
|
config_data: Optional[Dict[str, Any]] = None
|
@@ -207,8 +245,24 @@ def main() -> int:
|
|
207
245
|
console.print(f"\n[red]❌ PDCA Engine failed: {str(e)}[/]")
|
208
246
|
return 1
|
209
247
|
|
210
|
-
#
|
211
|
-
|
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
|
+
|
212
266
|
return 0 if result == 0 else 1
|
213
267
|
|
214
268
|
|