runbooks 1.1.4__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.
Files changed (228) 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 +138 -35
  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 +11 -0
  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 +63 -74
  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 +201 -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/cloud_foundations_integration.py +144 -149
  113. runbooks/inventory/collectors/aws_comprehensive.py +1 -1
  114. runbooks/inventory/collectors/aws_networking.py +109 -99
  115. runbooks/inventory/collectors/base.py +4 -0
  116. runbooks/inventory/core/collector.py +495 -313
  117. runbooks/inventory/drift_detection_cli.py +69 -96
  118. runbooks/inventory/inventory_mcp_cli.py +48 -46
  119. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  120. runbooks/inventory/mcp_inventory_validator.py +549 -465
  121. runbooks/inventory/mcp_vpc_validator.py +359 -442
  122. runbooks/inventory/organizations_discovery.py +55 -51
  123. runbooks/inventory/rich_inventory_display.py +33 -32
  124. runbooks/inventory/unified_validation_engine.py +278 -251
  125. runbooks/inventory/vpc_analyzer.py +732 -695
  126. runbooks/inventory/vpc_architecture_validator.py +293 -348
  127. runbooks/inventory/vpc_dependency_analyzer.py +382 -378
  128. runbooks/inventory/vpc_flow_analyzer.py +1 -1
  129. runbooks/main.py +49 -34
  130. runbooks/main_final.py +91 -60
  131. runbooks/main_minimal.py +22 -10
  132. runbooks/main_optimized.py +131 -100
  133. runbooks/main_ultra_minimal.py +7 -2
  134. runbooks/mcp/__init__.py +36 -0
  135. runbooks/mcp/integration.py +679 -0
  136. runbooks/monitoring/performance_monitor.py +9 -4
  137. runbooks/operate/dynamodb_operations.py +3 -1
  138. runbooks/operate/ec2_operations.py +145 -137
  139. runbooks/operate/iam_operations.py +146 -152
  140. runbooks/operate/networking_cost_heatmap.py +29 -8
  141. runbooks/operate/rds_operations.py +223 -254
  142. runbooks/operate/s3_operations.py +107 -118
  143. runbooks/operate/vpc_operations.py +646 -616
  144. runbooks/remediation/base.py +1 -1
  145. runbooks/remediation/commons.py +10 -7
  146. runbooks/remediation/commvault_ec2_analysis.py +70 -66
  147. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  148. runbooks/remediation/multi_account.py +24 -21
  149. runbooks/remediation/rds_snapshot_list.py +86 -60
  150. runbooks/remediation/remediation_cli.py +92 -146
  151. runbooks/remediation/universal_account_discovery.py +83 -79
  152. runbooks/remediation/workspaces_list.py +46 -41
  153. runbooks/security/__init__.py +19 -0
  154. runbooks/security/assessment_runner.py +1150 -0
  155. runbooks/security/baseline_checker.py +812 -0
  156. runbooks/security/cloudops_automation_security_validator.py +509 -535
  157. runbooks/security/compliance_automation_engine.py +17 -17
  158. runbooks/security/config/__init__.py +2 -2
  159. runbooks/security/config/compliance_config.py +50 -50
  160. runbooks/security/config_template_generator.py +63 -76
  161. runbooks/security/enterprise_security_framework.py +1 -1
  162. runbooks/security/executive_security_dashboard.py +519 -508
  163. runbooks/security/multi_account_security_controls.py +959 -1210
  164. runbooks/security/real_time_security_monitor.py +422 -444
  165. runbooks/security/security_baseline_tester.py +1 -1
  166. runbooks/security/security_cli.py +143 -112
  167. runbooks/security/test_2way_validation.py +439 -0
  168. runbooks/security/two_way_validation_framework.py +852 -0
  169. runbooks/sre/production_monitoring_framework.py +167 -177
  170. runbooks/tdd/__init__.py +15 -0
  171. runbooks/tdd/cli.py +1071 -0
  172. runbooks/utils/__init__.py +14 -17
  173. runbooks/utils/logger.py +7 -2
  174. runbooks/utils/version_validator.py +50 -47
  175. runbooks/validation/__init__.py +6 -6
  176. runbooks/validation/cli.py +9 -3
  177. runbooks/validation/comprehensive_2way_validator.py +745 -704
  178. runbooks/validation/mcp_validator.py +906 -228
  179. runbooks/validation/terraform_citations_validator.py +104 -115
  180. runbooks/validation/terraform_drift_detector.py +447 -451
  181. runbooks/vpc/README.md +617 -0
  182. runbooks/vpc/__init__.py +8 -1
  183. runbooks/vpc/analyzer.py +577 -0
  184. runbooks/vpc/cleanup_wrapper.py +476 -413
  185. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  186. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  187. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  188. runbooks/vpc/config.py +92 -97
  189. runbooks/vpc/cost_engine.py +411 -148
  190. runbooks/vpc/cost_explorer_integration.py +553 -0
  191. runbooks/vpc/cross_account_session.py +101 -106
  192. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  193. runbooks/vpc/eni_gate_validator.py +961 -0
  194. runbooks/vpc/heatmap_engine.py +185 -160
  195. runbooks/vpc/mcp_no_eni_validator.py +680 -639
  196. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  197. runbooks/vpc/networking_wrapper.py +15 -8
  198. runbooks/vpc/pdca_remediation_planner.py +528 -0
  199. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  200. runbooks/vpc/runbooks_adapter.py +1167 -241
  201. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  202. runbooks/vpc/test_data_loader.py +358 -0
  203. runbooks/vpc/tests/conftest.py +314 -4
  204. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  205. runbooks/vpc/tests/test_cost_engine.py +0 -2
  206. runbooks/vpc/topology_generator.py +326 -0
  207. runbooks/vpc/unified_scenarios.py +1297 -1124
  208. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  209. runbooks-1.1.5.dist-info/METADATA +328 -0
  210. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/RECORD +214 -193
  211. runbooks/finops/README.md +0 -414
  212. runbooks/finops/accuracy_cross_validator.py +0 -647
  213. runbooks/finops/business_cases.py +0 -950
  214. runbooks/finops/dashboard_router.py +0 -922
  215. runbooks/finops/ebs_optimizer.py +0 -973
  216. runbooks/finops/embedded_mcp_validator.py +0 -1629
  217. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  218. runbooks/finops/finops_dashboard.py +0 -584
  219. runbooks/finops/finops_scenarios.py +0 -1218
  220. runbooks/finops/legacy_migration.py +0 -730
  221. runbooks/finops/multi_dashboard.py +0 -1519
  222. runbooks/finops/single_dashboard.py +0 -1113
  223. runbooks/finops/unlimited_scenarios.py +0 -393
  224. runbooks-1.1.4.dist-info/METADATA +0 -800
  225. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
  226. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
  227. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
  228. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
@@ -40,8 +40,14 @@ import boto3
40
40
  from botocore.exceptions import ClientError
41
41
 
42
42
  from runbooks.common.rich_utils import (
43
- console, print_header, print_success, print_error, print_warning,
44
- create_table, create_progress_bar, STATUS_INDICATORS
43
+ console,
44
+ print_header,
45
+ print_success,
46
+ print_error,
47
+ print_warning,
48
+ create_table,
49
+ create_progress_bar,
50
+ STATUS_INDICATORS,
45
51
  )
46
52
 
47
53
  logger = logging.getLogger(__name__)
@@ -50,7 +56,7 @@ logger = logging.getLogger(__name__)
50
56
  @dataclass
51
57
  class MCPValidationResult:
52
58
  """MCP validation result for a specific validation check."""
53
-
59
+
54
60
  validation_type: str # dependency, architecture, cost, etc.
55
61
  check_name: str
56
62
  runbooks_value: Any
@@ -59,7 +65,7 @@ class MCPValidationResult:
59
65
  validation_status: str # PASS, FAIL, WARNING, UNKNOWN
60
66
  variance_details: Dict[str, Any] = field(default_factory=dict)
61
67
  validation_timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat())
62
-
68
+
63
69
  @property
64
70
  def meets_enterprise_standard(self) -> bool:
65
71
  """True if validation meets ≥99.5% accuracy requirement."""
@@ -69,206 +75,195 @@ class MCPValidationResult:
69
75
  @dataclass
70
76
  class AWSO5MCPValidationReport:
71
77
  """Comprehensive AWSO-5 MCP validation report."""
