runbooks 1.1.3__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/WEIGHT_CONFIG_README.md +1 -1
- runbooks/cfat/assessment/compliance.py +8 -8
- runbooks/cfat/assessment/runner.py +1 -0
- runbooks/cfat/cloud_foundations_assessment.py +227 -239
- runbooks/cfat/models.py +6 -2
- runbooks/cfat/tests/__init__.py +6 -1
- runbooks/cli/__init__.py +13 -0
- runbooks/cli/commands/cfat.py +274 -0
- runbooks/cli/commands/finops.py +1164 -0
- runbooks/cli/commands/inventory.py +379 -0
- runbooks/cli/commands/operate.py +239 -0
- runbooks/cli/commands/security.py +248 -0
- runbooks/cli/commands/validation.py +825 -0
- runbooks/cli/commands/vpc.py +310 -0
- runbooks/cli/registry.py +107 -0
- runbooks/cloudops/__init__.py +23 -30
- runbooks/cloudops/base.py +96 -107
- runbooks/cloudops/cost_optimizer.py +549 -547
- runbooks/cloudops/infrastructure_optimizer.py +5 -4
- runbooks/cloudops/interfaces.py +226 -227
- 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 +179 -215
- 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 +341 -0
- runbooks/common/aws_utils.py +75 -80
- runbooks/common/business_logic.py +127 -105
- runbooks/common/cli_decorators.py +36 -60
- runbooks/common/comprehensive_cost_explorer_integration.py +456 -464
- runbooks/common/cross_account_manager.py +198 -205
- runbooks/common/date_utils.py +27 -39
- runbooks/common/decorators.py +235 -0
- 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 +478 -495
- 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 +176 -194
- runbooks/common/patterns.py +204 -0
- runbooks/common/performance_monitoring.py +67 -71
- runbooks/common/performance_optimization_engine.py +283 -274
- runbooks/common/profile_utils.py +248 -39
- runbooks/common/rich_utils.py +643 -92
- 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 +29 -33
- 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 +488 -622
- 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 +40 -37
- runbooks/finops/enhanced_trend_visualization.py +3 -2
- runbooks/finops/enterprise_wrappers.py +230 -292
- 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 +338 -175
- runbooks/finops/mcp_validator.py +1952 -0
- runbooks/finops/nat_gateway_optimizer.py +1513 -482
- 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 +25 -29
- runbooks/finops/rds_snapshot_optimizer.py +367 -411
- runbooks/finops/reservation_optimizer.py +427 -363
- runbooks/finops/scenario_cli_integration.py +77 -78
- runbooks/finops/scenarios.py +1278 -439
- runbooks/finops/schemas.py +218 -182
- runbooks/finops/snapshot_manager.py +2289 -0
- runbooks/finops/tests/test_finops_dashboard.py +3 -3
- runbooks/finops/tests/test_reference_images_validation.py +2 -2
- runbooks/finops/tests/test_single_account_features.py +17 -17
- runbooks/finops/tests/validate_test_suite.py +1 -1
- runbooks/finops/types.py +3 -3
- runbooks/finops/validation_framework.py +263 -269
- runbooks/finops/vpc_cleanup_exporter.py +191 -146
- runbooks/finops/vpc_cleanup_optimizer.py +593 -575
- runbooks/finops/workspaces_analyzer.py +171 -182
- runbooks/hitl/enhanced_workflow_engine.py +1 -1
- runbooks/integration/__init__.py +89 -0
- runbooks/integration/mcp_integration.py +1920 -0
- runbooks/inventory/CLAUDE.md +816 -0
- runbooks/inventory/README.md +3 -3
- runbooks/inventory/Tests/common_test_data.py +30 -30
- runbooks/inventory/__init__.py +2 -2
- runbooks/inventory/cloud_foundations_integration.py +144 -149
- runbooks/inventory/collectors/aws_comprehensive.py +28 -11
- runbooks/inventory/collectors/aws_networking.py +111 -101
- runbooks/inventory/collectors/base.py +4 -0
- runbooks/inventory/core/collector.py +495 -313
- runbooks/inventory/discovery.md +2 -2
- runbooks/inventory/drift_detection_cli.py +69 -96
- runbooks/inventory/find_ec2_security_groups.py +1 -1
- 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 +56 -52
- runbooks/inventory/rich_inventory_display.py +33 -32
- runbooks/inventory/unified_validation_engine.py +278 -251
- runbooks/inventory/vpc_analyzer.py +733 -696
- runbooks/inventory/vpc_architecture_validator.py +293 -348
- runbooks/inventory/vpc_dependency_analyzer.py +382 -378
- runbooks/inventory/vpc_flow_analyzer.py +3 -3
- runbooks/main.py +152 -9147
- 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/metrics/dora_metrics_engine.py +2 -2
- 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/mcp_integration.py +1 -1
- runbooks/operate/networking_cost_heatmap.py +33 -10
- runbooks/operate/privatelink_operations.py +1 -1
- runbooks/operate/rds_operations.py +223 -254
- runbooks/operate/s3_operations.py +107 -118
- runbooks/operate/vpc_endpoints.py +1 -1
- runbooks/operate/vpc_operations.py +648 -618
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/commons.py +10 -7
- runbooks/remediation/commvault_ec2_analysis.py +71 -67
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
- runbooks/remediation/multi_account.py +24 -21
- runbooks/remediation/rds_snapshot_list.py +91 -65
- runbooks/remediation/remediation_cli.py +92 -146
- runbooks/remediation/universal_account_discovery.py +83 -79
- runbooks/remediation/workspaces_list.py +49 -44
- 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/integration_test_enterprise_security.py +5 -3
- runbooks/security/multi_account_security_controls.py +959 -1210
- runbooks/security/real_time_security_monitor.py +422 -444
- runbooks/security/run_script.py +1 -1
- 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/mcp_reliability_engine.py +6 -6
- 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 +51 -48
- runbooks/validation/__init__.py +6 -6
- runbooks/validation/cli.py +9 -3
- runbooks/validation/comprehensive_2way_validator.py +754 -708
- 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 +190 -162
- runbooks/vpc/mcp_no_eni_validator.py +681 -640
- 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 +1302 -1129
- runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
- runbooks-1.1.5.dist-info/METADATA +328 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/RECORD +233 -200
- 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 -956
- 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.3.dist-info/METADATA +0 -799
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
@@ -737,16 +737,13 @@ class S3Operations(BaseOperation):
|
|
737
737
|
return [result]
|
738
738
|
|
739
739
|
def find_buckets_without_lifecycle(
|
740
|
-
self,
|
741
|
-
context: OperationContext,
|
742
|
-
region: Optional[str] = None,
|
743
|
-
bucket_names: Optional[List[str]] = None
|
740
|
+
self, context: OperationContext, region: Optional[str] = None, bucket_names: Optional[List[str]] = None
|
744
741
|
) -> List[OperationResult]:
|
745
742
|
"""
|
746
743
|
Find S3 buckets without lifecycle policies.
|
747
|
-
|
744
|
+
|
748
745
|
Enhanced from unSkript notebook: AWS_Add_Lifecycle_Policy_To_S3_Buckets.ipynb
|
749
|
-
Identifies buckets that do not have any configured lifecycle rules for
|
746
|
+
Identifies buckets that do not have any configured lifecycle rules for
|
750
747
|
managing object lifecycle, valuable for optimizing storage costs.
|
751
748
|
|
752
749
|
Args:
|
@@ -757,15 +754,13 @@ class S3Operations(BaseOperation):
|
|
757
754
|
Returns:
|
758
755
|
List of operation results with buckets without lifecycle policies
|
759
756
|
"""
|
760
|
-
result = self.create_operation_result(
|
761
|
-
context, "find_buckets_without_lifecycle", "s3:bucket", "lifecycle-audit"
|
762
|
-
)
|
757
|
+
result = self.create_operation_result(context, "find_buckets_without_lifecycle", "s3:bucket", "lifecycle-audit")
|
763
758
|
|
764
759
|
try:
|
765
760
|
console.print(f"[bold blue]🔍 Scanning for S3 buckets without lifecycle policies...[/bold blue]")
|
766
|
-
|
761
|
+
|
767
762
|
buckets_without_policy = []
|
768
|
-
|
763
|
+
|
769
764
|
# Get list of regions to search
|
770
765
|
search_regions = []
|
771
766
|
if region:
|
@@ -786,7 +781,7 @@ class S3Operations(BaseOperation):
|
|
786
781
|
for reg in search_regions:
|
787
782
|
try:
|
788
783
|
s3_client = self.get_client("s3", reg)
|
789
|
-
|
784
|
+
|
790
785
|
# Get buckets to check
|
791
786
|
if bucket_names:
|
792
787
|
# Check specific buckets
|
@@ -801,17 +796,19 @@ class S3Operations(BaseOperation):
|
|
801
796
|
try:
|
802
797
|
# Get bucket location to ensure it's in the current region
|
803
798
|
try:
|
804
|
-
bucket_location = self.execute_aws_call(
|
799
|
+
bucket_location = self.execute_aws_call(
|
800
|
+
s3_client, "get_bucket_location", Bucket=bucket_name
|
801
|
+
)
|
805
802
|
bucket_region = bucket_location.get("LocationConstraint")
|
806
|
-
|
803
|
+
|
807
804
|
# us-east-1 returns None for LocationConstraint
|
808
805
|
if bucket_region is None:
|
809
806
|
bucket_region = "us-east-1"
|
810
|
-
|
807
|
+
|
811
808
|
# Skip if bucket is not in current region (when checking all regions)
|
812
809
|
if not bucket_names and bucket_region != reg:
|
813
810
|
continue
|
814
|
-
|
811
|
+
|
815
812
|
except ClientError as e:
|
816
813
|
if e.response["Error"]["Code"] in ["NoSuchBucket", "AccessDenied"]:
|
817
814
|
continue
|
@@ -822,25 +819,29 @@ class S3Operations(BaseOperation):
|
|
822
819
|
lifecycle_response = self.execute_aws_call(
|
823
820
|
s3_client, "get_bucket_lifecycle_configuration", Bucket=bucket_name
|
824
821
|
)
|
825
|
-
|
822
|
+
|
826
823
|
# If we get here, bucket has lifecycle rules
|
827
824
|
rules = lifecycle_response.get("Rules", [])
|
828
825
|
if not rules:
|
829
826
|
# Empty rules list means no active lifecycle
|
830
|
-
buckets_without_policy.append(
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
827
|
+
buckets_without_policy.append(
|
828
|
+
{
|
829
|
+
"bucket_name": bucket_name,
|
830
|
+
"region": bucket_region,
|
831
|
+
"issue": "Empty lifecycle configuration",
|
832
|
+
}
|
833
|
+
)
|
834
|
+
|
836
835
|
except ClientError as e:
|
837
836
|
if e.response["Error"]["Code"] == "NoSuchLifecycleConfiguration":
|
838
837
|
# No lifecycle configuration found
|
839
|
-
buckets_without_policy.append(
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
838
|
+
buckets_without_policy.append(
|
839
|
+
{
|
840
|
+
"bucket_name": bucket_name,
|
841
|
+
"region": bucket_region,
|
842
|
+
"issue": "No lifecycle configuration",
|
843
|
+
}
|
844
|
+
)
|
844
845
|
else:
|
845
846
|
logger.warning(f"Could not check lifecycle for bucket {bucket_name}: {e}")
|
846
847
|
|
@@ -854,27 +855,26 @@ class S3Operations(BaseOperation):
|
|
854
855
|
|
855
856
|
# Format results with Rich console output
|
856
857
|
if buckets_without_policy:
|
857
|
-
console.print(
|
858
|
-
|
858
|
+
console.print(
|
859
|
+
f"[bold yellow]⚠️ Found {len(buckets_without_policy)} bucket(s) without lifecycle policies:[/bold yellow]"
|
860
|
+
)
|
861
|
+
|
859
862
|
from rich.table import Table
|
863
|
+
|
860
864
|
table = Table(show_header=True, header_style="bold magenta")
|
861
865
|
table.add_column("Bucket Name", style="cyan")
|
862
866
|
table.add_column("Region", style="green")
|
863
867
|
table.add_column("Issue", style="yellow")
|
864
|
-
|
868
|
+
|
865
869
|
for bucket_info in buckets_without_policy:
|
866
|
-
table.add_row(
|
867
|
-
|
868
|
-
bucket_info["region"],
|
869
|
-
bucket_info["issue"]
|
870
|
-
)
|
871
|
-
|
870
|
+
table.add_row(bucket_info["bucket_name"], bucket_info["region"], bucket_info["issue"])
|
871
|
+
|
872
872
|
console.print(table)
|
873
|
-
|
873
|
+
|
874
874
|
result.response_data = {
|
875
875
|
"buckets_without_lifecycle": buckets_without_policy,
|
876
876
|
"total_count": len(buckets_without_policy),
|
877
|
-
"regions_scanned": search_regions
|
877
|
+
"regions_scanned": search_regions,
|
878
878
|
}
|
879
879
|
result.mark_completed(OperationStatus.SUCCESS)
|
880
880
|
else:
|
@@ -883,7 +883,7 @@ class S3Operations(BaseOperation):
|
|
883
883
|
"buckets_without_lifecycle": [],
|
884
884
|
"total_count": 0,
|
885
885
|
"regions_scanned": search_regions,
|
886
|
-
"message": "All buckets have lifecycle policies"
|
886
|
+
"message": "All buckets have lifecycle policies",
|
887
887
|
}
|
888
888
|
result.mark_completed(OperationStatus.SUCCESS)
|
889
889
|
|
@@ -906,7 +906,7 @@ class S3Operations(BaseOperation):
|
|
906
906
|
) -> List[OperationResult]:
|
907
907
|
"""
|
908
908
|
Add lifecycle policies to multiple S3 buckets in bulk.
|
909
|
-
|
909
|
+
|
910
910
|
Enhanced from unSkript notebook: AWS_Add_Lifecycle_Policy_To_S3_Buckets.ipynb
|
911
911
|
Applies optimized lifecycle configuration for cost management.
|
912
912
|
|
@@ -923,32 +923,30 @@ class S3Operations(BaseOperation):
|
|
923
923
|
List of operation results for each bucket processed
|
924
924
|
"""
|
925
925
|
results = []
|
926
|
-
|
926
|
+
|
927
927
|
console.print(f"[bold blue]📋 Adding lifecycle policies to {len(bucket_list)} bucket(s)...[/bold blue]")
|
928
|
-
|
928
|
+
|
929
929
|
if context.dry_run:
|
930
930
|
console.print("[yellow]🧪 DRY-RUN MODE: No actual changes will be made[/yellow]")
|
931
931
|
|
932
932
|
for i, bucket_info in enumerate(bucket_list, 1):
|
933
933
|
bucket_name = bucket_info.get("bucket_name")
|
934
934
|
bucket_region = bucket_info.get("region")
|
935
|
-
|
935
|
+
|
936
936
|
if not bucket_name or not bucket_region:
|
937
937
|
logger.error(f"Invalid bucket info: {bucket_info}")
|
938
938
|
continue
|
939
|
-
|
939
|
+
|
940
940
|
console.print(f"[cyan]({i}/{len(bucket_list)}) Processing bucket: {bucket_name}[/cyan]")
|
941
941
|
|
942
|
-
result = self.create_operation_result(
|
943
|
-
context, "add_lifecycle_policy", "s3:bucket", bucket_name
|
944
|
-
)
|
942
|
+
result = self.create_operation_result(context, "add_lifecycle_policy", "s3:bucket", bucket_name)
|
945
943
|
|
946
944
|
try:
|
947
945
|
s3_client = self.get_client("s3", bucket_region)
|
948
946
|
|
949
947
|
# Build lifecycle configuration
|
950
948
|
lifecycle_rules = []
|
951
|
-
|
949
|
+
|
952
950
|
# Main lifecycle rule
|
953
951
|
rule = {
|
954
952
|
"ID": f"lifecycle-rule-{int(datetime.now().timestamp())}",
|
@@ -964,17 +962,11 @@ class S3Operations(BaseOperation):
|
|
964
962
|
# Add storage class transitions
|
965
963
|
transitions = []
|
966
964
|
if transition_ia_days and transition_ia_days < expiration_days:
|
967
|
-
transitions.append({
|
968
|
-
|
969
|
-
"StorageClass": "STANDARD_IA"
|
970
|
-
})
|
971
|
-
|
965
|
+
transitions.append({"Days": transition_ia_days, "StorageClass": "STANDARD_IA"})
|
966
|
+
|
972
967
|
if transition_glacier_days and transition_glacier_days < expiration_days:
|
973
|
-
transitions.append({
|
974
|
-
|
975
|
-
"StorageClass": "GLACIER"
|
976
|
-
})
|
977
|
-
|
968
|
+
transitions.append({"Days": transition_glacier_days, "StorageClass": "GLACIER"})
|
969
|
+
|
978
970
|
if transitions:
|
979
971
|
rule["Transitions"] = transitions
|
980
972
|
|
@@ -988,7 +980,7 @@ class S3Operations(BaseOperation):
|
|
988
980
|
"bucket_name": bucket_name,
|
989
981
|
"region": bucket_region,
|
990
982
|
"lifecycle_config": lifecycle_config,
|
991
|
-
"dry_run": True
|
983
|
+
"dry_run": True,
|
992
984
|
}
|
993
985
|
result.mark_completed(OperationStatus.DRY_RUN)
|
994
986
|
else:
|
@@ -997,16 +989,16 @@ class S3Operations(BaseOperation):
|
|
997
989
|
s3_client,
|
998
990
|
"put_bucket_lifecycle_configuration",
|
999
991
|
Bucket=bucket_name,
|
1000
|
-
LifecycleConfiguration=lifecycle_config
|
992
|
+
LifecycleConfiguration=lifecycle_config,
|
1001
993
|
)
|
1002
994
|
|
1003
995
|
console.print(f"[green] ✅ Successfully applied lifecycle policy to {bucket_name}[/green]")
|
1004
|
-
|
996
|
+
|
1005
997
|
result.response_data = {
|
1006
998
|
"bucket_name": bucket_name,
|
1007
999
|
"region": bucket_region,
|
1008
1000
|
"lifecycle_config": lifecycle_config,
|
1009
|
-
"aws_response": response
|
1001
|
+
"aws_response": response,
|
1010
1002
|
}
|
1011
1003
|
result.mark_completed(OperationStatus.SUCCESS)
|
1012
1004
|
|
@@ -1026,7 +1018,7 @@ class S3Operations(BaseOperation):
|
|
1026
1018
|
# Summary report
|
1027
1019
|
successful = len([r for r in results if r.success])
|
1028
1020
|
failed = len(results) - successful
|
1029
|
-
|
1021
|
+
|
1030
1022
|
if context.dry_run:
|
1031
1023
|
console.print(f"[yellow]🧪 DRY-RUN SUMMARY: Would process {len(results)} bucket(s)[/yellow]")
|
1032
1024
|
else:
|
@@ -1037,9 +1029,7 @@ class S3Operations(BaseOperation):
|
|
1037
1029
|
|
1038
1030
|
return results
|
1039
1031
|
|
1040
|
-
def get_bucket_lifecycle(
|
1041
|
-
self, context: OperationContext, bucket_name: str
|
1042
|
-
) -> List[OperationResult]:
|
1032
|
+
def get_bucket_lifecycle(self, context: OperationContext, bucket_name: str) -> List[OperationResult]:
|
1043
1033
|
"""
|
1044
1034
|
Get current lifecycle configuration for an S3 bucket.
|
1045
1035
|
|
@@ -1051,56 +1041,49 @@ class S3Operations(BaseOperation):
|
|
1051
1041
|
List of operation results with current lifecycle configuration
|
1052
1042
|
"""
|
1053
1043
|
s3_client = self.get_client("s3")
|
1054
|
-
|
1055
|
-
result = self.create_operation_result(
|
1056
|
-
context, "get_bucket_lifecycle", "s3:bucket", bucket_name
|
1057
|
-
)
|
1044
|
+
|
1045
|
+
result = self.create_operation_result(context, "get_bucket_lifecycle", "s3:bucket", bucket_name)
|
1058
1046
|
|
1059
1047
|
try:
|
1060
1048
|
console.print(f"[blue]🔍 Checking lifecycle configuration for bucket: {bucket_name}[/blue]")
|
1061
|
-
|
1049
|
+
|
1062
1050
|
try:
|
1063
|
-
response = self.execute_aws_call(
|
1064
|
-
|
1065
|
-
)
|
1066
|
-
|
1051
|
+
response = self.execute_aws_call(s3_client, "get_bucket_lifecycle_configuration", Bucket=bucket_name)
|
1052
|
+
|
1067
1053
|
rules = response.get("Rules", [])
|
1068
1054
|
console.print(f"[green]✅ Found {len(rules)} lifecycle rule(s) for bucket {bucket_name}[/green]")
|
1069
|
-
|
1055
|
+
|
1070
1056
|
# Display rules in a formatted table
|
1071
1057
|
if rules:
|
1072
1058
|
from rich.table import Table
|
1059
|
+
|
1073
1060
|
table = Table(show_header=True, header_style="bold magenta")
|
1074
1061
|
table.add_column("Rule ID", style="cyan")
|
1075
1062
|
table.add_column("Status", style="green")
|
1076
1063
|
table.add_column("Prefix", style="yellow")
|
1077
1064
|
table.add_column("Expiration", style="red")
|
1078
|
-
|
1065
|
+
|
1079
1066
|
for rule in rules:
|
1080
1067
|
rule_id = rule.get("ID", "N/A")
|
1081
1068
|
status = rule.get("Status", "N/A")
|
1082
|
-
|
1069
|
+
|
1083
1070
|
# Handle different filter formats
|
1084
1071
|
filter_info = rule.get("Filter", {})
|
1085
1072
|
if isinstance(filter_info, dict):
|
1086
1073
|
prefix = filter_info.get("Prefix", "")
|
1087
1074
|
else:
|
1088
1075
|
prefix = ""
|
1089
|
-
|
1076
|
+
|
1090
1077
|
expiration = rule.get("Expiration", {})
|
1091
1078
|
exp_days = expiration.get("Days", "N/A")
|
1092
|
-
|
1079
|
+
|
1093
1080
|
table.add_row(rule_id, status, prefix or "All objects", str(exp_days))
|
1094
|
-
|
1081
|
+
|
1095
1082
|
console.print(table)
|
1096
|
-
|
1097
|
-
result.response_data = {
|
1098
|
-
"bucket_name": bucket_name,
|
1099
|
-
"lifecycle_rules": rules,
|
1100
|
-
"rules_count": len(rules)
|
1101
|
-
}
|
1083
|
+
|
1084
|
+
result.response_data = {"bucket_name": bucket_name, "lifecycle_rules": rules, "rules_count": len(rules)}
|
1102
1085
|
result.mark_completed(OperationStatus.SUCCESS)
|
1103
|
-
|
1086
|
+
|
1104
1087
|
except ClientError as e:
|
1105
1088
|
if e.response["Error"]["Code"] == "NoSuchLifecycleConfiguration":
|
1106
1089
|
console.print(f"[yellow]⚠️ No lifecycle configuration found for bucket {bucket_name}[/yellow]")
|
@@ -1108,7 +1091,7 @@ class S3Operations(BaseOperation):
|
|
1108
1091
|
"bucket_name": bucket_name,
|
1109
1092
|
"lifecycle_rules": [],
|
1110
1093
|
"rules_count": 0,
|
1111
|
-
"message": "No lifecycle configuration"
|
1094
|
+
"message": "No lifecycle configuration",
|
1112
1095
|
}
|
1113
1096
|
result.mark_completed(OperationStatus.SUCCESS)
|
1114
1097
|
else:
|
@@ -1126,9 +1109,9 @@ class S3Operations(BaseOperation):
|
|
1126
1109
|
) -> List[OperationResult]:
|
1127
1110
|
"""
|
1128
1111
|
Analyze lifecycle compliance across S3 buckets and provide cost optimization recommendations.
|
1129
|
-
|
1112
|
+
|
1130
1113
|
Args:
|
1131
|
-
context: Operation context
|
1114
|
+
context: Operation context
|
1132
1115
|
region: AWS region to analyze (if None, analyzes all regions)
|
1133
1116
|
|
1134
1117
|
Returns:
|
@@ -1139,41 +1122,47 @@ class S3Operations(BaseOperation):
|
|
1139
1122
|
)
|
1140
1123
|
|
1141
1124
|
try:
|
1142
|
-
console.print(
|
1143
|
-
|
1125
|
+
console.print(
|
1126
|
+
"[bold blue]📊 Analyzing S3 lifecycle compliance and cost optimization opportunities...[/bold blue]"
|
1127
|
+
)
|
1128
|
+
|
1144
1129
|
# Find buckets without lifecycle policies
|
1145
1130
|
find_results = self.find_buckets_without_lifecycle(context, region=region)
|
1146
|
-
|
1131
|
+
|
1147
1132
|
if not find_results or not find_results[0].success:
|
1148
1133
|
result.mark_completed(OperationStatus.FAILED, "Failed to analyze buckets")
|
1149
1134
|
return [result]
|
1150
|
-
|
1135
|
+
|
1151
1136
|
buckets_data = find_results[0].response_data
|
1152
1137
|
non_compliant_buckets = buckets_data.get("buckets_without_lifecycle", [])
|
1153
1138
|
total_buckets_scanned = buckets_data.get("total_count", 0)
|
1154
|
-
|
1139
|
+
|
1155
1140
|
# Calculate compliance metrics
|
1156
1141
|
s3_client = self.get_client("s3")
|
1157
|
-
|
1142
|
+
|
1158
1143
|
# Get total bucket count for compliance percentage
|
1159
1144
|
list_response = self.execute_aws_call(s3_client, "list_buckets")
|
1160
1145
|
total_buckets = len(list_response.get("Buckets", []))
|
1161
|
-
|
1162
|
-
compliance_percentage = (
|
1163
|
-
|
1146
|
+
|
1147
|
+
compliance_percentage = (
|
1148
|
+
((total_buckets - len(non_compliant_buckets)) / total_buckets * 100) if total_buckets > 0 else 100
|
1149
|
+
)
|
1150
|
+
|
1164
1151
|
# Generate recommendations
|
1165
1152
|
recommendations = []
|
1166
1153
|
potential_savings = 0
|
1167
|
-
|
1154
|
+
|
1168
1155
|
if non_compliant_buckets:
|
1169
|
-
recommendations.extend(
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1156
|
+
recommendations.extend(
|
1157
|
+
[
|
1158
|
+
"🎯 Implement lifecycle policies to automatically transition objects to cheaper storage classes",
|
1159
|
+
"💰 Configure automatic deletion of old object versions to reduce storage costs",
|
1160
|
+
"📈 Set up transitions: Standard → IA (30 days) → Glacier (90 days) → Deep Archive (365 days)",
|
1161
|
+
"🔧 Use prefixes to apply different policies to different object types",
|
1162
|
+
"📊 Monitor lifecycle rule effectiveness with CloudWatch metrics",
|
1163
|
+
]
|
1164
|
+
)
|
1165
|
+
|
1177
1166
|
# Estimate potential savings (rough calculation)
|
1178
1167
|
estimated_savings_per_bucket = 25 # Estimated 25% savings per bucket
|
1179
1168
|
potential_savings = len(non_compliant_buckets) * estimated_savings_per_bucket
|
@@ -1181,26 +1170,26 @@ class S3Operations(BaseOperation):
|
|
1181
1170
|
# Create summary report
|
1182
1171
|
from rich.panel import Panel
|
1183
1172
|
from rich.table import Table
|
1184
|
-
|
1173
|
+
|
1185
1174
|
# Compliance summary table
|
1186
1175
|
summary_table = Table(show_header=True, header_style="bold magenta")
|
1187
1176
|
summary_table.add_column("Metric", style="cyan")
|
1188
1177
|
summary_table.add_column("Value", style="green")
|
1189
|
-
|
1178
|
+
|
1190
1179
|
summary_table.add_row("Total Buckets", str(total_buckets))
|
1191
1180
|
summary_table.add_row("Compliant Buckets", str(total_buckets - len(non_compliant_buckets)))
|
1192
1181
|
summary_table.add_row("Non-Compliant Buckets", str(len(non_compliant_buckets)))
|
1193
1182
|
summary_table.add_row("Compliance Percentage", f"{compliance_percentage:.1f}%")
|
1194
1183
|
summary_table.add_row("Potential Cost Savings", f"~{potential_savings}%")
|
1195
|
-
|
1184
|
+
|
1196
1185
|
console.print(Panel(summary_table, title="S3 Lifecycle Compliance Report", border_style="blue"))
|
1197
|
-
|
1186
|
+
|
1198
1187
|
# Display recommendations if any
|
1199
1188
|
if recommendations:
|
1200
1189
|
console.print("\n[bold yellow]💡 Cost Optimization Recommendations:[/bold yellow]")
|
1201
1190
|
for i, rec in enumerate(recommendations, 1):
|
1202
1191
|
console.print(f" {i}. {rec}")
|
1203
|
-
|
1192
|
+
|
1204
1193
|
# Compliance status color coding
|
1205
1194
|
if compliance_percentage >= 90:
|
1206
1195
|
compliance_status = "[bold green]EXCELLENT[/bold green]"
|
@@ -1210,9 +1199,9 @@ class S3Operations(BaseOperation):
|
|
1210
1199
|
compliance_status = "[bold orange]NEEDS IMPROVEMENT[/bold orange]"
|
1211
1200
|
else:
|
1212
1201
|
compliance_status = "[bold red]POOR[/bold red]"
|
1213
|
-
|
1202
|
+
|
1214
1203
|
console.print(f"\n[bold blue]Overall Compliance Status: {compliance_status}[/bold blue]")
|
1215
|
-
|
1204
|
+
|
1216
1205
|
result.response_data = {
|
1217
1206
|
"compliance_percentage": compliance_percentage,
|
1218
1207
|
"total_buckets": total_buckets,
|
@@ -1221,7 +1210,7 @@ class S3Operations(BaseOperation):
|
|
1221
1210
|
"non_compliant_details": non_compliant_buckets,
|
1222
1211
|
"recommendations": recommendations,
|
1223
1212
|
"potential_savings_percentage": potential_savings,
|
1224
|
-
"compliance_status": compliance_status.replace("[bold ", "").replace("[/bold ", "").replace("]", "")
|
1213
|
+
"compliance_status": compliance_status.replace("[bold ", "").replace("[/bold ", "").replace("]", ""),
|
1225
1214
|
}
|
1226
1215
|
result.mark_completed(OperationStatus.SUCCESS)
|
1227
1216
|
|