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