72
-
78
+
73
79
  vpc_id: str
74
80
  account_id: str
75
81
  region: str
76
-
82
+
77
83
  # Validation results by category
78
84
  dependency_validation: List[MCPValidationResult] = field(default_factory=list)
79
85
  architecture_validation: List[MCPValidationResult] = field(default_factory=list)
80
86
  cost_validation: List[MCPValidationResult] = field(default_factory=list)
81
87
  security_validation: List[MCPValidationResult] = field(default_factory=list)
82
-
88
+
83
89
  # Overall metrics
84
90
  overall_accuracy: float = 0.0
85
91
  validation_status: str = "IN_PROGRESS" # IN_PROGRESS, PASSED, FAILED, WARNING
86
92
  total_validations: int = 0
87
93
  passed_validations: int = 0
88
94
  failed_validations: int = 0
89
-
95
+
90
96
  # Evidence and compliance
91
97
  evidence_artifacts: List[Dict[str, Any]] = field(default_factory=list)
92
98
  validation_hash: str = ""
93
99
  compliance_status: Dict[str, str] = field(default_factory=dict)
94
-
100
+
95
101
  # Analysis metadata
96
102
  validation_timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat())
97
103
  validation_duration_seconds: float = 0.0
98
-
104
+
99
105
  @property
100
106
  def meets_enterprise_accuracy(self) -> bool:
101
107
  """True if overall validation meets ≥99.5% accuracy requirement."""
102
108
  return self.overall_accuracy >= 99.5
103
-
109
+
104
110
  @property
105
111
  def validation_summary(self) -> str:
106
112
  """Human-readable validation summary."""
107
- return f"{self.passed_validations}/{self.total_validations} checks passed ({self.overall_accuracy:.2f}% accuracy)"
113
+ return (
114
+ f"{self.passed_validations}/{self.total_validations} checks passed ({self.overall_accuracy:.2f}% accuracy)"
115
+ )
108
116
 
109
117
 
110
118
  class AWSO5MCPValidator:
111
119
  """
112
120
  AWSO-5 MCP Validation Framework.
113
-
121
+
114
122
  Enterprise-grade MCP validation framework implementing comprehensive
115
123
  cross-validation against AWS APIs with ≥99.5% accuracy requirements
116
124
  and evidence bundle generation for AWSO-5 compliance.
117
-
125
+
118
126
  **Enterprise Integration**:
119
127
  - Real-time AWS API cross-validation
120
- - SHA256-verified evidence bundle generation
128
+ - SHA256-verified evidence bundle generation
121
129
  - PDCA quality gates with comprehensive analysis
122
130
  - Multi-dimensional validation framework
123
131
  - Regulatory compliance audit trail generation
124
132
  """
125
-
133
+
126
134
  def __init__(self, session: Optional[boto3.Session] = None, region: str = "us-east-1"):
127
135
  """Initialize AWSO-5 MCP validator."""
128
136
  self.session = session or boto3.Session()
129
137
  self.region = region
130
138
  self.console = console
131
-
139
+
132
140
  # Initialize AWS clients for validation
133
141
  self._ec2_client = None
134
142
  self._elbv2_client = None
135
143
  self._route53resolver_client = None
136
144
  self._cost_explorer_client = None
137
145
  self._organizations_client = None
138
-
146
+
139
147
  # Validation tracking
140
148
  self.validation_reports: Dict[str, AWSO5MCPValidationReport] = {}
141
149
  self.evidence_collector: List[Dict[str, Any]] = []
142
-
150
+
143
151
  @property
144
152
  def ec2_client(self):
145
153
  """Lazy-loaded EC2 client for validation."""
146
154
  if not self._ec2_client:
147
- self._ec2_client = self.session.client('ec2', region_name=self.region)
155
+ self._ec2_client = self.session.client("ec2", region_name=self.region)
148
156
  return self._ec2_client
149
-
150
- @property
157
+
158
+ @property
151
159
  def elbv2_client(self):
152
160
  """Lazy-loaded ELBv2 client for validation."""
153
161
  if not self._elbv2_client:
154
- self._elbv2_client = self.session.client('elbv2', region_name=self.region)
162
+ self._elbv2_client = self.session.client("elbv2", region_name=self.region)
155
163
  return self._elbv2_client
156
-
164
+
157
165
  @property
158
166
  def route53resolver_client(self):
159
167
  """Lazy-loaded Route53 Resolver client for validation."""
160
168
  if not self._route53resolver_client:
161
- self._route53resolver_client = self.session.client('route53resolver', region_name=self.region)
169
+ self._route53resolver_client = self.session.client("route53resolver", region_name=self.region)
162
170
  return self._route53resolver_client
163
-
171
+
164
172
  @property
165
173
  def cost_explorer_client(self):
166
174
  """Lazy-loaded Cost Explorer client for validation."""
167
175
  if not self._cost_explorer_client:
168
176
  # Cost Explorer is only available in us-east-1
169
- self._cost_explorer_client = self.session.client('ce', region_name='us-east-1')
177
+ self._cost_explorer_client = self.session.client("ce", region_name="us-east-1")
170
178
  return self._cost_explorer_client
171
-
179
+
172
180
  @property
173
181
  def organizations_client(self):
174
182
  """Lazy-loaded Organizations client for validation."""
175
183
  if not self._organizations_client:
176
184
  # Organizations is only available in us-east-1
177
- self._organizations_client = self.session.client('organizations', region_name='us-east-1')
185
+ self._organizations_client = self.session.client("organizations", region_name="us-east-1")
178
186
  return self._organizations_client
179
-
187
+
180
188
  def comprehensive_vpc_validation(
181
- self,
182
- vpc_id: str,
183
- dependency_result: Any,
184
- architecture_result: Any,
185
- evidence_bundle_path: Optional[str] = None
189
+ self, vpc_id: str, dependency_result: Any, architecture_result: Any, evidence_bundle_path: Optional[str] = None
186
190
  ) -> AWSO5MCPValidationReport:
187
191
  """
188
192
  Comprehensive VPC validation with ≥99.5% accuracy requirement.
189
-
193
+
190
194
  Cross-validates all AWSO-5 analysis results against real-time AWS APIs
191
195
  with enterprise quality gates and evidence bundle generation.
192
-
196
+
193
197
  Args:
194
198
  vpc_id: AWS VPC identifier
195
199
  dependency_result: VPC dependency analysis results
196
- architecture_result: Architecture validation results
200
+ architecture_result: Architecture validation results
197
201
  evidence_bundle_path: Optional path to save evidence bundle
198
-
202
+
199
203
  Returns:
200
204
  Comprehensive MCP validation report with quality metrics
201
205
  """
202
206
  start_time = datetime.utcnow()
203
-
204
- account_id = self.session.client('sts').get_caller_identity()['Account']
205
-
206
- report = AWSO5MCPValidationReport(
207
- vpc_id=vpc_id,
208
- account_id=account_id,
209
- region=self.region
210
- )
211
-
207
+
208
+ account_id = self.session.client("sts").get_caller_identity()["Account"]
209
+
210
+ report = AWSO5MCPValidationReport(vpc_id=vpc_id, account_id=account_id, region=self.region)
211
+
212
212
  print_header("AWSO-5 MCP Validation Framework", "1.0.0")
213
213
  self.console.print(f"\n[blue]MCP Cross-Validation:[/blue] {vpc_id}")
214
214
  self.console.print(f"[blue]Accuracy Target:[/blue] ≥99.5% (Enterprise Standard)")
215
215
  self.console.print(f"[blue]Validation Scope:[/blue] Comprehensive Multi-Dimensional")
216
-
216
+
217
217
  # Phase 1: Dependency Validation
218
218
  self.console.print("\n[yellow]Phase 1: Dependency Analysis MCP Validation[/yellow]")
219
219
  self._validate_dependency_analysis(vpc_id, dependency_result, report)
220
-
220
+
221
221
  # Phase 2: Architecture Validation
222
222
  self.console.print("\n[yellow]Phase 2: Architecture Compliance MCP Validation[/yellow]")
223
223
  self._validate_architecture_analysis(vpc_id, architecture_result, report)
224
-
225
- # Phase 3: Cost Impact Validation
224
+
225
+ # Phase 3: Cost Impact Validation
226
226
  self.console.print("\n[yellow]Phase 3: Cost Impact MCP Validation[/yellow]")
227
227
  self._validate_cost_impact(vpc_id, dependency_result, report)
