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
@@ -24,47 +24,53 @@ from typing import Dict, List, Optional, Set, Tuple
|
|
24
24
|
|
25
25
|
import boto3
|
26
26
|
from botocore.exceptions import ClientError, NoCredentialsError
|
27
|
-
from rich.console import Console
|
28
27
|
from rich.panel import Panel
|
29
|
-
from rich.progress import BarColumn,
|
28
|
+
from rich.progress import BarColumn, SpinnerColumn, TextColumn, TimeElapsedColumn
|
30
29
|
from rich.status import Status
|
31
30
|
from rich.table import Table
|
32
31
|
|
33
|
-
#
|
34
|
-
|
32
|
+
# Test Mode Support: Use centralized rich_utils for console and Progress (test-aware)
|
33
|
+
# Issue: Rich Console/Progress write to StringIO buffer that Click CliRunner closes, causing ValueError
|
34
|
+
# Solution: rich_utils provides MockConsole/MockProgress in test mode, Rich components in production
|
35
|
+
import os
|
36
|
+
import sys
|
37
|
+
import re
|
35
38
|
|
36
39
|
from ..utils.logger import configure_logger
|
37
40
|
from ..common.performance_optimization_engine import get_optimization_engine
|
41
|
+
from ..common.rich_utils import console, Progress
|
38
42
|
|
39
43
|
logger = configure_logger(__name__)
|
40
44
|
|
41
45
|
# Global Organizations cache to prevent duplicate API calls across all instances
|
42
|
-
_GLOBAL_ORGS_CACHE = {
|
43
|
-
|
44
|
-
'timestamp': None,
|
45
|
-
'ttl_minutes': 30
|
46
|
-
}
|
46
|
+
_GLOBAL_ORGS_CACHE = {"data": None, "timestamp": None, "ttl_minutes": 30}
|
47
|
+
|
47
48
|
|
48
49
|
def _get_global_organizations_cache():
|
49
50
|
"""Get cached Organizations data if valid (module-level cache)."""
|
50
|
-
if not _GLOBAL_ORGS_CACHE[
|
51
|
+
if not _GLOBAL_ORGS_CACHE["timestamp"]:
|
51
52
|
return None
|
52
|
-
|
53
|
-
cache_age_minutes = (datetime.now(timezone.utc) - _GLOBAL_ORGS_CACHE[
|
54
|
-
if cache_age_minutes < _GLOBAL_ORGS_CACHE[
|
53
|
+
|
54
|
+
cache_age_minutes = (datetime.now(timezone.utc) - _GLOBAL_ORGS_CACHE["timestamp"]).total_seconds() / 60
|
55
|
+
if cache_age_minutes < _GLOBAL_ORGS_CACHE["ttl_minutes"]:
|
55
56
|
console.print("[blue]🚀 Global performance optimization: Using cached Organizations data[/blue]")
|
56
|
-
return _GLOBAL_ORGS_CACHE[
|
57
|
+
return _GLOBAL_ORGS_CACHE["data"]
|
57
58
|
return None
|
58
59
|
|
60
|
+
|
59
61
|
def _set_global_organizations_cache(data):
|
60
62
|
"""Cache Organizations data globally (module-level cache)."""
|
61
|
-
_GLOBAL_ORGS_CACHE[
|
62
|
-
_GLOBAL_ORGS_CACHE[
|
63
|
-
accounts_count = len(data.get(
|
64
|
-
console.print(
|
63
|
+
_GLOBAL_ORGS_CACHE["data"] = data
|
64
|
+
_GLOBAL_ORGS_CACHE["timestamp"] = datetime.now(timezone.utc)
|
65
|
+
accounts_count = len(data.get("accounts", {}).get("discovered_accounts", [])) if data else 0
|
66
|
+
console.print(
|
67
|
+
f"[green]✅ Global Organizations cache: {accounts_count} accounts (TTL: {_GLOBAL_ORGS_CACHE['ttl_minutes']}min)[/green]"
|
68
|
+
)
|
69
|
+
|
65
70
|
|
66
71
|
# Universal AWS Environment Profile Support (Compatible with ANY AWS Setup)
|
67
72
|
import os
|
73
|
+
|
68
74
|
ENTERPRISE_PROFILES = {
|
69
75
|
"BILLING_PROFILE": os.getenv("BILLING_PROFILE", "default"), # Universal compatibility
|
70
76
|
"MANAGEMENT_PROFILE": os.getenv("MANAGEMENT_PROFILE", "default"), # Works with any profile
|
@@ -246,40 +252,44 @@ class EnhancedOrganizationsDiscovery:
|
|
246
252
|
"""Check if Organizations cache is still valid."""
|
247
253
|
if not self._organizations_cache_timestamp:
|
248
254
|
return False
|
249
|
-
|
255
|
+
|
250
256
|
from datetime import datetime, timedelta
|
257
|
+
|
251
258
|
cache_age_minutes = (datetime.now() - self._organizations_cache_timestamp).total_seconds() / 60
|
252
259
|
return cache_age_minutes < self._cache_ttl_minutes
|
253
260
|
|
254
261
|
async def discover_all_accounts(self) -> Dict:
|
255
262
|
"""
|
256
263
|
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
|
264
|
+
|
265
|
+
This method implements both global and instance-level caching to avoid the
|
266
|
+
performance penalty of duplicate Organizations API calls when multiple
|
260
267
|
components need the same account data.
|
261
268
|
"""
|
262
269
|
# Check global cache first (shared across all instances)
|
263
270
|
global_cached_result = _get_global_organizations_cache()
|
264
271
|
if global_cached_result:
|
265
272
|
return global_cached_result
|
266
|
-
|
273
|
+
|
267
274
|
# Check instance cache
|
268
275
|
if self._is_organizations_cache_valid() and self._organizations_cache:
|
269
276
|
console.print("[blue]🚀 Performance optimization: Using cached Organizations data[/blue]")
|
270
277
|
return self._organizations_cache
|
271
|
-
|
278
|
+
|
272
279
|
# Cache miss - perform discovery
|
273
280
|
console.print("[cyan]🔍 Performing Organizations discovery (cache miss)[/cyan]")
|
274
281
|
results = await self.discover_organization_structure()
|
275
|
-
|
282
|
+
|
276
283
|
# Cache the results
|
277
|
-
if results and results.get(
|
284
|
+
if results and results.get("accounts"):
|
278
285
|
self._organizations_cache = results
|
279
286
|
from datetime import datetime
|
287
|
+
|
280
288
|
self._organizations_cache_timestamp = datetime.now()
|
281
|
-
console.print(
|
282
|
-
|
289
|
+
console.print(
|
290
|
+
f"[green]✅ Cached Organizations data: {len(results.get('accounts', {}).get('discovered_accounts', []))} accounts (TTL: {self._cache_ttl_minutes}min)[/green]"
|
291
|
+
)
|
292
|
+
|
283
293
|
return results
|
284
294
|
|
285
295
|
def initialize_sessions(self) -> Dict[str, str]:
|
@@ -417,13 +427,13 @@ class EnhancedOrganizationsDiscovery:
|
|
417
427
|
"""
|
418
428
|
# Get performance optimization engine
|
419
429
|
optimization_engine = get_optimization_engine(
|
420
|
-
max_workers=self.max_workers,
|
421
|
-
cache_ttl_minutes=30,
|
422
|
-
memory_limit_mb=2048
|
430
|
+
max_workers=self.max_workers, cache_ttl_minutes=30, memory_limit_mb=2048
|
423
431
|
)
|
424
|
-
|
432
|
+
|
425
433
|
# Use optimized discovery with performance monitoring
|
426
|
-
with optimization_engine.optimize_operation(
|
434
|
+
with optimization_engine.optimize_operation(
|
435
|
+
"organization_structure_discovery", self.performance_target_seconds
|
436
|
+
):
|
427
437
|
return await self._discover_organization_structure_optimized(optimization_engine)
|
428
438
|
|
429
439
|
async def _discover_organization_structure_optimized(self, optimization_engine) -> Dict:
|
@@ -436,7 +446,7 @@ class EnhancedOrganizationsDiscovery:
|
|
436
446
|
)
|
437
447
|
|
438
448
|
logger.info("🏢 Starting optimized organization structure discovery with SRE automation patterns")
|
439
|
-
|
449
|
+
|
440
450
|
# Check global cache first to prevent duplicate calls
|
441
451
|
cached_result = _get_global_organizations_cache()
|
442
452
|
if cached_result:
|
@@ -446,7 +456,7 @@ class EnhancedOrganizationsDiscovery:
|
|
446
456
|
self.discovery_metrics["end_time"] = self.current_benchmark.end_time
|
447
457
|
self.discovery_metrics["duration_seconds"] = self.current_benchmark.duration_seconds
|
448
458
|
return cached_result
|
449
|
-
|
459
|
+
|
450
460
|
self.discovery_metrics["start_time"] = self.current_benchmark.start_time
|
451
461
|
|
452
462
|
with Status("Initializing enterprise discovery...", console=console, spinner="dots"):
|
@@ -561,10 +571,10 @@ class EnhancedOrganizationsDiscovery:
|
|
561
571
|
"performance_benchmark": performance_benchmark_dict,
|
562
572
|
"timestamp": datetime.now().isoformat(),
|
563
573
|
}
|
564
|
-
|
574
|
+
|
565
575
|
# Cache the successful result to prevent duplicate calls
|
566
576
|
_set_global_organizations_cache(discovery_result)
|
567
|
-
|
577
|
+
|
568
578
|
return discovery_result
|
569
579
|
|
570
580
|
except Exception as e:
|
@@ -606,7 +616,7 @@ class EnhancedOrganizationsDiscovery:
|
|
606
616
|
async def _discover_accounts_optimized(self, optimization_engine) -> Dict:
|
607
617
|
"""
|
608
618
|
Optimized account discovery using performance optimization engine
|
609
|
-
|
619
|
+
|
610
620
|
Addresses: Organization Discovery Performance (52.3s -> <30s target)
|
611
621
|
Features:
|
612
622
|
- Intelligent caching with TTL management
|
@@ -615,46 +625,44 @@ class EnhancedOrganizationsDiscovery:
|
|
615
625
|
- Memory-efficient processing
|
616
626
|
"""
|
617
627
|
logger.info("📊 Discovering organization accounts with SRE optimization patterns")
|
618
|
-
|
628
|
+
|
619
629
|
# Use optimization engine for discovery
|
620
630
|
optimized_discover_accounts = optimization_engine.optimize_organization_discovery(
|
621
|
-
management_profile=self.management_profile,
|
622
|
-
use_parallel_processing=True,
|
623
|
-
batch_size=20
|
631
|
+
management_profile=self.management_profile, use_parallel_processing=True, batch_size=20
|
624
632
|
)
|
625
|
-
|
633
|
+
|
626
634
|
# Execute optimized discovery
|
627
635
|
try:
|
628
636
|
result = optimized_discover_accounts()
|
629
|
-
|
637
|
+
|
630
638
|
# Convert to expected format
|
631
|
-
accounts_data = result.get(
|
632
|
-
|
639
|
+
accounts_data = result.get("accounts", [])
|
640
|
+
|
633
641
|
# Create AWSAccount objects for compatibility
|
634
642
|
for account_data in accounts_data:
|
635
643
|
account = AWSAccount(
|
636
644
|
account_id=account_data["Id"],
|
637
|
-
name=account_data["Name"],
|
645
|
+
name=account_data["Name"],
|
638
646
|
email=account_data["Email"],
|
639
647
|
status=account_data["Status"],
|
640
648
|
joined_method=account_data["JoinedMethod"],
|
641
649
|
joined_timestamp=account_data.get("JoinedTimestamp"),
|
642
|
-
tags=account_data.get("Tags", {})
|
650
|
+
tags=account_data.get("Tags", {}),
|
643
651
|
)
|
644
652
|
self.accounts_cache[account.account_id] = account
|
645
|
-
|
653
|
+
|
646
654
|
# Update metrics
|
647
655
|
self.discovery_metrics["accounts_discovered"] = len(accounts_data)
|
648
|
-
|
656
|
+
|
649
657
|
# Enhanced account categorization
|
650
658
|
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"]
|
659
|
+
suspended_accounts = [a for a in accounts_data if a.get("Status") == "SUSPENDED"]
|
652
660
|
closed_accounts = [a for a in accounts_data if a.get("Status") == "CLOSED"]
|
653
|
-
|
654
|
-
optimization_info = result.get(
|
661
|
+
|
662
|
+
optimization_info = result.get("optimizations_applied", [])
|
655
663
|
logger.info(f"✅ Optimized discovery: {len(accounts_data)} accounts ({len(active_accounts)} active)")
|
656
664
|
logger.info(f"🚀 Optimizations applied: {', '.join(optimization_info)}")
|
657
|
-
|
665
|
+
|
658
666
|
return {
|
659
667
|
"total_accounts": len(accounts_data),
|
660
668
|
"active_accounts": len(active_accounts),
|
@@ -665,7 +673,7 @@ class EnhancedOrganizationsDiscovery:
|
|
665
673
|
"profile_used": "management",
|
666
674
|
"optimizations_applied": optimization_info,
|
667
675
|
}
|
668
|
-
|
676
|
+
|
669
677
|
except Exception as e:
|
670
678
|
logger.error(f"Optimized account discovery failed: {e}")
|
671
679
|
# Fallback to original method
|
@@ -10,7 +10,7 @@ import Inventory_Modules
|
|
10
10
|
import simplejson as json
|
11
11
|
from account_class import aws_acct_access
|
12
12
|
from ArgumentsClass import CommonArguments
|
13
|
-
from
|
13
|
+
from runbooks.common.rich_utils import console
|
14
14
|
from Inventory_Modules import get_credentials_for_accounts_in_org
|
15
15
|
|
16
16
|
"""
|
@@ -18,7 +18,6 @@ This script was created to help solve a testing problem for the "move_stack_inst
|
|
18
18
|
Originally, that script didn't have built-in recovery, so we needed this script to "recover" those stack-instance ids that might have been lost during the move_stack_instances.py run. However, that script now has built-in recovery, so this script isn't really needed. However, it can still be used to find any stack-instances that have been orphaned from their original stack-set, if that happens.
|
19
19
|
"""
|
20
20
|
|
21
|
-
init()
|
22
21
|
__version__ = "2024.05.18"
|
23
22
|
|
24
23
|
|
@@ -70,9 +69,9 @@ def setup_auth_and_regions(fProfile: str = None) -> (object, list, list):
|
|
70
69
|
ChildAccounts = Inventory_Modules.RemoveCoreAccounts(ChildAccounts, pAccountsToSkip)
|
71
70
|
AccountList = [account["AccountId"] for account in ChildAccounts]
|
72
71
|
|
73
|
-
print(f"You asked to find stacks with this fragment list: {
|
74
|
-
print(f"\t\tin these accounts: {
|
75
|
-
print(f"\t\tin these regions: {
|
72
|
+
print(f"You asked to find stacks with this fragment list: [red]{pFragments}")
|
73
|
+
print(f"\t\tin these accounts: [red]{AccountList}")
|
74
|
+
print(f"\t\tin these regions: [red]{RegionList}")
|
76
75
|
return aws_acct, AccountList, RegionList
|
77
76
|
|
78
77
|
|
@@ -129,7 +128,7 @@ if __name__ == "__main__":
|
|
129
128
|
Stacks = Inventory_Modules.find_stacks2(cred, region, pFragments)
|
130
129
|
logging.warning(f"Account: {cred['AccountId']} | Region: {region} | Found {len(Stacks)} Stacks")
|
131
130
|
print(
|
132
|
-
f"{ERASE_LINE}
|
131
|
+
f"{ERASE_LINE}[red]Account: {cred['AccountId']} Region: {region} Found {len(Stacks)} Stacks ({item_counter} of {len(AccountList) * len(RegionList)})",
|
133
132
|
end="\r",
|
134
133
|
)
|
135
134
|
except Exception as my_Error:
|
@@ -162,7 +161,7 @@ if __name__ == "__main__":
|
|
162
161
|
lAccountsAndRegions.append((StacksFound[i]["Account"], StacksFound[i]["Region"]))
|
163
162
|
print(ERASE_LINE)
|
164
163
|
print(
|
165
|
-
f"
|
164
|
+
f"[red]Looked through {len(StacksFound)} Stacks across {len(AccountList)} accounts across {len(RegionList)} regions"
|
166
165
|
)
|
167
166
|
print()
|
168
167
|
if args.loglevel < 21: # INFO level
|
@@ -198,7 +197,7 @@ if __name__ == "__main__":
|
|
198
197
|
|
199
198
|
if pTiming:
|
200
199
|
print(ERASE_LINE)
|
201
|
-
print(f"
|
200
|
+
print(f"[green]This script took {time() - begin_time:.2f} seconds")
|
202
201
|
|
203
202
|
print()
|
204
203
|
print("Thanks for using this script...")
|
@@ -21,9 +21,9 @@ from datetime import datetime
|
|
21
21
|
from typing import Any, Dict, List, Optional
|
22
22
|
|
23
23
|
from rich import box
|
24
|
-
from rich.console import Console
|
25
24
|
from rich.panel import Panel
|
26
|
-
from rich.progress import BarColumn,
|
25
|
+
from rich.progress import BarColumn, SpinnerColumn, TaskProgressColumn, TextColumn
|
26
|
+
from runbooks.common.rich_utils import Progress
|
27
27
|
from rich.table import Table
|
28
28
|
from rich.text import Text
|
29
29
|
from rich.tree import Tree
|
@@ -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
|
]
|
@@ -7,14 +7,12 @@ import Inventory_Modules
|
|
7
7
|
from account_class import aws_acct_access
|
8
8
|
from ArgumentsClass import CommonArguments
|
9
9
|
from botocore.exceptions import ClientError
|
10
|
-
from
|
11
|
-
from rich.console import Console
|
10
|
+
from runbooks.common.rich_utils import console
|
12
11
|
from rich.panel import Panel
|
13
12
|
|
14
|
-
# Initialize Rich console
|
15
|
-
|
13
|
+
# Initialize Rich console with test mode support
|
14
|
+
from runbooks.common.rich_utils import console
|
16
15
|
|
17
|
-
init()
|
18
16
|
__version__ = "2023.05.04"
|
19
17
|
|
20
18
|
parser = CommonArguments()
|