runbooks 1.0.0__py3-none-any.whl → 1.0.2__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 +1 -1
- runbooks/cfat/WEIGHT_CONFIG_README.md +368 -0
- runbooks/cfat/app.ts +27 -19
- runbooks/cfat/assessment/runner.py +6 -5
- runbooks/cfat/tests/test_weight_configuration.ts +449 -0
- runbooks/cfat/weight_config.ts +574 -0
- runbooks/cloudops/models.py +20 -14
- runbooks/common/__init__.py +26 -9
- runbooks/common/aws_pricing.py +1070 -105
- runbooks/common/aws_pricing_api.py +276 -44
- runbooks/common/date_utils.py +115 -0
- runbooks/common/dry_run_examples.py +587 -0
- runbooks/common/dry_run_framework.py +520 -0
- runbooks/common/enhanced_exception_handler.py +10 -7
- runbooks/common/mcp_cost_explorer_integration.py +5 -4
- runbooks/common/memory_optimization.py +533 -0
- runbooks/common/performance_optimization_engine.py +1153 -0
- runbooks/common/profile_utils.py +86 -118
- runbooks/common/rich_utils.py +3 -3
- runbooks/common/sre_performance_suite.py +574 -0
- runbooks/finops/business_case_config.py +314 -0
- runbooks/finops/cost_processor.py +19 -4
- runbooks/finops/dashboard_runner.py +47 -28
- runbooks/finops/ebs_cost_optimizer.py +1 -1
- runbooks/finops/ebs_optimizer.py +56 -9
- runbooks/finops/embedded_mcp_validator.py +642 -36
- runbooks/finops/enhanced_trend_visualization.py +7 -2
- runbooks/finops/executive_export.py +789 -0
- runbooks/finops/finops_dashboard.py +6 -5
- runbooks/finops/finops_scenarios.py +34 -27
- runbooks/finops/iam_guidance.py +6 -1
- runbooks/finops/nat_gateway_optimizer.py +46 -27
- runbooks/finops/notebook_utils.py +1 -1
- runbooks/finops/schemas.py +73 -58
- runbooks/finops/single_dashboard.py +20 -4
- runbooks/finops/tests/test_integration.py +3 -1
- runbooks/finops/vpc_cleanup_exporter.py +2 -1
- runbooks/finops/vpc_cleanup_optimizer.py +22 -29
- runbooks/inventory/core/collector.py +51 -28
- runbooks/inventory/discovery.md +197 -247
- runbooks/inventory/inventory_modules.py +2 -2
- runbooks/inventory/list_ec2_instances.py +3 -3
- runbooks/inventory/models/account.py +5 -3
- runbooks/inventory/models/inventory.py +1 -1
- runbooks/inventory/models/resource.py +5 -3
- runbooks/inventory/organizations_discovery.py +102 -13
- runbooks/inventory/unified_validation_engine.py +2 -15
- runbooks/main.py +255 -92
- runbooks/operate/base.py +9 -6
- runbooks/operate/deployment_framework.py +5 -4
- runbooks/operate/deployment_validator.py +6 -5
- runbooks/operate/mcp_integration.py +6 -5
- runbooks/operate/networking_cost_heatmap.py +17 -13
- runbooks/operate/vpc_operations.py +82 -13
- runbooks/remediation/base.py +3 -1
- runbooks/remediation/commons.py +5 -5
- runbooks/remediation/commvault_ec2_analysis.py +66 -18
- runbooks/remediation/config/accounts_example.json +31 -0
- runbooks/remediation/multi_account.py +120 -7
- runbooks/remediation/remediation_cli.py +710 -0
- runbooks/remediation/universal_account_discovery.py +377 -0
- runbooks/remediation/workspaces_list.py +2 -2
- runbooks/security/compliance_automation_engine.py +99 -20
- runbooks/security/config/__init__.py +24 -0
- runbooks/security/config/compliance_config.py +255 -0
- runbooks/security/config/compliance_weights_example.json +22 -0
- runbooks/security/config_template_generator.py +500 -0
- runbooks/security/security_cli.py +377 -0
- runbooks/validation/cli.py +8 -7
- runbooks/validation/comprehensive_2way_validator.py +26 -15
- runbooks/validation/mcp_validator.py +62 -8
- runbooks/vpc/config.py +49 -15
- runbooks/vpc/cross_account_session.py +5 -1
- runbooks/vpc/heatmap_engine.py +438 -59
- runbooks/vpc/mcp_no_eni_validator.py +115 -36
- runbooks/vpc/performance_optimized_analyzer.py +546 -0
- runbooks/vpc/runbooks_adapter.py +33 -12
- runbooks/vpc/tests/conftest.py +4 -2
- runbooks/vpc/tests/test_cost_engine.py +3 -1
- {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/METADATA +1 -1
- {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/RECORD +85 -79
- runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
- runbooks/finops/runbooks.security.report_generator.log +0 -0
- runbooks/finops/runbooks.security.run_script.log +0 -0
- runbooks/finops/runbooks.security.security_export.log +0 -0
- runbooks/finops/tests/results_test_finops_dashboard.xml +0 -1
- runbooks/inventory/artifacts/scale-optimize-status.txt +0 -12
- runbooks/inventory/runbooks.inventory.organizations_discovery.log +0 -0
- runbooks/inventory/runbooks.security.report_generator.log +0 -0
- runbooks/inventory/runbooks.security.run_script.log +0 -0
- runbooks/inventory/runbooks.security.security_export.log +0 -0
- runbooks/vpc/runbooks.inventory.organizations_discovery.log +0 -0
- runbooks/vpc/runbooks.security.report_generator.log +0 -0
- runbooks/vpc/runbooks.security.run_script.log +0 -0
- runbooks/vpc/runbooks.security.security_export.log +0 -0
- {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/WHEEL +0 -0
- {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/entry_points.txt +0 -0
- {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/top_level.txt +0 -0
@@ -10,7 +10,7 @@ from datetime import datetime, timedelta
|
|
10
10
|
from enum import Enum
|
11
11
|
from typing import Any, Dict, List, Optional, Set
|
12
12
|
|
13
|
-
from pydantic import BaseModel, Field,
|
13
|
+
from pydantic import BaseModel, Field, field_validator
|
14
14
|
|
15
15
|
from runbooks.inventory.models.account import AWSAccount
|
16
16
|
from runbooks.inventory.models.resource import AWSResource
|
@@ -10,7 +10,7 @@ from datetime import datetime
|
|
10
10
|
from enum import Enum
|
11
11
|
from typing import Any, Dict, List, Optional, Union
|
12
12
|
|
13
|
-
from pydantic import BaseModel, Field,
|
13
|
+
from pydantic import BaseModel, Field, field_validator
|
14
14
|
|
15
15
|
|
16
16
|
class ResourceState(str, Enum):
|
@@ -178,14 +178,16 @@ class AWSResource(BaseModel):
|
|
178
178
|
use_enum_values = True
|
179
179
|
json_encoders = {datetime: lambda v: v.isoformat() if v else None}
|
180
180
|
|
181
|
-
@
|
181
|
+
@field_validator("resource_arn")
|
182
|
+
@classmethod
|
182
183
|
def validate_arn_format(cls, v):
|
183
184
|
"""Validate ARN format if provided."""
|
184
185
|
if v and not v.startswith("arn:aws:"):
|
185
186
|
raise ValueError('ARN must start with "arn:aws:"')
|
186
187
|
return v
|
187
188
|
|
188
|
-
@
|
189
|
+
@field_validator("account_id")
|
190
|
+
@classmethod
|
189
191
|
def validate_account_id(cls, v):
|
190
192
|
"""Validate account ID format."""
|
191
193
|
if not v.isdigit() or len(v) != 12:
|
@@ -34,6 +34,7 @@ from rich.table import Table
|
|
34
34
|
console = Console()
|
35
35
|
|
36
36
|
from ..utils.logger import configure_logger
|
37
|
+
from ..common.performance_optimization_engine import get_optimization_engine
|
37
38
|
|
38
39
|
logger = configure_logger(__name__)
|
39
40
|
|
@@ -62,12 +63,13 @@ def _set_global_organizations_cache(data):
|
|
62
63
|
accounts_count = len(data.get('accounts', {}).get('discovered_accounts', [])) if data else 0
|
63
64
|
console.print(f"[green]✅ Global Organizations cache: {accounts_count} accounts (TTL: {_GLOBAL_ORGS_CACHE['ttl_minutes']}min)[/green]")
|
64
65
|
|
65
|
-
#
|
66
|
+
# Universal AWS Environment Profile Support (Compatible with ANY AWS Setup)
|
67
|
+
import os
|
66
68
|
ENTERPRISE_PROFILES = {
|
67
|
-
"BILLING_PROFILE": "
|
68
|
-
"MANAGEMENT_PROFILE": "
|
69
|
-
"CENTRALISED_OPS_PROFILE": "
|
70
|
-
"SINGLE_ACCOUNT_PROFILE": "
|
69
|
+
"BILLING_PROFILE": os.getenv("BILLING_PROFILE", "default"), # Universal compatibility
|
70
|
+
"MANAGEMENT_PROFILE": os.getenv("MANAGEMENT_PROFILE", "default"), # Works with any profile
|
71
|
+
"CENTRALISED_OPS_PROFILE": os.getenv("CENTRALISED_OPS_PROFILE", "default"), # Universal operations
|
72
|
+
"SINGLE_ACCOUNT_PROFILE": os.getenv("SINGLE_AWS_PROFILE", "default"), # Universal single account
|
71
73
|
}
|
72
74
|
|
73
75
|
|
@@ -407,11 +409,25 @@ class EnhancedOrganizationsDiscovery:
|
|
407
409
|
Discover complete organization structure with performance benchmarking
|
408
410
|
|
409
411
|
Enhanced with:
|
410
|
-
- Performance benchmark tracking (<
|
412
|
+
- Performance benchmark tracking (<30s target optimized from 52.3s)
|
411
413
|
- Rich console progress monitoring
|
412
414
|
- Comprehensive error recovery
|
413
415
|
- Multi-profile fallback support
|
416
|
+
- Performance optimization engine integration
|
414
417
|
"""
|
418
|
+
# Get performance optimization engine
|
419
|
+
optimization_engine = get_optimization_engine(
|
420
|
+
max_workers=self.max_workers,
|
421
|
+
cache_ttl_minutes=30,
|
422
|
+
memory_limit_mb=2048
|
423
|
+
)
|
424
|
+
|
425
|
+
# Use optimized discovery with performance monitoring
|
426
|
+
with optimization_engine.optimize_operation("organization_structure_discovery", self.performance_target_seconds):
|
427
|
+
return await self._discover_organization_structure_optimized(optimization_engine)
|
428
|
+
|
429
|
+
async def _discover_organization_structure_optimized(self, optimization_engine) -> Dict:
|
430
|
+
"""Optimized organization structure discovery implementation"""
|
415
431
|
# Start performance benchmark
|
416
432
|
self.current_benchmark = PerformanceBenchmark(
|
417
433
|
operation_name="organization_structure_discovery",
|
@@ -419,7 +435,7 @@ class EnhancedOrganizationsDiscovery:
|
|
419
435
|
target_seconds=self.performance_target_seconds,
|
420
436
|
)
|
421
437
|
|
422
|
-
logger.info("🏢 Starting
|
438
|
+
logger.info("🏢 Starting optimized organization structure discovery with SRE automation patterns")
|
423
439
|
|
424
440
|
# Check global cache first to prevent duplicate calls
|
425
441
|
cached_result = _get_global_organizations_cache()
|
@@ -471,9 +487,9 @@ class EnhancedOrganizationsDiscovery:
|
|
471
487
|
) as progress:
|
472
488
|
discovery_task = progress.add_task("Discovering organization structure...", total=5)
|
473
489
|
|
474
|
-
# Discover accounts
|
475
|
-
progress.update(discovery_task, description="Discovering accounts...")
|
476
|
-
accounts_result = await self.
|
490
|
+
# Discover accounts using optimization engine
|
491
|
+
progress.update(discovery_task, description="Discovering accounts (optimized)...")
|
492
|
+
accounts_result = await self._discover_accounts_optimized(optimization_engine)
|
477
493
|
self.current_benchmark.accounts_processed = accounts_result.get("total_accounts", 0)
|
478
494
|
progress.advance(discovery_task)
|
479
495
|
|
@@ -587,6 +603,75 @@ class EnhancedOrganizationsDiscovery:
|
|
587
603
|
"performance_benchmark": performance_benchmark_dict,
|
588
604
|
}
|
589
605
|
|
606
|
+
async def _discover_accounts_optimized(self, optimization_engine) -> Dict:
|
607
|
+
"""
|
608
|
+
Optimized account discovery using performance optimization engine
|
609
|
+
|
610
|
+
Addresses: Organization Discovery Performance (52.3s -> <30s target)
|
611
|
+
Features:
|
612
|
+
- Intelligent caching with TTL management
|
613
|
+
- Parallel account processing with batch optimization
|
614
|
+
- Connection pooling for Organizations API
|
615
|
+
- Memory-efficient processing
|
616
|
+
"""
|
617
|
+
logger.info("📊 Discovering organization accounts with SRE optimization patterns")
|
618
|
+
|
619
|
+
# Use optimization engine for discovery
|
620
|
+
optimized_discover_accounts = optimization_engine.optimize_organization_discovery(
|
621
|
+
management_profile=self.management_profile,
|
622
|
+
use_parallel_processing=True,
|
623
|
+
batch_size=20
|
624
|
+
)
|
625
|
+
|
626
|
+
# Execute optimized discovery
|
627
|
+
try:
|
628
|
+
result = optimized_discover_accounts()
|
629
|
+
|
630
|
+
# Convert to expected format
|
631
|
+
accounts_data = result.get('accounts', [])
|
632
|
+
|
633
|
+
# Create AWSAccount objects for compatibility
|
634
|
+
for account_data in accounts_data:
|
635
|
+
account = AWSAccount(
|
636
|
+
account_id=account_data["Id"],
|
637
|
+
name=account_data["Name"],
|
638
|
+
email=account_data["Email"],
|
639
|
+
status=account_data["Status"],
|
640
|
+
joined_method=account_data["JoinedMethod"],
|
641
|
+
joined_timestamp=account_data.get("JoinedTimestamp"),
|
642
|
+
tags=account_data.get("Tags", {})
|
643
|
+
)
|
644
|
+
self.accounts_cache[account.account_id] = account
|
645
|
+
|
646
|
+
# Update metrics
|
647
|
+
self.discovery_metrics["accounts_discovered"] = len(accounts_data)
|
648
|
+
|
649
|
+
# Enhanced account categorization
|
650
|
+
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"]
|
652
|
+
closed_accounts = [a for a in accounts_data if a.get("Status") == "CLOSED"]
|
653
|
+
|
654
|
+
optimization_info = result.get('optimizations_applied', [])
|
655
|
+
logger.info(f"✅ Optimized discovery: {len(accounts_data)} accounts ({len(active_accounts)} active)")
|
656
|
+
logger.info(f"🚀 Optimizations applied: {', '.join(optimization_info)}")
|
657
|
+
|
658
|
+
return {
|
659
|
+
"total_accounts": len(accounts_data),
|
660
|
+
"active_accounts": len(active_accounts),
|
661
|
+
"suspended_accounts": len(suspended_accounts),
|
662
|
+
"closed_accounts": len(closed_accounts),
|
663
|
+
"accounts": [asdict(account) for account_id, account in self.accounts_cache.items()],
|
664
|
+
"discovery_method": "optimized_organizations_api",
|
665
|
+
"profile_used": "management",
|
666
|
+
"optimizations_applied": optimization_info,
|
667
|
+
}
|
668
|
+
|
669
|
+
except Exception as e:
|
670
|
+
logger.error(f"Optimized account discovery failed: {e}")
|
671
|
+
# Fallback to original method
|
672
|
+
logger.info("Falling back to original discovery method...")
|
673
|
+
return await self._discover_accounts()
|
674
|
+
|
590
675
|
async def _discover_accounts(self) -> Dict:
|
591
676
|
"""
|
592
677
|
Discover all accounts in the organization using 4-profile architecture
|
@@ -1290,10 +1375,10 @@ async def run_enhanced_organizations_discovery(
|
|
1290
1375
|
return org_results
|
1291
1376
|
|
1292
1377
|
|
1293
|
-
# Legacy compatibility function
|
1378
|
+
# Legacy compatibility function with universal defaults
|
1294
1379
|
async def run_organizations_discovery(
|
1295
|
-
management_profile: str =
|
1296
|
-
billing_profile: str =
|
1380
|
+
management_profile: str = None,
|
1381
|
+
billing_profile: str = None,
|
1297
1382
|
) -> Dict:
|
1298
1383
|
"""
|
1299
1384
|
Legacy compatibility function - redirects to enhanced discovery
|
@@ -1303,6 +1388,10 @@ async def run_organizations_discovery(
|
|
1303
1388
|
"""
|
1304
1389
|
console.print("[yellow]ℹ️ Using enhanced discovery engine for improved reliability and performance[/yellow]")
|
1305
1390
|
|
1391
|
+
# Apply universal environment defaults
|
1392
|
+
management_profile = management_profile or os.getenv("MANAGEMENT_PROFILE", "default-management-profile")
|
1393
|
+
billing_profile = billing_profile or os.getenv("BILLING_PROFILE", "default-billing-profile")
|
1394
|
+
|
1306
1395
|
return await run_enhanced_organizations_discovery(
|
1307
1396
|
management_profile=management_profile,
|
1308
1397
|
billing_profile=billing_profile,
|
@@ -564,21 +564,8 @@ class UnifiedValidationEngine:
|
|
564
564
|
total_weighted_accuracy = 0.0
|
565
565
|
total_weight = 0.0
|
566
566
|
|
567
|
-
#
|
568
|
-
resource_weights =
|
569
|
-
"ec2": 3.0, # High weight - critical compute resources
|
570
|
-
"vpc": 2.5, # High weight - foundational networking
|
571
|
-
"s3": 2.0, # Medium-high weight - core storage
|
572
|
-
"rds": 2.0, # Medium-high weight - critical databases
|
573
|
-
"iam": 2.0, # Medium-high weight - security foundation
|
574
|
-
"lambda": 1.5, # Medium weight - serverless compute
|
575
|
-
"elbv2": 1.5, # Medium weight - load balancing
|
576
|
-
"cloudformation": 1.0, # Medium weight - infrastructure management
|
577
|
-
"route53": 1.0, # Medium weight - DNS services
|
578
|
-
"sns": 0.8, # Lower weight - messaging
|
579
|
-
"eni": 0.8, # Lower weight - network interfaces
|
580
|
-
"ebs": 0.8, # Lower weight - block storage
|
581
|
-
}
|
567
|
+
# Dynamic resource weighting based on actual discovery for universal compatibility
|
568
|
+
resource_weights = self._calculate_dynamic_resource_weights(resource_counts)
|
582
569
|
|
583
570
|
for resource_type in self.supported_resources.keys():
|
584
571
|
runbooks_count = resource_counts["runbooks"].get(resource_type, 0)
|