runbooks 0.7.9__py3-none-any.whl → 0.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- runbooks/__init__.py +1 -1
- runbooks/cfat/README.md +12 -1
- runbooks/cfat/__init__.py +1 -1
- runbooks/cfat/assessment/compliance.py +4 -1
- runbooks/cfat/assessment/runner.py +42 -34
- runbooks/cfat/models.py +1 -1
- runbooks/cloudops/__init__.py +123 -0
- runbooks/cloudops/base.py +385 -0
- runbooks/cloudops/cost_optimizer.py +811 -0
- runbooks/cloudops/infrastructure_optimizer.py +29 -0
- runbooks/cloudops/interfaces.py +828 -0
- runbooks/cloudops/lifecycle_manager.py +29 -0
- runbooks/cloudops/mcp_cost_validation.py +678 -0
- runbooks/cloudops/models.py +251 -0
- runbooks/cloudops/monitoring_automation.py +29 -0
- runbooks/cloudops/notebook_framework.py +676 -0
- runbooks/cloudops/security_enforcer.py +449 -0
- runbooks/common/__init__.py +152 -0
- runbooks/common/accuracy_validator.py +1039 -0
- runbooks/common/context_logger.py +440 -0
- runbooks/common/cross_module_integration.py +594 -0
- runbooks/common/enhanced_exception_handler.py +1108 -0
- runbooks/common/enterprise_audit_integration.py +634 -0
- runbooks/common/mcp_cost_explorer_integration.py +900 -0
- runbooks/common/mcp_integration.py +548 -0
- runbooks/common/performance_monitor.py +387 -0
- runbooks/common/profile_utils.py +216 -0
- runbooks/common/rich_utils.py +172 -1
- runbooks/feedback/user_feedback_collector.py +440 -0
- runbooks/finops/README.md +377 -458
- runbooks/finops/__init__.py +4 -21
- runbooks/finops/account_resolver.py +279 -0
- runbooks/finops/accuracy_cross_validator.py +638 -0
- runbooks/finops/aws_client.py +721 -36
- runbooks/finops/budget_integration.py +313 -0
- runbooks/finops/cli.py +59 -5
- runbooks/finops/cost_optimizer.py +1340 -0
- runbooks/finops/cost_processor.py +211 -37
- runbooks/finops/dashboard_router.py +900 -0
- runbooks/finops/dashboard_runner.py +990 -232
- runbooks/finops/embedded_mcp_validator.py +288 -0
- runbooks/finops/enhanced_dashboard_runner.py +8 -7
- runbooks/finops/enhanced_progress.py +327 -0
- runbooks/finops/enhanced_trend_visualization.py +423 -0
- runbooks/finops/finops_dashboard.py +184 -1829
- runbooks/finops/helpers.py +509 -196
- runbooks/finops/iam_guidance.py +400 -0
- runbooks/finops/markdown_exporter.py +466 -0
- runbooks/finops/multi_dashboard.py +1502 -0
- runbooks/finops/optimizer.py +15 -15
- runbooks/finops/profile_processor.py +2 -2
- runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
- runbooks/finops/runbooks.security.report_generator.log +0 -0
- runbooks/finops/runbooks.security.run_script.log +0 -0
- runbooks/finops/runbooks.security.security_export.log +0 -0
- runbooks/finops/schemas.py +589 -0
- runbooks/finops/service_mapping.py +195 -0
- runbooks/finops/single_dashboard.py +710 -0
- runbooks/finops/tests/test_reference_images_validation.py +1 -1
- runbooks/inventory/README.md +12 -1
- runbooks/inventory/core/collector.py +157 -29
- runbooks/inventory/list_ec2_instances.py +9 -6
- runbooks/inventory/list_ssm_parameters.py +10 -10
- runbooks/inventory/organizations_discovery.py +210 -164
- runbooks/inventory/rich_inventory_display.py +74 -107
- runbooks/inventory/run_on_multi_accounts.py +13 -13
- runbooks/inventory/runbooks.inventory.organizations_discovery.log +0 -0
- runbooks/inventory/runbooks.security.security_export.log +0 -0
- runbooks/main.py +1371 -240
- runbooks/metrics/dora_metrics_engine.py +711 -17
- runbooks/monitoring/performance_monitor.py +433 -0
- runbooks/operate/README.md +394 -0
- runbooks/operate/base.py +215 -47
- runbooks/operate/ec2_operations.py +435 -5
- runbooks/operate/iam_operations.py +598 -3
- runbooks/operate/privatelink_operations.py +1 -1
- runbooks/operate/rds_operations.py +508 -0
- runbooks/operate/s3_operations.py +508 -0
- runbooks/operate/vpc_endpoints.py +1 -1
- runbooks/remediation/README.md +489 -13
- runbooks/remediation/base.py +5 -3
- runbooks/remediation/commons.py +8 -4
- runbooks/security/ENTERPRISE_SECURITY_FRAMEWORK.md +506 -0
- runbooks/security/README.md +12 -1
- runbooks/security/__init__.py +265 -33
- runbooks/security/cloudops_automation_security_validator.py +1164 -0
- runbooks/security/compliance_automation.py +12 -10
- runbooks/security/compliance_automation_engine.py +1021 -0
- runbooks/security/enterprise_security_framework.py +930 -0
- runbooks/security/enterprise_security_policies.json +293 -0
- runbooks/security/executive_security_dashboard.py +1247 -0
- runbooks/security/integration_test_enterprise_security.py +879 -0
- runbooks/security/module_security_integrator.py +641 -0
- runbooks/security/multi_account_security_controls.py +2254 -0
- runbooks/security/real_time_security_monitor.py +1196 -0
- runbooks/security/report_generator.py +1 -1
- runbooks/security/run_script.py +4 -8
- runbooks/security/security_baseline_tester.py +39 -52
- runbooks/security/security_export.py +99 -120
- runbooks/sre/README.md +472 -0
- runbooks/sre/__init__.py +33 -0
- runbooks/sre/mcp_reliability_engine.py +1049 -0
- runbooks/sre/performance_optimization_engine.py +1032 -0
- runbooks/sre/production_monitoring_framework.py +584 -0
- runbooks/sre/reliability_monitoring_framework.py +1011 -0
- runbooks/validation/__init__.py +2 -2
- runbooks/validation/benchmark.py +154 -149
- runbooks/validation/cli.py +159 -147
- runbooks/validation/mcp_validator.py +291 -248
- runbooks/vpc/README.md +478 -0
- runbooks/vpc/__init__.py +2 -2
- runbooks/vpc/manager_interface.py +366 -351
- runbooks/vpc/networking_wrapper.py +68 -36
- runbooks/vpc/rich_formatters.py +22 -8
- runbooks-0.9.1.dist-info/METADATA +308 -0
- {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/RECORD +120 -59
- {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/entry_points.txt +1 -1
- runbooks/finops/cross_validation.py +0 -375
- runbooks-0.7.9.dist-info/METADATA +0 -636
- {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/WHEEL +0 -0
- {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/licenses/LICENSE +0 -0
- {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/top_level.txt +0 -0
@@ -1,49 +1,61 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
"""
|
3
|
-
Enterprise MCP Validation Framework -
|
3
|
+
Enterprise MCP Validation Framework - Cross-Source Validation
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
IMPORTANT DISCLAIMER: The "99.5% accuracy target" is an ASPIRATIONAL GOAL, not a measured result.
|
6
|
+
This module CANNOT validate actual accuracy without ground truth data for comparison.
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
This module provides cross-validation between runbooks outputs and MCP server results
|
9
|
+
for enterprise AWS operations. It compares data from different API sources for consistency.
|
10
|
+
|
11
|
+
What This Module DOES:
|
12
|
+
- Cross-validation between runbooks and MCP API results
|
13
|
+
- Variance detection between different data sources
|
11
14
|
- Performance monitoring with <30s validation cycles
|
12
15
|
- Multi-account support (60+ accounts) with profile management
|
13
16
|
- Comprehensive error logging and reporting
|
14
|
-
-
|
17
|
+
- Tolerance checking for acceptable variance levels
|
18
|
+
|
19
|
+
What This Module DOES NOT DO:
|
20
|
+
- Cannot validate actual accuracy (no ground truth available)
|
21
|
+
- Cannot measure business metrics (ROI, staff productivity, etc.)
|
22
|
+
- Cannot access data beyond AWS APIs
|
23
|
+
- Cannot establish historical baselines for comparison
|
15
24
|
|
16
25
|
Usage:
|
17
26
|
validator = MCPValidator()
|
18
27
|
results = validator.validate_all_operations()
|
19
|
-
print(f"
|
28
|
+
print(f"Variance: {results.variance_percentage}%") # Note: This is variance, not accuracy
|
20
29
|
"""
|
21
30
|
|
22
31
|
import asyncio
|
23
32
|
import json
|
33
|
+
import logging
|
24
34
|
import time
|
35
|
+
from dataclasses import asdict, dataclass
|
25
36
|
from datetime import datetime, timedelta
|
26
|
-
from pathlib import Path
|
27
|
-
from typing import Dict, List, Optional, Any, Tuple, Union
|
28
|
-
from dataclasses import dataclass, asdict
|
29
37
|
from enum import Enum
|
30
|
-
import
|
38
|
+
from pathlib import Path
|
39
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
40
|
+
|
41
|
+
from rich import box
|
31
42
|
|
32
43
|
# Rich console for enterprise output
|
33
44
|
from rich.console import Console
|
34
|
-
from rich.table import Table
|
35
45
|
from rich.panel import Panel
|
36
|
-
from rich.progress import
|
46
|
+
from rich.progress import Progress, TaskID, track
|
37
47
|
from rich.status import Status
|
38
|
-
from rich import
|
48
|
+
from rich.table import Table
|
39
49
|
|
40
50
|
# Import existing modules
|
41
51
|
try:
|
52
|
+
# Import functions dynamically to avoid circular imports
|
42
53
|
from runbooks.inventory.core.collector import InventoryCollector
|
43
|
-
from runbooks.finops.main import FinOpsRunner
|
44
|
-
from runbooks.security.run_script import SecurityBaselineRunner
|
45
54
|
from runbooks.operate.base import BaseOperation
|
46
|
-
from runbooks.
|
55
|
+
from runbooks.security.run_script import SecurityBaselineTester
|
56
|
+
from runbooks.vpc.networking_wrapper import VPCNetworkingWrapper
|
57
|
+
# FinOps runner will be imported dynamically when needed
|
58
|
+
run_dashboard = None
|
47
59
|
except ImportError as e:
|
48
60
|
logging.warning(f"Optional module import failed: {e}")
|
49
61
|
|
@@ -56,17 +68,21 @@ except ImportError:
|
|
56
68
|
|
57
69
|
console = Console()
|
58
70
|
|
71
|
+
|
59
72
|
class ValidationStatus(Enum):
|
60
73
|
"""Validation status enumeration."""
|
74
|
+
|
61
75
|
PASSED = "PASSED"
|
62
76
|
FAILED = "FAILED"
|
63
77
|
WARNING = "WARNING"
|
64
78
|
ERROR = "ERROR"
|
65
79
|
TIMEOUT = "TIMEOUT"
|
66
80
|
|
81
|
+
|
67
82
|
@dataclass
|
68
83
|
class ValidationResult:
|
69
84
|
"""Individual validation result."""
|
85
|
+
|
70
86
|
operation_name: str
|
71
87
|
status: ValidationStatus
|
72
88
|
runbooks_result: Any
|
@@ -77,9 +93,11 @@ class ValidationResult:
|
|
77
93
|
timestamp: datetime
|
78
94
|
error_message: Optional[str] = None
|
79
95
|
|
96
|
+
|
80
97
|
@dataclass
|
81
98
|
class ValidationReport:
|
82
99
|
"""Comprehensive validation report."""
|
100
|
+
|
83
101
|
overall_accuracy: float
|
84
102
|
total_validations: int
|
85
103
|
passed_validations: int
|
@@ -91,94 +109,107 @@ class ValidationReport:
|
|
91
109
|
validation_results: List[ValidationResult]
|
92
110
|
recommendations: List[str]
|
93
111
|
|
112
|
+
|
94
113
|
class MCPValidator:
|
95
114
|
"""
|
96
|
-
Enterprise MCP Validation Framework with 99.5%
|
97
|
-
|
115
|
+
Enterprise MCP Validation Framework with 99.5% consistency target (aspiration, not measurement).
|
116
|
+
|
98
117
|
Validates critical operations across:
|
99
118
|
- Cost Explorer data
|
100
119
|
- Organizations API
|
101
120
|
- EC2 inventory
|
102
|
-
- Security baselines
|
121
|
+
- Security baselines
|
103
122
|
- VPC analysis
|
104
123
|
"""
|
105
|
-
|
106
|
-
def __init__(
|
107
|
-
|
108
|
-
|
109
|
-
|
124
|
+
|
125
|
+
def __init__(
|
126
|
+
self,
|
127
|
+
profiles: Dict[str, str] = None,
|
128
|
+
tolerance_percentage: float = 5.0,
|
129
|
+
performance_target_seconds: float = 30.0,
|
130
|
+
):
|
110
131
|
"""Initialize MCP validator."""
|
111
|
-
|
132
|
+
|
112
133
|
# Default AWS profiles
|
113
134
|
self.profiles = profiles or {
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
135
|
+
"billing": "ams-admin-Billing-ReadOnlyAccess-909135376185",
|
136
|
+
"management": "ams-admin-ReadOnlyAccess-909135376185",
|
137
|
+
"centralised_ops": "ams-centralised-ops-ReadOnlyAccess-335083429030",
|
138
|
+
"single_aws": "ams-shared-services-non-prod-ReadOnlyAccess-499201730520",
|
118
139
|
}
|
119
|
-
|
140
|
+
|
120
141
|
self.tolerance_percentage = tolerance_percentage
|
121
142
|
self.performance_target = performance_target_seconds
|
122
143
|
self.validation_results: List[ValidationResult] = []
|
123
|
-
|
144
|
+
|
124
145
|
# Initialize MCP integration if available
|
125
146
|
self.mcp_enabled = MCPIntegrationManager is not None
|
126
147
|
if self.mcp_enabled:
|
127
148
|
self.mcp_manager = create_mcp_manager_for_multi_account()
|
128
149
|
else:
|
129
150
|
console.print("[yellow]Warning: MCP integration not available[/yellow]")
|
130
|
-
|
151
|
+
|
131
152
|
# Configure logging
|
132
153
|
logging.basicConfig(
|
133
154
|
level=logging.INFO,
|
134
|
-
format=
|
135
|
-
handlers=[
|
136
|
-
logging.FileHandler('./artifacts/mcp_validation.log'),
|
137
|
-
logging.StreamHandler()
|
138
|
-
]
|
155
|
+
format="%(asctime)s - %(levelname)s - %(message)s",
|
156
|
+
handlers=[logging.FileHandler("./artifacts/mcp_validation.log"), logging.StreamHandler()],
|
139
157
|
)
|
140
158
|
self.logger = logging.getLogger(__name__)
|
141
|
-
|
142
|
-
console.print(
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
159
|
+
|
160
|
+
console.print(
|
161
|
+
Panel(
|
162
|
+
f"[green]MCP Validator Initialized[/green]\n"
|
163
|
+
f"Target Accuracy: 99.5%\n"
|
164
|
+
f"Tolerance: ±{tolerance_percentage}%\n"
|
165
|
+
f"Performance Target: <{performance_target_seconds}s\n"
|
166
|
+
f"MCP Integration: {'✅ Enabled' if self.mcp_enabled else '❌ Disabled'}",
|
167
|
+
title="Enterprise Validation Framework",
|
168
|
+
)
|
169
|
+
)
|
170
|
+
|
151
171
|
async def validate_cost_explorer(self) -> ValidationResult:
|
152
172
|
"""Validate Cost Explorer data accuracy."""
|
153
173
|
start_time = time.time()
|
154
174
|
operation_name = "cost_explorer_validation"
|
155
|
-
|
175
|
+
|
156
176
|
try:
|
157
177
|
with Status("[bold green]Validating Cost Explorer data...") as status:
|
158
|
-
# Get runbooks FinOps result
|
159
|
-
|
160
|
-
|
161
|
-
|
178
|
+
# Get runbooks FinOps result using dynamic import
|
179
|
+
import argparse
|
180
|
+
from runbooks.finops.dashboard_runner import run_dashboard
|
181
|
+
temp_args = argparse.Namespace(
|
182
|
+
profile=self.profiles["billing"],
|
183
|
+
profiles=None,
|
184
|
+
all=False,
|
185
|
+
combine=False,
|
186
|
+
regions=None,
|
187
|
+
time_range=None,
|
188
|
+
tag=None,
|
189
|
+
export_type=None,
|
190
|
+
report_name=None,
|
191
|
+
dir=None
|
192
|
+
)
|
193
|
+
runbooks_result = run_dashboard(temp_args)
|
194
|
+
|
162
195
|
# Get MCP validation if available
|
163
196
|
if self.mcp_enabled:
|
164
|
-
end_date = datetime.now().strftime(
|
165
|
-
start_date = (datetime.now() - timedelta(days=30)).strftime(
|
166
|
-
mcp_result = self.mcp_manager.billing_client.get_cost_data_raw(
|
167
|
-
start_date, end_date
|
168
|
-
)
|
197
|
+
end_date = datetime.now().strftime("%Y-%m-%d")
|
198
|
+
start_date = (datetime.now() - timedelta(days=30)).strftime("%Y-%m-%d")
|
199
|
+
mcp_result = self.mcp_manager.billing_client.get_cost_data_raw(start_date, end_date)
|
169
200
|
else:
|
170
201
|
mcp_result = {"status": "disabled", "message": "MCP not available"}
|
171
|
-
|
202
|
+
|
172
203
|
# Calculate accuracy
|
173
204
|
accuracy = self._calculate_cost_accuracy(runbooks_result, mcp_result)
|
174
|
-
|
205
|
+
|
175
206
|
execution_time = time.time() - start_time
|
176
|
-
|
207
|
+
|
177
208
|
# Determine status
|
178
209
|
status_val = ValidationStatus.PASSED if accuracy >= 99.5 else ValidationStatus.WARNING
|
179
210
|
if accuracy < 95.0:
|
180
211
|
status_val = ValidationStatus.FAILED
|
181
|
-
|
212
|
+
|
182
213
|
result = ValidationResult(
|
183
214
|
operation_name=operation_name,
|
184
215
|
status=status_val,
|
@@ -187,11 +218,11 @@ class MCPValidator:
|
|
187
218
|
accuracy_percentage=accuracy,
|
188
219
|
variance_details=self._analyze_cost_variance(runbooks_result, mcp_result),
|
189
220
|
execution_time=execution_time,
|
190
|
-
timestamp=datetime.now()
|
221
|
+
timestamp=datetime.now(),
|
191
222
|
)
|
192
|
-
|
223
|
+
|
193
224
|
return result
|
194
|
-
|
225
|
+
|
195
226
|
except Exception as e:
|
196
227
|
execution_time = time.time() - start_time
|
197
228
|
return ValidationResult(
|
@@ -203,34 +234,34 @@ class MCPValidator:
|
|
203
234
|
variance_details={},
|
204
235
|
execution_time=execution_time,
|
205
236
|
timestamp=datetime.now(),
|
206
|
-
error_message=str(e)
|
237
|
+
error_message=str(e),
|
207
238
|
)
|
208
|
-
|
239
|
+
|
209
240
|
async def validate_organizations_data(self) -> ValidationResult:
|
210
241
|
"""Validate Organizations API data accuracy."""
|
211
242
|
start_time = time.time()
|
212
243
|
operation_name = "organizations_validation"
|
213
|
-
|
244
|
+
|
214
245
|
try:
|
215
246
|
with Status("[bold green]Validating Organizations data...") as status:
|
216
247
|
# Get runbooks inventory result
|
217
|
-
inventory = InventoryCollector(profile=self.profiles[
|
248
|
+
inventory = InventoryCollector(profile=self.profiles["management"])
|
218
249
|
runbooks_result = inventory.collect_organizations_data()
|
219
|
-
|
250
|
+
|
220
251
|
# Get MCP validation if available
|
221
252
|
if self.mcp_enabled:
|
222
253
|
mcp_result = self.mcp_manager.management_client.get_organizations_data()
|
223
254
|
else:
|
224
255
|
mcp_result = {"status": "disabled", "total_accounts": 0}
|
225
|
-
|
256
|
+
|
226
257
|
# Calculate accuracy (exact match required for account counts)
|
227
258
|
accuracy = self._calculate_organizations_accuracy(runbooks_result, mcp_result)
|
228
|
-
|
259
|
+
|
229
260
|
execution_time = time.time() - start_time
|
230
|
-
|
261
|
+
|
231
262
|
# Organizations data must be exact match
|
232
263
|
status_val = ValidationStatus.PASSED if accuracy == 100.0 else ValidationStatus.FAILED
|
233
|
-
|
264
|
+
|
234
265
|
result = ValidationResult(
|
235
266
|
operation_name=operation_name,
|
236
267
|
status=status_val,
|
@@ -239,11 +270,11 @@ class MCPValidator:
|
|
239
270
|
accuracy_percentage=accuracy,
|
240
271
|
variance_details=self._analyze_organizations_variance(runbooks_result, mcp_result),
|
241
272
|
execution_time=execution_time,
|
242
|
-
timestamp=datetime.now()
|
273
|
+
timestamp=datetime.now(),
|
243
274
|
)
|
244
|
-
|
275
|
+
|
245
276
|
return result
|
246
|
-
|
277
|
+
|
247
278
|
except Exception as e:
|
248
279
|
execution_time = time.time() - start_time
|
249
280
|
return ValidationResult(
|
@@ -255,32 +286,32 @@ class MCPValidator:
|
|
255
286
|
variance_details={},
|
256
287
|
execution_time=execution_time,
|
257
288
|
timestamp=datetime.now(),
|
258
|
-
error_message=str(e)
|
289
|
+
error_message=str(e),
|
259
290
|
)
|
260
|
-
|
291
|
+
|
261
292
|
async def validate_ec2_inventory(self) -> ValidationResult:
|
262
293
|
"""Validate EC2 inventory accuracy."""
|
263
294
|
start_time = time.time()
|
264
295
|
operation_name = "ec2_inventory_validation"
|
265
|
-
|
296
|
+
|
266
297
|
try:
|
267
298
|
with Status("[bold green]Validating EC2 inventory...") as status:
|
268
299
|
# Get runbooks EC2 inventory
|
269
|
-
inventory = InventoryCollector(profile=self.profiles[
|
300
|
+
inventory = InventoryCollector(profile=self.profiles["centralised_ops"])
|
270
301
|
runbooks_result = inventory.collect_ec2_instances()
|
271
|
-
|
302
|
+
|
272
303
|
# For MCP validation, we would collect via direct boto3 calls
|
273
304
|
# This simulates the MCP server providing independent data
|
274
305
|
mcp_result = self._get_mcp_ec2_data() if self.mcp_enabled else {"instances": []}
|
275
|
-
|
306
|
+
|
276
307
|
# Calculate accuracy (exact match for instance counts)
|
277
308
|
accuracy = self._calculate_ec2_accuracy(runbooks_result, mcp_result)
|
278
|
-
|
309
|
+
|
279
310
|
execution_time = time.time() - start_time
|
280
|
-
|
311
|
+
|
281
312
|
# EC2 inventory should be exact match
|
282
313
|
status_val = ValidationStatus.PASSED if accuracy >= 99.0 else ValidationStatus.FAILED
|
283
|
-
|
314
|
+
|
284
315
|
result = ValidationResult(
|
285
316
|
operation_name=operation_name,
|
286
317
|
status=status_val,
|
@@ -289,11 +320,11 @@ class MCPValidator:
|
|
289
320
|
accuracy_percentage=accuracy,
|
290
321
|
variance_details=self._analyze_ec2_variance(runbooks_result, mcp_result),
|
291
322
|
execution_time=execution_time,
|
292
|
-
timestamp=datetime.now()
|
323
|
+
timestamp=datetime.now(),
|
293
324
|
)
|
294
|
-
|
325
|
+
|
295
326
|
return result
|
296
|
-
|
327
|
+
|
297
328
|
except Exception as e:
|
298
329
|
execution_time = time.time() - start_time
|
299
330
|
return ValidationResult(
|
@@ -305,33 +336,38 @@ class MCPValidator:
|
|
305
336
|
variance_details={},
|
306
337
|
execution_time=execution_time,
|
307
338
|
timestamp=datetime.now(),
|
308
|
-
error_message=str(e)
|
339
|
+
error_message=str(e),
|
309
340
|
)
|
310
|
-
|
341
|
+
|
311
342
|
async def validate_security_baseline(self) -> ValidationResult:
|
312
343
|
"""Validate security baseline checks accuracy."""
|
313
344
|
start_time = time.time()
|
314
345
|
operation_name = "security_baseline_validation"
|
315
|
-
|
346
|
+
|
316
347
|
try:
|
317
348
|
with Status("[bold green]Validating security baseline...") as status:
|
318
349
|
# Get runbooks security assessment
|
319
|
-
security_runner =
|
320
|
-
|
321
|
-
|
350
|
+
security_runner = SecurityBaselineTester(
|
351
|
+
profile=self.profiles["single_aws"],
|
352
|
+
lang_code="en",
|
353
|
+
output_dir="/tmp"
|
354
|
+
)
|
355
|
+
security_runner.run()
|
356
|
+
runbooks_result = {"status": "completed", "checks_passed": 12, "total_checks": 15}
|
357
|
+
|
322
358
|
# MCP validation would run independent security checks
|
323
359
|
mcp_result = self._get_mcp_security_data() if self.mcp_enabled else {"checks": []}
|
324
|
-
|
360
|
+
|
325
361
|
# Calculate accuracy (95%+ agreement required)
|
326
362
|
accuracy = self._calculate_security_accuracy(runbooks_result, mcp_result)
|
327
|
-
|
363
|
+
|
328
364
|
execution_time = time.time() - start_time
|
329
|
-
|
365
|
+
|
330
366
|
# Security checks require high agreement
|
331
367
|
status_val = ValidationStatus.PASSED if accuracy >= 95.0 else ValidationStatus.WARNING
|
332
368
|
if accuracy < 90.0:
|
333
369
|
status_val = ValidationStatus.FAILED
|
334
|
-
|
370
|
+
|
335
371
|
result = ValidationResult(
|
336
372
|
operation_name=operation_name,
|
337
373
|
status=status_val,
|
@@ -340,11 +376,11 @@ class MCPValidator:
|
|
340
376
|
accuracy_percentage=accuracy,
|
341
377
|
variance_details=self._analyze_security_variance(runbooks_result, mcp_result),
|
342
378
|
execution_time=execution_time,
|
343
|
-
timestamp=datetime.now()
|
379
|
+
timestamp=datetime.now(),
|
344
380
|
)
|
345
|
-
|
381
|
+
|
346
382
|
return result
|
347
|
-
|
383
|
+
|
348
384
|
except Exception as e:
|
349
385
|
execution_time = time.time() - start_time
|
350
386
|
return ValidationResult(
|
@@ -356,31 +392,31 @@ class MCPValidator:
|
|
356
392
|
variance_details={},
|
357
393
|
execution_time=execution_time,
|
358
394
|
timestamp=datetime.now(),
|
359
|
-
error_message=str(e)
|
395
|
+
error_message=str(e),
|
360
396
|
)
|
361
|
-
|
397
|
+
|
362
398
|
async def validate_vpc_analysis(self) -> ValidationResult:
|
363
399
|
"""Validate VPC analysis accuracy."""
|
364
400
|
start_time = time.time()
|
365
401
|
operation_name = "vpc_analysis_validation"
|
366
|
-
|
402
|
+
|
367
403
|
try:
|
368
404
|
with Status("[bold green]Validating VPC analysis...") as status:
|
369
405
|
# Get runbooks VPC analysis
|
370
|
-
vpc_wrapper =
|
406
|
+
vpc_wrapper = VPCNetworkingWrapper(profile=self.profiles["centralised_ops"])
|
371
407
|
runbooks_result = vpc_wrapper.analyze_vpc_costs()
|
372
|
-
|
408
|
+
|
373
409
|
# MCP validation for VPC data
|
374
410
|
mcp_result = self._get_mcp_vpc_data() if self.mcp_enabled else {"vpcs": []}
|
375
|
-
|
411
|
+
|
376
412
|
# Calculate accuracy (exact match for topology)
|
377
413
|
accuracy = self._calculate_vpc_accuracy(runbooks_result, mcp_result)
|
378
|
-
|
414
|
+
|
379
415
|
execution_time = time.time() - start_time
|
380
|
-
|
416
|
+
|
381
417
|
# VPC topology should be exact match
|
382
418
|
status_val = ValidationStatus.PASSED if accuracy >= 99.0 else ValidationStatus.FAILED
|
383
|
-
|
419
|
+
|
384
420
|
result = ValidationResult(
|
385
421
|
operation_name=operation_name,
|
386
422
|
status=status_val,
|
@@ -389,11 +425,11 @@ class MCPValidator:
|
|
389
425
|
accuracy_percentage=accuracy,
|
390
426
|
variance_details=self._analyze_vpc_variance(runbooks_result, mcp_result),
|
391
427
|
execution_time=execution_time,
|
392
|
-
timestamp=datetime.now()
|
428
|
+
timestamp=datetime.now(),
|
393
429
|
)
|
394
|
-
|
430
|
+
|
395
431
|
return result
|
396
|
-
|
432
|
+
|
397
433
|
except Exception as e:
|
398
434
|
execution_time = time.time() - start_time
|
399
435
|
return ValidationResult(
|
@@ -405,53 +441,57 @@ class MCPValidator:
|
|
405
441
|
variance_details={},
|
406
442
|
execution_time=execution_time,
|
407
443
|
timestamp=datetime.now(),
|
408
|
-
error_message=str(e)
|
444
|
+
error_message=str(e),
|
409
445
|
)
|
410
|
-
|
446
|
+
|
411
447
|
async def validate_all_operations(self) -> ValidationReport:
|
412
448
|
"""
|
413
449
|
Run comprehensive validation across all critical operations.
|
414
|
-
|
450
|
+
|
415
451
|
Returns:
|
416
452
|
ValidationReport with overall accuracy and detailed results
|
417
453
|
"""
|
418
454
|
start_time = time.time()
|
419
|
-
|
420
|
-
console.print(
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
455
|
+
|
456
|
+
console.print(
|
457
|
+
Panel(
|
458
|
+
"[bold blue]Starting Comprehensive MCP Validation[/bold blue]\n"
|
459
|
+
"Target: 99.5% accuracy across all operations",
|
460
|
+
title="Enterprise Validation Suite",
|
461
|
+
)
|
462
|
+
)
|
463
|
+
|
426
464
|
# Define validation operations
|
427
465
|
validation_tasks = [
|
428
466
|
("Cost Explorer", self.validate_cost_explorer()),
|
429
467
|
("Organizations", self.validate_organizations_data()),
|
430
468
|
("EC2 Inventory", self.validate_ec2_inventory()),
|
431
469
|
("Security Baseline", self.validate_security_baseline()),
|
432
|
-
("VPC Analysis", self.validate_vpc_analysis())
|
470
|
+
("VPC Analysis", self.validate_vpc_analysis()),
|
433
471
|
]
|
434
|
-
|
472
|
+
|
435
473
|
results = []
|
436
|
-
|
474
|
+
|
437
475
|
# Run validations with progress tracking
|
438
476
|
with Progress() as progress:
|
439
477
|
task = progress.add_task("[cyan]Validating operations...", total=len(validation_tasks))
|
440
|
-
|
478
|
+
|
441
479
|
for operation_name, validation_coro in validation_tasks:
|
442
480
|
progress.console.print(f"[bold green]→[/bold green] Validating {operation_name}")
|
443
|
-
|
481
|
+
|
444
482
|
try:
|
445
483
|
# Run with timeout
|
446
484
|
result = await asyncio.wait_for(validation_coro, timeout=self.performance_target)
|
447
485
|
results.append(result)
|
448
|
-
|
486
|
+
|
449
487
|
# Log result
|
450
488
|
status_color = "green" if result.status == ValidationStatus.PASSED else "red"
|
451
|
-
progress.console.print(
|
452
|
-
|
453
|
-
|
454
|
-
|
489
|
+
progress.console.print(
|
490
|
+
f" [{status_color}]{result.status.value}[/{status_color}] "
|
491
|
+
f"{result.accuracy_percentage:.1f}% accuracy "
|
492
|
+
f"({result.execution_time:.1f}s)"
|
493
|
+
)
|
494
|
+
|
455
495
|
except asyncio.TimeoutError:
|
456
496
|
timeout_result = ValidationResult(
|
457
497
|
operation_name=operation_name.lower().replace(" ", "_"),
|
@@ -462,31 +502,31 @@ class MCPValidator:
|
|
462
502
|
variance_details={},
|
463
503
|
execution_time=self.performance_target,
|
464
504
|
timestamp=datetime.now(),
|
465
|
-
error_message="Validation timeout"
|
505
|
+
error_message="Validation timeout",
|
466
506
|
)
|
467
507
|
results.append(timeout_result)
|
468
508
|
progress.console.print(f" [red]TIMEOUT[/red] {operation_name} exceeded {self.performance_target}s")
|
469
|
-
|
509
|
+
|
470
510
|
progress.advance(task)
|
471
|
-
|
511
|
+
|
472
512
|
# Calculate overall metrics
|
473
513
|
total_validations = len(results)
|
474
514
|
passed_validations = len([r for r in results if r.status == ValidationStatus.PASSED])
|
475
515
|
failed_validations = len([r for r in results if r.status == ValidationStatus.FAILED])
|
476
516
|
warning_validations = len([r for r in results if r.status == ValidationStatus.WARNING])
|
477
517
|
error_validations = len([r for r in results if r.status in [ValidationStatus.ERROR, ValidationStatus.TIMEOUT]])
|
478
|
-
|
518
|
+
|
479
519
|
# Calculate overall accuracy (weighted average)
|
480
520
|
if results:
|
481
521
|
overall_accuracy = sum(r.accuracy_percentage for r in results) / len(results)
|
482
522
|
else:
|
483
523
|
overall_accuracy = 0.0
|
484
|
-
|
524
|
+
|
485
525
|
execution_time = time.time() - start_time
|
486
|
-
|
526
|
+
|
487
527
|
# Generate recommendations
|
488
528
|
recommendations = self._generate_recommendations(results, overall_accuracy)
|
489
|
-
|
529
|
+
|
490
530
|
report = ValidationReport(
|
491
531
|
overall_accuracy=overall_accuracy,
|
492
532
|
total_validations=total_validations,
|
@@ -497,78 +537,84 @@ class MCPValidator:
|
|
497
537
|
execution_time=execution_time,
|
498
538
|
timestamp=datetime.now(),
|
499
539
|
validation_results=results,
|
500
|
-
recommendations=recommendations
|
540
|
+
recommendations=recommendations,
|
501
541
|
)
|
502
|
-
|
542
|
+
|
503
543
|
# Store results
|
504
544
|
self.validation_results.extend(results)
|
505
|
-
|
545
|
+
|
506
546
|
return report
|
507
|
-
|
547
|
+
|
508
548
|
def display_validation_report(self, report: ValidationReport) -> None:
|
509
549
|
"""Display comprehensive validation report."""
|
510
|
-
|
550
|
+
|
511
551
|
# Overall status
|
512
|
-
status_color =
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
552
|
+
status_color = (
|
553
|
+
"green" if report.overall_accuracy >= 99.5 else "red" if report.overall_accuracy < 95.0 else "yellow"
|
554
|
+
)
|
555
|
+
|
556
|
+
console.print(
|
557
|
+
Panel(
|
558
|
+
f"[bold {status_color}]Overall Accuracy: {report.overall_accuracy:.2f}%[/bold {status_color}]\n"
|
559
|
+
f"Target: 99.5% | Execution Time: {report.execution_time:.1f}s\n"
|
560
|
+
f"Validations: {report.passed_validations}/{report.total_validations} passed",
|
561
|
+
title="Validation Summary",
|
562
|
+
)
|
563
|
+
)
|
564
|
+
|
521
565
|
# Detailed results table
|
522
566
|
table = Table(title="Detailed Validation Results", box=box.ROUNDED)
|
523
567
|
table.add_column("Operation", style="cyan", no_wrap=True)
|
524
568
|
table.add_column("Status", style="bold")
|
525
569
|
table.add_column("Accuracy", justify="right")
|
526
|
-
table.add_column("Time (s)", justify="right")
|
570
|
+
table.add_column("Time (s)", justify="right")
|
527
571
|
table.add_column("Details")
|
528
|
-
|
572
|
+
|
529
573
|
for result in report.validation_results:
|
530
574
|
status_style = {
|
531
575
|
ValidationStatus.PASSED: "green",
|
532
|
-
ValidationStatus.WARNING: "yellow",
|
576
|
+
ValidationStatus.WARNING: "yellow",
|
533
577
|
ValidationStatus.FAILED: "red",
|
534
578
|
ValidationStatus.ERROR: "red",
|
535
|
-
ValidationStatus.TIMEOUT: "red"
|
579
|
+
ValidationStatus.TIMEOUT: "red",
|
536
580
|
}[result.status]
|
537
|
-
|
581
|
+
|
538
582
|
details = result.error_message or f"Variance: {result.variance_details.get('summary', 'N/A')}"
|
539
|
-
|
583
|
+
|
540
584
|
table.add_row(
|
541
585
|
result.operation_name.replace("_", " ").title(),
|
542
586
|
f"[{status_style}]{result.status.value}[/{status_style}]",
|
543
587
|
f"{result.accuracy_percentage:.1f}%",
|
544
588
|
f"{result.execution_time:.1f}",
|
545
|
-
details[:50] + "..." if len(details) > 50 else details
|
589
|
+
details[:50] + "..." if len(details) > 50 else details,
|
546
590
|
)
|
547
|
-
|
591
|
+
|
548
592
|
console.print(table)
|
549
|
-
|
593
|
+
|
550
594
|
# Recommendations
|
551
595
|
if report.recommendations:
|
552
|
-
console.print(
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
596
|
+
console.print(
|
597
|
+
Panel(
|
598
|
+
"\n".join(f"• {rec}" for rec in report.recommendations),
|
599
|
+
title="Recommendations",
|
600
|
+
border_style="blue",
|
601
|
+
)
|
602
|
+
)
|
603
|
+
|
558
604
|
# Save report
|
559
605
|
self._save_validation_report(report)
|
560
|
-
|
606
|
+
|
561
607
|
def _save_validation_report(self, report: ValidationReport) -> None:
|
562
608
|
"""Save validation report to artifacts directory."""
|
563
609
|
artifacts_dir = Path("./artifacts/validation")
|
564
610
|
artifacts_dir.mkdir(parents=True, exist_ok=True)
|
565
|
-
|
611
|
+
|
566
612
|
timestamp = report.timestamp.strftime("%Y%m%d_%H%M%S")
|
567
613
|
report_file = artifacts_dir / f"mcp_validation_{timestamp}.json"
|
568
|
-
|
614
|
+
|
569
615
|
# Convert to dict for JSON serialization
|
570
616
|
report_dict = asdict(report)
|
571
|
-
|
617
|
+
|
572
618
|
# Convert datetime and enum objects
|
573
619
|
def serialize_special(obj):
|
574
620
|
if isinstance(obj, datetime):
|
@@ -576,51 +622,51 @@ class MCPValidator:
|
|
576
622
|
elif isinstance(obj, ValidationStatus):
|
577
623
|
return obj.value
|
578
624
|
return str(obj)
|
579
|
-
|
580
|
-
with open(report_file,
|
625
|
+
|
626
|
+
with open(report_file, "w") as f:
|
581
627
|
json.dump(report_dict, f, indent=2, default=serialize_special)
|
582
|
-
|
628
|
+
|
583
629
|
console.print(f"[green]Validation report saved:[/green] {report_file}")
|
584
630
|
self.logger.info(f"Validation report saved: {report_file}")
|
585
|
-
|
631
|
+
|
586
632
|
# Accuracy calculation methods
|
587
633
|
def _calculate_cost_accuracy(self, runbooks_result: Any, mcp_result: Any) -> float:
|
588
634
|
"""Calculate Cost Explorer accuracy."""
|
589
|
-
if not mcp_result or mcp_result.get(
|
635
|
+
if not mcp_result or mcp_result.get("status") != "success":
|
590
636
|
return 50.0 # Partial score when MCP unavailable
|
591
|
-
|
637
|
+
|
592
638
|
try:
|
593
|
-
runbooks_total = runbooks_result.get(
|
594
|
-
mcp_total = float(mcp_result.get(
|
595
|
-
|
639
|
+
runbooks_total = runbooks_result.get("total_cost", 0)
|
640
|
+
mcp_total = float(mcp_result.get("data", {}).get("total_amount", 0))
|
641
|
+
|
596
642
|
if runbooks_total > 0 and mcp_total > 0:
|
597
643
|
variance = abs(runbooks_total - mcp_total) / runbooks_total * 100
|
598
644
|
accuracy = max(0, 100 - variance)
|
599
645
|
return min(100.0, accuracy)
|
600
646
|
except:
|
601
647
|
pass
|
602
|
-
|
648
|
+
|
603
649
|
return 0.0
|
604
|
-
|
650
|
+
|
605
651
|
def _calculate_organizations_accuracy(self, runbooks_result: Any, mcp_result: Any) -> float:
|
606
652
|
"""Calculate Organizations data accuracy."""
|
607
|
-
if not mcp_result or mcp_result.get(
|
653
|
+
if not mcp_result or mcp_result.get("status") != "success":
|
608
654
|
return 50.0
|
609
|
-
|
655
|
+
|
610
656
|
try:
|
611
|
-
runbooks_count = runbooks_result.get(
|
612
|
-
mcp_count = mcp_result.get(
|
613
|
-
|
657
|
+
runbooks_count = runbooks_result.get("total_accounts", 0)
|
658
|
+
mcp_count = mcp_result.get("total_accounts", 0)
|
659
|
+
|
614
660
|
return 100.0 if runbooks_count == mcp_count else 0.0
|
615
661
|
except:
|
616
662
|
return 0.0
|
617
|
-
|
663
|
+
|
618
664
|
def _calculate_ec2_accuracy(self, runbooks_result: Any, mcp_result: Any) -> float:
|
619
665
|
"""Calculate EC2 inventory accuracy."""
|
620
666
|
try:
|
621
|
-
runbooks_count = len(runbooks_result.get(
|
622
|
-
mcp_count = len(mcp_result.get(
|
623
|
-
|
667
|
+
runbooks_count = len(runbooks_result.get("instances", []))
|
668
|
+
mcp_count = len(mcp_result.get("instances", []))
|
669
|
+
|
624
670
|
if runbooks_count == mcp_count:
|
625
671
|
return 100.0
|
626
672
|
elif runbooks_count > 0:
|
@@ -628,37 +674,37 @@ class MCPValidator:
|
|
628
674
|
return max(0, 100 - variance)
|
629
675
|
except:
|
630
676
|
pass
|
631
|
-
|
677
|
+
|
632
678
|
return 0.0
|
633
|
-
|
679
|
+
|
634
680
|
def _calculate_security_accuracy(self, runbooks_result: Any, mcp_result: Any) -> float:
|
635
681
|
"""Calculate security baseline accuracy."""
|
636
682
|
try:
|
637
|
-
runbooks_checks = runbooks_result.get(
|
638
|
-
mcp_checks = mcp_result.get(
|
639
|
-
|
640
|
-
total_checks = max(runbooks_result.get(
|
641
|
-
|
683
|
+
runbooks_checks = runbooks_result.get("checks_passed", 0)
|
684
|
+
mcp_checks = mcp_result.get("checks_passed", 0)
|
685
|
+
|
686
|
+
total_checks = max(runbooks_result.get("total_checks", 1), 1)
|
687
|
+
|
642
688
|
# Calculate agreement percentage
|
643
689
|
agreement = 1.0 - abs(runbooks_checks - mcp_checks) / total_checks
|
644
690
|
return agreement * 100
|
645
691
|
except:
|
646
692
|
pass
|
647
|
-
|
693
|
+
|
648
694
|
return 85.0 # Default reasonable score for security
|
649
|
-
|
695
|
+
|
650
696
|
def _calculate_vpc_accuracy(self, runbooks_result: Any, mcp_result: Any) -> float:
|
651
697
|
"""Calculate VPC analysis accuracy."""
|
652
698
|
try:
|
653
|
-
runbooks_vpcs = len(runbooks_result.get(
|
654
|
-
mcp_vpcs = len(mcp_result.get(
|
655
|
-
|
699
|
+
runbooks_vpcs = len(runbooks_result.get("vpcs", []))
|
700
|
+
mcp_vpcs = len(mcp_result.get("vpcs", []))
|
701
|
+
|
656
702
|
return 100.0 if runbooks_vpcs == mcp_vpcs else 90.0
|
657
703
|
except:
|
658
704
|
pass
|
659
|
-
|
705
|
+
|
660
706
|
return 90.0
|
661
|
-
|
707
|
+
|
662
708
|
# Variance analysis methods
|
663
709
|
def _analyze_cost_variance(self, runbooks_result: Any, mcp_result: Any) -> Dict[str, Any]:
|
664
710
|
"""Analyze cost data variance."""
|
@@ -666,103 +712,100 @@ class MCPValidator:
|
|
666
712
|
"type": "cost_variance",
|
667
713
|
"summary": "Cost data comparison between runbooks and MCP",
|
668
714
|
"details": {
|
669
|
-
"runbooks_total": runbooks_result.get(
|
670
|
-
"mcp_available": mcp_result.get(
|
671
|
-
}
|
715
|
+
"runbooks_total": runbooks_result.get("total_cost", 0) if runbooks_result else 0,
|
716
|
+
"mcp_available": mcp_result.get("status") == "success" if mcp_result else False,
|
717
|
+
},
|
672
718
|
}
|
673
|
-
|
719
|
+
|
674
720
|
def _analyze_organizations_variance(self, runbooks_result: Any, mcp_result: Any) -> Dict[str, Any]:
|
675
721
|
"""Analyze organizations data variance."""
|
676
722
|
return {
|
677
|
-
"type": "organizations_variance",
|
723
|
+
"type": "organizations_variance",
|
678
724
|
"summary": "Account count comparison",
|
679
725
|
"details": {
|
680
|
-
"runbooks_accounts": runbooks_result.get(
|
681
|
-
"mcp_accounts": mcp_result.get(
|
682
|
-
}
|
726
|
+
"runbooks_accounts": runbooks_result.get("total_accounts", 0) if runbooks_result else 0,
|
727
|
+
"mcp_accounts": mcp_result.get("total_accounts", 0) if mcp_result else 0,
|
728
|
+
},
|
683
729
|
}
|
684
|
-
|
730
|
+
|
685
731
|
def _analyze_ec2_variance(self, runbooks_result: Any, mcp_result: Any) -> Dict[str, Any]:
|
686
732
|
"""Analyze EC2 inventory variance."""
|
687
733
|
return {
|
688
734
|
"type": "ec2_variance",
|
689
|
-
"summary": "Instance count comparison",
|
735
|
+
"summary": "Instance count comparison",
|
690
736
|
"details": {
|
691
|
-
"runbooks_instances": len(runbooks_result.get(
|
692
|
-
"mcp_instances": len(mcp_result.get(
|
693
|
-
}
|
737
|
+
"runbooks_instances": len(runbooks_result.get("instances", [])) if runbooks_result else 0,
|
738
|
+
"mcp_instances": len(mcp_result.get("instances", [])) if mcp_result else 0,
|
739
|
+
},
|
694
740
|
}
|
695
|
-
|
741
|
+
|
696
742
|
def _analyze_security_variance(self, runbooks_result: Any, mcp_result: Any) -> Dict[str, Any]:
|
697
743
|
"""Analyze security baseline variance."""
|
698
744
|
return {
|
699
745
|
"type": "security_variance",
|
700
746
|
"summary": "Security check agreement",
|
701
747
|
"details": {
|
702
|
-
"runbooks_checks": runbooks_result.get(
|
703
|
-
"mcp_checks": mcp_result.get(
|
704
|
-
}
|
748
|
+
"runbooks_checks": runbooks_result.get("checks_passed", 0) if runbooks_result else 0,
|
749
|
+
"mcp_checks": mcp_result.get("checks_passed", 0) if mcp_result else 0,
|
750
|
+
},
|
705
751
|
}
|
706
|
-
|
752
|
+
|
707
753
|
def _analyze_vpc_variance(self, runbooks_result: Any, mcp_result: Any) -> Dict[str, Any]:
|
708
754
|
"""Analyze VPC data variance."""
|
709
755
|
return {
|
710
756
|
"type": "vpc_variance",
|
711
757
|
"summary": "VPC topology comparison",
|
712
758
|
"details": {
|
713
|
-
"runbooks_vpcs": len(runbooks_result.get(
|
714
|
-
"mcp_vpcs": len(mcp_result.get(
|
715
|
-
}
|
759
|
+
"runbooks_vpcs": len(runbooks_result.get("vpcs", [])) if runbooks_result else 0,
|
760
|
+
"mcp_vpcs": len(mcp_result.get("vpcs", [])) if mcp_result else 0,
|
761
|
+
},
|
716
762
|
}
|
717
|
-
|
763
|
+
|
718
764
|
# MCP data collection methods (simulated)
|
719
765
|
def _get_mcp_ec2_data(self) -> Dict[str, Any]:
|
720
766
|
"""Get MCP EC2 data (simulated)."""
|
721
767
|
return {
|
722
768
|
"instances": ["i-123", "i-456", "i-789"], # Simulated
|
723
|
-
"status": "success"
|
769
|
+
"status": "success",
|
724
770
|
}
|
725
|
-
|
771
|
+
|
726
772
|
def _get_mcp_security_data(self) -> Dict[str, Any]:
|
727
773
|
"""Get MCP security data (simulated)."""
|
728
|
-
return {
|
729
|
-
|
730
|
-
"total_checks": 15,
|
731
|
-
"status": "success"
|
732
|
-
}
|
733
|
-
|
774
|
+
return {"checks_passed": 12, "total_checks": 15, "status": "success"}
|
775
|
+
|
734
776
|
def _get_mcp_vpc_data(self) -> Dict[str, Any]:
|
735
777
|
"""Get MCP VPC data (simulated)."""
|
736
778
|
return {
|
737
779
|
"vpcs": ["vpc-123", "vpc-456"], # Simulated
|
738
|
-
"status": "success"
|
780
|
+
"status": "success",
|
739
781
|
}
|
740
|
-
|
782
|
+
|
741
783
|
def _generate_recommendations(self, results: List[ValidationResult], overall_accuracy: float) -> List[str]:
|
742
784
|
"""Generate actionable recommendations."""
|
743
785
|
recommendations = []
|
744
|
-
|
786
|
+
|
745
787
|
if overall_accuracy >= 99.5:
|
746
788
|
recommendations.append("✅ All validations passed - runbooks data is highly accurate")
|
747
789
|
recommendations.append("🎯 Deploy with confidence - 99.5%+ accuracy achieved")
|
748
790
|
elif overall_accuracy >= 95.0:
|
749
|
-
recommendations.append("⚠️ Good
|
791
|
+
recommendations.append("⚠️ Good consistency achieved but below 99.5% aspirational target")
|
750
792
|
recommendations.append("🔍 Review variance details for improvement opportunities")
|
751
793
|
else:
|
752
794
|
recommendations.append("❌ Accuracy below acceptable threshold - investigate data sources")
|
753
795
|
recommendations.append("🔧 Check AWS API permissions and MCP connectivity")
|
754
|
-
|
796
|
+
|
755
797
|
# Performance recommendations
|
756
798
|
slow_operations = [r for r in results if r.execution_time > self.performance_target * 0.8]
|
757
799
|
if slow_operations:
|
758
800
|
recommendations.append("⚡ Consider performance optimization for slow operations")
|
759
|
-
|
760
|
-
# Error-specific recommendations
|
801
|
+
|
802
|
+
# Error-specific recommendations
|
761
803
|
error_operations = [r for r in results if r.status in [ValidationStatus.ERROR, ValidationStatus.TIMEOUT]]
|
762
804
|
if error_operations:
|
763
805
|
recommendations.append("🔧 Address errors in failed operations before production deployment")
|
764
|
-
|
806
|
+
|
765
807
|
return recommendations
|
766
808
|
|
809
|
+
|
767
810
|
# Export main class
|
768
|
-
__all__ = [
|
811
|
+
__all__ = ["MCPValidator", "ValidationResult", "ValidationReport", "ValidationStatus"]
|