runbooks 1.1.4__py3-none-any.whl → 1.1.6__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 +135 -91
- 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 +17 -12
- 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 +99 -79
- 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 +315 -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/aws_decorators.py +2 -3
- runbooks/inventory/check_cloudtrail_compliance.py +2 -4
- runbooks/inventory/check_controltower_readiness.py +152 -151
- runbooks/inventory/check_landingzone_readiness.py +85 -84
- 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/core/formatter.py +11 -0
- runbooks/inventory/draw_org_structure.py +8 -9
- runbooks/inventory/drift_detection_cli.py +69 -96
- runbooks/inventory/ec2_vpc_utils.py +2 -2
- runbooks/inventory/find_cfn_drift_detection.py +5 -7
- runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
- runbooks/inventory/find_cfn_stackset_drift.py +5 -6
- runbooks/inventory/find_ec2_security_groups.py +48 -42
- runbooks/inventory/find_landingzone_versions.py +4 -6
- runbooks/inventory/find_vpc_flow_logs.py +7 -9
- runbooks/inventory/inventory_mcp_cli.py +48 -46
- runbooks/inventory/inventory_modules.py +103 -91
- runbooks/inventory/list_cfn_stacks.py +9 -10
- runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
- runbooks/inventory/list_cfn_stackset_operations.py +79 -57
- runbooks/inventory/list_cfn_stacksets.py +8 -10
- runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
- runbooks/inventory/list_ds_directories.py +65 -53
- runbooks/inventory/list_ec2_availability_zones.py +2 -4
- runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
- runbooks/inventory/list_ec2_instances.py +23 -28
- runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
- runbooks/inventory/list_elbs_load_balancers.py +22 -20
- runbooks/inventory/list_enis_network_interfaces.py +26 -33
- runbooks/inventory/list_guardduty_detectors.py +2 -4
- runbooks/inventory/list_iam_policies.py +2 -4
- runbooks/inventory/list_iam_roles.py +5 -7
- runbooks/inventory/list_iam_saml_providers.py +4 -6
- runbooks/inventory/list_lambda_functions.py +38 -38
- runbooks/inventory/list_org_accounts.py +6 -8
- runbooks/inventory/list_org_accounts_users.py +55 -44
- runbooks/inventory/list_rds_db_instances.py +31 -33
- runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
- runbooks/inventory/list_route53_hosted_zones.py +3 -5
- runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
- runbooks/inventory/list_sns_topics.py +2 -4
- runbooks/inventory/list_ssm_parameters.py +4 -7
- runbooks/inventory/list_vpc_subnets.py +2 -4
- runbooks/inventory/list_vpcs.py +7 -10
- runbooks/inventory/mcp_inventory_validator.py +554 -468
- runbooks/inventory/mcp_vpc_validator.py +359 -442
- runbooks/inventory/organizations_discovery.py +63 -55
- runbooks/inventory/recover_cfn_stack_ids.py +7 -8
- runbooks/inventory/requirements.txt +0 -1
- runbooks/inventory/rich_inventory_display.py +35 -34
- runbooks/inventory/run_on_multi_accounts.py +3 -5
- runbooks/inventory/unified_validation_engine.py +281 -253
- runbooks/inventory/verify_ec2_security_groups.py +1 -1
- runbooks/inventory/vpc_analyzer.py +735 -697
- runbooks/inventory/vpc_architecture_validator.py +293 -348
- runbooks/inventory/vpc_dependency_analyzer.py +384 -380
- 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 +461 -454
- 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.6.dist-info/METADATA +327 -0
- runbooks-1.1.6.dist-info/RECORD +489 -0
- 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/RECORD +0 -468
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/top_level.txt +0 -0
@@ -16,8 +16,14 @@ from botocore.exceptions import ClientError
|
|
16
16
|
|
17
17
|
from .commons import display_aws_account_info, get_client, write_to_csv
|
18
18
|
from ..common.rich_utils import (
|
19
|
-
console,
|
20
|
-
|
19
|
+
console,
|
20
|
+
print_header,
|
21
|
+
print_success,
|
22
|
+
print_error,
|
23
|
+
print_warning,
|
24
|
+
create_table,
|
25
|
+
create_progress_bar,
|
26
|
+
format_cost,
|
21
27
|
)
|
22
28
|
|
23
29
|
logger = logging.getLogger(__name__)
|
@@ -36,16 +42,17 @@ def calculate_snapshot_age(create_time):
|
|
36
42
|
def estimate_snapshot_cost(allocated_storage, storage_type="gp2", days_old=1):
|
37
43
|
"""
|
38
44
|
Estimate monthly snapshot storage cost with enhanced accuracy.
|
39
|
-
|
45
|
+
|
40
46
|
JIRA FinOps-23: Enhanced cost estimation for measurable range annual savings target
|
41
47
|
Based on AWS RDS snapshot pricing: https://aws.amazon.com/rds/pricing/
|
42
48
|
"""
|
43
49
|
# Real-time RDS Snapshot cost from AWS Pricing API - NO hardcoded defaults
|
44
50
|
from runbooks.common.aws_pricing_api import pricing_api
|
51
|
+
|
45
52
|
# Get region from caller context or default to us-east-1
|
46
|
-
region = os.getenv(
|
53
|
+
region = os.getenv("AWS_DEFAULT_REGION", "us-east-1")
|
47
54
|
snapshot_cost_per_gb_month = pricing_api.get_rds_snapshot_cost_per_gb(region)
|
48
|
-
|
55
|
+
|
49
56
|
# Calculate base monthly cost
|
50
57
|
monthly_cost = allocated_storage * snapshot_cost_per_gb_month
|
51
58
|
|
@@ -59,28 +66,26 @@ def estimate_snapshot_cost(allocated_storage, storage_type="gp2", days_old=1):
|
|
59
66
|
def calculate_manual_snapshot_savings(snapshots_data: List[Dict]) -> Dict[str, float]:
|
60
67
|
"""
|
61
68
|
Calculate potential savings from manual snapshot cleanup.
|
62
|
-
|
69
|
+
|
63
70
|
JIRA FinOps-23: Focuses on 89 manual snapshots for cost optimization
|
64
71
|
"""
|
65
72
|
manual_snapshots = [s for s in snapshots_data if s.get("SnapshotType", "").lower() == "manual"]
|
66
|
-
|
73
|
+
|
67
74
|
# Calculate costs by age groups
|
68
75
|
old_manual_snapshots = [s for s in manual_snapshots if s.get("AgeDays", 0) >= 90] # 3+ months old
|
69
76
|
very_old_manual_snapshots = [s for s in manual_snapshots if s.get("AgeDays", 0) >= 180] # 6+ months old
|
70
|
-
|
77
|
+
|
71
78
|
total_manual_cost = sum(s.get("EstimatedMonthlyCost", 0) for s in manual_snapshots)
|
72
79
|
old_manual_cost = sum(s.get("EstimatedMonthlyCost", 0) for s in old_manual_snapshots)
|
73
80
|
very_old_manual_cost = sum(s.get("EstimatedMonthlyCost", 0) for s in very_old_manual_snapshots)
|
74
|
-
|
81
|
+
|
75
82
|
return {
|
76
83
|
"total_manual_snapshots": len(manual_snapshots),
|
77
84
|
"total_manual_monthly_cost": total_manual_cost,
|
78
85
|
"total_manual_annual_cost": total_manual_cost * 12,
|
79
|
-
|
80
86
|
"old_manual_snapshots": len(old_manual_snapshots), # 90+ days
|
81
87
|
"old_manual_monthly_savings": old_manual_cost,
|
82
88
|
"old_manual_annual_savings": old_manual_cost * 12,
|
83
|
-
|
84
89
|
"very_old_manual_snapshots": len(very_old_manual_snapshots), # 180+ days
|
85
90
|
"very_old_manual_monthly_savings": very_old_manual_cost,
|
86
91
|
"very_old_manual_annual_savings": very_old_manual_cost * 12,
|
@@ -96,15 +101,17 @@ def calculate_manual_snapshot_savings(snapshots_data: List[Dict]) -> Dict[str, f
|
|
96
101
|
@click.option("--older-than", default=90, help="Focus on snapshots older than X days")
|
97
102
|
@click.option("--calculate-savings", is_flag=True, help="Calculate detailed cost savings analysis")
|
98
103
|
@click.option("--analyze", is_flag=True, help="Perform comprehensive cost analysis")
|
99
|
-
def get_rds_snapshot_details(
|
104
|
+
def get_rds_snapshot_details(
|
105
|
+
output_file, old_days, include_cost, snapshot_type, manual_only, older_than, calculate_savings, analyze
|
106
|
+
):
|
100
107
|
"""
|
101
108
|
Analyze RDS snapshots for lifecycle management and cost optimization.
|
102
|
-
|
109
|
+
|
103
110
|
JIRA FinOps-23: Enhanced RDS snapshots analysis for measurable range annual savings
|
104
111
|
Focus on 89 manual snapshots causing storage costs and operational clutter
|
105
112
|
"""
|
106
113
|
print_header("RDS Snapshot Cost Optimization Analysis", "latest version")
|
107
|
-
|
114
|
+
|
108
115
|
account_info = display_aws_account_info()
|
109
116
|
console.print(f"[cyan]Analyzing RDS snapshots in {account_info}[/cyan]")
|
110
117
|
|
@@ -126,19 +133,25 @@ def get_rds_snapshot_details(output_file, old_days, include_cost, snapshot_type,
|
|
126
133
|
if manual_only:
|
127
134
|
original_count = len(snapshots)
|
128
135
|
snapshots = [s for s in snapshots if s.get("SnapshotType", "").lower() == "manual"]
|
129
|
-
console.print(
|
130
|
-
|
136
|
+
console.print(
|
137
|
+
f"[dim]JIRA FinOps-23 Filter: {len(snapshots)} manual snapshots (from {original_count} total)[/dim]"
|
138
|
+
)
|
139
|
+
|
131
140
|
if snapshot_type:
|
132
141
|
original_count = len(snapshots)
|
133
142
|
snapshots = [s for s in snapshots if s.get("SnapshotType", "").lower() == snapshot_type.lower()]
|
134
143
|
console.print(f"[dim]Filtered to {len(snapshots)} snapshots of type '{snapshot_type}'[/dim]")
|
135
|
-
|
144
|
+
|
136
145
|
if older_than > 0:
|
137
146
|
now = datetime.now(tz=timezone.utc)
|
138
147
|
threshold_date = now - timedelta(days=older_than)
|
139
148
|
original_count = len(snapshots)
|
140
|
-
snapshots = [
|
141
|
-
|
149
|
+
snapshots = [
|
150
|
+
s for s in snapshots if s.get("SnapshotCreateTime") and s["SnapshotCreateTime"] < threshold_date
|
151
|
+
]
|
152
|
+
console.print(
|
153
|
+
f"[dim]Age filter: {len(snapshots)} snapshots older than {older_than} days (from {original_count})[/dim]"
|
154
|
+
)
|
142
155
|
|
143
156
|
data = []
|
144
157
|
old_snapshots = []
|
@@ -148,11 +161,8 @@ def get_rds_snapshot_details(output_file, old_days, include_cost, snapshot_type,
|
|
148
161
|
total_estimated_cost = 0
|
149
162
|
|
150
163
|
with create_progress_bar() as progress:
|
151
|
-
task_id = progress.add_task(
|
152
|
-
|
153
|
-
total=len(snapshots)
|
154
|
-
)
|
155
|
-
|
164
|
+
task_id = progress.add_task(f"Analyzing {len(snapshots)} snapshots...", total=len(snapshots))
|
165
|
+
|
156
166
|
for i, snapshot in enumerate(snapshots, 1):
|
157
167
|
snapshot_id = snapshot["DBSnapshotIdentifier"]
|
158
168
|
|
@@ -183,7 +193,7 @@ def get_rds_snapshot_details(output_file, old_days, include_cost, snapshot_type,
|
|
183
193
|
if allocated_storage > 0:
|
184
194
|
estimated_cost = estimate_snapshot_cost(allocated_storage, storage_type, age_days)
|
185
195
|
total_estimated_cost += estimated_cost
|
186
|
-
|
196
|
+
|
187
197
|
snapshot_data["EstimatedMonthlyCost"] = estimated_cost
|
188
198
|
snapshot_data["EstimatedAnnualCost"] = estimated_cost * 12
|
189
199
|
|
@@ -234,12 +244,12 @@ def get_rds_snapshot_details(output_file, old_days, include_cost, snapshot_type,
|
|
234
244
|
"projected_annual_savings": 0.0,
|
235
245
|
"old_manual_snapshots": 0,
|
236
246
|
"old_manual_monthly_savings": 0.0,
|
237
|
-
"old_manual_annual_savings": 0.0
|
247
|
+
"old_manual_annual_savings": 0.0,
|
238
248
|
}
|
239
|
-
|
249
|
+
|
240
250
|
# Create comprehensive summary table with Rich CLI
|
241
251
|
print_header("RDS Snapshot Analysis Summary")
|
242
|
-
|
252
|
+
|
243
253
|
summary_table = create_table(
|
244
254
|
title="RDS Snapshot Cost Analysis - JIRA FinOps-23",
|
245
255
|
columns=[
|
@@ -247,8 +257,8 @@ def get_rds_snapshot_details(output_file, old_days, include_cost, snapshot_type,
|
|
247
257
|
{"header": "Count", "style": "green bold"},
|
248
258
|
{"header": "Storage (GB)", "style": "yellow"},
|
249
259
|
{"header": "Monthly Cost", "style": "red"},
|
250
|
-
{"header": "Annual Cost", "style": "red bold"}
|
251
|
-
]
|
260
|
+
{"header": "Annual Cost", "style": "red bold"},
|
261
|
+
],
|
252
262
|
)
|
253
263
|
|
254
264
|
# Basic metrics
|
@@ -257,31 +267,31 @@ def get_rds_snapshot_details(output_file, old_days, include_cost, snapshot_type,
|
|
257
267
|
str(len(data)),
|
258
268
|
str(total_storage),
|
259
269
|
format_cost(total_estimated_cost) if (include_cost or calculate_savings or analyze) else "N/A",
|
260
|
-
format_cost(total_estimated_cost * 12) if (include_cost or calculate_savings or analyze) else "N/A"
|
270
|
+
format_cost(total_estimated_cost * 12) if (include_cost or calculate_savings or analyze) else "N/A",
|
261
271
|
)
|
262
|
-
|
272
|
+
|
263
273
|
summary_table.add_row(
|
264
274
|
"Manual Snapshots",
|
265
275
|
str(len(manual_snapshots)),
|
266
276
|
str(sum(s["AllocatedStorage"] for s in data if s["SnapshotType"].lower() == "manual")),
|
267
277
|
format_cost(savings_analysis["total_manual_monthly_cost"]) if (calculate_savings or analyze) else "N/A",
|
268
|
-
format_cost(savings_analysis["total_manual_annual_cost"]) if (calculate_savings or analyze) else "N/A"
|
278
|
+
format_cost(savings_analysis["total_manual_annual_cost"]) if (calculate_savings or analyze) else "N/A",
|
269
279
|
)
|
270
|
-
|
280
|
+
|
271
281
|
summary_table.add_row(
|
272
|
-
"Automated Snapshots",
|
282
|
+
"Automated Snapshots",
|
273
283
|
str(len(automated_snapshots)),
|
274
284
|
str(sum(s["AllocatedStorage"] for s in data if s["SnapshotType"].lower() == "automated")),
|
275
285
|
"Retention Policy",
|
276
|
-
"Retention Policy"
|
286
|
+
"Retention Policy",
|
277
287
|
)
|
278
|
-
|
288
|
+
|
279
289
|
summary_table.add_row(
|
280
290
|
f"Old Snapshots (>{old_days} days)",
|
281
291
|
str(len(old_snapshots)),
|
282
292
|
str(sum(s["AllocatedStorage"] for s in data if s["IsOld"])),
|
283
293
|
"Mixed Types",
|
284
|
-
"Mixed Types"
|
294
|
+
"Mixed Types",
|
285
295
|
)
|
286
296
|
|
287
297
|
# JIRA FinOps-23 specific analysis
|
@@ -289,20 +299,30 @@ def get_rds_snapshot_details(output_file, old_days, include_cost, snapshot_type,
|
|
289
299
|
summary_table.add_row(
|
290
300
|
f"🎯 Manual >{older_than}d (Cleanup Target)",
|
291
301
|
str(savings_analysis["old_manual_snapshots"]),
|
292
|
-
str(
|
302
|
+
str(
|
303
|
+
sum(
|
304
|
+
s["AllocatedStorage"]
|
305
|
+
for s in data
|
306
|
+
if s["AgeDays"] >= older_than and s["SnapshotType"].lower() == "manual"
|
307
|
+
)
|
308
|
+
),
|
293
309
|
format_cost(savings_analysis["old_manual_monthly_savings"]),
|
294
|
-
format_cost(savings_analysis["old_manual_annual_savings"])
|
310
|
+
format_cost(savings_analysis["old_manual_annual_savings"]),
|
295
311
|
)
|
296
312
|
|
297
313
|
console.print(summary_table)
|
298
314
|
|
299
315
|
# Cleanup recommendations with Rich CLI
|
300
316
|
cleanup_candidates = [s for s in data if s["IsOld"] and s["SnapshotType"].lower() == "manual"]
|
301
|
-
high_priority_candidates = [
|
302
|
-
|
317
|
+
high_priority_candidates = [
|
318
|
+
s for s in data if s["AgeDays"] >= older_than and s["SnapshotType"].lower() == "manual"
|
319
|
+
]
|
320
|
+
|
303
321
|
if high_priority_candidates:
|
304
|
-
print_warning(
|
305
|
-
|
322
|
+
print_warning(
|
323
|
+
f"🎯 JIRA FinOps-23: {len(high_priority_candidates)} high-priority manual snapshots (>{older_than} days):"
|
324
|
+
)
|
325
|
+
|
306
326
|
# Create detailed cleanup candidates table
|
307
327
|
cleanup_table = create_table(
|
308
328
|
title=f"High-Priority Manual Snapshots (>{older_than} days old)",
|
@@ -312,25 +332,25 @@ def get_rds_snapshot_details(output_file, old_days, include_cost, snapshot_type,
|
|
312
332
|
{"header": "Age (Days)", "style": "yellow"},
|
313
333
|
{"header": "Size (GB)", "style": "green"},
|
314
334
|
{"header": "Monthly Cost", "style": "red"},
|
315
|
-
{"header": "Engine", "style": "magenta"}
|
316
|
-
]
|
335
|
+
{"header": "Engine", "style": "magenta"},
|
336
|
+
],
|
317
337
|
)
|
318
|
-
|
338
|
+
|
319
339
|
for snap in high_priority_candidates[:15]: # Show first 15 for readability
|
320
340
|
cleanup_table.add_row(
|
321
|
-
snap[
|
322
|
-
snap[
|
323
|
-
str(snap[
|
324
|
-
str(snap[
|
325
|
-
format_cost(snap[
|
326
|
-
snap[
|
341
|
+
snap["DBSnapshotIdentifier"],
|
342
|
+
snap["DBInstanceIdentifier"],
|
343
|
+
str(snap["AgeDays"]),
|
344
|
+
str(snap["AllocatedStorage"]),
|
345
|
+
format_cost(snap["EstimatedMonthlyCost"]) if snap["EstimatedMonthlyCost"] > 0 else "N/A",
|
346
|
+
snap["Engine"],
|
327
347
|
)
|
328
|
-
|
348
|
+
|
329
349
|
console.print(cleanup_table)
|
330
|
-
|
350
|
+
|
331
351
|
if len(high_priority_candidates) > 15:
|
332
352
|
console.print(f"[dim]... and {len(high_priority_candidates) - 15} more high-priority snapshots[/dim]")
|
333
|
-
|
353
|
+
|
334
354
|
elif cleanup_candidates:
|
335
355
|
print_warning(f"⚠ {len(cleanup_candidates)} old manual snapshots for review (>{old_days} days)")
|
336
356
|
else:
|
@@ -341,15 +361,21 @@ def get_rds_snapshot_details(output_file, old_days, include_cost, snapshot_type,
|
|
341
361
|
target_min_annual = 5000.0
|
342
362
|
target_max_annual = 24000.0
|
343
363
|
actual_savings = savings_analysis["old_manual_annual_savings"]
|
344
|
-
|
364
|
+
|
345
365
|
if actual_savings >= target_min_annual:
|
346
366
|
if actual_savings <= target_max_annual:
|
347
|
-
print_success(
|
367
|
+
print_success(
|
368
|
+
f"🎯 Target Achievement: ${actual_savings:,.0f} within JIRA FinOps-23 range (${target_min_annual:,.0f}-${target_max_annual:,.0f})"
|
369
|
+
)
|
348
370
|
else:
|
349
|
-
print_success(
|
371
|
+
print_success(
|
372
|
+
f"🎯 Target Exceeded: ${actual_savings:,.0f} exceeds JIRA FinOps-23 maximum target (${target_max_annual:,.0f})"
|
373
|
+
)
|
350
374
|
else:
|
351
375
|
percentage = (actual_savings / target_min_annual) * 100
|
352
|
-
print_warning(
|
376
|
+
print_warning(
|
377
|
+
f"📊 Analysis: ${actual_savings:,.0f} is {percentage:.1f}% of JIRA FinOps-23 minimum target (${target_min_annual:,.0f})"
|
378
|
+
)
|
353
379
|
|
354
380
|
# Encryption status
|
355
381
|
encrypted_count = sum(1 for s in data if s["Encrypted"])
|