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.
Files changed (122) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/cfat/README.md +12 -1
  3. runbooks/cfat/__init__.py +1 -1
  4. runbooks/cfat/assessment/compliance.py +4 -1
  5. runbooks/cfat/assessment/runner.py +42 -34
  6. runbooks/cfat/models.py +1 -1
  7. runbooks/cloudops/__init__.py +123 -0
  8. runbooks/cloudops/base.py +385 -0
  9. runbooks/cloudops/cost_optimizer.py +811 -0
  10. runbooks/cloudops/infrastructure_optimizer.py +29 -0
  11. runbooks/cloudops/interfaces.py +828 -0
  12. runbooks/cloudops/lifecycle_manager.py +29 -0
  13. runbooks/cloudops/mcp_cost_validation.py +678 -0
  14. runbooks/cloudops/models.py +251 -0
  15. runbooks/cloudops/monitoring_automation.py +29 -0
  16. runbooks/cloudops/notebook_framework.py +676 -0
  17. runbooks/cloudops/security_enforcer.py +449 -0
  18. runbooks/common/__init__.py +152 -0
  19. runbooks/common/accuracy_validator.py +1039 -0
  20. runbooks/common/context_logger.py +440 -0
  21. runbooks/common/cross_module_integration.py +594 -0
  22. runbooks/common/enhanced_exception_handler.py +1108 -0
  23. runbooks/common/enterprise_audit_integration.py +634 -0
  24. runbooks/common/mcp_cost_explorer_integration.py +900 -0
  25. runbooks/common/mcp_integration.py +548 -0
  26. runbooks/common/performance_monitor.py +387 -0
  27. runbooks/common/profile_utils.py +216 -0
  28. runbooks/common/rich_utils.py +172 -1
  29. runbooks/feedback/user_feedback_collector.py +440 -0
  30. runbooks/finops/README.md +377 -458
  31. runbooks/finops/__init__.py +4 -21
  32. runbooks/finops/account_resolver.py +279 -0
  33. runbooks/finops/accuracy_cross_validator.py +638 -0
  34. runbooks/finops/aws_client.py +721 -36
  35. runbooks/finops/budget_integration.py +313 -0
  36. runbooks/finops/cli.py +59 -5
  37. runbooks/finops/cost_optimizer.py +1340 -0
  38. runbooks/finops/cost_processor.py +211 -37
  39. runbooks/finops/dashboard_router.py +900 -0
  40. runbooks/finops/dashboard_runner.py +990 -232
  41. runbooks/finops/embedded_mcp_validator.py +288 -0
  42. runbooks/finops/enhanced_dashboard_runner.py +8 -7
  43. runbooks/finops/enhanced_progress.py +327 -0
  44. runbooks/finops/enhanced_trend_visualization.py +423 -0
  45. runbooks/finops/finops_dashboard.py +184 -1829
  46. runbooks/finops/helpers.py +509 -196
  47. runbooks/finops/iam_guidance.py +400 -0
  48. runbooks/finops/markdown_exporter.py +466 -0
  49. runbooks/finops/multi_dashboard.py +1502 -0
  50. runbooks/finops/optimizer.py +15 -15
  51. runbooks/finops/profile_processor.py +2 -2
  52. runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
  53. runbooks/finops/runbooks.security.report_generator.log +0 -0
  54. runbooks/finops/runbooks.security.run_script.log +0 -0
  55. runbooks/finops/runbooks.security.security_export.log +0 -0
  56. runbooks/finops/schemas.py +589 -0
  57. runbooks/finops/service_mapping.py +195 -0
  58. runbooks/finops/single_dashboard.py +710 -0
  59. runbooks/finops/tests/test_reference_images_validation.py +1 -1
  60. runbooks/inventory/README.md +12 -1
  61. runbooks/inventory/core/collector.py +157 -29
  62. runbooks/inventory/list_ec2_instances.py +9 -6
  63. runbooks/inventory/list_ssm_parameters.py +10 -10
  64. runbooks/inventory/organizations_discovery.py +210 -164
  65. runbooks/inventory/rich_inventory_display.py +74 -107
  66. runbooks/inventory/run_on_multi_accounts.py +13 -13
  67. runbooks/inventory/runbooks.inventory.organizations_discovery.log +0 -0
  68. runbooks/inventory/runbooks.security.security_export.log +0 -0
  69. runbooks/main.py +1371 -240
  70. runbooks/metrics/dora_metrics_engine.py +711 -17
  71. runbooks/monitoring/performance_monitor.py +433 -0
  72. runbooks/operate/README.md +394 -0
  73. runbooks/operate/base.py +215 -47
  74. runbooks/operate/ec2_operations.py +435 -5
  75. runbooks/operate/iam_operations.py +598 -3
  76. runbooks/operate/privatelink_operations.py +1 -1
  77. runbooks/operate/rds_operations.py +508 -0
  78. runbooks/operate/s3_operations.py +508 -0
  79. runbooks/operate/vpc_endpoints.py +1 -1
  80. runbooks/remediation/README.md +489 -13
  81. runbooks/remediation/base.py +5 -3
  82. runbooks/remediation/commons.py +8 -4
  83. runbooks/security/ENTERPRISE_SECURITY_FRAMEWORK.md +506 -0
  84. runbooks/security/README.md +12 -1
  85. runbooks/security/__init__.py +265 -33
  86. runbooks/security/cloudops_automation_security_validator.py +1164 -0
  87. runbooks/security/compliance_automation.py +12 -10
  88. runbooks/security/compliance_automation_engine.py +1021 -0
  89. runbooks/security/enterprise_security_framework.py +930 -0
  90. runbooks/security/enterprise_security_policies.json +293 -0
  91. runbooks/security/executive_security_dashboard.py +1247 -0
  92. runbooks/security/integration_test_enterprise_security.py +879 -0
  93. runbooks/security/module_security_integrator.py +641 -0
  94. runbooks/security/multi_account_security_controls.py +2254 -0
  95. runbooks/security/real_time_security_monitor.py +1196 -0
  96. runbooks/security/report_generator.py +1 -1
  97. runbooks/security/run_script.py +4 -8
  98. runbooks/security/security_baseline_tester.py +39 -52
  99. runbooks/security/security_export.py +99 -120
  100. runbooks/sre/README.md +472 -0
  101. runbooks/sre/__init__.py +33 -0
  102. runbooks/sre/mcp_reliability_engine.py +1049 -0
  103. runbooks/sre/performance_optimization_engine.py +1032 -0
  104. runbooks/sre/production_monitoring_framework.py +584 -0
  105. runbooks/sre/reliability_monitoring_framework.py +1011 -0
  106. runbooks/validation/__init__.py +2 -2
  107. runbooks/validation/benchmark.py +154 -149
  108. runbooks/validation/cli.py +159 -147
  109. runbooks/validation/mcp_validator.py +291 -248
  110. runbooks/vpc/README.md +478 -0
  111. runbooks/vpc/__init__.py +2 -2
  112. runbooks/vpc/manager_interface.py +366 -351
  113. runbooks/vpc/networking_wrapper.py +68 -36
  114. runbooks/vpc/rich_formatters.py +22 -8
  115. runbooks-0.9.1.dist-info/METADATA +308 -0
  116. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/RECORD +120 -59
  117. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/entry_points.txt +1 -1
  118. runbooks/finops/cross_validation.py +0 -375
  119. runbooks-0.7.9.dist-info/METADATA +0 -636
  120. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/WHEEL +0 -0
  121. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/licenses/LICENSE +0 -0
  122. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,288 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Embedded MCP Validator - Internal AWS API Validation for Enterprise Accuracy
