runbooks 0.9.8__py3-none-any.whl → 1.0.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.
Files changed (75) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/cfat/cloud_foundations_assessment.py +626 -0
  3. runbooks/cloudops/cost_optimizer.py +95 -33
  4. runbooks/common/aws_pricing.py +388 -0
  5. runbooks/common/aws_pricing_api.py +205 -0
  6. runbooks/common/aws_utils.py +2 -2
  7. runbooks/common/comprehensive_cost_explorer_integration.py +979 -0
  8. runbooks/common/cross_account_manager.py +606 -0
  9. runbooks/common/enhanced_exception_handler.py +4 -0
  10. runbooks/common/env_utils.py +96 -0
  11. runbooks/common/mcp_integration.py +49 -2
  12. runbooks/common/organizations_client.py +579 -0
  13. runbooks/common/profile_utils.py +96 -2
  14. runbooks/common/rich_utils.py +3 -0
  15. runbooks/finops/cost_optimizer.py +2 -1
  16. runbooks/finops/elastic_ip_optimizer.py +13 -9
  17. runbooks/finops/embedded_mcp_validator.py +31 -0
  18. runbooks/finops/enhanced_trend_visualization.py +3 -2
  19. runbooks/finops/markdown_exporter.py +441 -0
  20. runbooks/finops/nat_gateway_optimizer.py +57 -20
  21. runbooks/finops/optimizer.py +2 -0
  22. runbooks/finops/single_dashboard.py +2 -2
  23. runbooks/finops/vpc_cleanup_exporter.py +330 -0
  24. runbooks/finops/vpc_cleanup_optimizer.py +895 -40
  25. runbooks/inventory/__init__.py +10 -1
  26. runbooks/inventory/cloud_foundations_integration.py +409 -0
  27. runbooks/inventory/core/collector.py +1148 -88
  28. runbooks/inventory/discovery.md +389 -0
  29. runbooks/inventory/drift_detection_cli.py +327 -0
  30. runbooks/inventory/inventory_mcp_cli.py +171 -0
  31. runbooks/inventory/inventory_modules.py +4 -7
  32. runbooks/inventory/mcp_inventory_validator.py +2149 -0
  33. runbooks/inventory/mcp_vpc_validator.py +23 -6
  34. runbooks/inventory/organizations_discovery.py +91 -1
  35. runbooks/inventory/rich_inventory_display.py +129 -1
  36. runbooks/inventory/unified_validation_engine.py +1292 -0
  37. runbooks/inventory/verify_ec2_security_groups.py +3 -1
  38. runbooks/inventory/vpc_analyzer.py +825 -7
  39. runbooks/inventory/vpc_flow_analyzer.py +36 -42
  40. runbooks/main.py +969 -42
  41. runbooks/monitoring/performance_monitor.py +11 -7
  42. runbooks/operate/dynamodb_operations.py +6 -5
  43. runbooks/operate/ec2_operations.py +3 -2
  44. runbooks/operate/networking_cost_heatmap.py +4 -3
  45. runbooks/operate/s3_operations.py +13 -12
  46. runbooks/operate/vpc_operations.py +50 -2
  47. runbooks/remediation/base.py +1 -1
  48. runbooks/remediation/commvault_ec2_analysis.py +6 -1
  49. runbooks/remediation/ec2_unattached_ebs_volumes.py +6 -3
  50. runbooks/remediation/rds_snapshot_list.py +5 -3
  51. runbooks/validation/__init__.py +21 -1
  52. runbooks/validation/comprehensive_2way_validator.py +1996 -0
  53. runbooks/validation/mcp_validator.py +904 -94
  54. runbooks/validation/terraform_citations_validator.py +363 -0
  55. runbooks/validation/terraform_drift_detector.py +1098 -0
  56. runbooks/vpc/cleanup_wrapper.py +231 -10
  57. runbooks/vpc/config.py +310 -62
  58. runbooks/vpc/cross_account_session.py +308 -0
  59. runbooks/vpc/heatmap_engine.py +96 -29
  60. runbooks/vpc/manager_interface.py +9 -9
  61. runbooks/vpc/mcp_no_eni_validator.py +1551 -0
  62. runbooks/vpc/networking_wrapper.py +14 -8
  63. runbooks/vpc/runbooks.inventory.organizations_discovery.log +0 -0
  64. runbooks/vpc/runbooks.security.report_generator.log +0 -0
  65. runbooks/vpc/runbooks.security.run_script.log +0 -0
  66. runbooks/vpc/runbooks.security.security_export.log +0 -0
  67. runbooks/vpc/tests/test_cost_engine.py +1 -1
  68. runbooks/vpc/unified_scenarios.py +3269 -0
  69. runbooks/vpc/vpc_cleanup_integration.py +516 -82
  70. {runbooks-0.9.8.dist-info → runbooks-1.0.0.dist-info}/METADATA +94 -52
  71. {runbooks-0.9.8.dist-info → runbooks-1.0.0.dist-info}/RECORD +75 -51
  72. {runbooks-0.9.8.dist-info → runbooks-1.0.0.dist-info}/WHEEL +0 -0
  73. {runbooks-0.9.8.dist-info → runbooks-1.0.0.dist-info}/entry_points.txt +0 -0
  74. {runbooks-0.9.8.dist-info → runbooks-1.0.0.dist-info}/licenses/LICENSE +0 -0
  75. {runbooks-0.9.8.dist-info → runbooks-1.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ VPC Cleanup Exporter Module - Enterprise VPC Cleanup Result Export
4
+
5
+ This module provides export functionality for VPC cleanup analysis results,
6
+ leveraging the existing markdown_exporter infrastructure with VPC-specific formatting.
7
+
8
+ Author: CloudOps Runbooks Team
9
+ Version: 0.9.9
10
+ """
11
+
12
+ import csv
13
+ import json
14
+ import os
15
+ from datetime import datetime
16
+ from typing import Any, Dict, List
17
+
18
+ from .markdown_exporter import MarkdownExporter
19
+
20
+
21
+ def _format_tags_for_display(tags_dict: Dict[str, str]) -> str:
22
+ """Format tags for display with priority order, emphasizing ownership tags."""
23
+ if not tags_dict:
24
+ return "No tags"
25
+
26
+ # Enhanced priority keys with focus on ownership and approvals
27
+ priority_keys = ['Name', 'Owner', 'BusinessOwner', 'TechnicalOwner', 'Team', 'Contact',
28
+ 'Environment', 'Project', 'CostCenter', 'CreatedBy', 'ManagedBy']
29
+ relevant_tags = []
30
+
31
+ for key in priority_keys:
32
+ if key in tags_dict and tags_dict[key]:
33
+ relevant_tags.append(f"{key}:{tags_dict[key]}")
34
+
35
+ # Add CloudFormation/Terraform tags for IaC detection
36
+ iac_keys = ['aws:cloudformation:stack-name', 'terraform:module', 'cdktf:stack', 'pulumi:project']
37
+ for key in iac_keys:
38
+ if key in tags_dict and tags_dict[key] and len(relevant_tags) < 6:
39
+ relevant_tags.append(f"IaC:{tags_dict[key]}")
40
+
41
+ # Add other important tags
42
+ for key, value in tags_dict.items():
43
+ if key not in priority_keys + iac_keys and value and len(relevant_tags) < 5:
44
+ relevant_tags.append(f"{key}:{value}")
45
+
46
+ return "; ".join(relevant_tags) if relevant_tags else f"({len(tags_dict)} tags)"
47
+
48
+
49
+ def export_vpc_cleanup_results(vpc_result: Any, export_formats: List[str], output_dir: str = "./") -> Dict[str, str]:
50
+ """
51
+ Export VPC cleanup results in multiple formats.
52
+
53
+ Args:
54
+ vpc_result: VPC cleanup analysis result object
55
+ export_formats: List of formats to export (markdown, csv, json, pdf)
56
+ output_dir: Directory to save exported files
57
+
58
+ Returns:
59
+ Dict mapping format to exported filename
60
+ """
61
+ results = {}
62
+
63
+ # Extract VPC candidates from result - use correct attribute name
64
+ vpc_candidates = getattr(vpc_result, 'cleanup_candidates', [])
65
+ if not vpc_candidates:
66
+ # Fallback to other possible attribute names
67
+ vpc_candidates = getattr(vpc_result, 'vpc_candidates', [])
68
+
69
+ if 'markdown' in export_formats:
70
+ try:
71
+ exporter = MarkdownExporter()
72
+ markdown_filename = exporter.export_vpc_analysis_to_file(
73
+ vpc_candidates,
74
+ filename="vpc-cleanup-candidates.md",
75
+ output_dir=output_dir
76
+ )
77
+ results['markdown'] = markdown_filename
78
+ except Exception as e:
79
+ print(f"Warning: Markdown export failed: {e}")
80
+ results['markdown'] = None
81
+
82
+ # Real implementations for other formats
83
+ if 'csv' in export_formats:
84
+ try:
85
+ csv_filename = _export_vpc_candidates_csv(vpc_candidates, output_dir)
86
+ results['csv'] = csv_filename
87
+ except Exception as e:
88
+ print(f"Warning: CSV export failed: {e}")
89
+ results['csv'] = None
90
+
91
+ if 'json' in export_formats:
92
+ try:
93
+ json_filename = _export_vpc_candidates_json(vpc_candidates, output_dir)
94
+ results['json'] = json_filename
95
+ except Exception as e:
96
+ print(f"Warning: JSON export failed: {e}")
97
+ results['json'] = None
98
+
99
+ if 'pdf' in export_formats:
100
+ try:
101
+ pdf_filename = _export_vpc_candidates_pdf(vpc_candidates, output_dir)
102
+ results['pdf'] = pdf_filename
103
+ except Exception as e:
104
+ print(f"Warning: PDF export failed: {e}")
105
+ results['pdf'] = None
106
+
107
+ return results
108
+
109
+
110
+ def _export_vpc_candidates_csv(vpc_candidates: List[Any], output_dir: str) -> str:
111
+ """Export VPC candidates to CSV format with all 15 columns."""
112
+ filename = os.path.join(output_dir, "vpc-cleanup-candidates.csv")
113
+
114
+ # 15-column headers for comprehensive VPC analysis
115
+ headers = [
116
+ "Account_ID", "VPC_ID", "VPC_Name", "CIDR_Block", "Overlapping",
117
+ "Is_Default", "ENI_Count", "Tags", "Flow_Logs", "TGW/Peering",
118
+ "LBs_Present", "IaC", "Timeline", "Decision", "Owners/Approvals", "Notes"
119
+ ]
120
+
121
+ with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
122
+ writer = csv.writer(csvfile)
123
+ writer.writerow(headers)
124
+
125
+ for candidate in vpc_candidates:
126
+ # Extract data with enhanced tag and owner handling
127
+ tags_dict = getattr(candidate, 'tags', {}) or {}
128
+
129
+ # Use enhanced tag formatting function
130
+ tags_str = _format_tags_for_display(tags_dict)
131
+
132
+ load_balancers = getattr(candidate, 'load_balancers', []) or []
133
+ lbs_present = "Yes" if load_balancers else "No"
134
+
135
+ # Enhanced owner extraction from multiple sources
136
+ owners = getattr(candidate, 'owners_approvals', []) or []
137
+
138
+ # Extract owners from tags with enhanced logic
139
+ if not owners and tags_dict:
140
+ owner_keys = ['Owner', 'BusinessOwner', 'TechnicalOwner', 'Team', 'Contact', 'CreatedBy', 'ManagedBy']
141
+ for key in owner_keys:
142
+ if key in tags_dict and tags_dict[key]:
143
+ value = tags_dict[key]
144
+ if 'business' in key.lower() or 'manager' in value.lower():
145
+ owners.append(f"{value} (Business)")
146
+ elif 'technical' in key.lower() or 'engineer' in value.lower():
147
+ owners.append(f"{value} (Technical)")
148
+ elif 'team' in key.lower():
149
+ owners.append(f"{value} (Team)")
150
+ else:
151
+ owners.append(f"{value} ({key})")
152
+
153
+ # For default VPCs, add system indicator
154
+ is_default = getattr(candidate, 'is_default', False)
155
+ if is_default and not owners:
156
+ owners.append("System Default")
157
+
158
+ if owners:
159
+ owners_str = "; ".join(owners)
160
+ else:
161
+ # Enhanced fallback for CSV
162
+ if getattr(candidate, 'is_default', False):
163
+ owners_str = "System Default VPC"
164
+ elif getattr(candidate, 'iac_detected', False):
165
+ owners_str = "IaC Managed"
166
+ else:
167
+ owners_str = "No owner tags found"
168
+
169
+ row = [
170
+ getattr(candidate, 'account_id', 'Unknown'),
171
+ getattr(candidate, 'vpc_id', ''),
172
+ getattr(candidate, 'vpc_name', 'Unnamed'),
173
+ getattr(candidate, 'cidr_block', ''),
174
+ "No", # Overlapping analysis would need CIDR comparison
175
+ "Yes" if getattr(candidate, 'is_default', False) else "No",
176
+ getattr(candidate, 'dependency_analysis', {}).eni_count if hasattr(candidate, 'dependency_analysis') else 0,
177
+ tags_str,
178
+ "Yes" if getattr(candidate, 'flow_logs_enabled', False) else "No",
179
+ "No", # TGW/Peering analysis placeholder
180
+ lbs_present,
181
+ "Yes" if getattr(candidate, 'iac_detected', False) else "No",
182
+ "Unknown", # Timeline analysis placeholder
183
+ getattr(candidate, 'cleanup_recommendation', 'unknown'),
184
+ owners_str,
185
+ "Generated by CloudOps Runbooks VPC Module"
186
+ ]
187
+ writer.writerow(row)
188
+
189
+ return filename
190
+
191
+
192
+ def _export_vpc_candidates_json(vpc_candidates: List[Any], output_dir: str) -> str:
193
+ """Export VPC candidates to JSON format with full data structure."""
194
+ filename = os.path.join(output_dir, "vpc-cleanup-candidates.json")
195
+
196
+ # Convert candidates to serializable format
197
+ candidates_data = []
198
+ for candidate in vpc_candidates:
199
+ candidate_dict = {
200
+ "account_id": getattr(candidate, 'account_id', 'Unknown'),
201
+ "vpc_id": getattr(candidate, 'vpc_id', ''),
202
+ "vpc_name": getattr(candidate, 'vpc_name', 'Unnamed'),
203
+ "cidr_block": getattr(candidate, 'cidr_block', ''),
204
+ "region": getattr(candidate, 'region', 'unknown'),
205
+ "is_default": getattr(candidate, 'is_default', False),
206
+ "state": getattr(candidate, 'state', 'unknown'),
207
+ "tags": getattr(candidate, 'tags', {}) or {},
208
+ "tags_summary": _format_tags_for_display(getattr(candidate, 'tags', {}) or {}),
209
+ "flow_logs_enabled": getattr(candidate, 'flow_logs_enabled', False),
210
+ "load_balancers": getattr(candidate, 'load_balancers', []) or [],
211
+ "iac_detected": getattr(candidate, 'iac_detected', False),
212
+ "owners_approvals": getattr(candidate, 'owners_approvals', []) or [],
213
+ "cleanup_bucket": getattr(candidate, 'cleanup_bucket', 'unknown'),
214
+ "cleanup_recommendation": getattr(candidate, 'cleanup_recommendation', 'unknown'),
215
+ "risk_assessment": getattr(candidate, 'risk_assessment', 'unknown'),
216
+ "business_impact": getattr(candidate, 'business_impact', 'unknown')
217
+ }
218
+
219
+ # Add dependency analysis if available
220
+ if hasattr(candidate, 'dependency_analysis') and candidate.dependency_analysis:
221
+ dep_analysis = candidate.dependency_analysis
222
+ candidate_dict["dependency_analysis"] = {
223
+ "eni_count": getattr(dep_analysis, 'eni_count', 0),
224
+ "route_tables": getattr(dep_analysis, 'route_tables', []),
225
+ "security_groups": getattr(dep_analysis, 'security_groups', []),
226
+ "internet_gateways": getattr(dep_analysis, 'internet_gateways', []),
227
+ "nat_gateways": getattr(dep_analysis, 'nat_gateways', []),
228
+ "vpc_endpoints": getattr(dep_analysis, 'vpc_endpoints', []),
229
+ "peering_connections": getattr(dep_analysis, 'peering_connections', []),
230
+ "dependency_risk_level": getattr(dep_analysis, 'dependency_risk_level', 'unknown')
231
+ }
232
+
233
+ candidates_data.append(candidate_dict)
234
+
235
+ # Create export metadata
236
+ export_data = {
237
+ "metadata": {
238
+ "export_timestamp": datetime.now().isoformat(),
239
+ "total_candidates": len(candidates_data),
240
+ "generator": "CloudOps Runbooks VPC Module v0.9.9"
241
+ },
242
+ "vpc_candidates": candidates_data
243
+ }
244
+
245
+ with open(filename, 'w', encoding='utf-8') as jsonfile:
246
+ json.dump(export_data, jsonfile, indent=2, ensure_ascii=False)
247
+
248
+ return filename
249
+
250
+
251
+ def _export_vpc_candidates_pdf(vpc_candidates: List[Any], output_dir: str) -> str:
252
+ """Export VPC candidates to PDF format for executive presentation."""
253
+ filename = os.path.join(output_dir, "vpc-cleanup-candidates.pdf")
254
+
255
+ try:
256
+ # Try to use reportlab for PDF generation
257
+ from reportlab.lib import colors
258
+ from reportlab.lib.pagesizes import letter, A4
259
+ from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
260
+ from reportlab.lib.styles import getSampleStyleSheet
261
+
262
+ doc = SimpleDocTemplate(filename, pagesize=A4)
263
+ styles = getSampleStyleSheet()
264
+ story = []
265
+
266
+ # Title
267
+ title = Paragraph("VPC Cleanup Analysis Report", styles['Title'])
268
+ story.append(title)
269
+ story.append(Spacer(1, 20))
270
+
271
+ # Summary
272
+ summary_text = f"""
273
+ <b>Generated:</b> {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}<br/>
274
+ <b>Total VPC Candidates:</b> {len(vpc_candidates)}<br/>
275
+ <b>Analysis Source:</b> CloudOps Runbooks VPC Module v0.9.9
276
+ """
277
+ summary = Paragraph(summary_text, styles['Normal'])
278
+ story.append(summary)
279
+ story.append(Spacer(1, 20))
280
+
281
+ # Create table data
282
+ table_data = [
283
+ ["Account ID", "VPC ID", "VPC Name", "CIDR", "Default", "ENI Count", "Decision"]
284
+ ]
285
+
286
+ for candidate in vpc_candidates:
287
+ row = [
288
+ str(getattr(candidate, 'account_id', 'Unknown'))[:15], # Truncate for PDF width
289
+ str(getattr(candidate, 'vpc_id', ''))[:20],
290
+ str(getattr(candidate, 'vpc_name', 'Unnamed'))[:15],
291
+ str(getattr(candidate, 'cidr_block', ''))[:15],
292
+ "Yes" if getattr(candidate, 'is_default', False) else "No",
293
+ str(getattr(candidate, 'dependency_analysis', {}).eni_count if hasattr(candidate, 'dependency_analysis') else 0),
294
+ str(getattr(candidate, 'cleanup_recommendation', 'unknown'))[:10]
295
+ ]
296
+ table_data.append(row)
297
+
298
+ # Create table
299
+ table = Table(table_data)
300
+ table.setStyle(TableStyle([
301
+ ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
302
+ ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
303
+ ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
304
+ ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
305
+ ('FONTSIZE', (0, 0), (-1, 0), 10),
306
+ ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
307
+ ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
308
+ ('FONTSIZE', (0, 1), (-1, -1), 8),
309
+ ('GRID', (0, 0), (-1, -1), 1, colors.black)
310
+ ]))
311
+
312
+ story.append(table)
313
+ doc.build(story)
314
+
315
+ except ImportError:
316
+ # Fallback: create a simple text-based PDF placeholder
317
+ with open(filename, 'w', encoding='utf-8') as f:
318
+ f.write("VPC Cleanup Analysis Report (PDF)\n")
319
+ f.write("=" * 40 + "\n\n")
320
+ f.write(f"Generated: {datetime.now().isoformat()}\n")
321
+ f.write(f"Total VPC Candidates: {len(vpc_candidates)}\n\n")
322
+
323
+ for i, candidate in enumerate(vpc_candidates, 1):
324
+ f.write(f"{i}. VPC {getattr(candidate, 'vpc_id', 'Unknown')}\n")
325
+ f.write(f" Account: {getattr(candidate, 'account_id', 'Unknown')}\n")
326
+ f.write(f" CIDR: {getattr(candidate, 'cidr_block', 'Unknown')}\n")
327
+ f.write(f" ENI Count: {getattr(candidate, 'dependency_analysis', {}).eni_count if hasattr(candidate, 'dependency_analysis') else 0}\n")
328
+ f.write(f" Decision: {getattr(candidate, 'cleanup_recommendation', 'unknown')}\n\n")
329
+
330
+ return filename