228
-
228
+
229
229
  # Phase 4: Security Posture Validation
230
230
  self.console.print("\n[yellow]Phase 4: Security Posture MCP Validation[/yellow]")
231
231
  self._validate_security_posture(vpc_id, architecture_result, report)
232
-
232
+
233
233
  # Calculate final validation metrics
234
234
  end_time = datetime.utcnow()
235
235
  report.validation_duration_seconds = (end_time - start_time).total_seconds()
236
236
  self._calculate_validation_metrics(report)
237
-
237
+
238
238
  # Generate validation hash for evidence
239
239
  report.validation_hash = self._generate_validation_hash(report)
240
-
240
+
241
241
  # Generate evidence bundle
242
242
  if evidence_bundle_path:
243
243
  self._generate_evidence_bundle(report, evidence_bundle_path)
244
-
244
+
245
245
  # Store results
246
246
  self.validation_reports[vpc_id] = report
247
-
247
+
248
248
  # Display comprehensive validation results
249
249
  self._display_validation_results(report)
250
-
250
+
251
251
  return report
252
-
253
- def _validate_dependency_analysis(
254
- self,
255
- vpc_id: str,
256
- dependency_result: Any,
257
- report: AWSO5MCPValidationReport
258
- ):
252
+
253
+ def _validate_dependency_analysis(self, vpc_id: str, dependency_result: Any, report: AWSO5MCPValidationReport):
259
254
  """Validate dependency analysis against real AWS APIs."""
260
-
255
+
261
256
  # Validation 1: ENI Count Cross-Check
262
257
  try:
263
- mcp_enis = self.ec2_client.describe_network_interfaces(
264
- Filters=[{'Name': 'vpc-id', 'Values': [vpc_id]}]
265
- )['NetworkInterfaces']
266
-
258
+ mcp_enis = self.ec2_client.describe_network_interfaces(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])[
259
+ "NetworkInterfaces"
260
+ ]
261
+
267
262
  mcp_eni_count = len(mcp_enis)
268
263
  runbooks_eni_count = dependency_result.eni_count
269
-
264
+
270
265
  accuracy = self._calculate_accuracy(runbooks_eni_count, mcp_eni_count)
271
-
266
+
272
267
  validation_result = MCPValidationResult(
273
268
  validation_type="dependency",
274
269
  check_name="ENI Count Validation",
@@ -277,62 +272,53 @@ class AWSO5MCPValidator:
277
272
  accuracy_percentage=accuracy,
278
273
  validation_status="PASS" if accuracy >= 99.5 else "FAIL",
279
274
  variance_details={
280
- 'difference': abs(runbooks_eni_count - mcp_eni_count),
281
- 'percentage_variance': 100 - accuracy
282
- }
275
+ "difference": abs(runbooks_eni_count - mcp_eni_count),
276
+ "percentage_variance": 100 - accuracy,
277
+ },
283
278
  )
284
-
279
+
285
280
  report.dependency_validation.append(validation_result)
286
-
281
+
287
282
  if accuracy >= 99.5:
288
- self.console.print(f" ✅ ENI Count: {accuracy:.2f}% accuracy (Runbooks: {runbooks_eni_count}, MCP: {mcp_eni_count})")
283
+ self.console.print(
284
+ f" ✅ ENI Count: {accuracy:.2f}% accuracy (Runbooks: {runbooks_eni_count}, MCP: {mcp_eni_count})"
285
+ )
289
286
  else:
290
287
  self.console.print(f" ❌ ENI Count: {accuracy:.2f}% accuracy (Variance detected)")
291
-
288
+
292
289
  except ClientError as e:
293
290
  print_warning(f"ENI validation failed: {e}")
294
-
291
+
295
292
  # Validation 2: NAT Gateway Dependencies
296
293
  self._validate_nat_gateways(vpc_id, dependency_result, report)
297
-
294
+
298
295
  # Validation 3: Internet Gateway Dependencies
299
296
  self._validate_internet_gateways(vpc_id, dependency_result, report)
300
-
297
+
301
298
  # Validation 4: Route Tables Dependencies
302
299
  self._validate_route_tables(vpc_id, dependency_result, report)
303
-
300
+
304
301
  # Validation 5: VPC Endpoints Dependencies
305
302
  self._validate_vpc_endpoints(vpc_id, dependency_result, report)
306
-
303
+
307
304
  # Validation 6: Load Balancer Dependencies
308
305
  self._validate_load_balancers(vpc_id, dependency_result, report)
309
-
310
- def _validate_nat_gateways(
311
- self,
312
- vpc_id: str,
313
- dependency_result: Any,
314
- report: AWSO5MCPValidationReport
315
- ):
306
+
307
+ def _validate_nat_gateways(self, vpc_id: str, dependency_result: Any, report: AWSO5MCPValidationReport):
316
308
  """Validate NAT Gateway dependency analysis."""
317
309
  try:
318
- mcp_nat_gateways = self.ec2_client.describe_nat_gateways(
319
- Filters=[{'Name': 'vpc-id', 'Values': [vpc_id]}]
320
- )['NatGateways']
321
-
322
- # Count active NAT Gateways
323
- active_nat_gateways = [
324
- ng for ng in mcp_nat_gateways
325
- if ng['State'] in ['available', 'pending']
310
+ mcp_nat_gateways = self.ec2_client.describe_nat_gateways(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])[
311
+ "NatGateways"
326
312
  ]
327
-
313
+
314
+ # Count active NAT Gateways
315
+ active_nat_gateways = [ng for ng in mcp_nat_gateways if ng["State"] in ["available", "pending"]]
316
+
328
317
  mcp_count = len(active_nat_gateways)
329
- runbooks_count = len([
330
- dep for dep in dependency_result.dependencies
331
- if dep.resource_type == 'NatGateway'
332
- ])
333
-
318
+ runbooks_count = len([dep for dep in dependency_result.dependencies if dep.resource_type == "NatGateway"])
319
+
334
320
  accuracy = self._calculate_accuracy(runbooks_count, mcp_count)
335
-
321
+
336
322
  validation_result = MCPValidationResult(
337
323
  validation_type="dependency",
338
324
  check_name="NAT Gateway Dependencies",
@@ -340,294 +326,243 @@ class AWSO5MCPValidator:
340
326
  mcp_value=mcp_count,
341
327
  accuracy_percentage=accuracy,
342
328
  validation_status="PASS" if accuracy >= 99.5 else "FAIL",
343
- variance_details={
344
- 'nat_gateway_ids': [ng['NatGatewayId'] for ng in active_nat_gateways]
345
- }
329
+ variance_details={"nat_gateway_ids": [ng["NatGatewayId"] for ng in active_nat_gateways]},
346
330
  )
347
-
331
+
348
332
  report.dependency_validation.append(validation_result)
349
-
333
+
350
334
  if accuracy >= 99.5:
351
335
  self.console.print(f" ✅ NAT Gateways: {accuracy:.2f}% accuracy")
352
336
  else:
353
337
  self.console.print(f" ❌ NAT Gateways: {accuracy:.2f}% accuracy (Variance detected)")
354
-
338
+
355
339
  except ClientError as e:
356
340
  print_warning(f"NAT Gateway validation failed: {e}")
357
-
358
- def _validate_internet_gateways(
359
- self,
360
- vpc_id: str,
361
- dependency_result: Any,
362
- report: AWSO5MCPValidationReport
363
- ):
341
+
342
+ def _validate_internet_gateways(self, vpc_id: str, dependency_result: Any, report: AWSO5MCPValidationReport):
364
343
  """Validate Internet Gateway dependency analysis."""
365
344
  try:
366
345
  mcp_internet_gateways = self.ec2_client.describe_internet_gateways(
367
- Filters=[{'Name': 'attachment.vpc-id', 'Values': [vpc_id]}]
368
- )['InternetGateways']
369
-
346
+ Filters=[{"Name": "attachment.vpc-id", "Values": [vpc_id]}]
347
+ )["InternetGateways"]
348
+
370
349
  mcp_count = len(mcp_internet_gateways)
371
- runbooks_count = len([
372
- dep for dep in dependency_result.dependencies
373
- if dep.resource_type == 'InternetGateway'
374
- ])
375
-
350
+ runbooks_count = len(
351
+ [dep for dep in dependency_result.dependencies if dep.resource_type == "InternetGateway"]
352
+ )
353
+
376
354
  accuracy = self._calculate_accuracy(runbooks_count, mcp_count)
