runbooks 1.1.4__py3-none-any.whl → 1.1.5__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 +31 -2
- runbooks/__init___optimized.py +18 -4
- runbooks/_platform/__init__.py +1 -5
- runbooks/_platform/core/runbooks_wrapper.py +141 -138
- runbooks/aws2/accuracy_validator.py +812 -0
- runbooks/base.py +7 -0
- runbooks/cfat/assessment/compliance.py +1 -1
- runbooks/cfat/assessment/runner.py +1 -0
- runbooks/cfat/cloud_foundations_assessment.py +227 -239
- runbooks/cli/__init__.py +1 -1
- runbooks/cli/commands/cfat.py +64 -23
- runbooks/cli/commands/finops.py +1005 -54
- runbooks/cli/commands/inventory.py +138 -35
- runbooks/cli/commands/operate.py +9 -36
- runbooks/cli/commands/security.py +42 -18
- runbooks/cli/commands/validation.py +432 -18
- runbooks/cli/commands/vpc.py +81 -17
- runbooks/cli/registry.py +22 -10
- runbooks/cloudops/__init__.py +20 -27
- runbooks/cloudops/base.py +96 -107
- runbooks/cloudops/cost_optimizer.py +544 -542
- runbooks/cloudops/infrastructure_optimizer.py +5 -4
- runbooks/cloudops/interfaces.py +224 -225
- runbooks/cloudops/lifecycle_manager.py +5 -4
- runbooks/cloudops/mcp_cost_validation.py +252 -235
- runbooks/cloudops/models.py +78 -53
- runbooks/cloudops/monitoring_automation.py +5 -4
- runbooks/cloudops/notebook_framework.py +177 -213
- runbooks/cloudops/security_enforcer.py +125 -159
- runbooks/common/accuracy_validator.py +11 -0
- runbooks/common/aws_pricing.py +349 -326
- runbooks/common/aws_pricing_api.py +211 -212
- runbooks/common/aws_profile_manager.py +40 -36
- runbooks/common/aws_utils.py +74 -79
- runbooks/common/business_logic.py +126 -104
- runbooks/common/cli_decorators.py +36 -60
- runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
- runbooks/common/cross_account_manager.py +197 -204
- runbooks/common/date_utils.py +27 -39
- runbooks/common/decorators.py +29 -19
- runbooks/common/dry_run_examples.py +173 -208
- runbooks/common/dry_run_framework.py +157 -155
- runbooks/common/enhanced_exception_handler.py +15 -4
- runbooks/common/enhanced_logging_example.py +50 -64
- runbooks/common/enhanced_logging_integration_example.py +65 -37
- runbooks/common/env_utils.py +16 -16
- runbooks/common/error_handling.py +40 -38
- runbooks/common/lazy_loader.py +41 -23
- runbooks/common/logging_integration_helper.py +79 -86
- runbooks/common/mcp_cost_explorer_integration.py +476 -493
- runbooks/common/mcp_integration.py +63 -74
- runbooks/common/memory_optimization.py +140 -118
- runbooks/common/module_cli_base.py +37 -58
- runbooks/common/organizations_client.py +175 -193
- runbooks/common/patterns.py +23 -25
- runbooks/common/performance_monitoring.py +67 -71
- runbooks/common/performance_optimization_engine.py +283 -274
- runbooks/common/profile_utils.py +111 -37
- runbooks/common/rich_utils.py +201 -141
- runbooks/common/sre_performance_suite.py +177 -186
- runbooks/enterprise/__init__.py +1 -1
- runbooks/enterprise/logging.py +144 -106
- runbooks/enterprise/security.py +187 -204
- runbooks/enterprise/validation.py +43 -56
- runbooks/finops/__init__.py +26 -30
- runbooks/finops/account_resolver.py +1 -1
- runbooks/finops/advanced_optimization_engine.py +980 -0
- runbooks/finops/automation_core.py +268 -231
- runbooks/finops/business_case_config.py +184 -179
- runbooks/finops/cli.py +660 -139
- runbooks/finops/commvault_ec2_analysis.py +157 -164
- runbooks/finops/compute_cost_optimizer.py +336 -320
- runbooks/finops/config.py +20 -20
- runbooks/finops/cost_optimizer.py +484 -618
- runbooks/finops/cost_processor.py +332 -214
- runbooks/finops/dashboard_runner.py +1006 -172
- runbooks/finops/ebs_cost_optimizer.py +991 -657
- runbooks/finops/elastic_ip_optimizer.py +317 -257
- runbooks/finops/enhanced_mcp_integration.py +340 -0
- runbooks/finops/enhanced_progress.py +32 -29
- runbooks/finops/enhanced_trend_visualization.py +3 -2
- runbooks/finops/enterprise_wrappers.py +223 -285
- runbooks/finops/executive_export.py +203 -160
- runbooks/finops/helpers.py +130 -288
- runbooks/finops/iam_guidance.py +1 -1
- runbooks/finops/infrastructure/__init__.py +80 -0
- runbooks/finops/infrastructure/commands.py +506 -0
- runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
- runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
- runbooks/finops/markdown_exporter.py +337 -174
- runbooks/finops/mcp_validator.py +1952 -0
- runbooks/finops/nat_gateway_optimizer.py +1512 -481
- runbooks/finops/network_cost_optimizer.py +657 -587
- runbooks/finops/notebook_utils.py +226 -188
- runbooks/finops/optimization_engine.py +1136 -0
- runbooks/finops/optimizer.py +19 -23
- runbooks/finops/rds_snapshot_optimizer.py +367 -411
- runbooks/finops/reservation_optimizer.py +427 -363
- runbooks/finops/scenario_cli_integration.py +64 -65
- runbooks/finops/scenarios.py +1277 -438
- runbooks/finops/schemas.py +218 -182
- runbooks/finops/snapshot_manager.py +2289 -0
- runbooks/finops/types.py +3 -3
- runbooks/finops/validation_framework.py +259 -265
- runbooks/finops/vpc_cleanup_exporter.py +189 -144
- runbooks/finops/vpc_cleanup_optimizer.py +591 -573
- runbooks/finops/workspaces_analyzer.py +171 -182
- runbooks/integration/__init__.py +89 -0
- runbooks/integration/mcp_integration.py +1920 -0
- runbooks/inventory/CLAUDE.md +816 -0
- runbooks/inventory/__init__.py +2 -2
- runbooks/inventory/cloud_foundations_integration.py +144 -149
- runbooks/inventory/collectors/aws_comprehensive.py +1 -1
- runbooks/inventory/collectors/aws_networking.py +109 -99
- runbooks/inventory/collectors/base.py +4 -0
- runbooks/inventory/core/collector.py +495 -313
- runbooks/inventory/drift_detection_cli.py +69 -96
- runbooks/inventory/inventory_mcp_cli.py +48 -46
- runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
- runbooks/inventory/mcp_inventory_validator.py +549 -465
- runbooks/inventory/mcp_vpc_validator.py +359 -442
- runbooks/inventory/organizations_discovery.py +55 -51
- runbooks/inventory/rich_inventory_display.py +33 -32
- runbooks/inventory/unified_validation_engine.py +278 -251
- runbooks/inventory/vpc_analyzer.py +732 -695
- runbooks/inventory/vpc_architecture_validator.py +293 -348
- runbooks/inventory/vpc_dependency_analyzer.py +382 -378
- runbooks/inventory/vpc_flow_analyzer.py +1 -1
- runbooks/main.py +49 -34
- runbooks/main_final.py +91 -60
- runbooks/main_minimal.py +22 -10
- runbooks/main_optimized.py +131 -100
- runbooks/main_ultra_minimal.py +7 -2
- runbooks/mcp/__init__.py +36 -0
- runbooks/mcp/integration.py +679 -0
- runbooks/monitoring/performance_monitor.py +9 -4
- runbooks/operate/dynamodb_operations.py +3 -1
- runbooks/operate/ec2_operations.py +145 -137
- runbooks/operate/iam_operations.py +146 -152
- runbooks/operate/networking_cost_heatmap.py +29 -8
- runbooks/operate/rds_operations.py +223 -254
- runbooks/operate/s3_operations.py +107 -118
- runbooks/operate/vpc_operations.py +646 -616
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/commons.py +10 -7
- runbooks/remediation/commvault_ec2_analysis.py +70 -66
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
- runbooks/remediation/multi_account.py +24 -21
- runbooks/remediation/rds_snapshot_list.py +86 -60
- runbooks/remediation/remediation_cli.py +92 -146
- runbooks/remediation/universal_account_discovery.py +83 -79
- runbooks/remediation/workspaces_list.py +46 -41
- runbooks/security/__init__.py +19 -0
- runbooks/security/assessment_runner.py +1150 -0
- runbooks/security/baseline_checker.py +812 -0
- runbooks/security/cloudops_automation_security_validator.py +509 -535
- runbooks/security/compliance_automation_engine.py +17 -17
- runbooks/security/config/__init__.py +2 -2
- runbooks/security/config/compliance_config.py +50 -50
- runbooks/security/config_template_generator.py +63 -76
- runbooks/security/enterprise_security_framework.py +1 -1
- runbooks/security/executive_security_dashboard.py +519 -508
- runbooks/security/multi_account_security_controls.py +959 -1210
- runbooks/security/real_time_security_monitor.py +422 -444
- runbooks/security/security_baseline_tester.py +1 -1
- runbooks/security/security_cli.py +143 -112
- runbooks/security/test_2way_validation.py +439 -0
- runbooks/security/two_way_validation_framework.py +852 -0
- runbooks/sre/production_monitoring_framework.py +167 -177
- runbooks/tdd/__init__.py +15 -0
- runbooks/tdd/cli.py +1071 -0
- runbooks/utils/__init__.py +14 -17
- runbooks/utils/logger.py +7 -2
- runbooks/utils/version_validator.py +50 -47
- runbooks/validation/__init__.py +6 -6
- runbooks/validation/cli.py +9 -3
- runbooks/validation/comprehensive_2way_validator.py +745 -704
- runbooks/validation/mcp_validator.py +906 -228
- runbooks/validation/terraform_citations_validator.py +104 -115
- runbooks/validation/terraform_drift_detector.py +447 -451
- runbooks/vpc/README.md +617 -0
- runbooks/vpc/__init__.py +8 -1
- runbooks/vpc/analyzer.py +577 -0
- runbooks/vpc/cleanup_wrapper.py +476 -413
- runbooks/vpc/cli_cloudtrail_commands.py +339 -0
- runbooks/vpc/cli_mcp_validation_commands.py +480 -0
- runbooks/vpc/cloudtrail_audit_integration.py +717 -0
- runbooks/vpc/config.py +92 -97
- runbooks/vpc/cost_engine.py +411 -148
- runbooks/vpc/cost_explorer_integration.py +553 -0
- runbooks/vpc/cross_account_session.py +101 -106
- runbooks/vpc/enhanced_mcp_validation.py +917 -0
- runbooks/vpc/eni_gate_validator.py +961 -0
- runbooks/vpc/heatmap_engine.py +185 -160
- runbooks/vpc/mcp_no_eni_validator.py +680 -639
- runbooks/vpc/nat_gateway_optimizer.py +358 -0
- runbooks/vpc/networking_wrapper.py +15 -8
- runbooks/vpc/pdca_remediation_planner.py +528 -0
- runbooks/vpc/performance_optimized_analyzer.py +219 -231
- runbooks/vpc/runbooks_adapter.py +1167 -241
- runbooks/vpc/tdd_red_phase_stubs.py +601 -0
- runbooks/vpc/test_data_loader.py +358 -0
- runbooks/vpc/tests/conftest.py +314 -4
- runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
- runbooks/vpc/tests/test_cost_engine.py +0 -2
- runbooks/vpc/topology_generator.py +326 -0
- runbooks/vpc/unified_scenarios.py +1297 -1124
- runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
- runbooks-1.1.5.dist-info/METADATA +328 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/RECORD +214 -193
- runbooks/finops/README.md +0 -414
- runbooks/finops/accuracy_cross_validator.py +0 -647
- runbooks/finops/business_cases.py +0 -950
- runbooks/finops/dashboard_router.py +0 -922
- runbooks/finops/ebs_optimizer.py +0 -973
- runbooks/finops/embedded_mcp_validator.py +0 -1629
- runbooks/finops/enhanced_dashboard_runner.py +0 -527
- runbooks/finops/finops_dashboard.py +0 -584
- runbooks/finops/finops_scenarios.py +0 -1218
- runbooks/finops/legacy_migration.py +0 -730
- runbooks/finops/multi_dashboard.py +0 -1519
- runbooks/finops/single_dashboard.py +0 -1113
- runbooks/finops/unlimited_scenarios.py +0 -393
- runbooks-1.1.4.dist-info/METADATA +0 -800
- {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
@@ -15,317 +15,362 @@ import os
|
|
15
15
|
from datetime import datetime
|
16
16
|
from typing import Any, Dict, List
|
17
17
|
|
18
|
-
from .markdown_exporter import MarkdownExporter
|
19
18
|
from runbooks.common.rich_utils import console
|
20
19
|
|
20
|
+
from .markdown_exporter import MarkdownExporter
|
21
|
+
|
22
|
+
|
23
|
+
class VPCCleanupExporter:
|
24
|
+
"""VPC Cleanup results exporter class."""
|
25
|
+
|
26
|
+
def __init__(self, output_dir: str = "./"):
|
27
|
+
"""Initialize VPC cleanup exporter."""
|
28
|
+
self.output_dir = output_dir
|
29
|
+
|
30
|
+
def export_results(self, vpc_result: Any, export_formats: List[str]) -> Dict[str, str]:
|
31
|
+
"""Export VPC cleanup results in specified formats."""
|
32
|
+
return export_vpc_cleanup_results(vpc_result, export_formats, self.output_dir)
|
33
|
+
|
21
34
|
|
22
35
|
def _format_tags_for_display(tags_dict: Dict[str, str]) -> str:
|
23
36
|
"""Format tags for display with priority order, emphasizing ownership tags."""
|
24
37
|
if not tags_dict:
|
25
38
|
return "No tags"
|
26
|
-
|
39
|
+
|
27
40
|
# Enhanced priority keys with focus on ownership and approvals
|
28
|
-
priority_keys = [
|
29
|
-
|
41
|
+
priority_keys = [
|
42
|
+
"Name",
|
43
|
+
"Owner",
|
44
|
+
"BusinessOwner",
|
45
|
+
"TechnicalOwner",
|
46
|
+
"Team",
|
47
|
+
"Contact",
|
48
|
+
"Environment",
|
49
|
+
"Project",
|
50
|
+
"CostCenter",
|
51
|
+
"CreatedBy",
|
52
|
+
"ManagedBy",
|
53
|
+
]
|
30
54
|
relevant_tags = []
|
31
|
-
|
55
|
+
|
32
56
|
for key in priority_keys:
|
33
57
|
if key in tags_dict and tags_dict[key]:
|
34
58
|
relevant_tags.append(f"{key}:{tags_dict[key]}")
|
35
|
-
|
59
|
+
|
36
60
|
# Add CloudFormation/Terraform tags for IaC detection
|
37
|
-
iac_keys = [
|
61
|
+
iac_keys = ["aws:cloudformation:stack-name", "terraform:module", "cdktf:stack", "pulumi:project"]
|
38
62
|
for key in iac_keys:
|
39
63
|
if key in tags_dict and tags_dict[key] and len(relevant_tags) < 6:
|
40
64
|
relevant_tags.append(f"IaC:{tags_dict[key]}")
|
41
|
-
|
65
|
+
|
42
66
|
# Add other important tags
|
43
67
|
for key, value in tags_dict.items():
|
44
68
|
if key not in priority_keys + iac_keys and value and len(relevant_tags) < 5:
|
45
69
|
relevant_tags.append(f"{key}:{value}")
|
46
|
-
|
70
|
+
|
47
71
|
return "; ".join(relevant_tags) if relevant_tags else f"({len(tags_dict)} tags)"
|
48
72
|
|
49
73
|
|
50
74
|
def export_vpc_cleanup_results(vpc_result: Any, export_formats: List[str], output_dir: str = "./") -> Dict[str, str]:
|
51
75
|
"""
|
52
76
|
Export VPC cleanup results in multiple formats.
|
53
|
-
|
77
|
+
|
54
78
|
Args:
|
55
79
|
vpc_result: VPC cleanup analysis result object
|
56
80
|
export_formats: List of formats to export (markdown, csv, json, pdf)
|
57
81
|
output_dir: Directory to save exported files
|
58
|
-
|
82
|
+
|
59
83
|
Returns:
|
60
84
|
Dict mapping format to exported filename
|
61
85
|
"""
|
62
86
|
results = {}
|
63
|
-
|
87
|
+
|
64
88
|
# Extract VPC candidates from result - use correct attribute name
|
65
|
-
vpc_candidates = getattr(vpc_result,
|
89
|
+
vpc_candidates = getattr(vpc_result, "cleanup_candidates", [])
|
66
90
|
if not vpc_candidates:
|
67
91
|
# Fallback to other possible attribute names
|
68
|
-
vpc_candidates = getattr(vpc_result,
|
69
|
-
|
70
|
-
if
|
92
|
+
vpc_candidates = getattr(vpc_result, "vpc_candidates", [])
|
93
|
+
|
94
|
+
if "markdown" in export_formats:
|
71
95
|
try:
|
72
96
|
exporter = MarkdownExporter()
|
73
97
|
markdown_filename = exporter.export_vpc_analysis_to_file(
|
74
|
-
vpc_candidates,
|
75
|
-
filename="vpc-cleanup-candidates.md",
|
76
|
-
output_dir=output_dir
|
98
|
+
vpc_candidates, filename="vpc-cleanup-candidates.md", output_dir=output_dir
|
77
99
|
)
|
78
|
-
results[
|
100
|
+
results["markdown"] = markdown_filename
|
79
101
|
except Exception as e:
|
80
102
|
console.print(f"[yellow]Warning: Markdown export failed: {e}[/yellow]")
|
81
|
-
results[
|
82
|
-
|
103
|
+
results["markdown"] = None
|
104
|
+
|
83
105
|
# Real implementations for other formats
|
84
|
-
if
|
106
|
+
if "csv" in export_formats:
|
85
107
|
try:
|
86
108
|
csv_filename = _export_vpc_candidates_csv(vpc_candidates, output_dir)
|
87
|
-
results[
|
109
|
+
results["csv"] = csv_filename
|
88
110
|
except Exception as e:
|
89
111
|
print(f"Warning: CSV export failed: {e}")
|
90
|
-
results[
|
91
|
-
|
92
|
-
if
|
112
|
+
results["csv"] = None
|
113
|
+
|
114
|
+
if "json" in export_formats:
|
93
115
|
try:
|
94
116
|
json_filename = _export_vpc_candidates_json(vpc_candidates, output_dir)
|
95
|
-
results[
|
117
|
+
results["json"] = json_filename
|
96
118
|
except Exception as e:
|
97
119
|
print(f"Warning: JSON export failed: {e}")
|
98
|
-
results[
|
99
|
-
|
100
|
-
if
|
120
|
+
results["json"] = None
|
121
|
+
|
122
|
+
if "pdf" in export_formats:
|
101
123
|
try:
|
102
124
|
pdf_filename = _export_vpc_candidates_pdf(vpc_candidates, output_dir)
|
103
|
-
results[
|
125
|
+
results["pdf"] = pdf_filename
|
104
126
|
except Exception as e:
|
105
127
|
print(f"Warning: PDF export failed: {e}")
|
106
|
-
results[
|
107
|
-
|
128
|
+
results["pdf"] = None
|
129
|
+
|
108
130
|
return results
|
109
131
|
|
110
132
|
|
111
133
|
def _export_vpc_candidates_csv(vpc_candidates: List[Any], output_dir: str) -> str:
|
112
134
|
"""Export VPC candidates to CSV format with all 15 columns."""
|
113
135
|
filename = os.path.join(output_dir, "vpc-cleanup-candidates.csv")
|
114
|
-
|
136
|
+
|
115
137
|
# 15-column headers for comprehensive VPC analysis
|
116
138
|
headers = [
|
117
|
-
"Account_ID",
|
118
|
-
"
|
119
|
-
"
|
139
|
+
"Account_ID",
|
140
|
+
"VPC_ID",
|
141
|
+
"VPC_Name",
|
142
|
+
"CIDR_Block",
|
143
|
+
"Overlapping",
|
144
|
+
"Is_Default",
|
145
|
+
"ENI_Count",
|
146
|
+
"Tags",
|
147
|
+
"Flow_Logs",
|
148
|
+
"TGW/Peering",
|
149
|
+
"LBs_Present",
|
150
|
+
"IaC",
|
151
|
+
"Timeline",
|
152
|
+
"Decision",
|
153
|
+
"Owners/Approvals",
|
154
|
+
"Notes",
|
120
155
|
]
|
121
|
-
|
122
|
-
with open(filename,
|
156
|
+
|
157
|
+
with open(filename, "w", newline="", encoding="utf-8") as csvfile:
|
123
158
|
writer = csv.writer(csvfile)
|
124
159
|
writer.writerow(headers)
|
125
|
-
|
160
|
+
|
126
161
|
for candidate in vpc_candidates:
|
127
162
|
# Extract data with enhanced tag and owner handling
|
128
|
-
tags_dict = getattr(candidate,
|
129
|
-
|
163
|
+
tags_dict = getattr(candidate, "tags", {}) or {}
|
164
|
+
|
130
165
|
# Use enhanced tag formatting function
|
131
166
|
tags_str = _format_tags_for_display(tags_dict)
|
132
|
-
|
133
|
-
load_balancers = getattr(candidate,
|
167
|
+
|
168
|
+
load_balancers = getattr(candidate, "load_balancers", []) or []
|
134
169
|
lbs_present = "Yes" if load_balancers else "No"
|
135
|
-
|
170
|
+
|
136
171
|
# Enhanced owner extraction from multiple sources
|
137
|
-
owners = getattr(candidate,
|
138
|
-
|
172
|
+
owners = getattr(candidate, "owners_approvals", []) or []
|
173
|
+
|
139
174
|
# Extract owners from tags with enhanced logic
|
140
175
|
if not owners and tags_dict:
|
141
|
-
owner_keys = [
|
176
|
+
owner_keys = ["Owner", "BusinessOwner", "TechnicalOwner", "Team", "Contact", "CreatedBy", "ManagedBy"]
|
142
177
|
for key in owner_keys:
|
143
178
|
if key in tags_dict and tags_dict[key]:
|
144
179
|
value = tags_dict[key]
|
145
|
-
if
|
180
|
+
if "business" in key.lower() or "manager" in value.lower():
|
146
181
|
owners.append(f"{value} (Business)")
|
147
|
-
elif
|
182
|
+
elif "technical" in key.lower() or "engineer" in value.lower():
|
148
183
|
owners.append(f"{value} (Technical)")
|
149
|
-
elif
|
184
|
+
elif "team" in key.lower():
|
150
185
|
owners.append(f"{value} (Team)")
|
151
186
|
else:
|
152
187
|
owners.append(f"{value} ({key})")
|
153
|
-
|
188
|
+
|
154
189
|
# For default VPCs, add system indicator
|
155
|
-
is_default = getattr(candidate,
|
190
|
+
is_default = getattr(candidate, "is_default", False)
|
156
191
|
if is_default and not owners:
|
157
192
|
owners.append("System Default")
|
158
|
-
|
193
|
+
|
159
194
|
if owners:
|
160
195
|
owners_str = "; ".join(owners)
|
161
196
|
else:
|
162
197
|
# Enhanced fallback for CSV
|
163
|
-
if getattr(candidate,
|
198
|
+
if getattr(candidate, "is_default", False):
|
164
199
|
owners_str = "System Default VPC"
|
165
|
-
elif getattr(candidate,
|
200
|
+
elif getattr(candidate, "iac_detected", False):
|
166
201
|
owners_str = "IaC Managed"
|
167
202
|
else:
|
168
203
|
owners_str = "No owner tags found"
|
169
|
-
|
204
|
+
|
170
205
|
row = [
|
171
|
-
getattr(candidate,
|
172
|
-
getattr(candidate,
|
173
|
-
getattr(candidate,
|
174
|
-
getattr(candidate,
|
206
|
+
getattr(candidate, "account_id", "Unknown"),
|
207
|
+
getattr(candidate, "vpc_id", ""),
|
208
|
+
getattr(candidate, "vpc_name", "Unnamed"),
|
209
|
+
getattr(candidate, "cidr_block", ""),
|
175
210
|
"No", # Overlapping analysis would need CIDR comparison
|
176
|
-
"Yes" if getattr(candidate,
|
177
|
-
getattr(candidate,
|
211
|
+
"Yes" if getattr(candidate, "is_default", False) else "No",
|
212
|
+
getattr(candidate, "dependency_analysis", {}).eni_count
|
213
|
+
if hasattr(candidate, "dependency_analysis")
|
214
|
+
else 0,
|
178
215
|
tags_str,
|
179
|
-
"Yes" if getattr(candidate,
|
216
|
+
"Yes" if getattr(candidate, "flow_logs_enabled", False) else "No",
|
180
217
|
"No", # TGW/Peering analysis placeholder
|
181
218
|
lbs_present,
|
182
|
-
"Yes" if getattr(candidate,
|
219
|
+
"Yes" if getattr(candidate, "iac_detected", False) else "No",
|
183
220
|
"Unknown", # Timeline analysis placeholder
|
184
|
-
getattr(candidate,
|
221
|
+
getattr(candidate, "cleanup_recommendation", "unknown"),
|
185
222
|
owners_str,
|
186
|
-
"Generated by CloudOps Runbooks VPC Module"
|
223
|
+
"Generated by CloudOps Runbooks VPC Module",
|
187
224
|
]
|
188
225
|
writer.writerow(row)
|
189
|
-
|
226
|
+
|
190
227
|
return filename
|
191
228
|
|
192
229
|
|
193
230
|
def _export_vpc_candidates_json(vpc_candidates: List[Any], output_dir: str) -> str:
|
194
231
|
"""Export VPC candidates to JSON format with full data structure."""
|
195
232
|
filename = os.path.join(output_dir, "vpc-cleanup-candidates.json")
|
196
|
-
|
233
|
+
|
197
234
|
# Convert candidates to serializable format
|
198
235
|
candidates_data = []
|
199
236
|
for candidate in vpc_candidates:
|
200
237
|
candidate_dict = {
|
201
|
-
"account_id": getattr(candidate,
|
202
|
-
"vpc_id": getattr(candidate,
|
203
|
-
"vpc_name": getattr(candidate,
|
204
|
-
"cidr_block": getattr(candidate,
|
205
|
-
"region": getattr(candidate,
|
206
|
-
"is_default": getattr(candidate,
|
207
|
-
"state": getattr(candidate,
|
208
|
-
"tags": getattr(candidate,
|
209
|
-
"tags_summary": _format_tags_for_display(getattr(candidate,
|
210
|
-
"flow_logs_enabled": getattr(candidate,
|
211
|
-
"load_balancers": getattr(candidate,
|
212
|
-
"iac_detected": getattr(candidate,
|
213
|
-
"owners_approvals": getattr(candidate,
|
214
|
-
"cleanup_bucket": getattr(candidate,
|
215
|
-
"cleanup_recommendation": getattr(candidate,
|
216
|
-
"risk_assessment": getattr(candidate,
|
217
|
-
"business_impact": getattr(candidate,
|
238
|
+
"account_id": getattr(candidate, "account_id", "Unknown"),
|
239
|
+
"vpc_id": getattr(candidate, "vpc_id", ""),
|
240
|
+
"vpc_name": getattr(candidate, "vpc_name", "Unnamed"),
|
241
|
+
"cidr_block": getattr(candidate, "cidr_block", ""),
|
242
|
+
"region": getattr(candidate, "region", "unknown"),
|
243
|
+
"is_default": getattr(candidate, "is_default", False),
|
244
|
+
"state": getattr(candidate, "state", "unknown"),
|
245
|
+
"tags": getattr(candidate, "tags", {}) or {},
|
246
|
+
"tags_summary": _format_tags_for_display(getattr(candidate, "tags", {}) or {}),
|
247
|
+
"flow_logs_enabled": getattr(candidate, "flow_logs_enabled", False),
|
248
|
+
"load_balancers": getattr(candidate, "load_balancers", []) or [],
|
249
|
+
"iac_detected": getattr(candidate, "iac_detected", False),
|
250
|
+
"owners_approvals": getattr(candidate, "owners_approvals", []) or [],
|
251
|
+
"cleanup_bucket": getattr(candidate, "cleanup_bucket", "unknown"),
|
252
|
+
"cleanup_recommendation": getattr(candidate, "cleanup_recommendation", "unknown"),
|
253
|
+
"risk_assessment": getattr(candidate, "risk_assessment", "unknown"),
|
254
|
+
"business_impact": getattr(candidate, "business_impact", "unknown"),
|
218
255
|
}
|
219
|
-
|
256
|
+
|
220
257
|
# Add dependency analysis if available
|
221
|
-
if hasattr(candidate,
|
258
|
+
if hasattr(candidate, "dependency_analysis") and candidate.dependency_analysis:
|
222
259
|
dep_analysis = candidate.dependency_analysis
|
223
260
|
candidate_dict["dependency_analysis"] = {
|
224
|
-
"eni_count": getattr(dep_analysis,
|
225
|
-
"route_tables": getattr(dep_analysis,
|
226
|
-
"security_groups": getattr(dep_analysis,
|
227
|
-
"internet_gateways": getattr(dep_analysis,
|
228
|
-
"nat_gateways": getattr(dep_analysis,
|
229
|
-
"vpc_endpoints": getattr(dep_analysis,
|
230
|
-
"peering_connections": getattr(dep_analysis,
|
231
|
-
"dependency_risk_level": getattr(dep_analysis,
|
261
|
+
"eni_count": getattr(dep_analysis, "eni_count", 0),
|
262
|
+
"route_tables": getattr(dep_analysis, "route_tables", []),
|
263
|
+
"security_groups": getattr(dep_analysis, "security_groups", []),
|
264
|
+
"internet_gateways": getattr(dep_analysis, "internet_gateways", []),
|
265
|
+
"nat_gateways": getattr(dep_analysis, "nat_gateways", []),
|
266
|
+
"vpc_endpoints": getattr(dep_analysis, "vpc_endpoints", []),
|
267
|
+
"peering_connections": getattr(dep_analysis, "peering_connections", []),
|
268
|
+
"dependency_risk_level": getattr(dep_analysis, "dependency_risk_level", "unknown"),
|
232
269
|
}
|
233
|
-
|
270
|
+
|
234
271
|
candidates_data.append(candidate_dict)
|
235
|
-
|
272
|
+
|
236
273
|
# Create export metadata
|
237
274
|
export_data = {
|
238
275
|
"metadata": {
|
239
276
|
"export_timestamp": datetime.now().isoformat(),
|
240
277
|
"total_candidates": len(candidates_data),
|
241
|
-
"generator": "CloudOps Runbooks VPC Module latest version"
|
278
|
+
"generator": "CloudOps Runbooks VPC Module latest version",
|
242
279
|
},
|
243
|
-
"vpc_candidates": candidates_data
|
280
|
+
"vpc_candidates": candidates_data,
|
244
281
|
}
|
245
|
-
|
246
|
-
with open(filename,
|
282
|
+
|
283
|
+
with open(filename, "w", encoding="utf-8") as jsonfile:
|
247
284
|
json.dump(export_data, jsonfile, indent=2, ensure_ascii=False)
|
248
|
-
|
285
|
+
|
249
286
|
return filename
|
250
287
|
|
251
288
|
|
252
289
|
def _export_vpc_candidates_pdf(vpc_candidates: List[Any], output_dir: str) -> str:
|
253
290
|
"""Export VPC candidates to PDF format for executive presentation."""
|
254
291
|
filename = os.path.join(output_dir, "vpc-cleanup-candidates.pdf")
|
255
|
-
|
292
|
+
|
256
293
|
try:
|
257
294
|
# Try to use reportlab for PDF generation
|
258
295
|
from reportlab.lib import colors
|
259
|
-
from reportlab.lib.pagesizes import
|
260
|
-
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
|
296
|
+
from reportlab.lib.pagesizes import A4, letter
|
261
297
|
from reportlab.lib.styles import getSampleStyleSheet
|
262
|
-
|
298
|
+
from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle
|
299
|
+
|
263
300
|
doc = SimpleDocTemplate(filename, pagesize=A4)
|
264
301
|
styles = getSampleStyleSheet()
|
265
302
|
story = []
|
266
|
-
|
303
|
+
|
267
304
|
# Title
|
268
|
-
title = Paragraph("VPC Cleanup Analysis Report", styles[
|
305
|
+
title = Paragraph("VPC Cleanup Analysis Report", styles["Title"])
|
269
306
|
story.append(title)
|
270
307
|
story.append(Spacer(1, 20))
|
271
|
-
|
308
|
+
|
272
309
|
# Summary
|
273
310
|
summary_text = f"""
|
274
|
-
<b>Generated:</b> {datetime.now().strftime(
|
311
|
+
<b>Generated:</b> {datetime.now().strftime("%Y-%m-%d %H:%M:%S UTC")}<br/>
|
275
312
|
<b>Total VPC Candidates:</b> {len(vpc_candidates)}<br/>
|
276
313
|
<b>Analysis Source:</b> CloudOps Runbooks VPC Module latest version
|
277
314
|
"""
|
278
|
-
summary = Paragraph(summary_text, styles[
|
315
|
+
summary = Paragraph(summary_text, styles["Normal"])
|
279
316
|
story.append(summary)
|
280
317
|
story.append(Spacer(1, 20))
|
281
|
-
|
318
|
+
|
282
319
|
# Create table data
|
283
|
-
table_data = [
|
284
|
-
|
285
|
-
]
|
286
|
-
|
320
|
+
table_data = [["Account ID", "VPC ID", "VPC Name", "CIDR", "Default", "ENI Count", "Decision"]]
|
321
|
+
|
287
322
|
for candidate in vpc_candidates:
|
288
323
|
row = [
|
289
|
-
str(getattr(candidate,
|
290
|
-
str(getattr(candidate,
|
291
|
-
str(getattr(candidate,
|
292
|
-
str(getattr(candidate,
|
293
|
-
"Yes" if getattr(candidate,
|
294
|
-
str(
|
295
|
-
|
324
|
+
str(getattr(candidate, "account_id", "Unknown"))[:15], # Truncate for PDF width
|
325
|
+
str(getattr(candidate, "vpc_id", ""))[:20],
|
326
|
+
str(getattr(candidate, "vpc_name", "Unnamed"))[:15],
|
327
|
+
str(getattr(candidate, "cidr_block", ""))[:15],
|
328
|
+
"Yes" if getattr(candidate, "is_default", False) else "No",
|
329
|
+
str(
|
330
|
+
getattr(candidate, "dependency_analysis", {}).eni_count
|
331
|
+
if hasattr(candidate, "dependency_analysis")
|
332
|
+
else 0
|
333
|
+
),
|
334
|
+
str(getattr(candidate, "cleanup_recommendation", "unknown"))[:10],
|
296
335
|
]
|
297
336
|
table_data.append(row)
|
298
|
-
|
337
|
+
|
299
338
|
# Create table
|
300
339
|
table = Table(table_data)
|
301
|
-
table.setStyle(
|
302
|
-
(
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
340
|
+
table.setStyle(
|
341
|
+
TableStyle(
|
342
|
+
[
|
343
|
+
("BACKGROUND", (0, 0), (-1, 0), colors.grey),
|
344
|
+
("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke),
|
345
|
+
("ALIGN", (0, 0), (-1, -1), "CENTER"),
|
346
|
+
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
|
347
|
+
("FONTSIZE", (0, 0), (-1, 0), 10),
|
348
|
+
("BOTTOMPADDING", (0, 0), (-1, 0), 12),
|
349
|
+
("BACKGROUND", (0, 1), (-1, -1), colors.beige),
|
350
|
+
("FONTSIZE", (0, 1), (-1, -1), 8),
|
351
|
+
("GRID", (0, 0), (-1, -1), 1, colors.black),
|
352
|
+
]
|
353
|
+
)
|
354
|
+
)
|
355
|
+
|
313
356
|
story.append(table)
|
314
357
|
doc.build(story)
|
315
|
-
|
358
|
+
|
316
359
|
except ImportError:
|
317
360
|
# Fallback: create a simple text-based PDF placeholder
|
318
|
-
with open(filename,
|
361
|
+
with open(filename, "w", encoding="utf-8") as f:
|
319
362
|
f.write("VPC Cleanup Analysis Report (PDF)\n")
|
320
363
|
f.write("=" * 40 + "\n\n")
|
321
364
|
f.write(f"Generated: {datetime.now().isoformat()}\n")
|
322
365
|
f.write(f"Total VPC Candidates: {len(vpc_candidates)}\n\n")
|
323
|
-
|
366
|
+
|
324
367
|
for i, candidate in enumerate(vpc_candidates, 1):
|
325
368
|
f.write(f"{i}. VPC {getattr(candidate, 'vpc_id', 'Unknown')}\n")
|
326
369
|
f.write(f" Account: {getattr(candidate, 'account_id', 'Unknown')}\n")
|
327
370
|
f.write(f" CIDR: {getattr(candidate, 'cidr_block', 'Unknown')}\n")
|
328
|
-
f.write(
|
371
|
+
f.write(
|
372
|
+
f" ENI Count: {getattr(candidate, 'dependency_analysis', {}).eni_count if hasattr(candidate, 'dependency_analysis') else 0}\n"
|
373
|
+
)
|
329
374
|
f.write(f" Decision: {getattr(candidate, 'cleanup_recommendation', 'unknown')}\n\n")
|
330
|
-
|
331
|
-
return filename
|
375
|
+
|
376
|
+
return filename
|