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
@@ -39,32 +39,34 @@ from ..common.performance_optimization_engine import get_optimization_engine
|
|
39
39
|
logger = configure_logger(__name__)
|
40
40
|
|
41
41
|
# Global Organizations cache to prevent duplicate API calls across all instances
|
42
|
-
_GLOBAL_ORGS_CACHE = {
|
43
|
-
|
44
|
-
'timestamp': None,
|
45
|
-
'ttl_minutes': 30
|
46
|
-
}
|
42
|
+
_GLOBAL_ORGS_CACHE = {"data": None, "timestamp": None, "ttl_minutes": 30}
|
43
|
+
|
47
44
|
|
48
45
|
def _get_global_organizations_cache():
|
49
46
|
"""Get cached Organizations data if valid (module-level cache)."""
|
50
|
-
if not _GLOBAL_ORGS_CACHE[
|
47
|
+
if not _GLOBAL_ORGS_CACHE["timestamp"]:
|
51
48
|
return None
|
52
|
-
|
53
|
-
cache_age_minutes = (datetime.now(timezone.utc) - _GLOBAL_ORGS_CACHE[
|
54
|
-
if cache_age_minutes < _GLOBAL_ORGS_CACHE[
|
49
|
+
|
50
|
+
cache_age_minutes = (datetime.now(timezone.utc) - _GLOBAL_ORGS_CACHE["timestamp"]).total_seconds() / 60
|
51
|
+
if cache_age_minutes < _GLOBAL_ORGS_CACHE["ttl_minutes"]:
|
55
52
|
console.print("[blue]🚀 Global performance optimization: Using cached Organizations data[/blue]")
|
56
|
-
return _GLOBAL_ORGS_CACHE[
|
53
|
+
return _GLOBAL_ORGS_CACHE["data"]
|
57
54
|
return None
|
58
55
|
|
56
|
+
|
59
57
|
def _set_global_organizations_cache(data):
|
60
58
|
"""Cache Organizations data globally (module-level cache)."""
|
61
|
-
_GLOBAL_ORGS_CACHE[
|
62
|
-
_GLOBAL_ORGS_CACHE[
|
63
|
-
accounts_count = len(data.get(
|
64
|
-
console.print(
|
59
|
+
_GLOBAL_ORGS_CACHE["data"] = data
|
60
|
+
_GLOBAL_ORGS_CACHE["timestamp"] = datetime.now(timezone.utc)
|
61
|
+
accounts_count = len(data.get("accounts", {}).get("discovered_accounts", [])) if data else 0
|
62
|
+
console.print(
|
63
|
+
f"[green]✅ Global Organizations cache: {accounts_count} accounts (TTL: {_GLOBAL_ORGS_CACHE['ttl_minutes']}min)[/green]"
|
64
|
+
)
|
65
|
+
|
65
66
|
|
66
67
|
# Universal AWS Environment Profile Support (Compatible with ANY AWS Setup)
|
67
68
|
import os
|
69
|
+
|
68
70
|
ENTERPRISE_PROFILES = {
|
69
71
|
"BILLING_PROFILE": os.getenv("BILLING_PROFILE", "default"), # Universal compatibility
|
70
72
|
"MANAGEMENT_PROFILE": os.getenv("MANAGEMENT_PROFILE", "default"), # Works with any profile
|
@@ -246,40 +248,44 @@ class EnhancedOrganizationsDiscovery:
|
|
246
248
|
"""Check if Organizations cache is still valid."""
|
247
249
|
if not self._organizations_cache_timestamp:
|
248
250
|
return False
|
249
|
-
|
251
|
+
|
250
252
|
from datetime import datetime, timedelta
|
253
|
+
|
251
254
|
cache_age_minutes = (datetime.now() - self._organizations_cache_timestamp).total_seconds() / 60
|
252
255
|
return cache_age_minutes < self._cache_ttl_minutes
|
253
256
|
|
254
257
|
async def discover_all_accounts(self) -> Dict:
|
255
258
|
"""
|
256
259
|
Cached wrapper for Organizations discovery to prevent duplicate API calls.
|
257
|
-
|
258
|
-
This method implements both global and instance-level caching to avoid the
|
259
|
-
performance penalty of duplicate Organizations API calls when multiple
|
260
|
+
|
261
|
+
This method implements both global and instance-level caching to avoid the
|
262
|
+
performance penalty of duplicate Organizations API calls when multiple
|
260
263
|
components need the same account data.
|
261
264
|
"""
|
262
265
|
# Check global cache first (shared across all instances)
|
263
266
|
global_cached_result = _get_global_organizations_cache()
|
264
267
|
if global_cached_result:
|
265
268
|
return global_cached_result
|
266
|
-
|
269
|
+
|
267
270
|
# Check instance cache
|
268
271
|
if self._is_organizations_cache_valid() and self._organizations_cache:
|
269
272
|
console.print("[blue]🚀 Performance optimization: Using cached Organizations data[/blue]")
|
270
273
|
return self._organizations_cache
|
271
|
-
|
274
|
+
|
272
275
|
# Cache miss - perform discovery
|
273
276
|
console.print("[cyan]🔍 Performing Organizations discovery (cache miss)[/cyan]")
|
274
277
|
results = await self.discover_organization_structure()
|
275
|
-
|
278
|
+
|
276
279
|
# Cache the results
|
277
|
-
if results and results.get(
|
280
|
+
if results and results.get("accounts"):
|
278
281
|
self._organizations_cache = results
|
279
282
|
from datetime import datetime
|
283
|
+
|
280
284
|
self._organizations_cache_timestamp = datetime.now()
|
281
|
-
console.print(
|
282
|
-
|
285
|
+
console.print(
|
286
|
+
f"[green]✅ Cached Organizations data: {len(results.get('accounts', {}).get('discovered_accounts', []))} accounts (TTL: {self._cache_ttl_minutes}min)[/green]"
|
287
|
+
)
|
288
|
+
|
283
289
|
return results
|
284
290
|
|
285
291
|
def initialize_sessions(self) -> Dict[str, str]:
|
@@ -417,13 +423,13 @@ class EnhancedOrganizationsDiscovery:
|
|
417
423
|
"""
|
418
424
|
# Get performance optimization engine
|
419
425
|
optimization_engine = get_optimization_engine(
|
420
|
-
max_workers=self.max_workers,
|
421
|
-
cache_ttl_minutes=30,
|
422
|
-
memory_limit_mb=2048
|
426
|
+
max_workers=self.max_workers, cache_ttl_minutes=30, memory_limit_mb=2048
|
423
427
|
)
|
424
|
-
|
428
|
+
|
425
429
|
# Use optimized discovery with performance monitoring
|
426
|
-
with optimization_engine.optimize_operation(
|
430
|
+
with optimization_engine.optimize_operation(
|
431
|
+
"organization_structure_discovery", self.performance_target_seconds
|
432
|
+
):
|
427
433
|
return await self._discover_organization_structure_optimized(optimization_engine)
|
428
434
|
|
429
435
|
async def _discover_organization_structure_optimized(self, optimization_engine) -> Dict:
|
@@ -436,7 +442,7 @@ class EnhancedOrganizationsDiscovery:
|
|
436
442
|
)
|
437
443
|
|
438
444
|
logger.info("🏢 Starting optimized organization structure discovery with SRE automation patterns")
|
439
|
-
|
445
|
+
|
440
446
|
# Check global cache first to prevent duplicate calls
|
441
447
|
cached_result = _get_global_organizations_cache()
|
442
448
|
if cached_result:
|
@@ -446,7 +452,7 @@ class EnhancedOrganizationsDiscovery:
|
|
446
452
|
self.discovery_metrics["end_time"] = self.current_benchmark.end_time
|
447
453
|
self.discovery_metrics["duration_seconds"] = self.current_benchmark.duration_seconds
|
448
454
|
return cached_result
|
449
|
-
|
455
|
+
|
450
456
|
self.discovery_metrics["start_time"] = self.current_benchmark.start_time
|
451
457
|
|
452
458
|
with Status("Initializing enterprise discovery...", console=console, spinner="dots"):
|
@@ -561,10 +567,10 @@ class EnhancedOrganizationsDiscovery:
|
|
561
567
|
"performance_benchmark": performance_benchmark_dict,
|
562
568
|
"timestamp": datetime.now().isoformat(),
|
563
569
|
}
|
564
|
-
|
570
|
+
|
565
571
|
# Cache the successful result to prevent duplicate calls
|
566
572
|
_set_global_organizations_cache(discovery_result)
|
567
|
-
|
573
|
+
|
568
574
|
return discovery_result
|
569
575
|
|
570
576
|
except Exception as e:
|
@@ -606,7 +612,7 @@ class EnhancedOrganizationsDiscovery:
|
|
606
612
|
async def _discover_accounts_optimized(self, optimization_engine) -> Dict:
|
607
613
|
"""
|
608
614
|
Optimized account discovery using performance optimization engine
|
609
|
-
|
615
|
+
|
610
616
|
Addresses: Organization Discovery Performance (52.3s -> <30s target)
|
611
617
|
Features:
|
612
618
|
- Intelligent caching with TTL management
|
@@ -615,46 +621,44 @@ class EnhancedOrganizationsDiscovery:
|
|
615
621
|
- Memory-efficient processing
|
616
622
|
"""
|
617
623
|
logger.info("📊 Discovering organization accounts with SRE optimization patterns")
|
618
|
-
|
624
|
+
|
619
625
|
# Use optimization engine for discovery
|
620
626
|
optimized_discover_accounts = optimization_engine.optimize_organization_discovery(
|
621
|
-
management_profile=self.management_profile,
|
622
|
-
use_parallel_processing=True,
|
623
|
-
batch_size=20
|
627
|
+
management_profile=self.management_profile, use_parallel_processing=True, batch_size=20
|
624
628
|
)
|
625
|
-
|
629
|
+
|
626
630
|
# Execute optimized discovery
|
627
631
|
try:
|
628
632
|
result = optimized_discover_accounts()
|
629
|
-
|
633
|
+
|
630
634
|
# Convert to expected format
|
631
|
-
accounts_data = result.get(
|
632
|
-
|
635
|
+
accounts_data = result.get("accounts", [])
|
636
|
+
|
633
637
|
# Create AWSAccount objects for compatibility
|
634
638
|
for account_data in accounts_data:
|
635
639
|
account = AWSAccount(
|
636
640
|
account_id=account_data["Id"],
|
637
|
-
name=account_data["Name"],
|
641
|
+
name=account_data["Name"],
|
638
642
|
email=account_data["Email"],
|
639
643
|
status=account_data["Status"],
|
640
644
|
joined_method=account_data["JoinedMethod"],
|
641
645
|
joined_timestamp=account_data.get("JoinedTimestamp"),
|
642
|
-
tags=account_data.get("Tags", {})
|
646
|
+
tags=account_data.get("Tags", {}),
|
643
647
|
)
|
644
648
|
self.accounts_cache[account.account_id] = account
|
645
|
-
|
649
|
+
|
646
650
|
# Update metrics
|
647
651
|
self.discovery_metrics["accounts_discovered"] = len(accounts_data)
|
648
|
-
|
652
|
+
|
649
653
|
# Enhanced account categorization
|
650
654
|
active_accounts = [a for a in accounts_data if a.get("Status") == "ACTIVE"]
|
651
|
-
suspended_accounts = [a for a in accounts_data if a.get("Status") == "SUSPENDED"]
|
655
|
+
suspended_accounts = [a for a in accounts_data if a.get("Status") == "SUSPENDED"]
|
652
656
|
closed_accounts = [a for a in accounts_data if a.get("Status") == "CLOSED"]
|
653
|
-
|
654
|
-
optimization_info = result.get(
|
657
|
+
|
658
|
+
optimization_info = result.get("optimizations_applied", [])
|
655
659
|
logger.info(f"✅ Optimized discovery: {len(accounts_data)} accounts ({len(active_accounts)} active)")
|
656
660
|
logger.info(f"🚀 Optimizations applied: {', '.join(optimization_info)}")
|
657
|
-
|
661
|
+
|
658
662
|
return {
|
659
663
|
"total_accounts": len(accounts_data),
|
660
664
|
"active_accounts": len(active_accounts),
|
@@ -665,7 +669,7 @@ class EnhancedOrganizationsDiscovery:
|
|
665
669
|
"profile_used": "management",
|
666
670
|
"optimizations_applied": optimization_info,
|
667
671
|
}
|
668
|
-
|
672
|
+
|
669
673
|
except Exception as e:
|
670
674
|
logger.error(f"Optimized account discovery failed: {e}")
|
671
675
|
# Fallback to original method
|
@@ -1138,7 +1142,7 @@ class EnhancedOrganizationsDiscovery:
|
|
1138
1142
|
medium_spend_accounts = {
|
1139
1143
|
k: round(v, 2)
|
1140
1144
|
for k, v in cost_by_account.items()
|
1141
|
-
if 100 <= v <= 1000 # $
|
1145
|
+
if 100 <= v <= 1000 # significant value range$1000/month
|
1142
1146
|
}
|
1143
1147
|
|
1144
1148
|
logger.info(f"✅ Cost validation complete: ${total_cost:.2f} across {len(cost_by_account)} accounts")
|
@@ -349,18 +349,18 @@ def display_account_tree(accounts_data: Dict[str, Dict]) -> None:
|
|
349
349
|
|
350
350
|
|
351
351
|
def display_results_rich(
|
352
|
-
results_list: List[Dict[str, Any]],
|
353
|
-
fdisplay_dict: Dict[str, Dict],
|
354
|
-
defaultAction: Any = None,
|
355
|
-
file_to_save: Optional[str] = None,
|
352
|
+
results_list: List[Dict[str, Any]],
|
353
|
+
fdisplay_dict: Dict[str, Dict],
|
354
|
+
defaultAction: Any = None,
|
355
|
+
file_to_save: Optional[str] = None,
|
356
356
|
subdisplay: bool = False,
|
357
|
-
title: str = "Inventory Results"
|
357
|
+
title: str = "Inventory Results",
|
358
358
|
) -> None:
|
359
359
|
"""
|
360
360
|
Rich CLI replacement for legacy display_results function.
|
361
|
-
|
361
|
+
|
362
362
|
Provides backwards-compatible interface while using Rich formatting.
|
363
|
-
|
363
|
+
|
364
364
|
Args:
|
365
365
|
results_list: List of dictionaries with resource data
|
366
366
|
fdisplay_dict: Display configuration dictionary with format:
|
@@ -369,7 +369,7 @@ def display_results_rich(
|
|
369
369
|
file_to_save: Optional filename to save results
|
370
370
|
subdisplay: Whether this is a sub-display (affects formatting)
|
371
371
|
title: Title for the table display
|
372
|
-
|
372
|
+
|
373
373
|
Example:
|
374
374
|
display_dict = {
|
375
375
|
'AccountId': {'DisplayOrder': 1, 'Heading': 'Account'},
|
@@ -379,59 +379,60 @@ def display_results_rich(
|
|
379
379
|
display_results_rich(instance_data, display_dict, title="EC2 Instances")
|
380
380
|
"""
|
381
381
|
from datetime import datetime
|
382
|
-
|
382
|
+
|
383
383
|
if not results_list:
|
384
384
|
print_info("ℹ️ No results to display")
|
385
385
|
return
|
386
|
-
|
386
|
+
|
387
387
|
# Sort display fields by DisplayOrder
|
388
|
-
sorted_fields = sorted(fdisplay_dict.items(), key=lambda x: x[1].get(
|
389
|
-
|
388
|
+
sorted_fields = sorted(fdisplay_dict.items(), key=lambda x: x[1].get("DisplayOrder", 999))
|
389
|
+
|
390
390
|
# Create Rich table
|
391
391
|
table = create_table(
|
392
392
|
title=f"📊 {title}",
|
393
|
-
caption=f"Found {len(results_list)} results • {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
393
|
+
caption=f"Found {len(results_list)} results • {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
|
394
394
|
)
|
395
|
-
|
395
|
+
|
396
396
|
# Add columns based on display dictionary
|
397
397
|
for field_name, field_config in sorted_fields:
|
398
|
-
heading = field_config.get(
|
398
|
+
heading = field_config.get("Heading", field_name)
|
399
399
|
table.add_column(heading, style="cyan" if "Id" in field_name else "white", no_wrap=True)
|
400
|
-
|
400
|
+
|
401
401
|
# Add rows
|
402
402
|
for result in results_list[:100]: # Limit to first 100 for performance
|
403
403
|
row_data = []
|
404
|
-
|
404
|
+
|
405
405
|
for field_name, field_config in sorted_fields:
|
406
406
|
# Apply condition filter if specified
|
407
|
-
condition = field_config.get(
|
407
|
+
condition = field_config.get("Condition", [])
|
408
408
|
if condition:
|
409
409
|
value = result.get(field_name, defaultAction)
|
410
410
|
if value not in condition:
|
411
411
|
continue
|
412
|
-
|
412
|
+
|
413
413
|
# Get field value with default fallback
|
414
414
|
value = result.get(field_name, defaultAction)
|
415
415
|
if value is None:
|
416
416
|
value = "N/A"
|
417
|
-
|
417
|
+
|
418
418
|
# Format value as string
|
419
419
|
row_data.append(str(value)[:50]) # Truncate long values
|
420
|
-
|
420
|
+
|
421
421
|
table.add_row(*row_data)
|
422
|
-
|
422
|
+
|
423
423
|
# Display the table
|
424
424
|
console.print(table)
|
425
|
-
|
425
|
+
|
426
426
|
# Show truncation notice if needed
|
427
427
|
if len(results_list) > 100:
|
428
428
|
console.print(f"[dim]Showing first 100 results. Total found: {len(results_list)}[/dim]")
|
429
|
-
|
429
|
+
|
430
430
|
# Save to file if requested
|
431
431
|
if file_to_save:
|
432
432
|
try:
|
433
433
|
import json
|
434
|
-
|
434
|
+
|
435
|
+
with open(file_to_save, "w") as f:
|
435
436
|
json.dump(results_list, f, indent=2, default=str)
|
436
437
|
print_success(f"💾 Results saved to: {file_to_save}")
|
437
438
|
except Exception as e:
|
@@ -441,7 +442,7 @@ def display_results_rich(
|
|
441
442
|
def display_progress_rich(current: int, total: int, description: str = "Processing") -> None:
|
442
443
|
"""
|
443
444
|
Rich CLI replacement for legacy progress indicators.
|
444
|
-
|
445
|
+
|
445
446
|
Args:
|
446
447
|
current: Current progress count
|
447
448
|
total: Total items to process
|
@@ -454,21 +455,21 @@ def display_progress_rich(current: int, total: int, description: str = "Processi
|
|
454
455
|
def print_colorized_rich(text: str, color: str = "white") -> None:
|
455
456
|
"""
|
456
457
|
Rich CLI replacement for colorama print statements.
|
457
|
-
|
458
|
+
|
458
459
|
Args:
|
459
460
|
text: Text to print
|
460
461
|
color: Color name (red, green, yellow, cyan, blue, white)
|
461
462
|
"""
|
462
463
|
color_map = {
|
463
464
|
"red": "red",
|
464
|
-
"green": "green",
|
465
|
+
"green": "green",
|
465
466
|
"yellow": "yellow",
|
466
467
|
"cyan": "cyan",
|
467
468
|
"blue": "blue",
|
468
469
|
"white": "white",
|
469
|
-
"magenta": "magenta"
|
470
|
+
"magenta": "magenta",
|
470
471
|
}
|
471
|
-
|
472
|
+
|
472
473
|
rich_color = color_map.get(color.lower(), "white")
|
473
474
|
console.print(f"[{rich_color}]{text}[/{rich_color}]")
|
474
475
|
|
@@ -476,13 +477,13 @@ def print_colorized_rich(text: str, color: str = "white") -> None:
|
|
476
477
|
# Export public functions
|
477
478
|
__all__ = [
|
478
479
|
"display_inventory_header",
|
479
|
-
"create_inventory_progress",
|
480
|
+
"create_inventory_progress",
|
480
481
|
"display_ec2_inventory_results",
|
481
482
|
"display_generic_inventory_results",
|
482
483
|
"display_inventory_error",
|
483
484
|
"display_multi_resource_summary",
|
484
485
|
"display_account_tree",
|
485
486
|
"display_results_rich",
|
486
|
-
"display_progress_rich",
|
487
|
+
"display_progress_rich",
|
487
488
|
"print_colorized_rich",
|
488
489
|
]
|