runbooks 0.9.0__py3-none-any.whl → 0.9.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/cfat/assessment/compliance.py +4 -1
  3. runbooks/cloudops/__init__.py +123 -0
  4. runbooks/cloudops/base.py +385 -0
  5. runbooks/cloudops/cost_optimizer.py +811 -0
  6. runbooks/cloudops/infrastructure_optimizer.py +29 -0
  7. runbooks/cloudops/interfaces.py +828 -0
  8. runbooks/cloudops/lifecycle_manager.py +29 -0
  9. runbooks/cloudops/mcp_cost_validation.py +678 -0
  10. runbooks/cloudops/models.py +251 -0
  11. runbooks/cloudops/monitoring_automation.py +29 -0
  12. runbooks/cloudops/notebook_framework.py +676 -0
  13. runbooks/cloudops/security_enforcer.py +449 -0
  14. runbooks/common/mcp_cost_explorer_integration.py +900 -0
  15. runbooks/common/mcp_integration.py +19 -10
  16. runbooks/common/rich_utils.py +1 -1
  17. runbooks/finops/README.md +31 -0
  18. runbooks/finops/cost_optimizer.py +1340 -0
  19. runbooks/finops/finops_dashboard.py +211 -5
  20. runbooks/finops/schemas.py +589 -0
  21. runbooks/inventory/runbooks.inventory.organizations_discovery.log +0 -0
  22. runbooks/inventory/runbooks.security.security_export.log +0 -0
  23. runbooks/main.py +525 -0
  24. runbooks/operate/ec2_operations.py +428 -0
  25. runbooks/operate/iam_operations.py +598 -3
  26. runbooks/operate/rds_operations.py +508 -0
  27. runbooks/operate/s3_operations.py +508 -0
  28. runbooks/remediation/base.py +5 -3
  29. runbooks/security/__init__.py +101 -0
  30. runbooks/security/cloudops_automation_security_validator.py +1164 -0
  31. runbooks/security/compliance_automation_engine.py +4 -4
  32. runbooks/security/enterprise_security_framework.py +4 -5
  33. runbooks/security/executive_security_dashboard.py +1247 -0
  34. runbooks/security/multi_account_security_controls.py +2254 -0
  35. runbooks/security/real_time_security_monitor.py +1196 -0
  36. runbooks/security/security_baseline_tester.py +3 -3
  37. runbooks/sre/production_monitoring_framework.py +584 -0
  38. runbooks/validation/mcp_validator.py +29 -15
  39. runbooks/vpc/networking_wrapper.py +6 -3
  40. runbooks-0.9.2.dist-info/METADATA +525 -0
  41. {runbooks-0.9.0.dist-info → runbooks-0.9.2.dist-info}/RECORD +45 -23
  42. runbooks-0.9.0.dist-info/METADATA +0 -718
  43. {runbooks-0.9.0.dist-info → runbooks-0.9.2.dist-info}/WHEEL +0 -0
  44. {runbooks-0.9.0.dist-info → runbooks-0.9.2.dist-info}/entry_points.txt +0 -0
  45. {runbooks-0.9.0.dist-info → runbooks-0.9.2.dist-info}/licenses/LICENSE +0 -0
  46. {runbooks-0.9.0.dist-info → runbooks-0.9.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,589 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Pydantic v2 Schemas for FinOps Cost Optimization Platform
4
+
5
+ This module provides comprehensive Pydantic v2 validation schemas for all cost optimization
6
+ outputs, ensuring strict type safety and data integrity throughout the platform.
7
+
8
+ Features:
9
+ - Comprehensive cost optimization result validation
10
+ - Business metrics validation with executive reporting
11
+ - Technical validation schemas for functional testing
12
+ - Schema evolution support for backward compatibility
13
+ - Rich CLI integration for formatted output display
14
+ - Export validation for JSON, CSV, HTML, and PDF formats
15
+
16
+ Strategic Alignment:
17
+ - Supports dual-purpose architecture (business + technical users)
18
+ - Enables real-time MCP validation with strict tolerances
19
+ - Provides executive-ready reporting with validated metrics
20
+ - Maintains enterprise standards for data integrity
21
+ """
22
+
23
+ from decimal import Decimal, ROUND_HALF_UP
24
+ from datetime import datetime, date
25
+ from typing import Dict, List, Optional, Union, Any, Literal
26
+ from enum import Enum
27
+ import re
28
+
29
+ from pydantic import BaseModel, Field, validator, root_validator, ConfigDict
30
+ from pydantic.types import UUID4, PositiveFloat, NonNegativeFloat
31
+
32
+
33
+ # Configuration for Pydantic v2
34
+ class BaseSchema(BaseModel):
35
+ """Base schema with common configuration for all models."""
36
+
37
+ model_config = ConfigDict(
38
+ # Enable strict validation
39
+ str_strip_whitespace=True,
40
+ validate_assignment=True,
41
+ use_enum_values=True,
42
+ # Allow for schema evolution
43
+ extra='forbid', # Strict validation - no unexpected fields
44
+ # JSON encoding settings
45
+ json_encoders={
46
+ datetime: lambda dt: dt.isoformat(),
47
+ date: lambda d: d.isoformat(),
48
+ Decimal: lambda d: float(d),
49
+ }
50
+ )
51
+
52
+
53
+ class ComplexityLevel(str, Enum):
54
+ """Implementation complexity levels for cost optimization scenarios."""
55
+ LOW = "Low"
56
+ MEDIUM = "Medium"
57
+ HIGH = "High"
58
+
59
+
60
+ class RiskLevel(str, Enum):
61
+ """Risk levels for cost optimization implementations."""
62
+ LOW = "Low"
63
+ MEDIUM = "Medium"
64
+ HIGH = "High"
65
+ CRITICAL = "Critical"
66
+
67
+
68
+ class OptimizationCategory(str, Enum):
69
+ """Categories of cost optimization scenarios."""
70
+ UNUSED_RESOURCES = "Unused Resources"
71
+ RIGHTSIZING = "Rightsizing"
72
+ RESERVED_INSTANCES = "Reserved Instances"
73
+ SPOT_INSTANCES = "Spot Instances"
74
+ STORAGE_OPTIMIZATION = "Storage Optimization"
75
+ NETWORK_OPTIMIZATION = "Network Optimization"
76
+ GOVERNANCE = "Governance"
77
+ LIFECYCLE_MANAGEMENT = "Lifecycle Management"
78
+
79
+
80
+ class ValidationStatus(str, Enum):
81
+ """Validation status for cross-checking with MCP servers."""
82
+ VALIDATED = "validated"
83
+ VARIANCE_DETECTED = "variance_detected"
84
+ MCP_UNAVAILABLE = "mcp_unavailable"
85
+ ERROR = "error"
86
+ PENDING = "pending"
87
+
88
+
89
+ class ExportFormat(str, Enum):
90
+ """Supported export formats for reports."""
91
+ JSON = "json"
92
+ CSV = "csv"
93
+ HTML = "html"
94
+ PDF = "pdf"
95
+ MARKDOWN = "markdown"
96
+
97
+
98
+ # Core Cost Optimization Schemas
99
+
100
+ class CostBreakdown(BaseSchema):
101
+ """Detailed cost breakdown by service/category."""
102
+
103
+ service_name: str = Field(..., min_length=1, max_length=100)
104
+ monthly_cost: NonNegativeFloat = Field(..., ge=0)
105
+ annual_cost: NonNegativeFloat = Field(..., ge=0)
106
+ percentage_of_total: float = Field(..., ge=0, le=100)
107
+ resource_count: int = Field(..., ge=0)
108
+
109
+ @validator('service_name')
110
+ def validate_service_name(cls, v):
111
+ """Validate AWS service names."""
112
+ # Common AWS service patterns
113
+ valid_patterns = [
114
+ r'^EC2-', # EC2 services
115
+ r'^S3', # S3 services
116
+ r'^RDS', # RDS services
117
+ r'^Lambda', # Lambda
118
+ r'^CloudWatch', # CloudWatch
119
+ r'^VPC', # VPC services
120
+ r'^Route 53', # Route 53
121
+ r'^ElastiCache', # ElastiCache
122
+ r'^Redshift', # Redshift
123
+ r'^\w+$' # General service names
124
+ ]
125
+
126
+ if not any(re.match(pattern, v) for pattern in valid_patterns):
127
+ # Allow any string for flexibility, but validate length
128
+ if len(v.strip()) == 0:
129
+ raise ValueError('Service name cannot be empty')
130
+
131
+ return v.strip()
132
+
133
+ @validator('annual_cost')
134
+ def validate_annual_cost_consistency(cls, v, values):
135
+ """Ensure annual cost is approximately 12x monthly cost."""
136
+ if 'monthly_cost' in values:
137
+ expected_annual = values['monthly_cost'] * 12
138
+ # Allow 1% tolerance for rounding differences
139
+ if abs(v - expected_annual) > (expected_annual * 0.01):
140
+ raise ValueError(f'Annual cost {v} should be approximately 12x monthly cost {values["monthly_cost"]}')
141
+ return v
142
+
143
+
144
+ class OptimizationScenario(BaseSchema):
145
+ """Individual cost optimization scenario with validation."""
146
+
147
+ scenario_name: str = Field(..., min_length=3, max_length=200)
148
+ category: OptimizationCategory = Field(...)
149
+ description: str = Field(..., min_length=10, max_length=1000)
150
+
151
+ # Financial metrics
152
+ monthly_savings: NonNegativeFloat = Field(..., ge=0)
153
+ annual_savings: NonNegativeFloat = Field(..., ge=0)
154
+ implementation_cost: NonNegativeFloat = Field(0, ge=0)
155
+ payback_period_months: Optional[PositiveFloat] = Field(None, gt=0, le=120) # Max 10 years
156
+
157
+ # Implementation details
158
+ complexity: ComplexityLevel = Field(...)
159
+ risk_level: RiskLevel = Field(...)
160
+ estimated_hours: PositiveFloat = Field(..., gt=0, le=2000) # Reasonable implementation time
161
+
162
+ # Resource impact
163
+ affected_services: List[str] = Field(..., min_items=1)
164
+ affected_accounts: List[str] = Field(..., min_items=1)
165
+ resource_count: int = Field(..., ge=1)
166
+
167
+ # Validation metadata
168
+ validation_status: ValidationStatus = Field(ValidationStatus.PENDING)
169
+ validation_timestamp: Optional[datetime] = Field(None)
170
+ mcp_variance_percent: Optional[float] = Field(None, ge=0, le=100)
171
+
172
+ @validator('scenario_name')
173
+ def validate_scenario_name(cls, v):
174
+ """Validate scenario naming conventions."""
175
+ # Ensure professional naming
176
+ if not re.match(r'^[A-Z][A-Za-z0-9\s\-\(\)]{2,199}$', v):
177
+ raise ValueError('Scenario name must start with capital letter and contain only letters, numbers, spaces, hyphens, and parentheses')
178
+ return v.strip()
179
+
180
+ @validator('annual_savings')
181
+ def validate_annual_savings_consistency(cls, v, values):
182
+ """Ensure annual savings consistency with monthly savings."""
183
+ if 'monthly_savings' in values:
184
+ expected_annual = values['monthly_savings'] * 12
185
+ if abs(v - expected_annual) > (expected_annual * 0.01): # 1% tolerance
186
+ raise ValueError(f'Annual savings {v} should be approximately 12x monthly savings {values["monthly_savings"]}')
187
+ return v
188
+
189
+ @validator('payback_period_months')
190
+ def calculate_payback_period(cls, v, values):
191
+ """Calculate payback period if not provided."""
192
+ if v is None and 'implementation_cost' in values and 'monthly_savings' in values:
193
+ impl_cost = values['implementation_cost']
194
+ monthly_savings = values['monthly_savings']
195
+ if monthly_savings > 0:
196
+ calculated_payback = impl_cost / monthly_savings
197
+ return round(calculated_payback, 1)
198
+ return v
199
+
200
+ @validator('affected_services')
201
+ def validate_aws_services(cls, v):
202
+ """Validate AWS service names in affected services."""
203
+ common_services = {
204
+ 'EC2', 'S3', 'RDS', 'Lambda', 'CloudWatch', 'VPC', 'ELB',
205
+ 'Route53', 'CloudFront', 'ElastiCache', 'Redshift', 'DynamoDB',
206
+ 'EBS', 'EFS', 'FSx', 'Backup', 'Config', 'CloudTrail', 'IAM'
207
+ }
208
+
209
+ for service in v:
210
+ # Allow any service that starts with common patterns or is in common list
211
+ if not (service in common_services or
212
+ any(service.startswith(prefix) for prefix in ['AWS', 'Amazon']) or
213
+ re.match(r'^[A-Z][A-Za-z0-9\-]{1,50}$', service)):
214
+ raise ValueError(f'Invalid AWS service name: {service}')
215
+
216
+ return v
217
+
218
+ @validator('affected_accounts')
219
+ def validate_account_ids(cls, v):
220
+ """Validate AWS account ID format."""
221
+ account_pattern = r'^\d{12}$|^[\w\-\.]{1,50}$' # 12-digit ID or account name
222
+
223
+ for account in v:
224
+ if not re.match(account_pattern, account):
225
+ raise ValueError(f'Invalid account format: {account}. Must be 12-digit ID or valid account name')
226
+
227
+ return v
228
+
229
+
230
+ class CostOptimizationResult(BaseSchema):
231
+ """Comprehensive cost optimization analysis result."""
232
+
233
+ # Analysis metadata
234
+ analysis_id: UUID4 = Field(..., description="Unique analysis identifier")
235
+ analysis_timestamp: datetime = Field(..., description="Analysis execution time")
236
+ profile_name: str = Field(..., min_length=1, max_length=100)
237
+
238
+ # Scope and configuration
239
+ analysis_scope: Literal["single_account", "multi_account", "organization"] = Field(...)
240
+ total_accounts: int = Field(..., ge=1, le=1000) # Reasonable limit
241
+ analysis_period_days: int = Field(..., ge=1, le=365)
242
+
243
+ # Financial summary
244
+ current_monthly_spend: NonNegativeFloat = Field(..., ge=0)
245
+ total_potential_monthly_savings: NonNegativeFloat = Field(..., ge=0)
246
+ total_potential_annual_savings: NonNegativeFloat = Field(..., ge=0)
247
+ savings_percentage: float = Field(..., ge=0, le=100)
248
+
249
+ # Scenarios and breakdown
250
+ optimization_scenarios: List[OptimizationScenario] = Field(..., min_items=1, max_items=100)
251
+ cost_breakdown: List[CostBreakdown] = Field(..., min_items=1)
252
+
253
+ # Implementation summary
254
+ total_scenarios: int = Field(..., ge=1)
255
+ low_complexity_scenarios: int = Field(..., ge=0)
256
+ medium_complexity_scenarios: int = Field(..., ge=0)
257
+ high_complexity_scenarios: int = Field(..., ge=0)
258
+
259
+ # Risk assessment
260
+ average_risk_score: float = Field(..., ge=1, le=5) # 1-5 scale
261
+ high_risk_scenarios_count: int = Field(..., ge=0)
262
+
263
+ # Validation and quality
264
+ mcp_validation_status: ValidationStatus = Field(ValidationStatus.PENDING)
265
+ validation_summary: Optional[Dict[str, Any]] = Field(None)
266
+ accuracy_confidence: Optional[float] = Field(None, ge=0, le=100)
267
+
268
+ # Export metadata
269
+ supported_export_formats: List[ExportFormat] = Field(
270
+ default=[ExportFormat.JSON, ExportFormat.CSV, ExportFormat.PDF]
271
+ )
272
+
273
+ @validator('total_potential_annual_savings')
274
+ def validate_annual_consistency(cls, v, values):
275
+ """Validate annual savings consistency."""
276
+ if 'total_potential_monthly_savings' in values:
277
+ expected = values['total_potential_monthly_savings'] * 12
278
+ if abs(v - expected) > (expected * 0.01):
279
+ raise ValueError('Annual savings must be approximately 12x monthly savings')
280
+ return v
281
+
282
+ @validator('savings_percentage')
283
+ def calculate_savings_percentage(cls, v, values):
284
+ """Validate or calculate savings percentage."""
285
+ if 'current_monthly_spend' in values and 'total_potential_monthly_savings' in values:
286
+ current_spend = values['current_monthly_spend']
287
+ if current_spend > 0:
288
+ calculated = (values['total_potential_monthly_savings'] / current_spend) * 100
289
+ if abs(v - calculated) > 0.1: # 0.1% tolerance
290
+ raise ValueError(f'Savings percentage {v}% inconsistent with calculated {calculated:.1f}%')
291
+ return v
292
+
293
+ @validator('total_scenarios')
294
+ def validate_scenario_count(cls, v, values):
295
+ """Ensure scenario count matches actual scenarios."""
296
+ if 'optimization_scenarios' in values:
297
+ actual_count = len(values['optimization_scenarios'])
298
+ if v != actual_count:
299
+ raise ValueError(f'Total scenarios {v} does not match actual scenarios count {actual_count}')
300
+ return v
301
+
302
+ @root_validator
303
+ def validate_complexity_distribution(cls, values):
304
+ """Validate complexity scenario counts."""
305
+ scenarios = values.get('optimization_scenarios', [])
306
+ if scenarios:
307
+ low_count = sum(1 for s in scenarios if s.complexity == ComplexityLevel.LOW)
308
+ medium_count = sum(1 for s in scenarios if s.complexity == ComplexityLevel.MEDIUM)
309
+ high_count = sum(1 for s in scenarios if s.complexity == ComplexityLevel.HIGH)
310
+
311
+ expected_low = values.get('low_complexity_scenarios', 0)
312
+ expected_medium = values.get('medium_complexity_scenarios', 0)
313
+ expected_high = values.get('high_complexity_scenarios', 0)
314
+
315
+ if (low_count != expected_low or
316
+ medium_count != expected_medium or
317
+ high_count != expected_high):
318
+ raise ValueError(
319
+ f'Complexity counts mismatch: expected L:{expected_low} M:{expected_medium} H:{expected_high}, '
320
+ f'actual L:{low_count} M:{medium_count} H:{high_count}'
321
+ )
322
+
323
+ return values
324
+
325
+
326
+ # Business Interface Schemas
327
+
328
+ class ExecutiveSummary(BaseSchema):
329
+ """Executive-ready summary for business stakeholders."""
330
+
331
+ # High-level metrics
332
+ total_annual_opportunity: NonNegativeFloat = Field(..., ge=0)
333
+ confidence_level: float = Field(..., ge=70, le=100) # Must be high confidence for exec presentation
334
+ implementation_timeline_months: PositiveFloat = Field(..., gt=0, le=24) # Reasonable timeline
335
+
336
+ # Business impact
337
+ roi_percentage: PositiveFloat = Field(..., gt=0)
338
+ payback_period_months: PositiveFloat = Field(..., gt=0, le=60) # Max 5 years
339
+ risk_assessment: RiskLevel = Field(...)
340
+
341
+ # Quick wins
342
+ quick_wins_count: int = Field(..., ge=0, le=50)
343
+ quick_wins_annual_value: NonNegativeFloat = Field(..., ge=0)
344
+
345
+ # Implementation priority
346
+ priority_scenarios: List[str] = Field(..., max_items=10) # Top priorities only
347
+ recommended_next_steps: List[str] = Field(..., min_items=1, max_items=5)
348
+
349
+ # Validation status
350
+ data_validation_status: ValidationStatus = Field(...)
351
+ last_validated: datetime = Field(...)
352
+
353
+ @validator('roi_percentage')
354
+ def validate_reasonable_roi(cls, v):
355
+ """Ensure ROI is reasonable for executive presentation."""
356
+ if v > 1000: # 1000% ROI
357
+ raise ValueError('ROI over 1000% requires additional validation')
358
+ return v
359
+
360
+
361
+ # Technical Validation Schemas
362
+
363
+ class MCPValidationResult(BaseSchema):
364
+ """MCP cross-validation result with strict tolerance checking."""
365
+
366
+ validation_timestamp: datetime = Field(...)
367
+ notebook_value: NonNegativeFloat = Field(..., ge=0)
368
+ mcp_value: NonNegativeFloat = Field(..., ge=0)
369
+ variance_amount: NonNegativeFloat = Field(..., ge=0)
370
+ variance_percent: float = Field(..., ge=0)
371
+ tolerance_threshold: PositiveFloat = Field(..., gt=0, le=10) # Max 10% tolerance
372
+
373
+ validation_status: ValidationStatus = Field(...)
374
+ validation_message: str = Field(..., min_length=1)
375
+
376
+ # Technical details
377
+ mcp_source: str = Field(..., min_length=1)
378
+ response_time_seconds: Optional[PositiveFloat] = Field(None, le=300) # 5 minute timeout
379
+
380
+ @validator('variance_percent')
381
+ def calculate_variance_percent(cls, v, values):
382
+ """Calculate and validate variance percentage."""
383
+ if 'notebook_value' in values and 'mcp_value' in values:
384
+ notebook_val = values['notebook_value']
385
+ mcp_val = values['mcp_value']
386
+
387
+ if notebook_val > 0:
388
+ calculated = abs((notebook_val - mcp_val) / notebook_val) * 100
389
+ if abs(v - calculated) > 0.01: # Very tight tolerance for variance calculation
390
+ raise ValueError(f'Variance percent {v}% does not match calculated {calculated:.2f}%')
391
+ return v
392
+
393
+
394
+ class FunctionalTestResult(BaseSchema):
395
+ """Functional testing result for technical validation."""
396
+
397
+ test_name: str = Field(..., min_length=3, max_length=200)
398
+ test_category: Literal["cost_analysis", "mcp_validation", "export_validation", "performance"] = Field(...)
399
+
400
+ # Test execution
401
+ execution_timestamp: datetime = Field(...)
402
+ execution_time_seconds: PositiveFloat = Field(..., le=300) # 5 minute timeout
403
+ test_passed: bool = Field(...)
404
+
405
+ # Test details
406
+ expected_value: Optional[Union[str, float, int, bool]] = Field(None)
407
+ actual_value: Optional[Union[str, float, int, bool]] = Field(None)
408
+ tolerance_applied: Optional[float] = Field(None, ge=0, le=10)
409
+
410
+ # Error handling
411
+ error_message: Optional[str] = Field(None, max_length=1000)
412
+ stack_trace: Optional[str] = Field(None, max_length=5000)
413
+
414
+ # Performance metrics
415
+ memory_usage_mb: Optional[PositiveFloat] = Field(None, le=4096) # 4GB max
416
+ cpu_utilization_percent: Optional[float] = Field(None, ge=0, le=100)
417
+
418
+
419
+ class ComprehensiveTestSuite(BaseSchema):
420
+ """Complete test suite results for technical validation."""
421
+
422
+ # Test suite metadata
423
+ suite_id: UUID4 = Field(...)
424
+ execution_timestamp: datetime = Field(...)
425
+ total_execution_time_seconds: PositiveFloat = Field(...)
426
+
427
+ # Test results
428
+ total_tests: int = Field(..., ge=1)
429
+ passed_tests: int = Field(..., ge=0)
430
+ failed_tests: int = Field(..., ge=0)
431
+ skipped_tests: int = Field(..., ge=0)
432
+
433
+ test_results: List[FunctionalTestResult] = Field(..., min_items=1)
434
+
435
+ # Summary metrics
436
+ pass_rate_percent: float = Field(..., ge=0, le=100)
437
+ performance_target_met: bool = Field(...)
438
+ mcp_validation_success: bool = Field(...)
439
+
440
+ # Quality gates
441
+ meets_production_criteria: bool = Field(...)
442
+ quality_score: float = Field(..., ge=0, le=100)
443
+
444
+ @validator('passed_tests')
445
+ def validate_test_counts(cls, v, values):
446
+ """Ensure test counts are consistent."""
447
+ if 'failed_tests' in values and 'skipped_tests' in values and 'total_tests' in values:
448
+ calculated_total = v + values['failed_tests'] + values['skipped_tests']
449
+ if calculated_total != values['total_tests']:
450
+ raise ValueError(f'Test counts inconsistent: {calculated_total} ≠ {values["total_tests"]}')
451
+ return v
452
+
453
+ @validator('pass_rate_percent')
454
+ def calculate_pass_rate(cls, v, values):
455
+ """Calculate and validate pass rate."""
456
+ if 'passed_tests' in values and 'total_tests' in values:
457
+ total = values['total_tests']
458
+ if total > 0:
459
+ calculated = (values['passed_tests'] / total) * 100
460
+ if abs(v - calculated) > 0.01:
461
+ raise ValueError(f'Pass rate {v}% inconsistent with calculated {calculated:.2f}%')
462
+ return v
463
+
464
+
465
+ # Export and Integration Schemas
466
+
467
+ class ExportMetadata(BaseSchema):
468
+ """Metadata for exported reports."""
469
+
470
+ export_timestamp: datetime = Field(...)
471
+ export_format: ExportFormat = Field(...)
472
+ file_path: str = Field(..., min_length=1, max_length=500)
473
+ file_size_bytes: int = Field(..., ge=0)
474
+
475
+ # Content metadata
476
+ record_count: int = Field(..., ge=0)
477
+ schema_version: str = Field(..., pattern=r'^\d+\.\d+\.\d+$') # Semantic versioning
478
+
479
+ # Validation
480
+ export_validated: bool = Field(...)
481
+ validation_errors: List[str] = Field(default_factory=list)
482
+
483
+ @validator('file_path')
484
+ def validate_file_path(cls, v):
485
+ """Validate file path format."""
486
+ # Basic path validation
487
+ if not re.match(r'^[/\w\-\.\s]+\.(json|csv|html|pdf|md)$', v):
488
+ raise ValueError('Invalid file path format')
489
+ return v
490
+
491
+
492
+ # Utility Functions for Schema Validation
493
+
494
+ def validate_cost_optimization_result(data: Dict[str, Any]) -> CostOptimizationResult:
495
+ """
496
+ Validate and create CostOptimizationResult from raw data.
497
+
498
+ Args:
499
+ data: Raw data dictionary
500
+
501
+ Returns:
502
+ Validated CostOptimizationResult instance
503
+
504
+ Raises:
505
+ ValidationError: If data doesn't meet schema requirements
506
+ """
507
+ return CostOptimizationResult(**data)
508
+
509
+
510
+ def create_executive_summary(optimization_result: CostOptimizationResult) -> ExecutiveSummary:
511
+ """
512
+ Create executive summary from optimization result.
513
+
514
+ Args:
515
+ optimization_result: Validated optimization result
516
+
517
+ Returns:
518
+ ExecutiveSummary instance
519
+ """
520
+ # Calculate quick wins (Low complexity, Low-Medium risk)
521
+ quick_wins = [
522
+ scenario for scenario in optimization_result.optimization_scenarios
523
+ if (scenario.complexity == ComplexityLevel.LOW and
524
+ scenario.risk_level in [RiskLevel.LOW, RiskLevel.MEDIUM])
525
+ ]
526
+
527
+ quick_wins_value = sum(scenario.annual_savings for scenario in quick_wins)
528
+
529
+ # Calculate implementation timeline (weighted by complexity)
530
+ complexity_weights = {ComplexityLevel.LOW: 1, ComplexityLevel.MEDIUM: 3, ComplexityLevel.HIGH: 6}
531
+ total_weighted_hours = sum(
532
+ scenario.estimated_hours * complexity_weights[scenario.complexity]
533
+ for scenario in optimization_result.optimization_scenarios
534
+ )
535
+ timeline_months = max(1, total_weighted_hours / 160) # Assuming 160 hours/month
536
+
537
+ # ROI calculation
538
+ annual_savings = optimization_result.total_potential_annual_savings
539
+ implementation_cost = sum(scenario.implementation_cost for scenario in optimization_result.optimization_scenarios)
540
+ roi_percentage = (annual_savings / max(implementation_cost, 1)) * 100
541
+
542
+ return ExecutiveSummary(
543
+ total_annual_opportunity=annual_savings,
544
+ confidence_level=optimization_result.accuracy_confidence or 85.0,
545
+ implementation_timeline_months=timeline_months,
546
+ roi_percentage=roi_percentage,
547
+ payback_period_months=max(0.1, implementation_cost / (annual_savings / 12)) if annual_savings > 0 else 12.0,
548
+ risk_assessment=RiskLevel.MEDIUM, # Conservative default
549
+ quick_wins_count=len(quick_wins),
550
+ quick_wins_annual_value=quick_wins_value,
551
+ priority_scenarios=[
552
+ scenario.scenario_name for scenario in
553
+ sorted(optimization_result.optimization_scenarios,
554
+ key=lambda x: x.annual_savings, reverse=True)[:5]
555
+ ],
556
+ recommended_next_steps=[
557
+ "Review and approve quick-win scenarios",
558
+ "Establish implementation team and timeline",
559
+ "Set up monitoring for cost optimization KPIs",
560
+ "Schedule quarterly optimization reviews"
561
+ ],
562
+ data_validation_status=optimization_result.mcp_validation_status,
563
+ last_validated=datetime.now()
564
+ )
565
+
566
+
567
+ # Export all schemas and utilities
568
+ __all__ = [
569
+ # Enums
570
+ 'ComplexityLevel', 'RiskLevel', 'OptimizationCategory', 'ValidationStatus', 'ExportFormat',
571
+
572
+ # Core schemas
573
+ 'CostBreakdown', 'OptimizationScenario', 'CostOptimizationResult',
574
+
575
+ # Business schemas
576
+ 'ExecutiveSummary',
577
+
578
+ # Technical validation schemas
579
+ 'MCPValidationResult', 'FunctionalTestResult', 'ComprehensiveTestSuite',
580
+
581
+ # Export schemas
582
+ 'ExportMetadata',
583
+
584
+ # Utility functions
585
+ 'validate_cost_optimization_result', 'create_executive_summary',
586
+
587
+ # Base schema
588
+ 'BaseSchema'
589
+ ]