377
-
355
+
378
356
  validation_result = MCPValidationResult(
379
357
  validation_type="dependency",
380
358
  check_name="Internet Gateway Dependencies",
381
359
  runbooks_value=runbooks_count,
382
360
  mcp_value=mcp_count,
383
361
  accuracy_percentage=accuracy,
384
- validation_status="PASS" if accuracy >= 99.5 else "FAIL"
362
+ validation_status="PASS" if accuracy >= 99.5 else "FAIL",
385
363
  )
386
-
364
+
387
365
  report.dependency_validation.append(validation_result)
388
-
366
+
389
367
  if accuracy >= 99.5:
390
368
  self.console.print(f" ✅ Internet Gateways: {accuracy:.2f}% accuracy")
391
369
  else:
392
370
  self.console.print(f" ❌ Internet Gateways: {accuracy:.2f}% accuracy")
393
-
371
+
394
372
  except ClientError as e:
395
373
  print_warning(f"Internet Gateway validation failed: {e}")
396
-
397
- def _validate_route_tables(
398
- self,
399
- vpc_id: str,
400
- dependency_result: Any,
401
- report: AWSO5MCPValidationReport
402
- ):
374
+
375
+ def _validate_route_tables(self, vpc_id: str, dependency_result: Any, report: AWSO5MCPValidationReport):
403
376
  """Validate Route Table dependency analysis."""
404
377
  try:
405
- mcp_route_tables = self.ec2_client.describe_route_tables(
406
- Filters=[{'Name': 'vpc-id', 'Values': [vpc_id]}]
407
- )['RouteTables']
408
-
378
+ mcp_route_tables = self.ec2_client.describe_route_tables(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])[
379
+ "RouteTables"
380
+ ]
381
+
409
382
  # Count non-main route tables (main RT is auto-deleted)
410
383
  non_main_route_tables = [
411
- rt for rt in mcp_route_tables
412
- if not any(assoc.get('Main') for assoc in rt.get('Associations', []))
384
+ rt for rt in mcp_route_tables if not any(assoc.get("Main") for assoc in rt.get("Associations", []))
413
385
  ]
414
-
386
+
415
387
  mcp_count = len(non_main_route_tables)
416
- runbooks_count = len([
417
- dep for dep in dependency_result.dependencies
418
- if dep.resource_type == 'RouteTable'
419
- ])
420
-
388
+ runbooks_count = len([dep for dep in dependency_result.dependencies if dep.resource_type == "RouteTable"])
389
+
421
390
  accuracy = self._calculate_accuracy(runbooks_count, mcp_count)
422
-
391
+
423
392
  validation_result = MCPValidationResult(
424
393
  validation_type="dependency",
425
- check_name="Route Table Dependencies",
394
+ check_name="Route Table Dependencies",
426
395
  runbooks_value=runbooks_count,
427
396
  mcp_value=mcp_count,
428
397
  accuracy_percentage=accuracy,
429
- validation_status="PASS" if accuracy >= 99.5 else "FAIL"
398
+ validation_status="PASS" if accuracy >= 99.5 else "FAIL",
430
399
  )
431
-
400
+
432
401
  report.dependency_validation.append(validation_result)
433
-
402
+
434
403
  if accuracy >= 99.5:
435
404
  self.console.print(f" ✅ Route Tables: {accuracy:.2f}% accuracy")
436
405
  else:
437
406
  self.console.print(f" ❌ Route Tables: {accuracy:.2f}% accuracy")
438
-
407
+
439
408
  except ClientError as e:
440
409
  print_warning(f"Route Table validation failed: {e}")
441
-
442
- def _validate_vpc_endpoints(
443
- self,
444
- vpc_id: str,
445
- dependency_result: Any,
446
- report: AWSO5MCPValidationReport
447
- ):
410
+
411
+ def _validate_vpc_endpoints(self, vpc_id: str, dependency_result: Any, report: AWSO5MCPValidationReport):
448
412
  """Validate VPC Endpoints dependency analysis."""
449
413
  try:
450
414
  mcp_vpc_endpoints = self.ec2_client.describe_vpc_endpoints(
451
- Filters=[{'Name': 'vpc-id', 'Values': [vpc_id]}]
452
- )['VpcEndpoints']
453
-
415
+ Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]
416
+ )["VpcEndpoints"]
417
+
454
418
  # Count available endpoints
455
- available_endpoints = [
456
- ep for ep in mcp_vpc_endpoints
457
- if ep['State'] == 'available'
458
- ]
459
-
419
+ available_endpoints = [ep for ep in mcp_vpc_endpoints if ep["State"] == "available"]
420
+
460
421
  mcp_count = len(available_endpoints)
461
- runbooks_count = len([
462
- dep for dep in dependency_result.dependencies
463
- if dep.resource_type == 'VpcEndpoint'
464
- ])
465
-
422
+ runbooks_count = len([dep for dep in dependency_result.dependencies if dep.resource_type == "VpcEndpoint"])
423
+
466
424
  accuracy = self._calculate_accuracy(runbooks_count, mcp_count)
467
-
425
+
468
426
  validation_result = MCPValidationResult(
469
427
  validation_type="dependency",
470
428
  check_name="VPC Endpoint Dependencies",
471
429
  runbooks_value=runbooks_count,
472
430
  mcp_value=mcp_count,
473
431
  accuracy_percentage=accuracy,
474
- validation_status="PASS" if accuracy >= 99.5 else "FAIL"
432
+ validation_status="PASS" if accuracy >= 99.5 else "FAIL",
475
433
  )
476
-
434
+
477
435
  report.dependency_validation.append(validation_result)
478
-
436
+
479
437
  if accuracy >= 99.5:
480
438
  self.console.print(f" ✅ VPC Endpoints: {accuracy:.2f}% accuracy")
481
439
  else:
482
440
  self.console.print(f" ❌ VPC Endpoints: {accuracy:.2f}% accuracy")
483
-
441
+
484
442
  except ClientError as e:
485
443
  print_warning(f"VPC Endpoints validation failed: {e}")
486
-
487
- def _validate_load_balancers(
488
- self,
489
- vpc_id: str,
490
- dependency_result: Any,
491
- report: AWSO5MCPValidationReport
492
- ):
444
+
445
+ def _validate_load_balancers(self, vpc_id: str, dependency_result: Any, report: AWSO5MCPValidationReport):
493
446
  """Validate Load Balancer dependency analysis."""
494
447
  try:
495
- mcp_load_balancers = self.elbv2_client.describe_load_balancers()['LoadBalancers']
496
-
448
+ mcp_load_balancers = self.elbv2_client.describe_load_balancers()["LoadBalancers"]
449
+
497
450
  # Filter by VPC and active state
498
451
  vpc_load_balancers = [
499
- lb for lb in mcp_load_balancers
500
- if lb['VpcId'] == vpc_id and lb['State']['Code'] == 'active'
452
+ lb for lb in mcp_load_balancers if lb["VpcId"] == vpc_id and lb["State"]["Code"] == "active"
501
453
  ]
502
-
454
+
503
455
  mcp_count = len(vpc_load_balancers)
504
- runbooks_count = len([
505
- dep for dep in dependency_result.dependencies
506
- if dep.resource_type == 'LoadBalancer'
507
- ])
508
-
456
+ runbooks_count = len([dep for dep in dependency_result.dependencies if dep.resource_type == "LoadBalancer"])
457
+
509
458
  accuracy = self._calculate_accuracy(runbooks_count, mcp_count)
510
-
459
+
511
460
  validation_result = MCPValidationResult(
512
461
  validation_type="dependency",
513
462
  check_name="Load Balancer Dependencies",
514
463
  runbooks_value=runbooks_count,
515
464
  mcp_value=mcp_count,
516
465
  accuracy_percentage=accuracy,
517
- validation_status="PASS" if accuracy >= 99.5 else "FAIL"
466
+ validation_status="PASS" if accuracy >= 99.5 else "FAIL",
518
467
  )
519
-
468
+
520
469
  report.dependency_validation.append(validation_result)
521
-
470
+
522
471
  if accuracy >= 99.5:
523
472
  self.console.print(f" ✅ Load Balancers: {accuracy:.2f}% accuracy")
524
473
  else:
525
474
  self.console.print(f" ❌ Load Balancers: {accuracy:.2f}% accuracy")
