runbooks 0.7.6__py3-none-any.whl → 0.7.9__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 (111) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/base.py +5 -1
  3. runbooks/cfat/__init__.py +8 -4
  4. runbooks/cfat/assessment/collectors.py +171 -14
  5. runbooks/cfat/assessment/compliance.py +871 -0
  6. runbooks/cfat/assessment/runner.py +122 -11
  7. runbooks/cfat/models.py +6 -2
  8. runbooks/common/logger.py +14 -0
  9. runbooks/common/rich_utils.py +451 -0
  10. runbooks/enterprise/__init__.py +68 -0
  11. runbooks/enterprise/error_handling.py +411 -0
  12. runbooks/enterprise/logging.py +439 -0
  13. runbooks/enterprise/multi_tenant.py +583 -0
  14. runbooks/finops/README.md +468 -241
  15. runbooks/finops/__init__.py +39 -3
  16. runbooks/finops/cli.py +83 -18
  17. runbooks/finops/cross_validation.py +375 -0
  18. runbooks/finops/dashboard_runner.py +812 -164
  19. runbooks/finops/enhanced_dashboard_runner.py +525 -0
  20. runbooks/finops/finops_dashboard.py +1892 -0
  21. runbooks/finops/helpers.py +485 -51
  22. runbooks/finops/optimizer.py +823 -0
  23. runbooks/finops/tests/__init__.py +19 -0
  24. runbooks/finops/tests/results_test_finops_dashboard.xml +1 -0
  25. runbooks/finops/tests/run_comprehensive_tests.py +421 -0
  26. runbooks/finops/tests/run_tests.py +305 -0
  27. runbooks/finops/tests/test_finops_dashboard.py +705 -0
  28. runbooks/finops/tests/test_integration.py +477 -0
  29. runbooks/finops/tests/test_performance.py +380 -0
  30. runbooks/finops/tests/test_performance_benchmarks.py +500 -0
  31. runbooks/finops/tests/test_reference_images_validation.py +867 -0
  32. runbooks/finops/tests/test_single_account_features.py +715 -0
  33. runbooks/finops/tests/validate_test_suite.py +220 -0
  34. runbooks/finops/types.py +1 -1
  35. runbooks/hitl/enhanced_workflow_engine.py +725 -0
  36. runbooks/inventory/artifacts/scale-optimize-status.txt +12 -0
  37. runbooks/inventory/collectors/aws_comprehensive.py +442 -0
  38. runbooks/inventory/collectors/enterprise_scale.py +281 -0
  39. runbooks/inventory/core/collector.py +172 -13
  40. runbooks/inventory/discovery.md +1 -1
  41. runbooks/inventory/list_ec2_instances.py +18 -20
  42. runbooks/inventory/list_ssm_parameters.py +31 -3
  43. runbooks/inventory/organizations_discovery.py +1269 -0
  44. runbooks/inventory/rich_inventory_display.py +393 -0
  45. runbooks/inventory/run_on_multi_accounts.py +35 -19
  46. runbooks/inventory/runbooks.security.report_generator.log +0 -0
  47. runbooks/inventory/runbooks.security.run_script.log +0 -0
  48. runbooks/inventory/vpc_flow_analyzer.py +1030 -0
  49. runbooks/main.py +2215 -119
  50. runbooks/metrics/dora_metrics_engine.py +599 -0
  51. runbooks/operate/__init__.py +2 -2
  52. runbooks/operate/base.py +122 -10
  53. runbooks/operate/deployment_framework.py +1032 -0
  54. runbooks/operate/deployment_validator.py +853 -0
  55. runbooks/operate/dynamodb_operations.py +10 -6
  56. runbooks/operate/ec2_operations.py +319 -11
  57. runbooks/operate/executive_dashboard.py +779 -0
  58. runbooks/operate/mcp_integration.py +750 -0
  59. runbooks/operate/nat_gateway_operations.py +1120 -0
  60. runbooks/operate/networking_cost_heatmap.py +685 -0
  61. runbooks/operate/privatelink_operations.py +940 -0
  62. runbooks/operate/s3_operations.py +10 -6
  63. runbooks/operate/vpc_endpoints.py +644 -0
  64. runbooks/operate/vpc_operations.py +1038 -0
  65. runbooks/remediation/__init__.py +2 -2
  66. runbooks/remediation/acm_remediation.py +1 -1
  67. runbooks/remediation/base.py +1 -1
  68. runbooks/remediation/cloudtrail_remediation.py +1 -1
  69. runbooks/remediation/cognito_remediation.py +1 -1
  70. runbooks/remediation/dynamodb_remediation.py +1 -1
  71. runbooks/remediation/ec2_remediation.py +1 -1
  72. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -1
  73. runbooks/remediation/kms_enable_key_rotation.py +1 -1
  74. runbooks/remediation/kms_remediation.py +1 -1
  75. runbooks/remediation/lambda_remediation.py +1 -1
  76. runbooks/remediation/multi_account.py +1 -1
  77. runbooks/remediation/rds_remediation.py +1 -1
  78. runbooks/remediation/s3_block_public_access.py +1 -1
  79. runbooks/remediation/s3_enable_access_logging.py +1 -1
  80. runbooks/remediation/s3_encryption.py +1 -1
  81. runbooks/remediation/s3_remediation.py +1 -1
  82. runbooks/remediation/vpc_remediation.py +475 -0
  83. runbooks/security/__init__.py +3 -1
  84. runbooks/security/compliance_automation.py +632 -0
  85. runbooks/security/report_generator.py +10 -0
  86. runbooks/security/run_script.py +31 -5
  87. runbooks/security/security_baseline_tester.py +169 -30
  88. runbooks/security/security_export.py +477 -0
  89. runbooks/validation/__init__.py +10 -0
  90. runbooks/validation/benchmark.py +484 -0
  91. runbooks/validation/cli.py +356 -0
  92. runbooks/validation/mcp_validator.py +768 -0
  93. runbooks/vpc/__init__.py +38 -0
  94. runbooks/vpc/config.py +212 -0
  95. runbooks/vpc/cost_engine.py +347 -0
  96. runbooks/vpc/heatmap_engine.py +605 -0
  97. runbooks/vpc/manager_interface.py +634 -0
  98. runbooks/vpc/networking_wrapper.py +1260 -0
  99. runbooks/vpc/rich_formatters.py +679 -0
  100. runbooks/vpc/tests/__init__.py +5 -0
  101. runbooks/vpc/tests/conftest.py +356 -0
  102. runbooks/vpc/tests/test_cli_integration.py +530 -0
  103. runbooks/vpc/tests/test_config.py +458 -0
  104. runbooks/vpc/tests/test_cost_engine.py +479 -0
  105. runbooks/vpc/tests/test_networking_wrapper.py +512 -0
  106. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/METADATA +40 -12
  107. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/RECORD +111 -50
  108. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/WHEEL +0 -0
  109. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/entry_points.txt +0 -0
  110. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/licenses/LICENSE +0 -0
  111. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,679 @@
