runbooks 1.1.4__py3-none-any.whl → 1.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- runbooks/__init__.py +31 -2
- runbooks/__init___optimized.py +18 -4
- runbooks/_platform/__init__.py +1 -5
- runbooks/_platform/core/runbooks_wrapper.py +141 -138
- runbooks/aws2/accuracy_validator.py +812 -0
- runbooks/base.py +7 -0
- runbooks/cfat/assessment/compliance.py +1 -1
- runbooks/cfat/assessment/runner.py +1 -0
- runbooks/cfat/cloud_foundations_assessment.py +227 -239
- runbooks/cli/__init__.py +1 -1
- runbooks/cli/commands/cfat.py +64 -23
- runbooks/cli/commands/finops.py +1005 -54
- runbooks/cli/commands/inventory.py +135 -91
- runbooks/cli/commands/operate.py +9 -36
- runbooks/cli/commands/security.py +42 -18
- runbooks/cli/commands/validation.py +432 -18
- runbooks/cli/commands/vpc.py +81 -17
- runbooks/cli/registry.py +22 -10
- runbooks/cloudops/__init__.py +20 -27
- runbooks/cloudops/base.py +96 -107
- runbooks/cloudops/cost_optimizer.py +544 -542
- runbooks/cloudops/infrastructure_optimizer.py +5 -4
- runbooks/cloudops/interfaces.py +224 -225
- runbooks/cloudops/lifecycle_manager.py +5 -4
- runbooks/cloudops/mcp_cost_validation.py +252 -235
- runbooks/cloudops/models.py +78 -53
- runbooks/cloudops/monitoring_automation.py +5 -4
- runbooks/cloudops/notebook_framework.py +177 -213
- runbooks/cloudops/security_enforcer.py +125 -159
- runbooks/common/accuracy_validator.py +17 -12
- runbooks/common/aws_pricing.py +349 -326
- runbooks/common/aws_pricing_api.py +211 -212
- runbooks/common/aws_profile_manager.py +40 -36
- runbooks/common/aws_utils.py +74 -79
- runbooks/common/business_logic.py +126 -104
- runbooks/common/cli_decorators.py +36 -60
- runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
- runbooks/common/cross_account_manager.py +197 -204
- runbooks/common/date_utils.py +27 -39
- runbooks/common/decorators.py +29 -19
- runbooks/common/dry_run_examples.py +173 -208
- runbooks/common/dry_run_framework.py +157 -155
- runbooks/common/enhanced_exception_handler.py +15 -4
- runbooks/common/enhanced_logging_example.py +50 -64
- runbooks/common/enhanced_logging_integration_example.py +65 -37
- runbooks/common/env_utils.py +16 -16
- runbooks/common/error_handling.py +40 -38
- runbooks/common/lazy_loader.py +41 -23
- runbooks/common/logging_integration_helper.py +79 -86
- runbooks/common/mcp_cost_explorer_integration.py +476 -493
- runbooks/common/mcp_integration.py +99 -79
- runbooks/common/memory_optimization.py +140 -118
- runbooks/common/module_cli_base.py +37 -58
- runbooks/common/organizations_client.py +175 -193
- runbooks/common/patterns.py +23 -25
- runbooks/common/performance_monitoring.py +67 -71
- runbooks/common/performance_optimization_engine.py +283 -274
- runbooks/common/profile_utils.py +111 -37
- runbooks/common/rich_utils.py +315 -141
- runbooks/common/sre_performance_suite.py +177 -186
- runbooks/enterprise/__init__.py +1 -1
- runbooks/enterprise/logging.py +144 -106
- runbooks/enterprise/security.py +187 -204
- runbooks/enterprise/validation.py +43 -56
- runbooks/finops/__init__.py +26 -30
- runbooks/finops/account_resolver.py +1 -1
- runbooks/finops/advanced_optimization_engine.py +980 -0
- runbooks/finops/automation_core.py +268 -231
- runbooks/finops/business_case_config.py +184 -179
- runbooks/finops/cli.py +660 -139
- runbooks/finops/commvault_ec2_analysis.py +157 -164
- runbooks/finops/compute_cost_optimizer.py +336 -320
- runbooks/finops/config.py +20 -20
- runbooks/finops/cost_optimizer.py +484 -618
- runbooks/finops/cost_processor.py +332 -214
- runbooks/finops/dashboard_runner.py +1006 -172
- runbooks/finops/ebs_cost_optimizer.py +991 -657
- runbooks/finops/elastic_ip_optimizer.py +317 -257
- runbooks/finops/enhanced_mcp_integration.py +340 -0
- runbooks/finops/enhanced_progress.py +32 -29
- runbooks/finops/enhanced_trend_visualization.py +3 -2
- runbooks/finops/enterprise_wrappers.py +223 -285
- runbooks/finops/executive_export.py +203 -160
- runbooks/finops/helpers.py +130 -288
- runbooks/finops/iam_guidance.py +1 -1
- runbooks/finops/infrastructure/__init__.py +80 -0
- runbooks/finops/infrastructure/commands.py +506 -0
- runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
- runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
- runbooks/finops/markdown_exporter.py +337 -174
- runbooks/finops/mcp_validator.py +1952 -0
- runbooks/finops/nat_gateway_optimizer.py +1512 -481
- runbooks/finops/network_cost_optimizer.py +657 -587
- runbooks/finops/notebook_utils.py +226 -188
- runbooks/finops/optimization_engine.py +1136 -0
- runbooks/finops/optimizer.py +19 -23
- runbooks/finops/rds_snapshot_optimizer.py +367 -411
- runbooks/finops/reservation_optimizer.py +427 -363
- runbooks/finops/scenario_cli_integration.py +64 -65
- runbooks/finops/scenarios.py +1277 -438
- runbooks/finops/schemas.py +218 -182
- runbooks/finops/snapshot_manager.py +2289 -0
- runbooks/finops/types.py +3 -3
- runbooks/finops/validation_framework.py +259 -265
- runbooks/finops/vpc_cleanup_exporter.py +189 -144
- runbooks/finops/vpc_cleanup_optimizer.py +591 -573
- runbooks/finops/workspaces_analyzer.py +171 -182
- runbooks/integration/__init__.py +89 -0
- runbooks/integration/mcp_integration.py +1920 -0
- runbooks/inventory/CLAUDE.md +816 -0
- runbooks/inventory/__init__.py +2 -2
- runbooks/inventory/aws_decorators.py +2 -3
- runbooks/inventory/check_cloudtrail_compliance.py +2 -4
- runbooks/inventory/check_controltower_readiness.py +152 -151
- runbooks/inventory/check_landingzone_readiness.py +85 -84
- runbooks/inventory/cloud_foundations_integration.py +144 -149
- runbooks/inventory/collectors/aws_comprehensive.py +1 -1
- runbooks/inventory/collectors/aws_networking.py +109 -99
- runbooks/inventory/collectors/base.py +4 -0
- runbooks/inventory/core/collector.py +495 -313
- runbooks/inventory/core/formatter.py +11 -0
- runbooks/inventory/draw_org_structure.py +8 -9
- runbooks/inventory/drift_detection_cli.py +69 -96
- runbooks/inventory/ec2_vpc_utils.py +2 -2
- runbooks/inventory/find_cfn_drift_detection.py +5 -7
- runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
- runbooks/inventory/find_cfn_stackset_drift.py +5 -6
- runbooks/inventory/find_ec2_security_groups.py +48 -42
- runbooks/inventory/find_landingzone_versions.py +4 -6
- runbooks/inventory/find_vpc_flow_logs.py +7 -9
- runbooks/inventory/inventory_mcp_cli.py +48 -46
- runbooks/inventory/inventory_modules.py +103 -91
- runbooks/inventory/list_cfn_stacks.py +9 -10
- runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
- runbooks/inventory/list_cfn_stackset_operations.py +79 -57
- runbooks/inventory/list_cfn_stacksets.py +8 -10
- runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
- runbooks/inventory/list_ds_directories.py +65 -53
- runbooks/inventory/list_ec2_availability_zones.py +2 -4
- runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
- runbooks/inventory/list_ec2_instances.py +23 -28
- runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
- runbooks/inventory/list_elbs_load_balancers.py +22 -20
- runbooks/inventory/list_enis_network_interfaces.py +26 -33
- runbooks/inventory/list_guardduty_detectors.py +2 -4
- runbooks/inventory/list_iam_policies.py +2 -4
- runbooks/inventory/list_iam_roles.py +5 -7
- runbooks/inventory/list_iam_saml_providers.py +4 -6
- runbooks/inventory/list_lambda_functions.py +38 -38
- runbooks/inventory/list_org_accounts.py +6 -8
- runbooks/inventory/list_org_accounts_users.py +55 -44
- runbooks/inventory/list_rds_db_instances.py +31 -33
- runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
- runbooks/inventory/list_route53_hosted_zones.py +3 -5
- runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
- runbooks/inventory/list_sns_topics.py +2 -4
- runbooks/inventory/list_ssm_parameters.py +4 -7
- runbooks/inventory/list_vpc_subnets.py +2 -4
- runbooks/inventory/list_vpcs.py +7 -10
- runbooks/inventory/mcp_inventory_validator.py +554 -468
- runbooks/inventory/mcp_vpc_validator.py +359 -442
- runbooks/inventory/organizations_discovery.py +63 -55
- runbooks/inventory/recover_cfn_stack_ids.py +7 -8
- runbooks/inventory/requirements.txt +0 -1
- runbooks/inventory/rich_inventory_display.py +35 -34
- runbooks/inventory/run_on_multi_accounts.py +3 -5
- runbooks/inventory/unified_validation_engine.py +281 -253
- runbooks/inventory/verify_ec2_security_groups.py +1 -1
- runbooks/inventory/vpc_analyzer.py +735 -697
- runbooks/inventory/vpc_architecture_validator.py +293 -348
- runbooks/inventory/vpc_dependency_analyzer.py +384 -380
- runbooks/inventory/vpc_flow_analyzer.py +1 -1
- runbooks/main.py +49 -34
- runbooks/main_final.py +91 -60
- runbooks/main_minimal.py +22 -10
- runbooks/main_optimized.py +131 -100
- runbooks/main_ultra_minimal.py +7 -2
- runbooks/mcp/__init__.py +36 -0
- runbooks/mcp/integration.py +679 -0
- runbooks/monitoring/performance_monitor.py +9 -4
- runbooks/operate/dynamodb_operations.py +3 -1
- runbooks/operate/ec2_operations.py +145 -137
- runbooks/operate/iam_operations.py +146 -152
- runbooks/operate/networking_cost_heatmap.py +29 -8
- runbooks/operate/rds_operations.py +223 -254
- runbooks/operate/s3_operations.py +107 -118
- runbooks/operate/vpc_operations.py +646 -616
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/commons.py +10 -7
- runbooks/remediation/commvault_ec2_analysis.py +70 -66
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
- runbooks/remediation/multi_account.py +24 -21
- runbooks/remediation/rds_snapshot_list.py +86 -60
- runbooks/remediation/remediation_cli.py +92 -146
- runbooks/remediation/universal_account_discovery.py +83 -79
- runbooks/remediation/workspaces_list.py +46 -41
- runbooks/security/__init__.py +19 -0
- runbooks/security/assessment_runner.py +1150 -0
- runbooks/security/baseline_checker.py +812 -0
- runbooks/security/cloudops_automation_security_validator.py +509 -535
- runbooks/security/compliance_automation_engine.py +17 -17
- runbooks/security/config/__init__.py +2 -2
- runbooks/security/config/compliance_config.py +50 -50
- runbooks/security/config_template_generator.py +63 -76
- runbooks/security/enterprise_security_framework.py +1 -1
- runbooks/security/executive_security_dashboard.py +519 -508
- runbooks/security/multi_account_security_controls.py +959 -1210
- runbooks/security/real_time_security_monitor.py +422 -444
- runbooks/security/security_baseline_tester.py +1 -1
- runbooks/security/security_cli.py +143 -112
- runbooks/security/test_2way_validation.py +439 -0
- runbooks/security/two_way_validation_framework.py +852 -0
- runbooks/sre/production_monitoring_framework.py +167 -177
- runbooks/tdd/__init__.py +15 -0
- runbooks/tdd/cli.py +1071 -0
- runbooks/utils/__init__.py +14 -17
- runbooks/utils/logger.py +7 -2
- runbooks/utils/version_validator.py +50 -47
- runbooks/validation/__init__.py +6 -6
- runbooks/validation/cli.py +9 -3
- runbooks/validation/comprehensive_2way_validator.py +745 -704
- runbooks/validation/mcp_validator.py +906 -228
- runbooks/validation/terraform_citations_validator.py +104 -115
- runbooks/validation/terraform_drift_detector.py +461 -454
- runbooks/vpc/README.md +617 -0
- runbooks/vpc/__init__.py +8 -1
- runbooks/vpc/analyzer.py +577 -0
- runbooks/vpc/cleanup_wrapper.py +476 -413
- runbooks/vpc/cli_cloudtrail_commands.py +339 -0
- runbooks/vpc/cli_mcp_validation_commands.py +480 -0
- runbooks/vpc/cloudtrail_audit_integration.py +717 -0
- runbooks/vpc/config.py +92 -97
- runbooks/vpc/cost_engine.py +411 -148
- runbooks/vpc/cost_explorer_integration.py +553 -0
- runbooks/vpc/cross_account_session.py +101 -106
- runbooks/vpc/enhanced_mcp_validation.py +917 -0
- runbooks/vpc/eni_gate_validator.py +961 -0
- runbooks/vpc/heatmap_engine.py +185 -160
- runbooks/vpc/mcp_no_eni_validator.py +680 -639
- runbooks/vpc/nat_gateway_optimizer.py +358 -0
- runbooks/vpc/networking_wrapper.py +15 -8
- runbooks/vpc/pdca_remediation_planner.py +528 -0
- runbooks/vpc/performance_optimized_analyzer.py +219 -231
- runbooks/vpc/runbooks_adapter.py +1167 -241
- runbooks/vpc/tdd_red_phase_stubs.py +601 -0
- runbooks/vpc/test_data_loader.py +358 -0
- runbooks/vpc/tests/conftest.py +314 -4
- runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
- runbooks/vpc/tests/test_cost_engine.py +0 -2
- runbooks/vpc/topology_generator.py +326 -0
- runbooks/vpc/unified_scenarios.py +1297 -1124
- runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
- runbooks-1.1.6.dist-info/METADATA +327 -0
- runbooks-1.1.6.dist-info/RECORD +489 -0
- runbooks/finops/README.md +0 -414
- runbooks/finops/accuracy_cross_validator.py +0 -647
- runbooks/finops/business_cases.py +0 -950
- runbooks/finops/dashboard_router.py +0 -922
- runbooks/finops/ebs_optimizer.py +0 -973
- runbooks/finops/embedded_mcp_validator.py +0 -1629
- runbooks/finops/enhanced_dashboard_runner.py +0 -527
- runbooks/finops/finops_dashboard.py +0 -584
- runbooks/finops/finops_scenarios.py +0 -1218
- runbooks/finops/legacy_migration.py +0 -730
- runbooks/finops/multi_dashboard.py +0 -1519
- runbooks/finops/single_dashboard.py +0 -1113
- runbooks/finops/unlimited_scenarios.py +0 -393
- runbooks-1.1.4.dist-info/METADATA +0 -800
- runbooks-1.1.4.dist-info/RECORD +0 -468
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.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,
|
44
|
-
|
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
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
205
|
-
|
206
|
-
report = AWSO5MCPValidationReport(
|
207
|
-
|
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
|
-
|
265
|
-
|
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
|
-
|
281
|
-
|
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(
|
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
|
-
|
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
|
-
|
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=[{
|
368
|
-
)[
|
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
|
-
|
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
|
-
|
407
|
-
|
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
|
-
|
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=[{
|
452
|
-
)[
|
453
|
-
|
415
|
+
Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]
|
416
|
+
)["VpcEndpoints"]
|
417
|
+
|
454
418
|
# Count available endpoints
|
455
|
-
available_endpoints = [
|
456
|
-
|
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
|
-
|
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()[
|
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
|
-
|
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])[
|
541
|
-
|
542
|
-
mcp_is_default = vpc_info.get(
|
543
|
-
runbooks_is_default = getattr(architecture_result,
|
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=[{
|
582
|
-
)[
|
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,
|
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])[
|
619
|
-
|
552
|
+
vpc_info = self.ec2_client.describe_vpcs(VpcIds=[vpc_id])["Vpcs"][0]
|
553
|
+
|
620
554
|
mcp_cidrs = [
|
621
|
-
block[
|
622
|
-
|
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,
|
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,
|
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
|
-
|
686
|
-
|
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,
|
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
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
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
|
-
|
853
|
-
|
854
|
-
|
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
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
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
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
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
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
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
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
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
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
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[
|
925
|
-
|
838
|
+
evidence_bundle["evidence_bundle_hash"] = evidence_hash
|
839
|
+
|
926
840
|
# Save evidence bundle
|
927
|
-
with open(evidence_path,
|
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(
|
943
|
-
|
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 =
|
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: {
|
1008
|
-
• Validation Completeness: {
|
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"
|
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")
|