526
-
475
+
527
476
  except ClientError as e:
528
477
  print_warning(f"Load Balancer validation failed: {e}")
529
-
530
- def _validate_architecture_analysis(
531
- self,
532
- vpc_id: str,
533
- architecture_result: Any,
534
- report: AWSO5MCPValidationReport
535
- ):
478
+
479
+ def _validate_architecture_analysis(self, vpc_id: str, architecture_result: Any, report: AWSO5MCPValidationReport):
536
480
  """Validate architecture analysis against compliance frameworks."""
537
-
481
+
538
482
  # Validation 1: Default VPC Status
539
483
  try:
540
- vpc_info = self.ec2_client.describe_vpcs(VpcIds=[vpc_id])['Vpcs'][0]
541
-
542
- mcp_is_default = vpc_info.get('IsDefault', False)
543
- runbooks_is_default = getattr(architecture_result, 'is_default', False)
544
-
484
+ vpc_info = self.ec2_client.describe_vpcs(VpcIds=[vpc_id])["Vpcs"][0]
485
+
486
+ mcp_is_default = vpc_info.get("IsDefault", False)
487
+ runbooks_is_default = getattr(architecture_result, "is_default", False)
488
+
545
489
  accuracy = 100.0 if mcp_is_default == runbooks_is_default else 0.0
546
-
490
+
547
491
  validation_result = MCPValidationResult(
548
492
  validation_type="architecture",
549
493
  check_name="Default VPC Status",
550
494
  runbooks_value=runbooks_is_default,
551
495
  mcp_value=mcp_is_default,
552
496
  accuracy_percentage=accuracy,
553
- validation_status="PASS" if accuracy == 100.0 else "FAIL"
497
+ validation_status="PASS" if accuracy == 100.0 else "FAIL",
554
498
  )
555
-
499
+
556
500
  report.architecture_validation.append(validation_result)
557
-
501
+
558
502
  if accuracy == 100.0:
559
503
  self.console.print(f" ✅ Default VPC Status: {accuracy:.2f}% accuracy")
560
504
  else:
561
505
  self.console.print(f" ❌ Default VPC Status: {accuracy:.2f}% accuracy")
562
-
506
+
563
507
  except ClientError as e:
564
508
  print_warning(f"Default VPC validation failed: {e}")
565
-
509
+
566
510
  # Validation 2: Security Group Count
567
511
  self._validate_security_groups(vpc_id, architecture_result, report)
568
-
512
+
569
513
  # Validation 3: CIDR Block Validation
570
514
  self._validate_cidr_blocks(vpc_id, architecture_result, report)
571
-
572
- def _validate_security_groups(
573
- self,
574
- vpc_id: str,
575
- architecture_result: Any,
576
- report: AWSO5MCPValidationReport
577
- ):
515
+
516
+ def _validate_security_groups(self, vpc_id: str, architecture_result: Any, report: AWSO5MCPValidationReport):
578
517
  """Validate security group analysis."""
579
518
  try:
580
519
  mcp_security_groups = self.ec2_client.describe_security_groups(
581
- Filters=[{'Name': 'vpc-id', 'Values': [vpc_id]}]
582
- )['SecurityGroups']
583
-
520
+ Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]
521
+ )["SecurityGroups"]
522
+
584
523
  mcp_count = len(mcp_security_groups)
585
-
524
+
586
525
  # Extract count from architecture results (if available)
587
- runbooks_count = getattr(architecture_result, 'security_group_count', mcp_count)
588
-
526
+ runbooks_count = getattr(architecture_result, "security_group_count", mcp_count)
527
+
589
528
  accuracy = self._calculate_accuracy(runbooks_count, mcp_count)
590
-
529
+
591
530
  validation_result = MCPValidationResult(
592
531
  validation_type="architecture",
593
532
  check_name="Security Group Count",
594
533
  runbooks_value=runbooks_count,
595
534
  mcp_value=mcp_count,
596
535
  accuracy_percentage=accuracy,
597
- validation_status="PASS" if accuracy >= 99.5 else "FAIL"
536
+ validation_status="PASS" if accuracy >= 99.5 else "FAIL",
598
537
  )
599
-
538
+
600
539
  report.architecture_validation.append(validation_result)
601
-
540
+
602
541
  if accuracy >= 99.5:
603
542
  self.console.print(f" ✅ Security Groups: {accuracy:.2f}% accuracy")
604
543
  else:
605
544
  self.console.print(f" ❌ Security Groups: {accuracy:.2f}% accuracy")
606
-
545
+
607
546
  except ClientError as e:
608
547
  print_warning(f"Security Group validation failed: {e}")
609
-
610
- def _validate_cidr_blocks(
611
- self,
612
- vpc_id: str,
613
- architecture_result: Any,
614
- report: AWSO5MCPValidationReport
615
- ):
548
+
549
+ def _validate_cidr_blocks(self, vpc_id: str, architecture_result: Any, report: AWSO5MCPValidationReport):
616
550
  """Validate CIDR block analysis."""
617
551
  try:
618
- vpc_info = self.ec2_client.describe_vpcs(VpcIds=[vpc_id])['Vpcs'][0]
619
-
552
+ vpc_info = self.ec2_client.describe_vpcs(VpcIds=[vpc_id])["Vpcs"][0]
553
+
620
554
  mcp_cidrs = [
621
- block['CidrBlock'] for block in vpc_info.get('CidrBlockAssociationSet', [])
622
- if block['CidrBlockState']['State'] == 'associated'
555
+ block["CidrBlock"]
556
+ for block in vpc_info.get("CidrBlockAssociationSet", [])
557
+ if block["CidrBlockState"]["State"] == "associated"
623
558
  ]
624
-
625
- runbooks_cidrs = getattr(architecture_result, 'cidr_blocks', mcp_cidrs)
626
-
559
+
560
+ runbooks_cidrs = getattr(architecture_result, "cidr_blocks", mcp_cidrs)
561
+
627
562
  # Compare CIDR sets
628
563
  mcp_cidr_set = set(mcp_cidrs)
629
564
  runbooks_cidr_set = set(runbooks_cidrs)
630
-
565
+
631
566
  if mcp_cidr_set == runbooks_cidr_set:
632
567
  accuracy = 100.0
633
568
  else:
@@ -635,37 +570,32 @@ class AWSO5MCPValidator:
635
570
  intersection = len(mcp_cidr_set & runbooks_cidr_set)
636
571
  union = len(mcp_cidr_set | runbooks_cidr_set)
637
572
  accuracy = (intersection / union * 100) if union > 0 else 0.0
638
-
573
+
639
574
  validation_result = MCPValidationResult(
640
575
  validation_type="architecture",
641
576
  check_name="CIDR Block Configuration",
642
577
  runbooks_value=list(runbooks_cidr_set),
643
578
  mcp_value=list(mcp_cidr_set),
644
579
  accuracy_percentage=accuracy,
645
- validation_status="PASS" if accuracy >= 99.5 else "FAIL"
580
+ validation_status="PASS" if accuracy >= 99.5 else "FAIL",
646
581
  )
647
-
582
+
648
583
  report.architecture_validation.append(validation_result)
649
-
584
+
650
585
  if accuracy >= 99.5:
651
586
  self.console.print(f" ✅ CIDR Blocks: {accuracy:.2f}% accuracy")
652
587
  else:
653
588
  self.console.print(f" ❌ CIDR Blocks: {accuracy:.2f}% accuracy")
654
-
589
+
655
590
  except ClientError as e:
656
591
  print_warning(f"CIDR Block validation failed: {e}")
657
-
658
- def _validate_cost_impact(
659
- self,
660
- vpc_id: str,
661
- dependency_result: Any,
662
- report: AWSO5MCPValidationReport
663
- ):
592
+
593
+ def _validate_cost_impact(self, vpc_id: str, dependency_result: Any, report: AWSO5MCPValidationReport):
664
594
  """Validate cost impact projections."""
665
-
595
+
666
596
  # Basic cost validation - in production would integrate with Cost Explorer
667
- estimated_monthly_savings = getattr(dependency_result, 'estimated_monthly_savings', 0.0)
668
-
597
+ estimated_monthly_savings = getattr(dependency_result, "estimated_monthly_savings", 0.0)
598
+
669
599
  # For now, validate that cost estimation is reasonable (0-1000 range for VPC cleanup)
670
600
  if 0 <= estimated_monthly_savings <= 1000:
