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
@@ -25,29 +25,39 @@ Strategic Alignment:
|
|
25
25
|
import asyncio
|
26
26
|
import logging
|
27
27
|
import time
|
28
|
-
from datetime import datetime, timedelta
|
29
|
-
from typing import Any, Dict, List, Optional, Tuple, Union
|
30
28
|
from dataclasses import dataclass, field
|
29
|
+
from datetime import datetime, timedelta
|
31
30
|
from enum import Enum
|
31
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
32
32
|
|
33
33
|
import boto3
|
34
34
|
from botocore.exceptions import ClientError, NoCredentialsError
|
35
35
|
from pydantic import BaseModel, Field
|
36
36
|
|
37
|
+
from ..common.profile_utils import get_profile_for_operation
|
37
38
|
from ..common.rich_utils import (
|
38
|
-
|
39
|
-
|
39
|
+
STATUS_INDICATORS,
|
40
|
+
console,
|
41
|
+
create_panel,
|
42
|
+
create_progress_bar,
|
43
|
+
create_table,
|
44
|
+
format_cost,
|
45
|
+
print_error,
|
46
|
+
print_header,
|
47
|
+
print_info,
|
48
|
+
print_success,
|
49
|
+
print_warning,
|
40
50
|
)
|
41
|
-
from .
|
42
|
-
from ..common.profile_utils import get_profile_for_operation
|
51
|
+
from .mcp_validator import EmbeddedMCPValidator
|
43
52
|
|
44
53
|
logger = logging.getLogger(__name__)
|
45
54
|
|
46
55
|
|
47
56
|
class OptimizationCategory(str, Enum):
|
48
57
|
"""Optimization categories for CloudOps automation consolidation."""
|
58
|
+
|
49
59
|
COST_OPTIMIZATION = "cost_optimization"
|
50
|
-
SECURITY_COMPLIANCE = "security_compliance"
|
60
|
+
SECURITY_COMPLIANCE = "security_compliance"
|
51
61
|
RESOURCE_MANAGEMENT = "resource_management"
|
52
62
|
NETWORK_INFRASTRUCTURE = "network_infrastructure"
|
53
63
|
SPECIALIZED_OPERATIONS = "specialized_operations"
|
@@ -55,14 +65,16 @@ class OptimizationCategory(str, Enum):
|
|
55
65
|
|
56
66
|
class BusinessImpactLevel(str, Enum):
|
57
67
|
"""Business impact levels for prioritization."""
|
58
|
-
|
59
|
-
|
60
|
-
|
68
|
+
|
69
|
+
HIGH = "high" # >$1M annual impact
|
70
|
+
MEDIUM = "medium" # $100K-$1M annual impact
|
71
|
+
LOW = "low" # <$100K annual impact
|
61
72
|
|
62
73
|
|
63
74
|
@dataclass
|
64
75
|
class AutomationPattern:
|
65
76
|
"""Universal automation pattern from CloudOps consolidation."""
|
77
|
+
|
66
78
|
name: str
|
67
79
|
category: OptimizationCategory
|
68
80
|
business_impact: BusinessImpactLevel
|
@@ -76,7 +88,7 @@ class AutomationPattern:
|
|
76
88
|
class UniversalAutomationEngine:
|
77
89
|
"""
|
78
90
|
Universal Automation Engine - Core Patterns from CloudOps-Automation Consolidation
|
79
|
-
|
91
|
+
|
80
92
|
Following $132,720+ methodology with proven automation patterns:
|
81
93
|
- Multi-service AWS resource discovery and analysis
|
82
94
|
- Universal cost calculation patterns across all optimization categories
|
@@ -85,33 +97,52 @@ class UniversalAutomationEngine:
|
|
85
97
|
- MCP validation integration for evidence-based automation
|
86
98
|
- Rich CLI integration for executive and technical stakeholder interfaces
|
87
99
|
"""
|
88
|
-
|
100
|
+
|
89
101
|
def __init__(self, profile_name: Optional[str] = None, regions: Optional[List[str]] = None):
|
90
102
|
"""Initialize universal automation engine with enterprise profile support."""
|
91
103
|
self.profile_name = profile_name
|
92
104
|
self.regions = regions or [
|
93
|
-
|
94
|
-
|
105
|
+
"us-east-1",
|
106
|
+
"us-west-2",
|
107
|
+
"us-east-2",
|
108
|
+
"us-west-1",
|
109
|
+
"eu-west-1",
|
110
|
+
"eu-central-1",
|
111
|
+
"ap-southeast-1",
|
112
|
+
"ap-northeast-1",
|
95
113
|
]
|
96
|
-
|
114
|
+
|
97
115
|
# Initialize AWS session with profile priority system
|
98
|
-
self.session = boto3.Session(
|
99
|
-
|
100
|
-
)
|
101
|
-
|
116
|
+
self.session = boto3.Session(profile_name=get_profile_for_operation("operational", profile_name))
|
117
|
+
|
102
118
|
# Universal automation patterns from CloudOps consolidation analysis
|
103
119
|
self.automation_patterns = self._initialize_automation_patterns()
|
104
|
-
|
120
|
+
|
105
121
|
# All AWS regions for comprehensive discovery
|
106
122
|
self.all_regions = [
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
123
|
+
"us-east-1",
|
124
|
+
"us-east-2",
|
125
|
+
"us-west-1",
|
126
|
+
"us-west-2",
|
127
|
+
"af-south-1",
|
128
|
+
"ap-east-1",
|
129
|
+
"ap-south-1",
|
130
|
+
"ap-northeast-1",
|
131
|
+
"ap-northeast-2",
|
132
|
+
"ap-northeast-3",
|
133
|
+
"ap-southeast-1",
|
134
|
+
"ap-southeast-2",
|
135
|
+
"ca-central-1",
|
136
|
+
"eu-central-1",
|
137
|
+
"eu-west-1",
|
138
|
+
"eu-west-2",
|
139
|
+
"eu-west-3",
|
140
|
+
"eu-south-1",
|
141
|
+
"eu-north-1",
|
142
|
+
"me-south-1",
|
143
|
+
"sa-east-1",
|
113
144
|
]
|
114
|
-
|
145
|
+
|
115
146
|
def _initialize_automation_patterns(self) -> List[AutomationPattern]:
|
116
147
|
"""Initialize automation patterns from CloudOps-Automation consolidation analysis."""
|
117
148
|
return [
|
@@ -122,15 +153,15 @@ class UniversalAutomationEngine:
|
|
122
153
|
business_impact=BusinessImpactLevel.HIGH,
|
123
154
|
aws_services=["EC2", "CloudWatch"],
|
124
155
|
annual_savings_potential=(1_500_000, 9_300_000),
|
125
|
-
implementation_weeks=3
|
156
|
+
implementation_weeks=3,
|
126
157
|
),
|
127
158
|
AutomationPattern(
|
128
|
-
name="EC2 Instance Cost Optimization",
|
159
|
+
name="EC2 Instance Cost Optimization",
|
129
160
|
category=OptimizationCategory.COST_OPTIMIZATION,
|
130
161
|
business_impact=BusinessImpactLevel.HIGH,
|
131
162
|
aws_services=["EC2", "CloudWatch", "Auto Scaling"],
|
132
163
|
annual_savings_potential=(2_000_000, 8_000_000),
|
133
|
-
implementation_weeks=4
|
164
|
+
implementation_weeks=4,
|
134
165
|
),
|
135
166
|
AutomationPattern(
|
136
167
|
name="RDS Cost Optimization",
|
@@ -138,7 +169,7 @@ class UniversalAutomationEngine:
|
|
138
169
|
business_impact=BusinessImpactLevel.HIGH,
|
139
170
|
aws_services=["RDS", "CloudWatch"],
|
140
171
|
annual_savings_potential=(1_500_000, 6_000_000),
|
141
|
-
implementation_weeks=3
|
172
|
+
implementation_weeks=3,
|
142
173
|
),
|
143
174
|
AutomationPattern(
|
144
175
|
name="Reserved Instance Optimization",
|
@@ -146,9 +177,8 @@ class UniversalAutomationEngine:
|
|
146
177
|
business_impact=BusinessImpactLevel.HIGH,
|
147
178
|
aws_services=["EC2", "RDS", "Redshift", "ElastiCache"],
|
148
179
|
annual_savings_potential=(3_200_000, 17_000_000),
|
149
|
-
implementation_weeks=5
|
180
|
+
implementation_weeks=5,
|
150
181
|
),
|
151
|
-
|
152
182
|
# Security & Compliance Patterns (15 notebooks → 4 modules)
|
153
183
|
AutomationPattern(
|
154
184
|
name="IAM Security Optimization",
|
@@ -156,7 +186,7 @@ class UniversalAutomationEngine:
|
|
156
186
|
business_impact=BusinessImpactLevel.MEDIUM,
|
157
187
|
aws_services=["IAM", "CloudTrail"],
|
158
188
|
annual_savings_potential=(100_000, 500_000),
|
159
|
-
implementation_weeks=4
|
189
|
+
implementation_weeks=4,
|
160
190
|
),
|
161
191
|
AutomationPattern(
|
162
192
|
name="S3 Security & Compliance",
|
@@ -164,9 +194,8 @@ class UniversalAutomationEngine:
|
|
164
194
|
business_impact=BusinessImpactLevel.MEDIUM,
|
165
195
|
aws_services=["S3", "CloudTrail"],
|
166
196
|
annual_savings_potential=(150_000, 800_000),
|
167
|
-
implementation_weeks=3
|
197
|
+
implementation_weeks=3,
|
168
198
|
),
|
169
|
-
|
170
199
|
# Resource Management Patterns (14 notebooks → 4 modules)
|
171
200
|
AutomationPattern(
|
172
201
|
name="Resource Tagging & Governance",
|
@@ -174,7 +203,7 @@ class UniversalAutomationEngine:
|
|
174
203
|
business_impact=BusinessImpactLevel.MEDIUM,
|
175
204
|
aws_services=["EC2", "S3", "RDS", "Lambda"],
|
176
205
|
annual_savings_potential=(200_000, 1_000_000),
|
177
|
-
implementation_weeks=4
|
206
|
+
implementation_weeks=4,
|
178
207
|
),
|
179
208
|
AutomationPattern(
|
180
209
|
name="Resource Lifecycle Management",
|
@@ -182,46 +211,46 @@ class UniversalAutomationEngine:
|
|
182
211
|
business_impact=BusinessImpactLevel.MEDIUM,
|
183
212
|
aws_services=["EC2", "EBS", "RDS"],
|
184
213
|
annual_savings_potential=(300_000, 1_500_000),
|
185
|
-
implementation_weeks=3
|
214
|
+
implementation_weeks=3,
|
186
215
|
),
|
187
216
|
]
|
188
|
-
|
189
|
-
async def discover_resources_universal(
|
190
|
-
|
191
|
-
|
217
|
+
|
218
|
+
async def discover_resources_universal(
|
219
|
+
self, service_types: List[str] = None, optimization_focus: OptimizationCategory = None
|
220
|
+
) -> Dict[str, Any]:
|
192
221
|
"""
|
193
222
|
Universal resource discovery across all AWS services.
|
194
|
-
|
223
|
+
|
195
224
|
Args:
|
196
225
|
service_types: List of AWS service names to discover (None = all)
|
197
226
|
optimization_focus: Focus on specific optimization category
|
198
|
-
|
227
|
+
|
199
228
|
Returns:
|
200
229
|
Comprehensive resource inventory with optimization opportunities
|
201
230
|
"""
|
202
231
|
print_header("Universal Resource Discovery", "Enterprise Multi-Service Analysis")
|
203
|
-
|
232
|
+
|
204
233
|
discovery_start_time = time.time()
|
205
234
|
service_types = service_types or ["EC2", "EBS", "S3", "RDS", "Lambda", "IAM"]
|
206
|
-
|
235
|
+
|
207
236
|
try:
|
208
237
|
with create_progress_bar() as progress:
|
209
238
|
# Step 1: Multi-region service discovery
|
210
239
|
discovery_task = progress.add_task("Discovering resources...", total=len(self.regions))
|
211
240
|
resources = await self._discover_resources_by_service(service_types, progress, discovery_task)
|
212
|
-
|
241
|
+
|
213
242
|
# Step 2: Optimization opportunity analysis
|
214
243
|
analysis_task = progress.add_task("Analyzing optimization opportunities...", total=len(resources))
|
215
244
|
optimization_opportunities = await self._analyze_optimization_opportunities(
|
216
245
|
resources, optimization_focus, progress, analysis_task
|
217
246
|
)
|
218
|
-
|
247
|
+
|
219
248
|
# Step 3: Cost calculation and business impact
|
220
249
|
calculation_task = progress.add_task("Calculating business impact...", total=1)
|
221
250
|
business_impact = await self._calculate_business_impact(
|
222
251
|
optimization_opportunities, progress, calculation_task
|
223
252
|
)
|
224
|
-
|
253
|
+
|
225
254
|
discovery_results = {
|
226
255
|
"total_resources_discovered": sum(len(resources[service]) for service in resources),
|
227
256
|
"services_analyzed": list(resources.keys()),
|
@@ -229,187 +258,201 @@ class UniversalAutomationEngine:
|
|
229
258
|
"optimization_opportunities": optimization_opportunities,
|
230
259
|
"business_impact": business_impact,
|
231
260
|
"execution_time_seconds": time.time() - discovery_start_time,
|
232
|
-
"analysis_timestamp": datetime.now()
|
261
|
+
"analysis_timestamp": datetime.now(),
|
233
262
|
}
|
234
|
-
|
263
|
+
|
235
264
|
# Display executive summary
|
236
265
|
self._display_discovery_summary(discovery_results)
|
237
|
-
|
266
|
+
|
238
267
|
return discovery_results
|
239
|
-
|
268
|
+
|
240
269
|
except Exception as e:
|
241
270
|
print_error(f"Universal resource discovery failed: {e}")
|
242
271
|
logger.error(f"Discovery error: {e}", exc_info=True)
|
243
272
|
raise
|
244
|
-
|
245
|
-
async def _discover_resources_by_service(
|
246
|
-
|
273
|
+
|
274
|
+
async def _discover_resources_by_service(
|
275
|
+
self, service_types: List[str], progress, task_id
|
276
|
+
) -> Dict[str, List[Dict[str, Any]]]:
|
247
277
|
"""Discover resources by AWS service type across regions."""
|
248
278
|
all_resources = {}
|
249
|
-
|
279
|
+
|
250
280
|
for region in self.regions:
|
251
281
|
try:
|
252
282
|
for service_type in service_types:
|
253
283
|
if service_type not in all_resources:
|
254
284
|
all_resources[service_type] = []
|
255
|
-
|
285
|
+
|
256
286
|
# Service-specific discovery logic
|
257
287
|
service_resources = await self._discover_service_resources(service_type, region)
|
258
288
|
all_resources[service_type].extend(service_resources)
|
259
|
-
|
289
|
+
|
260
290
|
print_info(f"Region {region}: {sum(len(all_resources[s]) for s in service_types)} resources discovered")
|
261
|
-
|
291
|
+
|
262
292
|
except ClientError as e:
|
263
293
|
print_warning(f"Region {region}: Access denied or region unavailable - {e.response['Error']['Code']}")
|
264
294
|
except Exception as e:
|
265
295
|
print_error(f"Region {region}: Discovery error - {str(e)}")
|
266
|
-
|
296
|
+
|
267
297
|
progress.advance(task_id)
|
268
|
-
|
298
|
+
|
269
299
|
return all_resources
|
270
|
-
|
300
|
+
|
271
301
|
async def _discover_service_resources(self, service_type: str, region: str) -> List[Dict[str, Any]]:
|
272
302
|
"""Discover resources for specific AWS service type."""
|
273
303
|
resources = []
|
274
|
-
|
304
|
+
|
275
305
|
try:
|
276
306
|
if service_type == "EC2":
|
277
|
-
ec2_client = self.session.client(
|
307
|
+
ec2_client = self.session.client("ec2", region_name=region)
|
278
308
|
response = ec2_client.describe_instances()
|
279
|
-
for reservation in response.get(
|
280
|
-
for instance in reservation.get(
|
281
|
-
resources.append(
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
309
|
+
for reservation in response.get("Reservations", []):
|
310
|
+
for instance in reservation.get("Instances", []):
|
311
|
+
resources.append(
|
312
|
+
{
|
313
|
+
"resource_id": instance.get("InstanceId"),
|
314
|
+
"resource_type": "EC2Instance",
|
315
|
+
"region": region,
|
316
|
+
"state": instance.get("State", {}).get("Name"),
|
317
|
+
"instance_type": instance.get("InstanceType"),
|
318
|
+
"tags": {tag["Key"]: tag["Value"] for tag in instance.get("Tags", [])},
|
319
|
+
"launch_time": instance.get("LaunchTime"),
|
320
|
+
}
|
321
|
+
)
|
322
|
+
|
291
323
|
elif service_type == "EBS":
|
292
|
-
ec2_client = self.session.client(
|
324
|
+
ec2_client = self.session.client("ec2", region_name=region)
|
293
325
|
response = ec2_client.describe_volumes()
|
294
|
-
for volume in response.get(
|
295
|
-
resources.append(
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
326
|
+
for volume in response.get("Volumes", []):
|
327
|
+
resources.append(
|
328
|
+
{
|
329
|
+
"resource_id": volume.get("VolumeId"),
|
330
|
+
"resource_type": "EBSVolume",
|
331
|
+
"region": region,
|
332
|
+
"state": volume.get("State"),
|
333
|
+
"volume_type": volume.get("VolumeType"),
|
334
|
+
"size": volume.get("Size"),
|
335
|
+
"tags": {tag["Key"]: tag["Value"] for tag in volume.get("Tags", [])},
|
336
|
+
"attachments": volume.get("Attachments", []),
|
337
|
+
}
|
338
|
+
)
|
339
|
+
|
306
340
|
elif service_type == "RDS":
|
307
|
-
rds_client = self.session.client(
|
341
|
+
rds_client = self.session.client("rds", region_name=region)
|
308
342
|
response = rds_client.describe_db_instances()
|
309
|
-
for db_instance in response.get(
|
310
|
-
resources.append(
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
343
|
+
for db_instance in response.get("DBInstances", []):
|
344
|
+
resources.append(
|
345
|
+
{
|
346
|
+
"resource_id": db_instance.get("DBInstanceIdentifier"),
|
347
|
+
"resource_type": "RDSInstance",
|
348
|
+
"region": region,
|
349
|
+
"status": db_instance.get("DBInstanceStatus"),
|
350
|
+
"instance_class": db_instance.get("DBInstanceClass"),
|
351
|
+
"engine": db_instance.get("Engine"),
|
352
|
+
"allocated_storage": db_instance.get("AllocatedStorage"),
|
353
|
+
"tags": [], # RDS tags require separate API call
|
354
|
+
}
|
355
|
+
)
|
356
|
+
|
321
357
|
elif service_type == "S3":
|
322
|
-
s3_client = self.session.client(
|
358
|
+
s3_client = self.session.client("s3")
|
323
359
|
response = s3_client.list_buckets()
|
324
|
-
for bucket in response.get(
|
360
|
+
for bucket in response.get("Buckets", []):
|
325
361
|
# Get bucket region
|
326
362
|
try:
|
327
|
-
bucket_region =
|
328
|
-
Bucket=bucket[
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
363
|
+
bucket_region = (
|
364
|
+
s3_client.get_bucket_location(Bucket=bucket["Name"]).get("LocationConstraint")
|
365
|
+
or "us-east-1"
|
366
|
+
)
|
367
|
+
|
368
|
+
if bucket_region == region or region == "us-east-1":
|
369
|
+
resources.append(
|
370
|
+
{
|
371
|
+
"resource_id": bucket["Name"],
|
372
|
+
"resource_type": "S3Bucket",
|
373
|
+
"region": bucket_region,
|
374
|
+
"creation_date": bucket.get("CreationDate"),
|
375
|
+
"tags": {}, # S3 tags require separate API call
|
376
|
+
}
|
377
|
+
)
|
339
378
|
except ClientError:
|
340
379
|
# Bucket region access denied - skip
|
341
380
|
pass
|
342
|
-
|
381
|
+
|
343
382
|
elif service_type == "Lambda":
|
344
|
-
lambda_client = self.session.client(
|
383
|
+
lambda_client = self.session.client("lambda", region_name=region)
|
345
384
|
response = lambda_client.list_functions()
|
346
|
-
for function in response.get(
|
347
|
-
resources.append(
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
385
|
+
for function in response.get("Functions", []):
|
386
|
+
resources.append(
|
387
|
+
{
|
388
|
+
"resource_id": function.get("FunctionName"),
|
389
|
+
"resource_type": "LambdaFunction",
|
390
|
+
"region": region,
|
391
|
+
"runtime": function.get("Runtime"),
|
392
|
+
"memory_size": function.get("MemorySize"),
|
393
|
+
"timeout": function.get("Timeout"),
|
394
|
+
"last_modified": function.get("LastModified"),
|
395
|
+
"tags": {}, # Lambda tags require separate API call
|
396
|
+
}
|
397
|
+
)
|
398
|
+
|
358
399
|
elif service_type == "IAM":
|
359
400
|
# IAM is global service - only process in us-east-1
|
360
|
-
if region ==
|
361
|
-
iam_client = self.session.client(
|
401
|
+
if region == "us-east-1":
|
402
|
+
iam_client = self.session.client("iam")
|
362
403
|
response = iam_client.list_users()
|
363
|
-
for user in response.get(
|
364
|
-
resources.append(
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
404
|
+
for user in response.get("Users", []):
|
405
|
+
resources.append(
|
406
|
+
{
|
407
|
+
"resource_id": user.get("UserName"),
|
408
|
+
"resource_type": "IAMUser",
|
409
|
+
"region": "global",
|
410
|
+
"path": user.get("Path"),
|
411
|
+
"create_date": user.get("CreateDate"),
|
412
|
+
"tags": [], # IAM tags require separate API call
|
413
|
+
}
|
414
|
+
)
|
415
|
+
|
373
416
|
except ClientError as e:
|
374
417
|
print_warning(f"Service {service_type} in {region}: {e.response['Error']['Code']}")
|
375
418
|
except Exception as e:
|
376
419
|
print_error(f"Service {service_type} in {region}: {str(e)}")
|
377
|
-
|
420
|
+
|
378
421
|
return resources
|
379
|
-
|
380
|
-
async def _analyze_optimization_opportunities(
|
381
|
-
|
382
|
-
|
422
|
+
|
423
|
+
async def _analyze_optimization_opportunities(
|
424
|
+
self, resources: Dict[str, List[Dict[str, Any]]], optimization_focus: OptimizationCategory, progress, task_id
|
425
|
+
) -> List[Dict[str, Any]]:
|
383
426
|
"""Analyze optimization opportunities across discovered resources."""
|
384
427
|
opportunities = []
|
385
|
-
|
428
|
+
|
386
429
|
for service_type, service_resources in resources.items():
|
387
430
|
try:
|
388
431
|
# Apply optimization pattern matching
|
389
432
|
for pattern in self.automation_patterns:
|
390
433
|
if optimization_focus and pattern.category != optimization_focus:
|
391
434
|
continue
|
392
|
-
|
435
|
+
|
393
436
|
if any(service in pattern.aws_services for service in [service_type]):
|
394
437
|
# Pattern matches - analyze resources for optimization
|
395
438
|
service_opportunities = await self._analyze_service_optimization(
|
396
439
|
service_type, service_resources, pattern
|
397
440
|
)
|
398
441
|
opportunities.extend(service_opportunities)
|
399
|
-
|
442
|
+
|
400
443
|
except Exception as e:
|
401
444
|
print_warning(f"Optimization analysis failed for {service_type}: {str(e)}")
|
402
|
-
|
445
|
+
|
403
446
|
progress.advance(task_id)
|
404
|
-
|
447
|
+
|
405
448
|
return opportunities
|
406
|
-
|
407
|
-
async def _analyze_service_optimization(
|
408
|
-
|
409
|
-
|
449
|
+
|
450
|
+
async def _analyze_service_optimization(
|
451
|
+
self, service_type: str, resources: List[Dict[str, Any]], pattern: AutomationPattern
|
452
|
+
) -> List[Dict[str, Any]]:
|
410
453
|
"""Analyze optimization opportunities for specific service type."""
|
411
454
|
opportunities = []
|
412
|
-
|
455
|
+
|
413
456
|
for resource in resources:
|
414
457
|
try:
|
415
458
|
optimization_opportunity = {
|
@@ -422,22 +465,22 @@ class UniversalAutomationEngine:
|
|
422
465
|
"potential_annual_savings": pattern.annual_savings_potential,
|
423
466
|
"recommended_action": self._get_recommended_action(resource, pattern),
|
424
467
|
"implementation_complexity": pattern.technical_complexity,
|
425
|
-
"safety_score": self._calculate_safety_score(resource, pattern)
|
468
|
+
"safety_score": self._calculate_safety_score(resource, pattern),
|
426
469
|
}
|
427
|
-
|
470
|
+
|
428
471
|
# Only include if there's actual optimization potential
|
429
472
|
if optimization_opportunity["recommended_action"] != "no_action":
|
430
473
|
opportunities.append(optimization_opportunity)
|
431
|
-
|
474
|
+
|
432
475
|
except Exception as e:
|
433
476
|
logger.warning(f"Optimization analysis failed for resource {resource.get('resource_id')}: {e}")
|
434
|
-
|
477
|
+
|
435
478
|
return opportunities
|
436
|
-
|
479
|
+
|
437
480
|
def _get_recommended_action(self, resource: Dict[str, Any], pattern: AutomationPattern) -> str:
|
438
481
|
"""Get recommended optimization action for resource."""
|
439
482
|
resource_type = resource.get("resource_type")
|
440
|
-
|
483
|
+
|
441
484
|
# Cost optimization recommendations
|
442
485
|
if pattern.category == OptimizationCategory.COST_OPTIMIZATION:
|
443
486
|
if resource_type == "EC2Instance":
|
@@ -446,175 +489,170 @@ class UniversalAutomationEngine:
|
|
446
489
|
elif not resource.get("tags"):
|
447
490
|
return "evaluate_untagged_instance"
|
448
491
|
return "evaluate_rightsizing"
|
449
|
-
|
492
|
+
|
450
493
|
elif resource_type == "EBSVolume":
|
451
494
|
if not resource.get("attachments"):
|
452
495
|
return "delete_unattached_volume"
|
453
496
|
elif resource.get("volume_type") == "gp2":
|
454
497
|
return "convert_gp2_to_gp3"
|
455
498
|
return "evaluate_volume_usage"
|
456
|
-
|
499
|
+
|
457
500
|
elif resource_type == "RDSInstance":
|
458
501
|
if resource.get("status") == "stopped":
|
459
502
|
return "evaluate_idle_database"
|
460
503
|
return "evaluate_instance_class"
|
461
|
-
|
504
|
+
|
462
505
|
# Security optimization recommendations
|
463
506
|
elif pattern.category == OptimizationCategory.SECURITY_COMPLIANCE:
|
464
507
|
if resource_type == "IAMUser":
|
465
508
|
return "audit_access_keys"
|
466
509
|
elif resource_type == "S3Bucket":
|
467
510
|
return "audit_bucket_permissions"
|
468
|
-
|
511
|
+
|
469
512
|
return "no_action"
|
470
|
-
|
513
|
+
|
471
514
|
def _calculate_safety_score(self, resource: Dict[str, Any], pattern: AutomationPattern) -> float:
|
472
515
|
"""Calculate safety score for optimization action (0.0 = high risk, 1.0 = safe)."""
|
473
516
|
base_score = 0.7 # Conservative baseline
|
474
|
-
|
517
|
+
|
475
518
|
# Increase safety for resources with proper tagging
|
476
519
|
if resource.get("tags"):
|
477
520
|
base_score += 0.2
|
478
|
-
|
521
|
+
|
479
522
|
# Decrease safety for running/active resources
|
480
523
|
if resource.get("state") == "running" or resource.get("status") == "available":
|
481
524
|
base_score -= 0.1
|
482
|
-
|
525
|
+
|
483
526
|
# Pattern-specific adjustments
|
484
527
|
if pattern.category == OptimizationCategory.COST_OPTIMIZATION:
|
485
528
|
if "delete" in self._get_recommended_action(resource, pattern):
|
486
529
|
base_score -= 0.2 # Deletion is higher risk
|
487
|
-
|
530
|
+
|
488
531
|
return max(0.0, min(1.0, base_score)) # Clamp between 0.0 and 1.0
|
489
|
-
|
490
|
-
async def _calculate_business_impact(
|
491
|
-
|
532
|
+
|
533
|
+
async def _calculate_business_impact(
|
534
|
+
self, opportunities: List[Dict[str, Any]], progress, task_id
|
535
|
+
) -> Dict[str, Any]:
|
492
536
|
"""Calculate comprehensive business impact from optimization opportunities."""
|
493
537
|
total_potential_savings = 0.0
|
494
538
|
impact_by_category = {}
|
495
539
|
high_impact_opportunities = 0
|
496
|
-
|
540
|
+
|
497
541
|
for opportunity in opportunities:
|
498
542
|
# Calculate potential savings (take conservative estimate)
|
499
543
|
min_savings, max_savings = opportunity["potential_annual_savings"]
|
500
544
|
conservative_savings = min_savings * 0.3 # 30% of minimum estimate
|
501
545
|
total_potential_savings += conservative_savings
|
502
|
-
|
546
|
+
|
503
547
|
# Categorize impact
|
504
548
|
category = opportunity["category"]
|
505
549
|
if category not in impact_by_category:
|
506
|
-
impact_by_category[category] = {
|
507
|
-
|
508
|
-
"potential_savings": 0.0,
|
509
|
-
"high_impact_count": 0
|
510
|
-
}
|
511
|
-
|
550
|
+
impact_by_category[category] = {"count": 0, "potential_savings": 0.0, "high_impact_count": 0}
|
551
|
+
|
512
552
|
impact_by_category[category]["count"] += 1
|
513
553
|
impact_by_category[category]["potential_savings"] += conservative_savings
|
514
|
-
|
554
|
+
|
515
555
|
if opportunity["business_impact"] == "high":
|
516
556
|
high_impact_opportunities += 1
|
517
557
|
impact_by_category[category]["high_impact_count"] += 1
|
518
|
-
|
558
|
+
|
519
559
|
progress.advance(task_id)
|
520
|
-
|
560
|
+
|
521
561
|
return {
|
522
562
|
"total_opportunities": len(opportunities),
|
523
563
|
"high_impact_opportunities": high_impact_opportunities,
|
524
564
|
"total_potential_annual_savings": total_potential_savings,
|
525
565
|
"impact_by_category": impact_by_category,
|
526
|
-
"roi_timeline_months": 3 if total_potential_savings > 100_000 else 6
|
566
|
+
"roi_timeline_months": 3 if total_potential_savings > 100_000 else 6,
|
527
567
|
}
|
528
|
-
|
568
|
+
|
529
569
|
def _display_discovery_summary(self, results: Dict[str, Any]) -> None:
|
530
570
|
"""Display executive summary of universal resource discovery."""
|
531
|
-
|
571
|
+
|
532
572
|
# Executive Summary Panel
|
533
573
|
business_impact = results["business_impact"]
|
534
574
|
summary_content = f"""
|
535
575
|
🌐 Universal Resource Discovery Results
|
536
576
|
|
537
577
|
📊 Infrastructure Analysis:
|
538
|
-
• Total Resources Discovered: {results[
|
539
|
-
• Services Analyzed: {
|
540
|
-
• Regions Covered: {
|
541
|
-
• Optimization Opportunities: {business_impact[
|
578
|
+
• Total Resources Discovered: {results["total_resources_discovered"]:,}
|
579
|
+
• Services Analyzed: {", ".join(results["services_analyzed"])}
|
580
|
+
• Regions Covered: {", ".join(results["regions_covered"])}
|
581
|
+
• Optimization Opportunities: {business_impact["total_opportunities"]:,}
|
542
582
|
|
543
583
|
💰 Business Impact Analysis:
|
544
|
-
• High-Impact Opportunities: {business_impact[
|
545
|
-
• Total Potential Annual Savings: {format_cost(business_impact[
|
546
|
-
• ROI Timeline: {business_impact[
|
547
|
-
• Analysis Execution Time: {results[
|
584
|
+
• High-Impact Opportunities: {business_impact["high_impact_opportunities"]:,}
|
585
|
+
• Total Potential Annual Savings: {format_cost(business_impact["total_potential_annual_savings"])}
|
586
|
+
• ROI Timeline: {business_impact["roi_timeline_months"]} months
|
587
|
+
• Analysis Execution Time: {results["execution_time_seconds"]:.2f}s
|
548
588
|
|
549
589
|
🎯 Strategic Recommendations:
|
550
590
|
• Priority Focus: Cost optimization opportunities with immediate impact
|
551
591
|
• Implementation Approach: Systematic automation using consolidated patterns
|
552
592
|
• Safety Controls: Enterprise approval workflows with audit trails
|
553
593
|
"""
|
554
|
-
|
555
|
-
console.print(
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
)
|
560
|
-
|
594
|
+
|
595
|
+
console.print(
|
596
|
+
create_panel(
|
597
|
+
summary_content.strip(), title="🏆 Universal Resource Discovery Executive Summary", border_style="green"
|
598
|
+
)
|
599
|
+
)
|
600
|
+
|
561
601
|
# Category Breakdown Table
|
562
602
|
if business_impact["impact_by_category"]:
|
563
|
-
table = create_table(
|
564
|
-
|
565
|
-
)
|
566
|
-
|
603
|
+
table = create_table(title="Optimization Opportunities by Category")
|
604
|
+
|
567
605
|
table.add_column("Category", style="cyan", no_wrap=True)
|
568
606
|
table.add_column("Opportunities", justify="center")
|
569
607
|
table.add_column("High Impact", justify="center", style="red")
|
570
608
|
table.add_column("Potential Savings", justify="right", style="green")
|
571
609
|
table.add_column("Implementation", justify="center", style="dim")
|
572
|
-
|
610
|
+
|
573
611
|
for category, impact_data in business_impact["impact_by_category"].items():
|
574
612
|
category_display = category.replace("_", " ").title()
|
575
|
-
|
613
|
+
|
576
614
|
table.add_row(
|
577
615
|
category_display,
|
578
616
|
str(impact_data["count"]),
|
579
617
|
str(impact_data["high_impact_count"]),
|
580
618
|
format_cost(impact_data["potential_savings"]),
|
581
|
-
"2-4 weeks"
|
619
|
+
"2-4 weeks",
|
582
620
|
)
|
583
|
-
|
621
|
+
|
584
622
|
console.print(table)
|
585
|
-
|
623
|
+
|
586
624
|
async def validate_with_mcp(self, results: Dict[str, Any]) -> float:
|
587
625
|
"""Validate discovery results with embedded MCP validator."""
|
588
626
|
try:
|
589
627
|
# Prepare validation data in FinOps format
|
590
628
|
validation_data = {
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
629
|
+
"total_opportunities": results["business_impact"]["total_opportunities"],
|
630
|
+
"potential_annual_savings": results["business_impact"]["total_potential_annual_savings"],
|
631
|
+
"resources_analyzed": results["total_resources_discovered"],
|
632
|
+
"services_covered": results["services_analyzed"],
|
633
|
+
"analysis_timestamp": results["analysis_timestamp"].isoformat(),
|
596
634
|
}
|
597
|
-
|
635
|
+
|
598
636
|
# Initialize MCP validator if profile is available
|
599
637
|
if self.profile_name:
|
600
638
|
mcp_validator = EmbeddedMCPValidator([self.profile_name])
|
601
639
|
validation_results = await mcp_validator.validate_cost_data_async(validation_data)
|
602
|
-
accuracy = validation_results.get(
|
603
|
-
|
640
|
+
accuracy = validation_results.get("total_accuracy", 0.0)
|
641
|
+
|
604
642
|
if accuracy >= 99.5:
|
605
643
|
print_success(f"MCP Validation: {accuracy:.1f}% accuracy achieved (target: ≥99.5%)")
|
606
644
|
else:
|
607
645
|
print_warning(f"MCP Validation: {accuracy:.1f}% accuracy (target: ≥99.5%)")
|
608
|
-
|
646
|
+
|
609
647
|
return accuracy
|
610
648
|
else:
|
611
649
|
print_info("MCP validation skipped - no profile specified")
|
612
650
|
return 0.0
|
613
|
-
|
651
|
+
|
614
652
|
except Exception as e:
|
615
653
|
print_warning(f"MCP validation failed: {str(e)}")
|
616
654
|
return 0.0
|
617
|
-
|
655
|
+
|
618
656
|
def get_automation_patterns(self, category: OptimizationCategory = None) -> List[AutomationPattern]:
|
619
657
|
"""Get automation patterns, optionally filtered by category."""
|
620
658
|
if category:
|
@@ -628,16 +666,15 @@ def get_universal_automation_engine(profile: str = None, regions: List[str] = No
|
|
628
666
|
return UniversalAutomationEngine(profile_name=profile, regions=regions)
|
629
667
|
|
630
668
|
|
631
|
-
if __name__ ==
|
669
|
+
if __name__ == "__main__":
|
632
670
|
# Test universal automation engine
|
633
671
|
import asyncio
|
634
|
-
|
672
|
+
|
635
673
|
async def test_discovery():
|
636
674
|
engine = UniversalAutomationEngine()
|
637
675
|
results = await engine.discover_resources_universal(
|
638
|
-
service_types=["EC2", "EBS"],
|
639
|
-
optimization_focus=OptimizationCategory.COST_OPTIMIZATION
|
676
|
+
service_types=["EC2", "EBS"], optimization_focus=OptimizationCategory.COST_OPTIMIZATION
|
640
677
|
)
|
641
678
|
print(f"Discovery completed: {results['total_resources_discovered']} resources analyzed")
|
642
|
-
|
643
|
-
asyncio.run(test_discovery())
|
679
|
+
|
680
|
+
asyncio.run(test_discovery())
|