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.
Files changed (273) hide show
  1. runbooks/__init__.py +31 -2
  2. runbooks/__init___optimized.py +18 -4
  3. runbooks/_platform/__init__.py +1 -5
  4. runbooks/_platform/core/runbooks_wrapper.py +141 -138
  5. runbooks/aws2/accuracy_validator.py +812 -0
  6. runbooks/base.py +7 -0
  7. runbooks/cfat/assessment/compliance.py +1 -1
  8. runbooks/cfat/assessment/runner.py +1 -0
  9. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  10. runbooks/cli/__init__.py +1 -1
  11. runbooks/cli/commands/cfat.py +64 -23
  12. runbooks/cli/commands/finops.py +1005 -54
  13. runbooks/cli/commands/inventory.py +135 -91
  14. runbooks/cli/commands/operate.py +9 -36
  15. runbooks/cli/commands/security.py +42 -18
  16. runbooks/cli/commands/validation.py +432 -18
  17. runbooks/cli/commands/vpc.py +81 -17
  18. runbooks/cli/registry.py +22 -10
  19. runbooks/cloudops/__init__.py +20 -27
  20. runbooks/cloudops/base.py +96 -107
  21. runbooks/cloudops/cost_optimizer.py +544 -542
  22. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  23. runbooks/cloudops/interfaces.py +224 -225
  24. runbooks/cloudops/lifecycle_manager.py +5 -4
  25. runbooks/cloudops/mcp_cost_validation.py +252 -235
  26. runbooks/cloudops/models.py +78 -53
  27. runbooks/cloudops/monitoring_automation.py +5 -4
  28. runbooks/cloudops/notebook_framework.py +177 -213
  29. runbooks/cloudops/security_enforcer.py +125 -159
  30. runbooks/common/accuracy_validator.py +17 -12
  31. runbooks/common/aws_pricing.py +349 -326
  32. runbooks/common/aws_pricing_api.py +211 -212
  33. runbooks/common/aws_profile_manager.py +40 -36
  34. runbooks/common/aws_utils.py +74 -79
  35. runbooks/common/business_logic.py +126 -104
  36. runbooks/common/cli_decorators.py +36 -60
  37. runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
  38. runbooks/common/cross_account_manager.py +197 -204
  39. runbooks/common/date_utils.py +27 -39
  40. runbooks/common/decorators.py +29 -19
  41. runbooks/common/dry_run_examples.py +173 -208
  42. runbooks/common/dry_run_framework.py +157 -155
  43. runbooks/common/enhanced_exception_handler.py +15 -4
  44. runbooks/common/enhanced_logging_example.py +50 -64
  45. runbooks/common/enhanced_logging_integration_example.py +65 -37
  46. runbooks/common/env_utils.py +16 -16
  47. runbooks/common/error_handling.py +40 -38
  48. runbooks/common/lazy_loader.py +41 -23
  49. runbooks/common/logging_integration_helper.py +79 -86
  50. runbooks/common/mcp_cost_explorer_integration.py +476 -493
  51. runbooks/common/mcp_integration.py +99 -79
  52. runbooks/common/memory_optimization.py +140 -118
  53. runbooks/common/module_cli_base.py +37 -58
  54. runbooks/common/organizations_client.py +175 -193
  55. runbooks/common/patterns.py +23 -25
  56. runbooks/common/performance_monitoring.py +67 -71
  57. runbooks/common/performance_optimization_engine.py +283 -274
  58. runbooks/common/profile_utils.py +111 -37
  59. runbooks/common/rich_utils.py +315 -141
  60. runbooks/common/sre_performance_suite.py +177 -186
  61. runbooks/enterprise/__init__.py +1 -1
  62. runbooks/enterprise/logging.py +144 -106
  63. runbooks/enterprise/security.py +187 -204
  64. runbooks/enterprise/validation.py +43 -56
  65. runbooks/finops/__init__.py +26 -30
  66. runbooks/finops/account_resolver.py +1 -1
  67. runbooks/finops/advanced_optimization_engine.py +980 -0
  68. runbooks/finops/automation_core.py +268 -231
  69. runbooks/finops/business_case_config.py +184 -179
  70. runbooks/finops/cli.py +660 -139
  71. runbooks/finops/commvault_ec2_analysis.py +157 -164
  72. runbooks/finops/compute_cost_optimizer.py +336 -320
  73. runbooks/finops/config.py +20 -20
  74. runbooks/finops/cost_optimizer.py +484 -618
  75. runbooks/finops/cost_processor.py +332 -214
  76. runbooks/finops/dashboard_runner.py +1006 -172
  77. runbooks/finops/ebs_cost_optimizer.py +991 -657
  78. runbooks/finops/elastic_ip_optimizer.py +317 -257
  79. runbooks/finops/enhanced_mcp_integration.py +340 -0
  80. runbooks/finops/enhanced_progress.py +32 -29
  81. runbooks/finops/enhanced_trend_visualization.py +3 -2
  82. runbooks/finops/enterprise_wrappers.py +223 -285
  83. runbooks/finops/executive_export.py +203 -160
  84. runbooks/finops/helpers.py +130 -288
  85. runbooks/finops/iam_guidance.py +1 -1
  86. runbooks/finops/infrastructure/__init__.py +80 -0
  87. runbooks/finops/infrastructure/commands.py +506 -0
  88. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  89. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  90. runbooks/finops/markdown_exporter.py +337 -174
  91. runbooks/finops/mcp_validator.py +1952 -0
  92. runbooks/finops/nat_gateway_optimizer.py +1512 -481
  93. runbooks/finops/network_cost_optimizer.py +657 -587
  94. runbooks/finops/notebook_utils.py +226 -188
  95. runbooks/finops/optimization_engine.py +1136 -0
  96. runbooks/finops/optimizer.py +19 -23
  97. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  98. runbooks/finops/reservation_optimizer.py +427 -363
  99. runbooks/finops/scenario_cli_integration.py +64 -65
  100. runbooks/finops/scenarios.py +1277 -438
  101. runbooks/finops/schemas.py +218 -182
  102. runbooks/finops/snapshot_manager.py +2289 -0
  103. runbooks/finops/types.py +3 -3
  104. runbooks/finops/validation_framework.py +259 -265
  105. runbooks/finops/vpc_cleanup_exporter.py +189 -144
  106. runbooks/finops/vpc_cleanup_optimizer.py +591 -573
  107. runbooks/finops/workspaces_analyzer.py +171 -182
  108. runbooks/integration/__init__.py +89 -0
  109. runbooks/integration/mcp_integration.py +1920 -0
  110. runbooks/inventory/CLAUDE.md +816 -0
  111. runbooks/inventory/__init__.py +2 -2
  112. runbooks/inventory/aws_decorators.py +2 -3
  113. runbooks/inventory/check_cloudtrail_compliance.py +2 -4
  114. runbooks/inventory/check_controltower_readiness.py +152 -151
  115. runbooks/inventory/check_landingzone_readiness.py +85 -84
  116. runbooks/inventory/cloud_foundations_integration.py +144 -149
  117. runbooks/inventory/collectors/aws_comprehensive.py +1 -1
  118. runbooks/inventory/collectors/aws_networking.py +109 -99
  119. runbooks/inventory/collectors/base.py +4 -0
  120. runbooks/inventory/core/collector.py +495 -313
  121. runbooks/inventory/core/formatter.py +11 -0
  122. runbooks/inventory/draw_org_structure.py +8 -9
  123. runbooks/inventory/drift_detection_cli.py +69 -96
  124. runbooks/inventory/ec2_vpc_utils.py +2 -2
  125. runbooks/inventory/find_cfn_drift_detection.py +5 -7
  126. runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
  127. runbooks/inventory/find_cfn_stackset_drift.py +5 -6
  128. runbooks/inventory/find_ec2_security_groups.py +48 -42
  129. runbooks/inventory/find_landingzone_versions.py +4 -6
  130. runbooks/inventory/find_vpc_flow_logs.py +7 -9
  131. runbooks/inventory/inventory_mcp_cli.py +48 -46
  132. runbooks/inventory/inventory_modules.py +103 -91
  133. runbooks/inventory/list_cfn_stacks.py +9 -10
  134. runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
  135. runbooks/inventory/list_cfn_stackset_operations.py +79 -57
  136. runbooks/inventory/list_cfn_stacksets.py +8 -10
  137. runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
  138. runbooks/inventory/list_ds_directories.py +65 -53
  139. runbooks/inventory/list_ec2_availability_zones.py +2 -4
  140. runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
  141. runbooks/inventory/list_ec2_instances.py +23 -28
  142. runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
  143. runbooks/inventory/list_elbs_load_balancers.py +22 -20
  144. runbooks/inventory/list_enis_network_interfaces.py +26 -33
  145. runbooks/inventory/list_guardduty_detectors.py +2 -4
  146. runbooks/inventory/list_iam_policies.py +2 -4
  147. runbooks/inventory/list_iam_roles.py +5 -7
  148. runbooks/inventory/list_iam_saml_providers.py +4 -6
  149. runbooks/inventory/list_lambda_functions.py +38 -38
  150. runbooks/inventory/list_org_accounts.py +6 -8
  151. runbooks/inventory/list_org_accounts_users.py +55 -44
  152. runbooks/inventory/list_rds_db_instances.py +31 -33
  153. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  154. runbooks/inventory/list_route53_hosted_zones.py +3 -5
  155. runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
  156. runbooks/inventory/list_sns_topics.py +2 -4
  157. runbooks/inventory/list_ssm_parameters.py +4 -7
  158. runbooks/inventory/list_vpc_subnets.py +2 -4
  159. runbooks/inventory/list_vpcs.py +7 -10
  160. runbooks/inventory/mcp_inventory_validator.py +554 -468
  161. runbooks/inventory/mcp_vpc_validator.py +359 -442
  162. runbooks/inventory/organizations_discovery.py +63 -55
  163. runbooks/inventory/recover_cfn_stack_ids.py +7 -8
  164. runbooks/inventory/requirements.txt +0 -1
  165. runbooks/inventory/rich_inventory_display.py +35 -34
  166. runbooks/inventory/run_on_multi_accounts.py +3 -5
  167. runbooks/inventory/unified_validation_engine.py +281 -253
  168. runbooks/inventory/verify_ec2_security_groups.py +1 -1
  169. runbooks/inventory/vpc_analyzer.py +735 -697
  170. runbooks/inventory/vpc_architecture_validator.py +293 -348
  171. runbooks/inventory/vpc_dependency_analyzer.py +384 -380
  172. runbooks/inventory/vpc_flow_analyzer.py +1 -1
  173. runbooks/main.py +49 -34
  174. runbooks/main_final.py +91 -60
  175. runbooks/main_minimal.py +22 -10
  176. runbooks/main_optimized.py +131 -100
  177. runbooks/main_ultra_minimal.py +7 -2
  178. runbooks/mcp/__init__.py +36 -0
  179. runbooks/mcp/integration.py +679 -0
  180. runbooks/monitoring/performance_monitor.py +9 -4
  181. runbooks/operate/dynamodb_operations.py +3 -1
  182. runbooks/operate/ec2_operations.py +145 -137
  183. runbooks/operate/iam_operations.py +146 -152
  184. runbooks/operate/networking_cost_heatmap.py +29 -8
  185. runbooks/operate/rds_operations.py +223 -254
  186. runbooks/operate/s3_operations.py +107 -118
  187. runbooks/operate/vpc_operations.py +646 -616
  188. runbooks/remediation/base.py +1 -1
  189. runbooks/remediation/commons.py +10 -7
  190. runbooks/remediation/commvault_ec2_analysis.py +70 -66
  191. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  192. runbooks/remediation/multi_account.py +24 -21
  193. runbooks/remediation/rds_snapshot_list.py +86 -60
  194. runbooks/remediation/remediation_cli.py +92 -146
  195. runbooks/remediation/universal_account_discovery.py +83 -79
  196. runbooks/remediation/workspaces_list.py +46 -41
  197. runbooks/security/__init__.py +19 -0
  198. runbooks/security/assessment_runner.py +1150 -0
  199. runbooks/security/baseline_checker.py +812 -0
  200. runbooks/security/cloudops_automation_security_validator.py +509 -535
  201. runbooks/security/compliance_automation_engine.py +17 -17
  202. runbooks/security/config/__init__.py +2 -2
  203. runbooks/security/config/compliance_config.py +50 -50
  204. runbooks/security/config_template_generator.py +63 -76
  205. runbooks/security/enterprise_security_framework.py +1 -1
  206. runbooks/security/executive_security_dashboard.py +519 -508
  207. runbooks/security/multi_account_security_controls.py +959 -1210
  208. runbooks/security/real_time_security_monitor.py +422 -444
  209. runbooks/security/security_baseline_tester.py +1 -1
  210. runbooks/security/security_cli.py +143 -112
  211. runbooks/security/test_2way_validation.py +439 -0
  212. runbooks/security/two_way_validation_framework.py +852 -0
  213. runbooks/sre/production_monitoring_framework.py +167 -177
  214. runbooks/tdd/__init__.py +15 -0
  215. runbooks/tdd/cli.py +1071 -0
  216. runbooks/utils/__init__.py +14 -17
  217. runbooks/utils/logger.py +7 -2
  218. runbooks/utils/version_validator.py +50 -47
  219. runbooks/validation/__init__.py +6 -6
  220. runbooks/validation/cli.py +9 -3
  221. runbooks/validation/comprehensive_2way_validator.py +745 -704
  222. runbooks/validation/mcp_validator.py +906 -228
  223. runbooks/validation/terraform_citations_validator.py +104 -115
  224. runbooks/validation/terraform_drift_detector.py +461 -454
  225. runbooks/vpc/README.md +617 -0
  226. runbooks/vpc/__init__.py +8 -1
  227. runbooks/vpc/analyzer.py +577 -0
  228. runbooks/vpc/cleanup_wrapper.py +476 -413
  229. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  230. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  231. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  232. runbooks/vpc/config.py +92 -97
  233. runbooks/vpc/cost_engine.py +411 -148
  234. runbooks/vpc/cost_explorer_integration.py +553 -0
  235. runbooks/vpc/cross_account_session.py +101 -106
  236. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  237. runbooks/vpc/eni_gate_validator.py +961 -0
  238. runbooks/vpc/heatmap_engine.py +185 -160
  239. runbooks/vpc/mcp_no_eni_validator.py +680 -639
  240. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  241. runbooks/vpc/networking_wrapper.py +15 -8
  242. runbooks/vpc/pdca_remediation_planner.py +528 -0
  243. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  244. runbooks/vpc/runbooks_adapter.py +1167 -241
  245. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  246. runbooks/vpc/test_data_loader.py +358 -0
  247. runbooks/vpc/tests/conftest.py +314 -4
  248. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  249. runbooks/vpc/tests/test_cost_engine.py +0 -2
  250. runbooks/vpc/topology_generator.py +326 -0
  251. runbooks/vpc/unified_scenarios.py +1297 -1124
  252. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  253. runbooks-1.1.6.dist-info/METADATA +327 -0
  254. runbooks-1.1.6.dist-info/RECORD +489 -0
  255. runbooks/finops/README.md +0 -414
  256. runbooks/finops/accuracy_cross_validator.py +0 -647
  257. runbooks/finops/business_cases.py +0 -950
  258. runbooks/finops/dashboard_router.py +0 -922
  259. runbooks/finops/ebs_optimizer.py +0 -973
  260. runbooks/finops/embedded_mcp_validator.py +0 -1629
  261. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  262. runbooks/finops/finops_dashboard.py +0 -584
  263. runbooks/finops/finops_scenarios.py +0 -1218
  264. runbooks/finops/legacy_migration.py +0 -730
  265. runbooks/finops/multi_dashboard.py +0 -1519
  266. runbooks/finops/single_dashboard.py +0 -1113
  267. runbooks/finops/unlimited_scenarios.py +0 -393
  268. runbooks-1.1.4.dist-info/METADATA +0 -800
  269. runbooks-1.1.4.dist-info/RECORD +0 -468
  270. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
  271. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
  272. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
  273. {runbooks-1.1.4.dist-info → runbooks-1.1.6.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
- console, print_header, print_success, print_error, print_warning, print_info,
39
- create_table, create_progress_bar, format_cost, create_panel, STATUS_INDICATORS
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 .embedded_mcp_validator import EmbeddedMCPValidator
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
- HIGH = "high" # >$1M annual impact
59
- MEDIUM = "medium" # $100K-$1M annual impact
60
- LOW = "low" # <$100K annual impact
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
- 'us-east-1', 'us-west-2', 'us-east-2', 'us-west-1',
94
- 'eu-west-1', 'eu-central-1', 'ap-southeast-1', 'ap-northeast-1'
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
- profile_name=get_profile_for_operation("operational", profile_name)
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
- 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2',
108
- 'af-south-1', 'ap-east-1', 'ap-south-1', 'ap-northeast-1',
109
- 'ap-northeast-2', 'ap-northeast-3', 'ap-southeast-1', 'ap-southeast-2',
110
- 'ca-central-1', 'eu-central-1', 'eu-west-1', 'eu-west-2',
111
- 'eu-west-3', 'eu-south-1', 'eu-north-1', 'me-south-1',
112
- 'sa-east-1'
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(self,
190
- service_types: List[str] = None,
191
- optimization_focus: OptimizationCategory = None) -> Dict[str, Any]:
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(self, service_types: List[str],
246
- progress, task_id) -> Dict[str, List[Dict[str, Any]]]:
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('ec2', region_name=region)
307
+ ec2_client = self.session.client("ec2", region_name=region)
278
308
  response = ec2_client.describe_instances()
279
- for reservation in response.get('Reservations', []):
280
- for instance in reservation.get('Instances', []):
281
- resources.append({
282
- "resource_id": instance.get('InstanceId'),
283
- "resource_type": "EC2Instance",
284
- "region": region,
285
- "state": instance.get('State', {}).get('Name'),
286
- "instance_type": instance.get('InstanceType'),
287
- "tags": {tag['Key']: tag['Value'] for tag in instance.get('Tags', [])},
288
- "launch_time": instance.get('LaunchTime')
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('ec2', region_name=region)
324
+ ec2_client = self.session.client("ec2", region_name=region)
293
325
  response = ec2_client.describe_volumes()
294
- for volume in response.get('Volumes', []):
295
- resources.append({
296
- "resource_id": volume.get('VolumeId'),
297
- "resource_type": "EBSVolume",
298
- "region": region,
299
- "state": volume.get('State'),
300
- "volume_type": volume.get('VolumeType'),
301
- "size": volume.get('Size'),
302
- "tags": {tag['Key']: tag['Value'] for tag in volume.get('Tags', [])},
303
- "attachments": volume.get('Attachments', [])
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('rds', region_name=region)
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('DBInstances', []):
310
- resources.append({
311
- "resource_id": db_instance.get('DBInstanceIdentifier'),
312
- "resource_type": "RDSInstance",
313
- "region": region,
314
- "status": db_instance.get('DBInstanceStatus'),
315
- "instance_class": db_instance.get('DBInstanceClass'),
316
- "engine": db_instance.get('Engine'),
317
- "allocated_storage": db_instance.get('AllocatedStorage'),
318
- "tags": [] # RDS tags require separate API call
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('s3')
358
+ s3_client = self.session.client("s3")
323
359
  response = s3_client.list_buckets()
324
- for bucket in response.get('Buckets', []):
360
+ for bucket in response.get("Buckets", []):
325
361
  # Get bucket region
326
362
  try:
327
- bucket_region = s3_client.get_bucket_location(
328
- Bucket=bucket['Name']
329
- ).get('LocationConstraint') or 'us-east-1'
330
-
331
- if bucket_region == region or region == 'us-east-1':
332
- resources.append({
333
- "resource_id": bucket['Name'],
334
- "resource_type": "S3Bucket",
335
- "region": bucket_region,
336
- "creation_date": bucket.get('CreationDate'),
337
- "tags": {} # S3 tags require separate API call
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('lambda', region_name=region)
383
+ lambda_client = self.session.client("lambda", region_name=region)
345
384
  response = lambda_client.list_functions()
346
- for function in response.get('Functions', []):
347
- resources.append({
348
- "resource_id": function.get('FunctionName'),
349
- "resource_type": "LambdaFunction",
350
- "region": region,
351
- "runtime": function.get('Runtime'),
352
- "memory_size": function.get('MemorySize'),
353
- "timeout": function.get('Timeout'),
354
- "last_modified": function.get('LastModified'),
355
- "tags": {} # Lambda tags require separate API call
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 == 'us-east-1':
361
- iam_client = self.session.client('iam')
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('Users', []):
364
- resources.append({
365
- "resource_id": user.get('UserName'),
366
- "resource_type": "IAMUser",
367
- "region": "global",
368
- "path": user.get('Path'),
369
- "create_date": user.get('CreateDate'),
370
- "tags": [] # IAM tags require separate API call
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(self, resources: Dict[str, List[Dict[str, Any]]],
381
- optimization_focus: OptimizationCategory,
382
- progress, task_id) -> List[Dict[str, Any]]:
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(self, service_type: str,
408
- resources: List[Dict[str, Any]],
409
- pattern: AutomationPattern) -> List[Dict[str, Any]]:
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(self, opportunities: List[Dict[str, Any]],
491
- progress, task_id) -> Dict[str, Any]:
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
- "count": 0,
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['total_resources_discovered']:,}
539
- • Services Analyzed: {', '.join(results['services_analyzed'])}
540
- • Regions Covered: {', '.join(results['regions_covered'])}
541
- • Optimization Opportunities: {business_impact['total_opportunities']:,}
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['high_impact_opportunities']:,}
545
- • Total Potential Annual Savings: {format_cost(business_impact['total_potential_annual_savings'])}
546
- • ROI Timeline: {business_impact['roi_timeline_months']} months
547
- • Analysis Execution Time: {results['execution_time_seconds']:.2f}s
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(create_panel(
556
- summary_content.strip(),
557
- title="🏆 Universal Resource Discovery Executive Summary",
558
- border_style="green"
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
- title="Optimization Opportunities by Category"
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
- 'total_opportunities': results["business_impact"]["total_opportunities"],
592
- 'potential_annual_savings': results["business_impact"]["total_potential_annual_savings"],
593
- 'resources_analyzed': results["total_resources_discovered"],
594
- 'services_covered': results["services_analyzed"],
595
- 'analysis_timestamp': results["analysis_timestamp"].isoformat()
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('total_accuracy', 0.0)
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__ == '__main__':
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())