671
601
  accuracy = 100.0
@@ -673,7 +603,7 @@ class AWSO5MCPValidator:
673
603
  else:
674
604
  accuracy = 75.0 # Questionable but not failing
675
605
  status = "WARNING"
676
-
606
+
677
607
  validation_result = MCPValidationResult(
678
608
  validation_type="cost",
679
609
  check_name="Cost Impact Estimation",
@@ -682,32 +612,27 @@ class AWSO5MCPValidator:
682
612
  accuracy_percentage=accuracy,
683
613
  validation_status=status,
684
614
  variance_details={
685
- 'validation_method': 'Range validation (0-1000 USD/month)',
686
- 'assessment': 'Within expected range for VPC cleanup'
687
- }
615
+ "validation_method": "Range validation (0-1000 USD/month)",
616
+ "assessment": "Within expected range for VPC cleanup",
617
+ },
688
618
  )
689
-
619
+
690
620
  report.cost_validation.append(validation_result)
691
-
621
+
692
622
  if accuracy >= 99.5:
693
623
  self.console.print(f" ✅ Cost Estimation: {accuracy:.2f}% accuracy")
694
624
  else:
695
625
  self.console.print(f" ⚠️ Cost Estimation: {accuracy:.2f}% accuracy (Range validated)")
696
-
697
- def _validate_security_posture(
698
- self,
699
- vpc_id: str,
700
- architecture_result: Any,
701
- report: AWSO5MCPValidationReport
702
- ):
626
+
627
+ def _validate_security_posture(self, vpc_id: str, architecture_result: Any, report: AWSO5MCPValidationReport):
703
628
  """Validate security posture assessment."""
704
-
629
+
705
630
  # Validation 1: Flow Logs Status
706
631
  self._validate_flow_logs_status(vpc_id, architecture_result, report)
707
-
632
+
708
633
  # Validation 2: Compliance Status Consistency
709
- compliance_score = getattr(architecture_result, 'overall_compliance_score', 0.0)
710
-
634
+ compliance_score = getattr(architecture_result, "overall_compliance_score", 0.0)
635
+
711
636
  # Validate compliance score is within reasonable range
712
637
  if 0 <= compliance_score <= 100:
713
638
  accuracy = 100.0
@@ -715,89 +640,78 @@ class AWSO5MCPValidator:
715
640
  else:
716
641
  accuracy = 0.0
717
642
  status = "FAIL"
718
-
643
+
719
644
  validation_result = MCPValidationResult(
720
645
  validation_type="security",
721
646
  check_name="Compliance Score Range",
722
647
  runbooks_value=compliance_score,
723
648
  mcp_value="Valid Range (0-100)",
724
649
  accuracy_percentage=accuracy,
725
- validation_status=status
650
+ validation_status=status,
726
651
  )
727
-
652
+
728
653
  report.security_validation.append(validation_result)
729
-
654
+
730
655
  if accuracy >= 99.5:
731
656
  self.console.print(f" ✅ Compliance Score: {accuracy:.2f}% accuracy")
732
657
  else:
733
658
  self.console.print(f" ❌ Compliance Score: {accuracy:.2f}% accuracy")
734
-
735
- def _validate_flow_logs_status(
736
- self,
737
- vpc_id: str,
738
- architecture_result: Any,
739
- report: AWSO5MCPValidationReport
740
- ):
659
+
660
+ def _validate_flow_logs_status(self, vpc_id: str, architecture_result: Any, report: AWSO5MCPValidationReport):
741
661
  """Validate VPC Flow Logs status."""
742
662
  try:
743
663
  mcp_flow_logs = self.ec2_client.describe_flow_logs(
744
- Filters=[
745
- {'Name': 'resource-id', 'Values': [vpc_id]},
746
- {'Name': 'resource-type', 'Values': ['VPC']}
747
- ]
748
- )['FlowLogs']
749
-
750
- active_flow_logs = [
751
- fl for fl in mcp_flow_logs
752
- if fl['FlowLogStatus'] == 'ACTIVE'
753
- ]
754
-
664
+ Filters=[{"Name": "resource-id", "Values": [vpc_id]}, {"Name": "resource-type", "Values": ["VPC"]}]
665
+ )["FlowLogs"]
666
+
667
+ active_flow_logs = [fl for fl in mcp_flow_logs if fl["FlowLogStatus"] == "ACTIVE"]
668
+
755
669
  mcp_has_flow_logs = len(active_flow_logs) > 0
756
-
670
+
757
671
  # Extract from architecture results
758
672
  runbooks_has_flow_logs = True # Default assumption
759
-
673
+
760
674
  accuracy = 100.0 if mcp_has_flow_logs == runbooks_has_flow_logs else 95.0 # Minor variance acceptable
761
-
675
+
762
676
  validation_result = MCPValidationResult(
763
677
  validation_type="security",
764
678
  check_name="VPC Flow Logs Status",
765
679
  runbooks_value=runbooks_has_flow_logs,
766
680
  mcp_value=mcp_has_flow_logs,
767
681
  accuracy_percentage=accuracy,
768
- validation_status="PASS" if accuracy >= 95.0 else "FAIL"
682
+ validation_status="PASS" if accuracy >= 95.0 else "FAIL",
769
683
  )
770
-
684
+
771
685
  report.security_validation.append(validation_result)
772
-
686
+
773
687
  if accuracy >= 99.5:
774
688
  self.console.print(f" ✅ Flow Logs Status: {accuracy:.2f}% accuracy")
775
689
  else:
776
690
  self.console.print(f" ⚠️ Flow Logs Status: {accuracy:.2f}% accuracy")
777
-
691
+
778
692
  except ClientError as e:
779
693
  print_warning(f"Flow Logs validation failed: {e}")
780
-
694
+
781
695
  def _calculate_accuracy(self, runbooks_value: Any, mcp_value: Any) -> float:
782
696
  """Calculate accuracy percentage between runbooks and MCP values with enterprise tolerance."""
783
-
697
+
784
698
  if isinstance(runbooks_value, (int, float)) and isinstance(mcp_value, (int, float)):
785
699
  # Perfect match
786
700
  if runbooks_value == mcp_value:
787
701
  return 100.0
788
-
702
+
789
703
  # Both zero
790
704
  if mcp_value == 0 and runbooks_value == 0:
791
705
  return 100.0
792
-
706
+
793
707
  # One zero, other non-zero
794
708
  if mcp_value == 0 or runbooks_value == 0:
795
709
  return 0.0
796
-
710
+
797
711
  # Calculate percentage variance
798
712
  max_value = max(abs(runbooks_value), abs(mcp_value))
799
713
  variance_percent = abs(runbooks_value - mcp_value) / max_value * 100
800
-
714
+
801
715
  # Apply enterprise tolerance (±5% acceptable)
802
716
  if variance_percent <= 5.0:
803
717
  return 100.0
@@ -805,40 +719,40 @@ class AWSO5MCPValidator:
805
719
  # Scale accuracy based on variance beyond tolerance
806
720
  accuracy = max(0.0, 100.0 - (variance_percent - 5.0))
807
721
  return min(100.0, accuracy)
808
-
722
+
809
723
  elif runbooks_value == mcp_value:
810
724
  return 100.0
811
725
  else:
812
726
  return 0.0
813
-
727
+
814
728
  def _calculate_validation_metrics(self, report: AWSO5MCPValidationReport):
815
729
  """Calculate overall validation metrics."""
816
-
730
+
817
731
  all_validations = (
818
- report.dependency_validation +
819
- report.architecture_validation +
820
- report.cost_validation +
821
- report.security_validation
732
+ report.dependency_validation
733
+ + report.architecture_validation
734
+ + report.cost_validation
735
+ + report.security_validation
822
736
  )
823
-
737
+
824
738
  report.total_validations = len(all_validations)
825
739
  report.passed_validations = len([v for v in all_validations if v.validation_status == "PASS"])
826
740
  report.failed_validations = len([v for v in all_validations if v.validation_status == "FAIL"])
827
-
741
+
828
742
  if report.total_validations > 0:
829
743
  # Weighted accuracy calculation
830
744
  total_weight = 0
831
745
  weighted_accuracy = 0
832
-
746
+
833
747
  for validation in all_validations:
834
748
  weight = self._get_validation_weight(validation.validation_type)
835
749
  total_weight += weight
836
750
  weighted_accuracy += validation.accuracy_percentage * weight