4
+
5
+ This module provides self-contained MCP-style validation without external dependencies.
6
+ Direct AWS API integration ensures >=99.5% financial accuracy for enterprise compliance.
7
+
8
+ User Innovation: "MCP inside runbooks API may be a good feature"
9
+ Implementation: Embedded validation eliminates external MCP server requirements
10
+ """
11
+
12
+ import asyncio
13
+ import time
14
+ from datetime import datetime, timedelta
15
+ from typing import Any, Dict, List, Optional, Tuple
16
+
17
+ import boto3
18
+ from rich.console import Console
19
+ from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn, TimeElapsedColumn
20
+
21
+ from ..common.rich_utils import (
22
+ console as rich_console,
23
+ )
24
+ from ..common.rich_utils import (
25
+ format_cost,
26
+ print_error,
27
+ print_info,
28
+ print_success,
29
+ print_warning,
30
+ )
31
+
32
+
33
+ class EmbeddedMCPValidator:
34
+ """
35
+ Internal MCP-style validator with direct AWS API integration.
36
+
37
+ Provides real-time cost validation without external MCP server dependencies.
38
+ Ensures >=99.5% accuracy for enterprise financial compliance.
39
+ """
40
+
41
+ def __init__(self, profiles: List[str], console: Optional[Console] = None):
42
+ """Initialize embedded MCP validator with AWS profiles."""
43
+ self.profiles = profiles
44
+ self.console = console or rich_console
45
+ self.aws_sessions = {}
46
+ self.validation_threshold = 99.5 # Enterprise accuracy requirement
47
+ self.tolerance_percent = 5.0 # ±5% tolerance for validation
48
+
49
+ # Initialize AWS sessions for each profile
50
+ self._initialize_aws_sessions()
51
+
52
+ def _initialize_aws_sessions(self) -> None:
53
+ """Initialize AWS sessions for all profiles with error handling."""
54
+ for profile in self.profiles:
55
+ try:
56
+ session = boto3.Session(profile_name=profile)
57
+ # Test session validity
58
+ session.client("sts").get_caller_identity()
59
+ self.aws_sessions[profile] = session
60
+ print_info(f"MCP session initialized for profile: {profile[:30]}...")
61
+ except Exception as e:
62
+ print_warning(f"MCP session failed for {profile[:20]}...: {str(e)[:30]}")
63
+
64
+ async def validate_cost_data_async(self, runbooks_data: Dict[str, Any]) -> Dict[str, Any]:
65
+ """
66
+ Asynchronously validate runbooks cost data against direct AWS API calls.
67
+
68
+ Args:
69
+ runbooks_data: Cost data from runbooks FinOps analysis
70
+
71
+ Returns:
72
+ Validation results with accuracy metrics
73
+ """
74
+ validation_results = {
75
+ "validation_timestamp": datetime.now().isoformat(),
76
+ "profiles_validated": 0,
77
+ "total_accuracy": 0.0,
78
+ "passed_validation": False,
79
+ "profile_results": [],
80
+ "validation_method": "embedded_mcp_direct_aws_api",
81
+ }
82
+
83
+ with Progress(
84
+ SpinnerColumn(),
85
+ TextColumn("[progress.description]{task.description}"),
86
+ BarColumn(),
87
+ TaskProgressColumn(),
88
+ TimeElapsedColumn(),
89
+ console=self.console,
90
+ ) as progress:
91
+ task = progress.add_task("Validating financial data with embedded MCP...", total=len(self.aws_sessions))
92
+
93
+ for profile, session in self.aws_sessions.items():
94
+ try:
95
+ # Get independent cost data from AWS API
96
+ aws_cost_data = await self._get_independent_cost_data(session, profile)
97
+
98
+ # Find corresponding runbooks data
99
+ runbooks_cost_data = self._extract_runbooks_cost_data(runbooks_data, profile)
100
+
101
+ # Calculate accuracy
102
+ accuracy_result = self._calculate_accuracy(runbooks_cost_data, aws_cost_data, profile)
103
+ validation_results["profile_results"].append(accuracy_result)
104
+
105
+ progress.advance(task)
106
+
107
+ except Exception as e:
108
+ print_warning(f"Validation failed for {profile[:20]}...: {str(e)[:40]}")
109
+ progress.advance(task)
110
+
111
+ # Calculate overall validation metrics
112
+ self._finalize_validation_results(validation_results)
113
+ return validation_results
114
+
115
+ async def _get_independent_cost_data(self, session: boto3.Session, profile: str) -> Dict[str, Any]:
116
+ """Get independent cost data directly from AWS Cost Explorer API."""
117
+ try:
118
+ ce_client = session.client("ce", region_name="us-east-1")
119
+
120
+ # Calculate date range (current month)
121
+ end_date = datetime.now().date()
122
+ start_date = end_date.replace(day=1)
123
+
124
+ # Get cost and usage data (independent from runbooks)
125
+ response = ce_client.get_cost_and_usage(
126
+ TimePeriod={"Start": start_date.isoformat(), "End": end_date.isoformat()},
127
+ Granularity="MONTHLY",
128
+ Metrics=["BlendedCost"],
129
+ GroupBy=[{"Type": "DIMENSION", "Key": "SERVICE"}],
130
+ )
131
+
132
+ # Process AWS response into comparable format
133
+ total_cost = 0.0
134
+ services_cost = {}
135
+
136
+ if response.get("ResultsByTime"):
137
+ for result in response["ResultsByTime"]:
138
+ for group in result.get("Groups", []):
139
+ service = group.get("Keys", ["Unknown"])[0]
140
+ cost = float(group.get("Metrics", {}).get("BlendedCost", {}).get("Amount", 0))
141
+ services_cost[service] = cost
142
+ total_cost += cost
143
+
144
+ return {
145
+ "profile": profile,
146
+ "total_cost": total_cost,
147
+ "services": services_cost,
148
+ "data_source": "direct_aws_cost_explorer",
149
+ "timestamp": datetime.now().isoformat(),
150
+ }
151
+
152
+ except Exception as e:
153
+ return {
154
+ "profile": profile,
155
+ "error": str(e),
156
+ "total_cost": 0.0,
157
+ "services": {},
158
+ "data_source": "error_fallback",
159
+ }
160
+
161
+ def _extract_runbooks_cost_data(self, runbooks_data: Dict[str, Any], profile: str) -> Dict[str, Any]:
162
+ """Extract cost data from runbooks results for comparison."""
163
+ # This method adapts to the actual runbooks data structure
164
+ # Implementation depends on the runbooks data format
165
+ return {
166
+ "profile": profile,
167
+ "total_cost": runbooks_data.get("total_cost", 0.0),
168
+ "services": runbooks_data.get("services", {}),
169
+ "data_source": "runbooks_finops_analysis",
170
+ }
171
+
172
+ def _calculate_accuracy(self, runbooks_data: Dict, aws_data: Dict, profile: str) -> Dict[str, Any]:
173
+ """Calculate accuracy between runbooks and AWS API data."""
174
+ try:
175
+ runbooks_cost = float(runbooks_data.get("total_cost", 0))
176
+ aws_cost = float(aws_data.get("total_cost", 0))
177
+
178
+ if runbooks_cost > 0:
179
+ accuracy_percent = (1 - abs(runbooks_cost - aws_cost) / runbooks_cost) * 100
180
+ else:
181
+ accuracy_percent = 100.0 if aws_cost == 0 else 0.0
182
+
183
+ # Determine validation status
184
+ passed = accuracy_percent >= self.validation_threshold
185
+
186
+ return {
187
+ "profile": profile,
188
+ "runbooks_cost": runbooks_cost,
189
+ "aws_api_cost": aws_cost,
190
+ "accuracy_percent": accuracy_percent,
191
+ "passed_validation": passed,
192
+ "tolerance_met": abs(runbooks_cost - aws_cost) / max(runbooks_cost, 1) * 100 <= self.tolerance_percent,
193
+ "cost_difference": abs(runbooks_cost - aws_cost),
194
+ "validation_status": "PASSED" if passed else "FAILED",
195
+ }
196
+
197
+ except Exception as e:
198
+ return {
199
+ "profile": profile,
200
+ "accuracy_percent": 0.0,
201
+ "passed_validation": False,
202
+ "error": str(e),
203
+ "validation_status": "ERROR",
204
+ }
205
+
206
+ def _finalize_validation_results(self, validation_results: Dict[str, Any]) -> None:
207
+ """Calculate overall validation metrics and status."""
208
+ profile_results = validation_results["profile_results"]
209
+
210
+ if not profile_results:
211
+ validation_results["total_accuracy"] = 0.0
212
+ validation_results["passed_validation"] = False
213
+ return
214
+
215
+ # Calculate overall accuracy
216
+ valid_results = [r for r in profile_results if r.get("accuracy_percent", 0) > 0]
217
+ if valid_results:
218
+ total_accuracy = sum(r["accuracy_percent"] for r in valid_results) / len(valid_results)
219
+ validation_results["total_accuracy"] = total_accuracy
220
+ validation_results["profiles_validated"] = len(valid_results)
221
+ validation_results["passed_validation"] = total_accuracy >= self.validation_threshold
222
+
223
+ # Display results
224
+ self._display_validation_results(validation_results)
225
+
226
+ def _display_validation_results(self, results: Dict[str, Any]) -> None:
227
+ """Display validation results with Rich CLI formatting."""
228
+ overall_accuracy = results.get("total_accuracy", 0)
229
+ passed = results.get("passed_validation", False)
230
+
231
+ self.console.print(f"\n[bright_cyan]🔍 Embedded MCP Validation Results[/]")
232
+
233
+ # Display per-profile results
234
+ for profile_result in results.get("profile_results", []):
235
+ accuracy = profile_result.get("accuracy_percent", 0)
236
+ status = profile_result.get("validation_status", "UNKNOWN")
237
+ profile = profile_result.get("profile", "Unknown")
238
+
239
+ if status == "PASSED":
240
+ icon = "✅"
241
+ color = "green"
242
+ elif status == "FAILED":
243
+ icon = "⚠️"
244
+ color = "yellow"
245
+ else:
246
+ icon = "❌"
247
+ color = "red"
248
+
249
+ self.console.print(f"[dim] {profile[:30]}: {icon} [{color}]{accuracy:.1f}% accuracy[/][/]")
250
+
251
+ # Overall summary
252
+ if passed:
253
+ print_success(f"✅ MCP Validation PASSED: {overall_accuracy:.1f}% accuracy achieved")
254
+ print_info(f"Enterprise compliance: {results['profiles_validated']} profiles validated")
255
+ else:
256
+ print_warning(f"⚠️ MCP Validation: {overall_accuracy:.1f}% accuracy (≥99.5% required)")
257
+ print_info("Consider reviewing data sources for accuracy improvements")
258
+
259
+ def validate_cost_data(self, runbooks_data: Dict[str, Any]) -> Dict[str, Any]:
260
+ """Synchronous wrapper for async validation."""
261
+ try:
262
+ loop = asyncio.get_event_loop()
263
+ except RuntimeError:
264
+ loop = asyncio.new_event_loop()
265
+ asyncio.set_event_loop(loop)
266
+
267
+ return loop.run_until_complete(self.validate_cost_data_async(runbooks_data))
268
+
269
+
270
+ def create_embedded_mcp_validator(profiles: List[str], console: Optional[Console] = None) -> EmbeddedMCPValidator:
271
+ """Factory function to create embedded MCP validator."""
272
+ return EmbeddedMCPValidator(profiles=profiles, console=console)
273
+
274
+
275
+ # Integration with existing FinOps dashboard
276
+ def validate_finops_results_with_embedded_mcp(profiles: List[str], runbooks_results: Dict[str, Any]) -> Dict[str, Any]:
277
+ """
278
+ Convenience function to validate FinOps results with embedded MCP.
279
+
280
+ Args:
281
+ profiles: List of AWS profiles to validate
282
+ runbooks_results: Results from runbooks FinOps analysis
283
+
284
+ Returns:
285
+ Validation results with accuracy metrics
286
+ """
287
+ validator = create_embedded_mcp_validator(profiles)
288
+ return validator.validate_cost_data(runbooks_results)
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Enhanced FinOps Dashboard Runner
4
- Migrated and optimized from README/aws-finops-dashboard
5
4
 
6
5
  This module provides enterprise-grade FinOps dashboard capabilities including:
7
6
  - Multi-profile AWS cost analysis with Rich console formatting
@@ -29,16 +28,17 @@ from rich.table import Column, Table
29
28
  from rich.tree import Tree
30
29
 
31
30
  from ..common.rich_utils import get_console
32
- from .finops_dashboard import FinOpsConfig
31
+
32
+ # FinOpsConfig dependency removed - using simple dict configuration instead
33
33
 
34
34
  console = Console()
35
35
 
36
36
 
37
37
  class EnhancedFinOpsDashboard:
38
- """Enhanced FinOps Dashboard with production-tested capabilities from aws-finops-dashboard"""
38
+ """Enhanced FinOps Dashboard with production-tested capabilities from runbooks finops"""
39
39
 
40
- def __init__(self, config: Optional[FinOpsConfig] = None):
41
- self.config = config or FinOpsConfig()
40
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
41
+ self.config = config or {}
42
42
  self.console = Console()
43
43
  self.rich_console = self.console # Use the console instance directly
44
44
 
@@ -202,8 +202,9 @@ class EnhancedFinOpsDashboard:
202
202
  unused_volumes = volumes_response["Volumes"]
203
203
  region_data["unused_volumes"] = len(unused_volumes)
204
204
 
205
- # Calculate EBS volume costs (rough estimate: $0.10/GB/month)
206
- volume_savings = sum(vol["Size"] * 0.10 for vol in unused_volumes)
205
+ # Note: EBS cost calculation requires real AWS Cost Explorer pricing data
206
+ # Hardcoded pricing estimates removed per compliance requirements
207
+ volume_savings = 0 # Cannot calculate without real AWS pricing API
207
208
  region_data["potential_savings"] += volume_savings
208
209
 
209
210
  if unused_volumes:
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Enhanced Progress Bar Implementation - Smooth Progress Tracking
4
+
5
+ This module provides enhanced progress bar implementations that address the
6
+ 0%→100% jump issue by providing meaningful incremental progress updates
7
+ during AWS API operations and data processing.
8
+
9
+ Features:
10
+ - Smooth incremental progress updates
11
+ - Real-time operation tracking
12
+ - Context-aware progress estimation
13
+ - Rich CLI integration with beautiful progress bars
14
+ - Performance monitoring and timing
15
+ - Operation-specific progress patterns
16
+
17
+ Author: CloudOps Runbooks Team
18
+ Version: 0.8.0
19
+ """
20
+
21
+ import time
22
+ from contextlib import contextmanager
23
+ from typing import Any, Callable, Dict, Iterator, List, Optional, Union
24
+
25
+ from rich.console import Console
26
+ from rich.progress import (
27
+ BarColumn,
28
+ Progress,
29
+ SpinnerColumn,
30
+ TaskProgressColumn,
31
+ TextColumn,
32
+ TimeElapsedColumn,
33
+ TimeRemainingColumn,
34
+ )
35
+
36
+ from ..common.rich_utils import console as rich_console
37
+
38
+
39
+ class EnhancedProgressTracker:
40
+ """
41
+ Enhanced progress tracking with smooth incremental updates.
42
+
43
+ Provides context-aware progress estimation for AWS operations
44
+ and prevents jarring 0%→100% jumps by breaking operations into
45
+ meaningful sub-steps with realistic timing.
46
+ """
47
+
48
+ def __init__(self, console: Optional[Console] = None):
49
+ self.console = console or rich_console
50
+ self.operation_timing = {
51
+ "aws_cost_data": {"steps": 5, "estimated_seconds": 8},
52
+ "budget_analysis": {"steps": 3, "estimated_seconds": 4},
53
+ "service_analysis": {"steps": 4, "estimated_seconds": 6},
54
+ "multi_account_analysis": {"steps": 6, "estimated_seconds": 12},
55
+ "resource_discovery": {"steps": 8, "estimated_seconds": 15},
56
+ }
57
+
58
+ @contextmanager
59
+ def create_enhanced_progress(
60
+ self, operation_type: str = "default", total_items: Optional[int] = None
61
+ ) -> Iterator["ProgressContext"]:
62
+ """
63
+ Create enhanced progress context with smooth incremental updates.
64
+
65
+ Args:
66
+ operation_type: Type of operation for timing estimation
67
+ total_items: Total number of items to process (for accurate progress)
68
+
69
+ Yields:
70
+ ProgressContext: Enhanced progress context manager
71
+ """
72
+ timing_info = self.operation_timing.get(operation_type, {"steps": 5, "estimated_seconds": 8})
73
+
74
+ progress = Progress(
75
+ SpinnerColumn(),
76
+ TextColumn("[progress.description]{task.description}"),
77
+ BarColumn(complete_style="bright_green", finished_style="bright_green"),
78
+ TaskProgressColumn(),
79
+ TimeElapsedColumn(),
80
+ TimeRemainingColumn(),
81
+ console=self.console,
82
+ transient=False,
83
+ )
84
+
85
+ with progress:
86
+ context = ProgressContext(progress, timing_info, total_items)
87
+ yield context
88
+
89
+ def create_multi_stage_progress(self, stages: List[Dict[str, Any]]) -> "MultiStageProgress":
90
+ """
91
+ Create multi-stage progress for complex operations.
92
+
93
+ Args:
94
+ stages: List of stage definitions with names and estimated durations
95
+
96
+ Returns:
97
+ MultiStageProgress: Multi-stage progress manager
98
+ """
99
+ return MultiStageProgress(self.console, stages)
100
+
101
+
102
+ class ProgressContext:
103
+ """
104
+ Enhanced progress context with smooth incremental updates.
105
+
106
+ Provides methods for updating progress with realistic timing
107
+ and prevents jarring progress jumps.
108
+ """
109
+
110
+ def __init__(self, progress: Progress, timing_info: Dict[str, Any], total_items: Optional[int] = None):
111
+ self.progress = progress
112
+ self.timing_info = timing_info
113
+ self.total_items = total_items or 100
114
+ self.current_step = 0
115
+ self.max_steps = timing_info["steps"]
116
+ self.estimated_seconds = timing_info["estimated_seconds"]
117
+ self.step_duration = self.estimated_seconds / self.max_steps
118
+ self.task_id = None
119
+
120
+ def start_operation(self, description: str) -> None:
121
+ """Start the operation with initial progress."""
122
+ self.task_id = self.progress.add_task(description, total=self.total_items)
123
+ self.current_step = 0
124
+
125
+ def update_step(self, step_name: str, increment: Optional[int] = None) -> None:
126
+ """
127
+ Update progress to next step with smooth incremental updates.
128
+
129
+ Args:
130
+ step_name: Name of the current step
131
+ increment: Optional specific increment amount
132
+ """
133
+ if self.task_id is None:
134
+ return
135
+
136
+ self.current_step += 1
137
+
138
+ # Calculate target progress based on current step
139
+ target_progress = (self.current_step / self.max_steps) * self.total_items
140
+
141
+ if increment:
142
+ target_progress = min(self.total_items, increment)
143
+
144
+ # Update with smooth incremental steps
145
+ current_progress = self.progress.tasks[self.task_id].completed
146
+ steps_needed = max(1, int((target_progress - current_progress) / 5)) # Break into 5 increments
147
+ increment_size = (target_progress - current_progress) / steps_needed
148
+
149
+ for i in range(steps_needed):
150
+ new_progress = current_progress + (increment_size * (i + 1))
151
+ self.progress.update(self.task_id, completed=min(self.total_items, new_progress), description=step_name)
152
+ # Small delay for smooth visual effect
153
+ time.sleep(0.1)
154
+
155
+ def complete_operation(self, final_message: str = "Operation completed") -> None:
156
+ """Complete the operation with 100% progress."""
157
+ if self.task_id is not None:
158
+ self.progress.update(self.task_id, completed=self.total_items, description=final_message)
159
+
160
+
161
+ class MultiStageProgress:
162
+ """
163
+ Multi-stage progress manager for complex operations.
164
+
165
+ Manages multiple progress bars for operations with distinct phases,
166
+ providing clear visual feedback for each stage of processing.
167
+ """
168
+
169
+ def __init__(self, console: Console, stages: List[Dict[str, Any]]):
170
+ self.console = console
171
+ self.stages = stages
172
+ self.current_stage = 0
173
+ self.progress = None
174
+ self.active_tasks = {}
175
+
176
+ def __enter__(self) -> "MultiStageProgress":
177
+ self.progress = Progress(
178
+ TextColumn("[progress.description]{task.description}"),
179
+ BarColumn(complete_style="bright_green"),
180
+ TaskProgressColumn(),
181
+ TimeElapsedColumn(),
182
+ console=self.console,
183
+ transient=False,
184
+ )
185
+
186
+ self.progress.__enter__()
187
+
188
+ # Initialize all stage tasks
189
+ for i, stage in enumerate(self.stages):
190
+ task_id = self.progress.add_task(
191
+ stage["name"],
192
+ total=stage.get("total", 100),
193
+ visible=(i == 0), # Only show first stage initially
194
+ )
195
+ self.active_tasks[i] = task_id
196
+
197
+ return self
198
+
199
+ def __exit__(self, *args) -> None:
200
+ if self.progress:
201
+ self.progress.__exit__(*args)
202
+
203
+ def advance_stage(self, stage_index: int, progress_amount: int, description: Optional[str] = None) -> None:
204
+ """Advance progress for a specific stage."""
205
+ if stage_index in self.active_tasks and self.progress:
206
+ task_id = self.active_tasks[stage_index]
207
+
208
+ # Make current stage visible if not already
209
+ if not self.progress.tasks[task_id].visible:
210
+ self.progress.update(task_id, visible=True)
211
+
212
+ # Update progress
213
+ update_kwargs = {"advance": progress_amount}
214
+ if description:
215
+ update_kwargs["description"] = description
216
+
217
+ self.progress.update(task_id, **update_kwargs)
218
+
219
+ def complete_stage(self, stage_index: int) -> None:
220
+ """Mark a stage as completed."""
221
+ if stage_index in self.active_tasks and self.progress:
222
+ task_id = self.active_tasks[stage_index]
223
+ stage_total = self.progress.tasks[task_id].total or 100
224
+ self.progress.update(task_id, completed=stage_total)
225
+
226
+ def next_stage(self) -> bool:
227
+ """Move to the next stage and return True if successful."""
228
+ if self.current_stage < len(self.stages) - 1:
229
+ self.current_stage += 1
230
+
231
+ # Mark current stage as visible
232
+ if self.current_stage in self.active_tasks and self.progress:
233
+ task_id = self.active_tasks[self.current_stage]
234
+ self.progress.update(task_id, visible=True)
235
+
236
+ return True
237
+ return False
238
+
239
+
240
+ # Convenience functions for common progress patterns
241
+
242
+
243
+ def track_aws_cost_analysis(items: List[Any], console: Optional[Console] = None) -> Iterator[Any]:
244
+ """
245
+ Track AWS cost analysis with enhanced progress.
246
+
247
+ Args:
248
+ items: Items to process
249
+ console: Optional console instance
250
+
251
+ Yields:
252
+ Items with progress tracking
253
+ """
254
+ tracker = EnhancedProgressTracker(console)
255
+
256
+ with tracker.create_enhanced_progress("aws_cost_data", len(items)) as progress:
257
+ progress.start_operation("Analyzing AWS cost data...")
258
+
259
+ for i, item in enumerate(items):
260
+ progress.update_step(f"Processing item {i + 1}/{len(items)}", i + 1)
261
+ yield item
262
+
263
+ progress.complete_operation("Cost analysis completed")
264
+
265
+
266
+ def track_multi_account_analysis(accounts: List[str], console: Optional[Console] = None) -> Iterator[str]:
267
+ """
268
+ Track multi-account analysis with enhanced progress.
269
+
270
+ Args:
271
+ accounts: Account profiles to process
272
+ console: Optional console instance
273
+
274
+ Yields:
275
+ Account profiles with progress tracking
276
+ """
277
+ tracker = EnhancedProgressTracker(console)
278
+
279
+ with tracker.create_enhanced_progress("multi_account_analysis", len(accounts)) as progress:
280
+ progress.start_operation("Analyzing multiple accounts...")
281
+
282
+ for i, account in enumerate(accounts):
283
+ progress.update_step(f"Analyzing account: {account}", i + 1)
284
+ yield account
285
+
286
+ progress.complete_operation("Multi-account analysis completed")
287
+
288
+
289
+ @contextmanager
290
+ def enhanced_finops_progress(
291
+ operation_name: str, total_steps: int = 100, console: Optional[Console] = None
292
+ ) -> Iterator[Callable[[int, str], None]]:
293
+ """
294
+ Context manager for enhanced FinOps operations progress.
295
+
296
+ Args:
297
+ operation_name: Name of the operation
298
+ total_steps: Total number of progress steps
299
+ console: Optional console instance
300
+
301
+ Yields:
302
+ Progress update function: (step, description) -> None
303
+ """
304
+ console = console or rich_console
305
+
306
+ progress = Progress(
307
+ SpinnerColumn(),
308
+ TextColumn("[progress.description]{task.description}"),
309
+ BarColumn(complete_style="bright_green", finished_style="bright_green"),
310
+ TaskProgressColumn(),
311
+ TimeElapsedColumn(),
312
+ console=console,
313
+ transient=False,
314
+ )
315
+
316
+ with progress:
317
+ task_id = progress.add_task(operation_name, total=total_steps)
318
+
319
+ def update_progress(step: int, description: str) -> None:
320
+ progress.update(task_id, completed=step, description=description)
321
+
322
+ yield update_progress
323
+
324
+
325
+ def create_progress_tracker(console: Optional[Console] = None) -> EnhancedProgressTracker:
326
+ """Factory function to create enhanced progress tracker."""
327
+ return EnhancedProgressTracker(console=console)