1
+ """
2
+ Rich Formatters - Beautiful console output formatting for VPC operations
3
+
4
+ This module provides consistent, beautiful formatting using the Rich library
5
+ for both CLI and Jupyter notebook environments.
6
+ """
7
+
8
+ from datetime import datetime
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ from rich import box
12
+ from rich.console import Console
13
+ from rich.layout import Layout
14
+ from rich.panel import Panel
15
+ from rich.progress import BarColumn, Progress, TextColumn
16
+ from rich.table import Table
17
+ from rich.text import Text
18
+ from rich.tree import Tree
19
+
20
+
21
+ def display_cost_table(console: Console, data: Dict[str, Any], title: str = "Cost Analysis"):
22
+ """
23
+ Display cost data in a beautiful table format
24
+
25
+ Args:
26
+ console: Rich console instance
27
+ data: Cost data to display
28
+ title: Table title
29
+ """
30
+ table = Table(title=title, show_header=True, header_style="bold magenta", box=box.ROUNDED)
31
+
32
+ # Determine columns based on data
33
+ if "nat_gateways" in data:
34
+ table.add_column("Resource", style="cyan")
35
+ table.add_column("ID", style="yellow")
36
+ table.add_column("State", style="green")
37
+ table.add_column("Monthly Cost", justify="right", style="red")
38
+ table.add_column("Optimization", style="magenta")
39
+
40
+ for ng in data["nat_gateways"]:
41
+ table.add_row(
42
+ "NAT Gateway",
43
+ ng["id"][-12:], # Show last 12 chars
44
+ ng.get("state", "active"),
45
+ f"${ng.get('monthly_cost', 0):.2f}",
46
+ ng.get("optimization", {}).get("recommendation", "Optimized"),
47
+ )
48
+
49
+ elif "vpc_endpoints" in data:
50
+ table.add_column("Endpoint ID", style="cyan")
51
+ table.add_column("Type", style="yellow")
52
+ table.add_column("Service", style="green")
53
+ table.add_column("Monthly Cost", justify="right", style="red")
54
+
55
+ for endpoint in data["vpc_endpoints"]:
56
+ service = endpoint["service"].split(".")[-1] if "." in endpoint["service"] else endpoint["service"]
57
+ table.add_row(endpoint["id"][-12:], endpoint["type"], service, f"${endpoint.get('monthly_cost', 0):.2f}")
58
+
59
+ else:
60
+ # Generic cost table
61
+ table.add_column("Item", style="cyan")
62
+ table.add_column("Value", justify="right", style="yellow")
63
+
64
+ for key, value in data.items():
65
+ if isinstance(value, (int, float)):
66
+ if "cost" in key.lower():
67
+ table.add_row(key.replace("_", " ").title(), f"${value:.2f}")
68
+ else:
69
+ table.add_row(key.replace("_", " ").title(), str(value))
70
+
71
+ console.print(table)
72
+
73
+
74
+ def display_heatmap(console: Console, heat_maps: Dict[str, Any]):
75
+ """
76
+ Display heat map data with visual representation
77
+
78
+ Args:
79
+ console: Rich console instance
80
+ heat_maps: Heat map data to display
81
+ """
82
+ # Display single account heat map
83
+ if "single_account_heat_map" in heat_maps:
84
+ single = heat_maps["single_account_heat_map"]
85
+
86
+ # Create a visual heat map using colored blocks
87
+ console.print(
88
+ Panel.fit(
89
+ f"[bold cyan]Single Account Heat Map[/bold cyan]\n"
90
+ f"Account: {single.get('account_id', 'N/A')}\n"
91
+ f"Total Monthly Cost: [bold red]${single.get('total_monthly_cost', 0):.2f}[/bold red]",
92
+ title="🔥 Cost Heat Map",
93
+ border_style="blue",
94
+ )
95
+ )
96
+
97
+ # Display regional distribution
98
+ if "cost_distribution" in single:
99
+ regional_table = Table(title="Regional Cost Distribution", box=box.SIMPLE)
100
+ regional_table.add_column("Region", style="cyan")
101
+ regional_table.add_column("Cost", justify="right", style="yellow")
102
+ regional_table.add_column("Heat Level", style="red")
103
+
104
+ regional_totals = single["cost_distribution"].get("regional_totals", [])
105
+ regions = single.get("regions", [])
106
+
107
+ for idx, (region, cost) in enumerate(zip(regions, regional_totals)):
108
+ heat_level = _get_heat_level(cost)
109
+ regional_table.add_row(region, f"${cost:.2f}", heat_level)
110
+
111
+ console.print(regional_table)
112
+
113
+ # Display multi-account aggregation
114
+ if "multi_account_aggregated" in heat_maps:
115
+ multi = heat_maps["multi_account_aggregated"]
116
+
117
+ console.print(
118
+ Panel.fit(
119
+ f"[bold cyan]Multi-Account Heat Map[/bold cyan]\n"
120
+ f"Total Accounts: {multi.get('total_accounts', 0)}\n"
121
+ f"Total Monthly Cost: [bold red]${multi.get('total_monthly_cost', 0):.2f}[/bold red]\n"
122
+ f"Average per Account: [bold yellow]${multi.get('average_account_cost', 0):.2f}[/bold yellow]",
123
+ title="🔥 Aggregated Heat Map",
124
+ border_style="blue",
125
+ )
126
+ )
127
+
128
+ # Display hotspots
129
+ if "cost_hotspots" in multi and multi["cost_hotspots"]:
130
+ hotspot_table = Table(title="Cost Hotspots", box=box.SIMPLE)
131
+ hotspot_table.add_column("Region", style="cyan")
132
+ hotspot_table.add_column("Service", style="yellow")
133
+ hotspot_table.add_column("Cost", justify="right", style="red")
134
+ hotspot_table.add_column("Severity", style="magenta")
135
+
136
+ for hotspot in multi["cost_hotspots"][:10]: # Top 10
137
+ hotspot_table.add_row(
138
+ hotspot["region"], hotspot["service"], f"${hotspot['monthly_cost']:.2f}", hotspot["severity"]
139
+ )
140
+
141
+ console.print(hotspot_table)
142
+
143
+ # Display time series trends
144
+ if "time_series_heat_maps" in heat_maps:
145
+ time_series = heat_maps["time_series_heat_maps"]
146
+ if "trend_analysis" in time_series:
147
+ trend = time_series["trend_analysis"]
148
+ console.print(
149
+ Panel(
150
+ f"Growth Rate: [bold yellow]{trend.get('growth_rate', 0)}%[/bold yellow]\n"
151
+ f"Patterns: {trend.get('seasonal_patterns', 'None')}\n"
152
+ f"Opportunities: {trend.get('optimization_opportunities', 'None')}",
153
+ title="📈 Trend Analysis",
154
+ border_style="green",
155
+ )
156
+ )
157
+
158
+
159
+ def display_optimization_recommendations(console: Console, recommendations: Dict[str, Any]):
160
+ """
161
+ Display optimization recommendations with visual impact
162
+
163
+ Args:
164
+ console: Rich console instance
165
+ recommendations: Optimization recommendations data
166
+ """
167
+ # Display summary panel
168
+ current = recommendations.get("current_monthly_cost", 0)
169
+ projected = recommendations.get("projected_monthly_cost", 0)
170
+ savings = recommendations.get("potential_savings", 0)
171
+ target = recommendations.get("target_reduction", 0)
172
+
173
+ summary_text = f"""
174
+ [bold cyan]Cost Optimization Summary[/bold cyan]
175
+
176
+ Current Monthly Cost: [bold red]${current:.2f}[/bold red]
177
+ Potential Savings: [bold green]${savings:.2f}[/bold green]
178
+ Projected Cost: [bold yellow]${projected:.2f}[/bold yellow]
179
+ Target Reduction: [bold magenta]{target}%[/bold magenta]
180
+
181
+ Savings Percentage: [bold green]{(savings / current * 100 if current > 0 else 0):.1f}%[/bold green]
182
+ Annual Savings: [bold green]${savings * 12:.2f}[/bold green]
183
+ """
184
+
185
+ console.print(Panel(summary_text.strip(), title="💰 Optimization Summary", border_style="green"))
186
+
187
+ # Display recommendations table
188
+ if "recommendations" in recommendations and recommendations["recommendations"]:
189
+ rec_table = Table(title="Optimization Recommendations", show_header=True, box=box.ROUNDED)
190
+ rec_table.add_column("Priority", style="cyan")
191
+ rec_table.add_column("Resource", style="yellow")
192
+ rec_table.add_column("Action", style="green")
193
+ rec_table.add_column("Savings", justify="right", style="red")
194
+ rec_table.add_column("Risk", style="magenta")
195
+ rec_table.add_column("Effort", style="blue")
196
+
197
+ for idx, rec in enumerate(recommendations["recommendations"][:10], 1):
198
+ rec_table.add_row(
199
+ str(idx),
200
+ rec.get("resource_id", "N/A")[-12:],
201
+ rec.get("action", "N/A"),
202
+ f"${rec.get('potential_savings', 0):.2f}",
203
+ rec.get("risk_level", "medium"),
204
+ rec.get("implementation_effort", "medium"),
205
+ )
206
+
207
+ console.print(rec_table)
208
+
209
+ # Display implementation plan
210
+ if "implementation_plan" in recommendations and recommendations["implementation_plan"]:
211
+ plan_tree = Tree("📋 Implementation Plan")
212
+
213
+ current_phase = None
214
+ phase_branch = None
215
+
216
+ for item in recommendations["implementation_plan"][:15]: # First 15 items
217
+ phase = item.get("phase", 1)
218
+
219
+ if phase != current_phase:
220
+ phase_branch = plan_tree.add(f"[bold cyan]Phase {phase}[/bold cyan]")
221
+ current_phase = phase
222
+
223
+ if phase_branch:
224
+ phase_branch.add(
225
+ f"{item.get('action', 'N/A')} - "
226
+ f"[green]${item.get('savings', 0):.2f}[/green] - "
227
+ f"[yellow]{item.get('risk', 'medium')} risk[/yellow]"
228
+ )
229
+
230
+ console.print(plan_tree)
231
+
232
+
233
+ def display_progress(console: Console, title: str, total: int, current: int):
234
+ """
235
+ Display a progress bar for long-running operations
236
+
237
+ Args:
238
+ console: Rich console instance
239
+ title: Progress bar title
240
+ total: Total items
241
+ current: Current item count
242
+ """
243
+ with Progress(
244
+ TextColumn("[progress.description]{task.description}"),
245
+ BarColumn(),
246
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
247
+ console=console,
248
+ ) as progress:
249
+ task = progress.add_task(title, total=total)
250
+ progress.update(task, advance=current)
251
+
252
+
253
+ def display_multi_account_progress(console: Console, accounts: List[str]) -> Progress:
254
+ """
255
+ Create multi-task progress bar for concurrent account analysis
256
+
257
+ Args:
258
+ console: Rich console instance
259
+ accounts: List of AWS account IDs
260
+
261
+ Returns:
262
+ Progress instance with tasks for each account
263
+ """
264
+ progress = Progress(
265
+ TextColumn("[progress.description]{task.description}"),
266
+ BarColumn(bar_width=40),
267
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
268
+ TextColumn("•"),
269
+ TextColumn("[blue]{task.completed}/{task.total} accounts"),
270
+ console=console,
271
+ )
272
+
273
+ # Add tasks for different analysis phases
274
+ progress.add_task("🔍 Discovery", total=len(accounts))
275
+ progress.add_task("💰 Cost Analysis", total=len(accounts))
276
+ progress.add_task("🔥 Heat Maps", total=len(accounts))
277
+
278
+ return progress
279
+
280
+
281
+ def display_transit_gateway_architecture(console: Console, tgw_data: Dict[str, Any]):
282
+ """
283
+ Display Transit Gateway architecture as Rich Tree for Issue #97
284
+
285
+ Args:
286
+ console: Rich console instance
287
+ tgw_data: Transit Gateway architecture data
288
+ """
289
+ tgw_tree = Tree("🌐 [bold cyan]AWS Transit Gateway Architecture[/bold cyan]")
290
+
291
+ # Central Egress VPC
292
+ central_vpc_id = tgw_data.get("central_vpc_id", "vpc-central")
293
+ central_vpc = tgw_tree.add(f"🏢 [yellow]Central Egress VPC[/yellow] ({central_vpc_id})")
294
+
295
+ # Add Transit Gateway details
296
+ tgw_id = tgw_data.get("transit_gateway_id", "tgw-central")
297
+ tgw_branch = central_vpc.add(f"🔗 [magenta]Transit Gateway[/magenta] ({tgw_id})")
298
+
299
+ # Organizational Units
300
+ for ou in tgw_data.get("organizational_units", []):
301
+ ou_branch = tgw_tree.add(f"🏗️ [green]OU: {ou.get('name', 'Unknown')}[/green]")
302
+
303
+ # Accounts within OU
304
+ for account in ou.get("accounts", []):
305
+ account_id = account.get("id", "unknown")
306
+ account_branch = ou_branch.add(f"📊 [cyan]Account: {account_id}[/cyan]")
307
+
308
+ # VPCs in account
309
+ for vpc in account.get("vpcs", []):
310
+ vpc_id = vpc.get("id", "vpc-unknown")
311
+ monthly_cost = vpc.get("monthly_cost", 0)
312
+ vpc_branch = account_branch.add(f"🌐 VPC: {vpc_id} - [red]${monthly_cost:.2f}[/red]/month")
313
+
314
+ # VPC Endpoints
315
+ for endpoint in vpc.get("endpoints", []):
316
+ endpoint_service = endpoint.get("service", "Unknown")
317
+ endpoint_type = endpoint.get("type", "Interface")
318
+ vpc_branch.add(f"🔗 {endpoint_service} - [yellow]{endpoint_type}[/yellow]")
319
+
320
+ # NAT Gateways
321
+ for nat in vpc.get("nat_gateways", []):
322
+ nat_id = nat.get("id", "nat-unknown")
323
+ nat_cost = nat.get("monthly_cost", 0)
324
+ vpc_branch.add(f"🚪 NAT Gateway: {nat_id} - [red]${nat_cost:.2f}[/red]/month")
325
+
326
+ console.print(tgw_tree)
327
+
328
+
329
+ def display_error(console: Console, error_message: str, suggestion: Optional[str] = None):
330
+ """
331
+ Display an error message with optional suggestion
332
+
333
+ Args:
334
+ console: Rich console instance
335
+ error_message: Error message to display
336
+ suggestion: Optional suggestion for resolution
337
+ """
338
+ error_panel = f"[bold red]❌ Error[/bold red]\n\n{error_message}"
339
+
340
+ if suggestion:
341
+ error_panel += f"\n\n[yellow]💡 Suggestion:[/yellow] {suggestion}"
342
+
343
+ console.print(Panel(error_panel, border_style="red", box=box.DOUBLE))
344
+
345
+
346
+ def display_success(console: Console, message: str, details: Optional[Dict] = None):
347
+ """
348
+ Display a success message with optional details
349
+
350
+ Args:
351
+ console: Rich console instance
352
+ message: Success message
353
+ details: Optional details dictionary
354
+ """
355
+ success_text = f"[bold green]✅ Success[/bold green]\n\n{message}"
356
+
357
+ if details:
358
+ success_text += "\n\n[cyan]Details:[/cyan]\n"
359
+ for key, value in details.items():
360
+ success_text += f" • {key}: {value}\n"
361
+
362
+ console.print(Panel(success_text, border_style="green"))
363
+
364
+
365
+ def format_currency(value: float) -> str:
366
+ """Format a value as currency"""
367
+ return f"${value:,.2f}"
368
+
369
+
370
+ def format_percentage(value: float) -> str:
371
+ """Format a value as percentage"""
372
+ return f"{value:.1f}%"
373
+
374
+
375
+ def display_optimized_cost_table(console: Console, data: Dict[str, Any], sort_by: str = "monthly_cost"):
376
+ """
377
+ Display cost table with advanced optimization features
378
+
379
+ Args:
380
+ console: Rich console instance
381
+ data: Cost data with optimization recommendations
382
+ sort_by: Column to sort by (monthly_cost, potential_savings, risk_level)
383
+ """
384
+ table = Table(
385
+ title="💰 [bold cyan]Cost Optimization Analysis[/bold cyan]",
386
+ show_header=True,
387
+ header_style="bold magenta",
388
+ box=box.ROUNDED,
389
+ )
390
+
391
+ # Enhanced columns with sorting indicators
392
+ table.add_column("Resource", style="cyan")
393
+ table.add_column("ID", style="yellow")
394
+ table.add_column("Monthly Cost ⬇️", justify="right", style="red")
395
+ table.add_column("Potential Savings", justify="right", style="green")
396
+ table.add_column("Optimization Priority", style="magenta")
397
+ table.add_column("Risk Level", style="blue")
398
+
399
+ # Sort data by specified column
400
+ if "nat_gateways" in data:
401
+ sorted_items = sorted(data["nat_gateways"], key=lambda x: x.get(sort_by, 0), reverse=True)
402
+
403
+ for ng in sorted_items:
404
+ # Color-coded priority indicators
405
+ priority = _get_optimization_priority(ng)
406
+ risk_indicator = _get_risk_indicator(ng.get("optimization", {}).get("risk_level", "medium"))
407
+
408
+ table.add_row(
409
+ "NAT Gateway",
410
+ ng["id"][-12:],
411
+ f"${ng.get('monthly_cost', 0):.2f}",
412
+ f"${ng.get('optimization', {}).get('potential_savings', 0):.2f}",
413
+ priority,
414
+ risk_indicator,
415
+ )
416
+
417
+ elif "vpc_endpoints" in data:
418
+ sorted_items = sorted(data["vpc_endpoints"], key=lambda x: x.get(sort_by, 0), reverse=True)
419
+
420
+ for endpoint in sorted_items:
421
+ priority = _get_optimization_priority(endpoint)
422
+ risk_indicator = _get_risk_indicator(endpoint.get("optimization", {}).get("risk_level", "low"))
423
+
424
+ # Shorten service name for display
425
+ service = (
426
+ endpoint["service"].split(".")[-1]
427
+ if "." in endpoint.get("service", "")
428
+ else endpoint.get("service", "Unknown")
429
+ )
430
+
431
+ table.add_row(
432
+ f"VPC Endpoint ({endpoint.get('type', 'Interface')})",
433
+ endpoint["id"][-12:],
434
+ f"${endpoint.get('monthly_cost', 0):.2f}",
435
+ f"${endpoint.get('optimization', {}).get('potential_savings', 0):.2f}",
436
+ priority,
437
+ risk_indicator,
438
+ )
439
+
440
+ console.print(table)
441
+
442
+
443
+ def _get_optimization_priority(resource: Dict) -> str:
444
+ """Get color-coded optimization priority"""
445
+ savings = resource.get("optimization", {}).get("potential_savings", 0)
446
+ if savings > 40:
447
+ return "[bold red]🔥 CRITICAL[/bold red]"
448
+ elif savings > 20:
449
+ return "[red]⚠️ HIGH[/red]"
450
+ elif savings > 10:
451
+ return "[yellow]📈 MEDIUM[/yellow]"
452
+ else:
453
+ return "[green]✅ LOW[/green]"
454
+
455
+
456
+ def _get_risk_indicator(risk_level: str) -> str:
457
+ """Get color-coded risk indicator"""
458
+ risk_colors = {
459
+ "high": "[bold red]🔴 HIGH[/bold red]",
460
+ "medium": "[yellow]🟡 MEDIUM[/yellow]",
461
+ "low": "[green]🟢 LOW[/green]",
462
+ }
463
+ return risk_colors.get(risk_level.lower(), "[yellow]🟡 MEDIUM[/yellow]")
464
+
465
+
466
+ def _get_heat_level(cost: float) -> str:
467
+ """
468
+ Get heat level indicator based on cost
469
+
470
+ Args:
471
+ cost: Cost value
472
+
473
+ Returns:
474
+ Heat level string with color
475
+ """
476
+ if cost > 500:
477
+ return "[bold red]🔥🔥🔥 CRITICAL[/bold red]"
478
+ elif cost > 100:
479
+ return "[red]🔥🔥 HIGH[/red]"
480
+ elif cost > 50:
481
+ return "[yellow]🔥 MEDIUM[/yellow]"
482
+ elif cost > 10:
483
+ return "[green]• LOW[/green]"
484
+ else:
485
+ return "[dim]○ MINIMAL[/dim]"
486
+
487
+
488
+ def create_summary_layout(data: Dict[str, Any]) -> Layout:
489
+ """
490
+ Create a summary layout for comprehensive display
491
+
492
+ Args:
493
+ data: Summary data to display
494
+
495
+ Returns:
496
+ Rich Layout object
497
+ """
498
+ layout = Layout()
499
+
500
+ # Create main sections
501
+ layout.split_column(Layout(name="header", size=3), Layout(name="body"), Layout(name="footer", size=3))
502
+
503
+ # Header
504
+ layout["header"].update(Panel("[bold cyan]VPC Networking Cost Analysis[/bold cyan]", style="blue"))
505
+
506
+ # Body split into columns
507
+ layout["body"].split_row(Layout(name="costs"), Layout(name="recommendations"))
508
+
509
+ # Footer
510
+ layout["footer"].update(Panel(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", style="dim"))
511
+
512
+ return layout
513
+
514
+
515
+ def display_transit_gateway_analysis(results: Dict[str, Any], console: Console) -> None:
516
+ """
517
+ Display comprehensive Transit Gateway analysis results with Rich formatting.
518
+
519
+ Enhanced for Issue #97: Strategic executive reporting with $325+/month savings focus
520
+ and business impact visualization for 60-account enterprise environment.
521
+ """
522
+
523
+ # Executive header with business impact
524
+ console.print("\n🎯 Enterprise Transit Gateway Strategic Analysis", style="bold bright_blue")
525
+ console.print("Issue #97: Multi-Account Cost Optimization Campaign", style="dim italic")
526
+ console.print("=" * 85, style="dim")
527
+
528
+ # Executive Business Impact Panel (TOP PRIORITY)
529
+ cost_analysis = results.get("cost_analysis", {})
530
+ business_impact = cost_analysis.get("business_impact", {})
531
+
532
+ if business_impact:
533
+ executive_panel = Panel(
534
+ f"💰 Monthly Savings: [bold green]${business_impact.get('monthly_savings', 0):.0f}[/bold green]\n"
535
+ f"📊 Annual Impact: [bold cyan]${business_impact.get('annual_savings', 0):,.0f}[/bold cyan]\n"
536
+ f"🎯 Target Achievement: [bold yellow]{business_impact.get('target_achievement', 'N/A')}[/bold yellow]\n"
537
+ f"⭐ ROI Grade: [bold magenta]{business_impact.get('roi_grade', 'UNKNOWN')}[/bold magenta]\n"
538
+ f"📋 {business_impact.get('executive_summary', 'Analysis pending')}",
539
+ title="🎯 Executive Business Impact",
540
+ border_style="green" if business_impact.get('roi_grade') == 'EXCEEDS TARGET' else "yellow"
541
+ )
542
+ console.print(executive_panel)
543
+
544
+ # Enhanced summary metrics table with optimization focus
545
+ summary_table = Table(title="📊 Strategic Analysis Dashboard", show_header=True, header_style="bold magenta")
546
+ summary_table.add_column("Metric", style="cyan", no_wrap=True)
547
+ summary_table.add_column("Current State", style="green")
548
+ summary_table.add_column("Optimization Target", style="yellow")
549
+ summary_table.add_column("Business Value", style="bold green")
550
+
551
+ tgw_count = len(results.get("transit_gateways", []))
552
+ attachment_count = len(results.get("attachments", []))
553
+ route_table_count = len(results.get("route_tables", []))
554
+
555
+ total_cost = cost_analysis.get("total_monthly_cost", 0)
556
+ savings_potential = cost_analysis.get("savings_potential", 0)
557
+
558
+ summary_table.add_row("Transit Gateways", str(tgw_count), "Optimized topology", f"Architecture review")
559
+ summary_table.add_row("Attachments", str(attachment_count), f"{attachment_count * 0.85:.0f} (15% reduction)", "Remove underutilized")
560
+ summary_table.add_row("Route Tables", str(route_table_count), f"{route_table_count * 0.75:.0f} (25% consolidation)", "Streamlined routing")
561
+ summary_table.add_row("Monthly Cost", f"${total_cost:.2f}", f"${total_cost - savings_potential:.2f}", f"-${savings_potential:.0f}/month")
562
+ summary_table.add_row("Annual Savings", "Current baseline", f"${savings_potential * 12:,.0f}/year", "Target: $325+/month")
563
+
564
+ console.print(summary_table)
565
+
566
+ # Transit Gateway details
567
+ if results.get("transit_gateways"):
568
+ tgw_table = Table(title="🌉 Transit Gateway Details", show_header=True, header_style="bold blue")
569
+ tgw_table.add_column("ID", style="cyan")
570
+ tgw_table.add_column("State", style="green")
571
+ tgw_table.add_column("Owner", style="yellow")
572
+ tgw_table.add_column("ASN", style="magenta")
573
+ tgw_table.add_column("Description", style="white")
574
+
575
+ for tgw in results["transit_gateways"]:
576
+ tgw_table.add_row(
577
+ tgw.get("TransitGatewayId", "")[:20],
578
+ tgw.get("State", ""),
579
+ tgw.get("OwnerId", "")[:12],
580
+ str(tgw.get("AmazonSideAsn", "")),
581
+ tgw.get("Description", "No description")[:30],
582
+ )
583
+
584
+ console.print(tgw_table)
585
+
586
+ # Central Egress VPC information
587
+ if results.get("central_egress_vpc"):
588
+ egress_vpc = results["central_egress_vpc"]
589
+ egress_panel = Panel(
590
+ f"[bold green]VPC ID:[/bold green] {egress_vpc.get('VpcId', 'N/A')}\n"
591
+ f"[bold green]Name:[/bold green] {egress_vpc.get('VpcName', 'N/A')}\n"
592
+ f"[bold green]CIDR:[/bold green] {egress_vpc.get('CidrBlock', 'N/A')}\n"
593
+ f"[bold green]Transit Gateway:[/bold green] {egress_vpc.get('TransitGatewayId', 'N/A')}",
594
+ title="🏗️ Central Egress VPC",
595
+ border_style="green",
596
+ )
597
+ console.print(egress_panel)
598
+
599
+ # Cost breakdown
600
+ cost_analysis = results.get("cost_analysis", {})
601
+ if cost_analysis.get("cost_breakdown"):
602
+ cost_table = Table(title="💰 Cost Breakdown", show_header=True, header_style="bold green")
603
+ cost_table.add_column("Component", style="cyan")
604
+ cost_table.add_column("Monthly Cost", style="green", justify="right")
605
+ cost_table.add_column("Percentage", style="yellow", justify="right")
606
+
607
+ total_cost = cost_analysis.get("total_monthly_cost", 0)
608
+ for component in cost_analysis["cost_breakdown"]:
609
+ cost = component.get("monthly_cost", 0)
610
+ percentage = (cost / total_cost * 100) if total_cost > 0 else 0
611
+ cost_table.add_row(component.get("component", ""), f"${cost:.2f}", f"{percentage:.1f}%")
612
+
613
+ console.print(cost_table)
614
+
615
+ # Optimization recommendations
616
+ recommendations = results.get("optimization_recommendations", [])
617
+ if recommendations:
618
+ rec_table = Table(title="🎯 Optimization Recommendations", show_header=True, header_style="bold yellow")
619
+ rec_table.add_column("Priority", style="red")
620
+ rec_table.add_column("Title", style="cyan")
621
+ rec_table.add_column("Monthly Savings", style="green", justify="right")
622
+ rec_table.add_column("Effort", style="yellow")
623
+ rec_table.add_column("Description", style="white")
624
+
625
+ for rec in recommendations[:5]: # Top 5 recommendations
626
+ rec_table.add_row(
627
+ rec.get("priority", ""),
628
+ rec.get("title", ""),
629
+ f"${rec.get('monthly_savings', 0):.2f}",
630
+ rec.get("effort", ""),
631
+ rec.get("description", "")[:50] + "..."
632
+ if len(rec.get("description", "")) > 50
633
+ else rec.get("description", ""),
634
+ )
635
+
636
+ console.print(rec_table)
637
+
638
+ # Architecture gaps and drift detection
639
+ gaps = results.get("architecture_gaps", [])
640
+ if gaps:
641
+ gap_table = Table(title="⚠️ Architecture Gaps & Drift Detection", show_header=True, header_style="bold red")
642
+ gap_table.add_column("Category", style="cyan")
643
+ gap_table.add_column("Severity", style="red")
644
+ gap_table.add_column("Description", style="white")
645
+ gap_table.add_column("Details", style="yellow")
646
+
647
+ for gap in gaps:
648
+ severity_style = {
649
+ "Info": "blue",
650
+ "Warning": "yellow",
651
+ "Medium": "orange",
652
+ "High": "red",
653
+ "Critical": "bright_red",
654
+ }.get(gap.get("severity", ""), "white")
655
+
656
+ gap_table.add_row(
657
+ gap.get("category", ""),
658
+ f"[{severity_style}]{gap.get('severity', '')}[/{severity_style}]",
659
+ gap.get("description", ""),
660
+ gap.get("details", "")[:60],
661
+ )
662
+
663
+ console.print(gap_table)
664
+
665
+ # Footer with next steps
666
+ next_steps = Panel(
667
+ "[bold cyan]Next Steps:[/bold cyan]\n"
668
+ "1. Review optimization recommendations by priority\n"
669
+ "2. Address architecture gaps and drift detection issues\n"
670
+ "3. Implement centralized VPC endpoint sharing\n"
671
+ "4. Monitor cost savings and performance impact\n"
672
+ "5. Schedule regular analysis runs for continuous optimization",
673
+ title="🚀 Recommended Actions",
674
+ border_style="bright_blue",
675
+ )
676
+ console.print(next_steps)
677
+
678
+ # Analysis timestamp
679
+ console.print(f"\n[dim]Analysis completed: {results.get('analysis_timestamp', 'N/A')}[/dim]")
@@ -0,0 +1,5 @@
1
+ """
2
+ VPC Testing Module
3
+
4
+ Comprehensive test suite for VPC networking wrapper and components.
5
+ """