837
-
751
+
838
752
  report.overall_accuracy = weighted_accuracy / total_weight if total_weight > 0 else 0
839
753
  else:
840
754
  report.overall_accuracy = 0
841
-
755
+
842
756
  # Determine overall validation status
843
757
  if report.overall_accuracy >= 99.5:
844
758
  report.validation_status = "PASSED"
@@ -846,121 +760,123 @@ class AWSO5MCPValidator:
846
760
  report.validation_status = "WARNING"
847
761
  else:
848
762
  report.validation_status = "FAILED"
849
-
763
+
850
764
  # Set compliance status
851
765
  report.compliance_status = {
852
- 'enterprise_accuracy_target': 'MET' if report.meets_enterprise_accuracy else 'NOT_MET',
853
- 'validation_completeness': 'COMPLETE' if report.total_validations >= 10 else 'PARTIAL',
854
- 'evidence_generation': 'READY'
766
+ "enterprise_accuracy_target": "MET" if report.meets_enterprise_accuracy else "NOT_MET",
767
+ "validation_completeness": "COMPLETE" if report.total_validations >= 10 else "PARTIAL",
768
+ "evidence_generation": "READY",
855
769
  }
856
-
770
+
857
771
  def _get_validation_weight(self, validation_type: str) -> float:
858
772
  """Get weight for validation type in accuracy calculation."""
859
773
  weights = {
860
- 'dependency': 3.0, # Highest weight - core functionality
861
- 'architecture': 2.5, # High weight - compliance critical
862
- 'security': 2.0, # Important for compliance
863
- 'cost': 1.5 # Lower weight - estimation vs exact
774
+ "dependency": 3.0, # Highest weight - core functionality
775
+ "architecture": 2.5, # High weight - compliance critical
776
+ "security": 2.0, # Important for compliance
777
+ "cost": 1.5, # Lower weight - estimation vs exact
864
778
  }
865
779
  return weights.get(validation_type, 1.0)
866
-
780
+
867
781
  def _generate_validation_hash(self, report: AWSO5MCPValidationReport) -> str:
868
782
  """Generate SHA256 hash for validation report integrity."""
869
-
783
+
870
784
  # Create deterministic content for hashing
871
785
  hash_content = {
872
- 'vpc_id': report.vpc_id,
873
- 'account_id': report.account_id,
874
- 'region': report.region,
875
- 'overall_accuracy': report.overall_accuracy,
876
- 'total_validations': report.total_validations,
877
- 'passed_validations': report.passed_validations,
878
- 'validation_status': report.validation_status,
879
- 'validation_timestamp': report.validation_timestamp
786
+ "vpc_id": report.vpc_id,
787
+ "account_id": report.account_id,
788
+ "region": report.region,
789
+ "overall_accuracy": report.overall_accuracy,
790
+ "total_validations": report.total_validations,
791
+ "passed_validations": report.passed_validations,
792
+ "validation_status": report.validation_status,
793
+ "validation_timestamp": report.validation_timestamp,
880
794
  }
881
-
795
+
882
796
  content_json = json.dumps(hash_content, sort_keys=True)
883
797
  return hashlib.sha256(content_json.encode()).hexdigest()
884
-
798
+
885
799
  def _generate_evidence_bundle(self, report: AWSO5MCPValidationReport, evidence_path: str):
886
800
  """Generate SHA256-verified evidence bundle."""
887
-
801
+
888
802
  evidence_bundle = {
889
- 'metadata': {
890
- 'framework': 'AWSO-5 MCP Validation',
891
- 'version': '1.0.0',
892
- 'vpc_id': report.vpc_id,
893
- 'account_id': report.account_id,
894
- 'region': report.region,
895
- 'timestamp': datetime.utcnow().isoformat(),
896
- 'validator': 'qa-testing-specialist'
803
+ "metadata": {
804
+ "framework": "AWSO-5 MCP Validation",
805
+ "version": "1.0.0",
806
+ "vpc_id": report.vpc_id,
807
+ "account_id": report.account_id,
808
+ "region": report.region,
809
+ "timestamp": datetime.utcnow().isoformat(),
810
+ "validator": "qa-testing-specialist",
897
811
  },
898
- 'validation_summary': {
899
- 'overall_accuracy': report.overall_accuracy,
900
- 'validation_status': report.validation_status,
901
- 'total_validations': report.total_validations,
902
- 'passed_validations': report.passed_validations,
903
- 'failed_validations': report.failed_validations,
904
- 'meets_enterprise_standard': report.meets_enterprise_accuracy
812
+ "validation_summary": {
813
+ "overall_accuracy": report.overall_accuracy,
814
+ "validation_status": report.validation_status,
815
+ "total_validations": report.total_validations,
816
+ "passed_validations": report.passed_validations,
817
+ "failed_validations": report.failed_validations,
818
+ "meets_enterprise_standard": report.meets_enterprise_accuracy,
905
819
  },
906
- 'detailed_results': {
907
- 'dependency_validation': [v.__dict__ for v in report.dependency_validation],
908
- 'architecture_validation': [v.__dict__ for v in report.architecture_validation],
909
- 'cost_validation': [v.__dict__ for v in report.cost_validation],
910
- 'security_validation': [v.__dict__ for v in report.security_validation]
820
+ "detailed_results": {
821
+ "dependency_validation": [v.__dict__ for v in report.dependency_validation],
822
+ "architecture_validation": [v.__dict__ for v in report.architecture_validation],
823
+ "cost_validation": [v.__dict__ for v in report.cost_validation],
824
+ "security_validation": [v.__dict__ for v in report.security_validation],
825
+ },
826
+ "compliance_status": report.compliance_status,
827
+ "validation_hash": report.validation_hash,
828
+ "quality_gates": {
829
+ "enterprise_accuracy_met": report.meets_enterprise_accuracy,
830
+ "validation_completeness": report.total_validations >= 10,
831
+ "evidence_integrity": True,
911
832
  },
912
- 'compliance_status': report.compliance_status,
913
- 'validation_hash': report.validation_hash,
914
- 'quality_gates': {
915
- 'enterprise_accuracy_met': report.meets_enterprise_accuracy,
916
- 'validation_completeness': report.total_validations >= 10,
917
- 'evidence_integrity': True
918
- }
919
833
  }
920
-
834
+
921
835
  # Calculate evidence bundle hash
922
836
  bundle_content = json.dumps(evidence_bundle, sort_keys=True, default=str)
923
837
  evidence_hash = hashlib.sha256(bundle_content.encode()).hexdigest()
924
- evidence_bundle['evidence_bundle_hash'] = evidence_hash
925
-
838
+ evidence_bundle["evidence_bundle_hash"] = evidence_hash
839
+
926
840
  # Save evidence bundle
927
- with open(evidence_path, 'w') as f:
841
+ with open(evidence_path, "w") as f:
928
842
  json.dump(evidence_bundle, f, indent=2, default=str)
929
-
843
+
930
844
  self.console.print(f" ✅ Evidence bundle saved: {evidence_path}")
931
845
  self.console.print(f" ✅ Evidence hash: {evidence_hash[:16]}...")
932
-
846
+
933
847
  def _display_validation_results(self, report: AWSO5MCPValidationReport):
934
848
  """Display comprehensive MCP validation results."""
935
-
849
+
936
850
  # Overall Summary
937
851
  summary_table = create_table(title="AWSO-5 MCP Validation Summary")
938
852
  summary_table.add_column("Metric", style="cyan", no_wrap=True)
939
853
  summary_table.add_column("Result", style="green")
940
854
  summary_table.add_column("Status", style="yellow")
941
-
942
- summary_table.add_row("Overall Accuracy", f"{report.overall_accuracy:.2f}%",
943
- " MEETS STANDARD" if report.meets_enterprise_accuracy else "❌ BELOW STANDARD")
855
+
856
+ summary_table.add_row(
857
+ "Overall Accuracy",
858
+ f"{report.overall_accuracy:.2f}%",
859
+ "✅ MEETS STANDARD" if report.meets_enterprise_accuracy else "❌ BELOW STANDARD",
860
+ )
944
861
  summary_table.add_row("Validation Status", report.validation_status, "")
945
862
  summary_table.add_row("Total Validations", str(report.total_validations), "")
946
863
  summary_table.add_row("Passed Validations", str(report.passed_validations), "")
947
864
  summary_table.add_row("Failed Validations", str(report.failed_validations), "")
948
- summary_table.add_row("Enterprise Standard", "≥99.5%",
949
- "MET" if report.meets_enterprise_accuracy else "NOT MET")
865
+ summary_table.add_row("Enterprise Standard", "≥99.5%", "MET" if report.meets_enterprise_accuracy else "NOT MET")
950
866
  summary_table.add_row("Validation Duration", f"{report.validation_duration_seconds:.2f}s", "")
951
867
  summary_table.add_row("Evidence Hash", report.validation_hash[:16] + "...", "")
952
-
868
+
953
869
  self.console.print("\n")
954
870
  self.console.print(summary_table)
955
-
871
+
956
872
  # Detailed Results by Category
957
873
  categories = [
958
874
  ("Dependency Validation", report.dependency_validation),
959
875
  ("Architecture Validation", report.architecture_validation),
960
876
  ("Cost Validation", report.cost_validation),
961
- ("Security Validation", report.security_validation)
877
+ ("Security Validation", report.security_validation),
962
878
  ]
963
-
879
+
964
880
  for category_name, validations in categories:
965
881
  if validations:
966
882
  category_table = create_table(title=category_name)
@@ -969,21 +885,27 @@ class AWSO5MCPValidator:
969
885
  category_table.add_column("MCP Value", style="green")
970
886
  category_table.add_column("Accuracy", style="yellow")
971
887
  category_table.add_column("Status", style="red")
972
-
888
+
973
889
  for validation in validations:
974
- status_icon = "✅" if validation.validation_status == "PASS" else "❌" if validation.validation_status == "FAIL" else "⚠️"
975
-
890
+ status_icon = (
891
+ "✅"
892
+ if validation.validation_status == "PASS"
893
+ else "❌"
894
+ if validation.validation_status == "FAIL"
895
+ else "⚠️"
896
+ )
897
+
976
898
  category_table.add_row(
977
899
  validation.check_name,
978
900
  str(validation.runbooks_value),
979
901
  str(validation.mcp_value),
980
902
  f"{validation.accuracy_percentage:.2f}%",
981
- f"{status_icon} {validation.validation_status}"
903
+ f"{status_icon} {validation.validation_status}",
982
904
  )
983
-
905
+
984
906
  self.console.print("\n")
985
907
  self.console.print(category_table)
986
-
908
+
987
909
  # Final Status Panel
988
910
  if report.validation_status == "PASSED":
989
911
  status_text = "[green]✅ MCP VALIDATION PASSED[/green]"
@@ -994,7 +916,7 @@ class AWSO5MCPValidator:
994
916
  else:
995
917
  status_text = "[red]❌ MCP VALIDATION FAILED[/red]"
996
918
  details = "Critical validations failed, review and remediate"
997
-
919
+
998
920
  final_text = f"""
999
921
  {status_text}
1000
922
 
@@ -1004,21 +926,26 @@ class AWSO5MCPValidator:
1004
926
  **Evidence Hash:** {report.validation_hash[:16]}...
1005
927
 
1006
928
  **Quality Gates:**
1007
- • Enterprise Accuracy: {'✅ MET' if report.meets_enterprise_accuracy else '❌ NOT MET'}
1008
- • Validation Completeness: {'✅ COMPLETE' if report.total_validations >= 10 else '⚠️ PARTIAL'}
929
+ • Enterprise Accuracy: {"✅ MET" if report.meets_enterprise_accuracy else "❌ NOT MET"}
930
+ • Validation Completeness: {"✅ COMPLETE" if report.total_validations >= 10 else "⚠️ PARTIAL"}
1009
931
  • Evidence Integrity: ✅ VERIFIED
1010
932
 
1011
933
  **Next Steps:**
1012
934
  {details}
1013
935
  """
1014
-
936
+
1015
937
  from rich.panel import Panel
938
+
1016
939
  status_panel = Panel(
1017
940
  final_text,
1018
941
  title="🧪 MCP Validation Results",
1019
- border_style="green" if report.validation_status == "PASSED" else "yellow" if report.validation_status == "WARNING" else "red"
942
+ border_style="green"
943
+ if report.validation_status == "PASSED"
944
+ else "yellow"
945
+ if report.validation_status == "WARNING"
946
+ else "red",
1020
947
  )
1021
-
948
+
1022
949
  self.console.print("\n")
1023
950
  self.console.print(status_panel)
1024
951
 
@@ -1029,11 +956,11 @@ def validate_vpc_with_mcp(
1029
956
  architecture_result: Any,
1030
957
  profile: Optional[str] = None,
1031
958
  region: str = "us-east-1",
1032
- evidence_bundle_path: Optional[str] = None
959
+ evidence_bundle_path: Optional[str] = None,
1033
960
  ) -> AWSO5MCPValidationReport:
1034
961
  """
1035
962
  CLI wrapper for AWSO-5 MCP validation.
1036
-
963
+
1037
964
  Args:
1038
965
  vpc_id: AWS VPC identifier
1039
966
  dependency_result: VPC dependency analysis results
@@ -1041,68 +968,58 @@ def validate_vpc_with_mcp(
1041
968
  profile: AWS profile name
1042
969
  region: AWS region
1043
970
  evidence_bundle_path: Path to save evidence bundle
1044
-
971
+
1045
972
  Returns:
1046
973
  Comprehensive MCP validation report
1047
974
  """
1048
975
  session = boto3.Session(profile_name=profile) if profile else boto3.Session()
1049
976
  validator = AWSO5MCPValidator(session=session, region=region)
1050
-
1051
- return validator.comprehensive_vpc_validation(
1052
- vpc_id,
1053
- dependency_result,
1054
- architecture_result,
1055
- evidence_bundle_path
1056
- )
977
+
978
+ return validator.comprehensive_vpc_validation(vpc_id, dependency_result, architecture_result, evidence_bundle_path)
1057
979
 
1058
980
 
1059
981
  if __name__ == "__main__":
1060
982
  import argparse
1061
-
983
+
1062
984
  parser = argparse.ArgumentParser(description="AWSO-5 MCP Validation Framework")
1063
985
  parser.add_argument("--vpc-id", required=True, help="VPC ID to validate")
1064
986
  parser.add_argument("--profile", help="AWS profile name")
1065
987
  parser.add_argument("--region", default="us-east-1", help="AWS region")
1066
988
  parser.add_argument("--evidence-bundle", help="Path to save evidence bundle")
1067
-
989
+
1068
990
  args = parser.parse_args()
1069
-
991
+
1070
992
  # For standalone testing, create minimal dependency and architecture results
1071
993
  from dataclasses import dataclass
1072
-
994
+
1073
995
  @dataclass
1074
996
  class MockDependencyResult:
1075
997
  eni_count: int = 0
1076
998
  dependencies: list = None
1077
999
  estimated_monthly_savings: float = 50.0
1078
-
1000
+
1079
1001
  def __post_init__(self):
1080
1002
  if self.dependencies is None:
1081
1003
  self.dependencies = []
1082
-
1083
- @dataclass
1004
+
1005
+ @dataclass
1084
1006
  class MockArchitectureResult:
1085
1007
  is_default: bool = False
1086
1008
  cidr_blocks: list = None
1087
1009
  overall_compliance_score: float = 85.0
1088
-
1010
+
1089
1011
  def __post_init__(self):
1090
1012
  if self.cidr_blocks is None:
1091
1013
  self.cidr_blocks = ["10.0.0.0/16"]
1092
-
1014
+
1093
1015
  mock_dependency = MockDependencyResult()
1094
1016
  mock_architecture = MockArchitectureResult()
1095
-
1017
+
1096
1018
  result = validate_vpc_with_mcp(
1097
- args.vpc_id,
1098
- mock_dependency,
1099
- mock_architecture,
1100
- args.profile,
1101
- args.region,
1102
- args.evidence_bundle
1019
+ args.vpc_id, mock_dependency, mock_architecture, args.profile, args.region, args.evidence_bundle
1103
1020
  )
1104
-
1021
+
1105
1022
  if result.meets_enterprise_accuracy:
1106
1023
  print_success(f"✅ MCP validation PASSED with {result.overall_accuracy:.2f}% accuracy")
1107
1024
  else:
1108
- print_error(f"❌ MCP validation FAILED with {result.overall_accuracy:.2f}% accuracy")
1025
+ print_error(f"❌ MCP validation FAILED with {result.overall_accuracy:.2f}% accuracy")