petter2025 commited on
Commit
8853053
·
verified ·
1 Parent(s): 9ec7605

Update models.py

Browse files
Files changed (1) hide show
  1. models.py +252 -40
models.py CHANGED
@@ -1,16 +1,26 @@
1
- from pydantic import BaseModel, Field
2
- from typing import Optional, Dict, List, Any
 
 
 
 
 
3
  from enum import Enum
4
- import datetime
5
  import hashlib
 
 
6
 
7
  class EventSeverity(Enum):
 
8
  LOW = "low"
9
- MEDIUM = "medium"
10
  HIGH = "high"
11
  CRITICAL = "critical"
12
 
 
13
  class HealingAction(Enum):
 
14
  RESTART_CONTAINER = "restart_container"
15
  SCALE_OUT = "scale_out"
16
  TRAFFIC_SHIFT = "traffic_shift"
@@ -19,53 +29,255 @@ class HealingAction(Enum):
19
  ALERT_TEAM = "alert_team"
20
  NO_ACTION = "no_action"
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  class ReliabilityEvent(BaseModel):
23
- timestamp: str = Field(default_factory=lambda: datetime.datetime.now().isoformat())
24
- component: str
25
- service_mesh: str = "default"
 
 
 
 
 
26
 
27
- # Core metrics
28
- latency_p99: float = Field(ge=0)
29
- error_rate: float = Field(ge=0, le=1)
30
- throughput: float = Field(ge=0)
 
31
 
32
- # Resource metrics
33
- cpu_util: Optional[float] = Field(default=None, ge=0, le=1)
34
- memory_util: Optional[float] = Field(default=None, ge=0, le=1)
 
 
35
 
36
- # Business metrics
37
- revenue_impact: Optional[float] = Field(default=None, ge=0)
38
- user_impact: Optional[int] = Field(default=None, ge=0)
 
 
39
 
40
- # Topology context
41
- upstream_deps: List[str] = Field(default_factory=list)
42
- downstream_deps: List[str] = Field(default_factory=list)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  severity: EventSeverity = EventSeverity.LOW
45
- fingerprint: str = Field(default="")
46
 
47
- def __init__(self, **data):
48
- super().__init__(**data)
49
- # Generate fingerprint for deduplication
50
- if not self.fingerprint:
51
- fingerprint_str = f"{self.component}_{self.latency_p99}_{self.error_rate}_{self.timestamp}"
52
- self.fingerprint = hashlib.md5(fingerprint_str.encode()).hexdigest()
 
 
 
 
 
 
 
 
 
 
 
53
 
54
- class Config:
55
- use_enum_values = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
  class HealingPolicy(BaseModel):
58
- name: str
59
- conditions: Dict[str, Any]
60
- actions: List[HealingAction]
61
- priority: int = Field(ge=1, le=5)
62
- cool_down_seconds: int = 300
63
- enabled: bool = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
  class AnomalyResult(BaseModel):
 
 
66
  is_anomaly: bool
67
- confidence: float
68
- predicted_cause: str
69
- recommended_actions: List[HealingAction]
70
- similar_incidents: List[str] = Field(default_factory=list)
71
- business_impact: Optional[Dict[str, Any]] = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Data Models for Enterprise Agentic Reliability Framework
3
+ Fixed version with security patches and validation improvements
4
+ """
5
+
6
+ from pydantic import BaseModel, Field, field_validator, computed_field, ConfigDict
7
+ from typing import Optional, List, Literal
8
  from enum import Enum
9
+ from datetime import datetime, timezone
10
  import hashlib
11
+ import re
12
+
13
 
14
  class EventSeverity(Enum):
15
+ """Event severity levels"""
16
  LOW = "low"
17
+ MEDIUM = "medium"
18
  HIGH = "high"
19
  CRITICAL = "critical"
20
 
21
+
22
  class HealingAction(Enum):
23
+ """Available healing actions for policy engine"""
24
  RESTART_CONTAINER = "restart_container"
25
  SCALE_OUT = "scale_out"
26
  TRAFFIC_SHIFT = "traffic_shift"
 
29
  ALERT_TEAM = "alert_team"
30
  NO_ACTION = "no_action"
31
 
32
+
33
+ class HealthStatus(Enum):
34
+ """Component health status"""
35
+ HEALTHY = "healthy"
36
+ DEGRADED = "degraded"
37
+ UNHEALTHY = "unhealthy"
38
+ UNKNOWN = "unknown"
39
+
40
+
41
+ class PolicyCondition(BaseModel):
42
+ """
43
+ Structured policy condition - replaces Dict[str, Any]
44
+ Provides type safety and validation
45
+ """
46
+ metric: Literal["latency_p99", "error_rate", "cpu_util", "memory_util", "throughput"]
47
+ operator: Literal["gt", "lt", "eq", "gte", "lte"]
48
+ threshold: float = Field(ge=0)
49
+
50
+ model_config = ConfigDict(frozen=True)
51
+
52
+
53
  class ReliabilityEvent(BaseModel):
54
+ """
55
+ Core reliability event model with comprehensive validation
56
+
57
+ SECURITY FIX: Changed timestamp from str to datetime
58
+ SECURITY FIX: Changed fingerprint from MD5 to SHA-256
59
+ IMPROVEMENT: Added frozen=True for immutability
60
+ IMPROVEMENT: Added validators for all fields
61
+ """
62
 
63
+ # FIXED: timestamp is now datetime instead of string
64
+ timestamp: datetime = Field(
65
+ default_factory=lambda: datetime.now(timezone.utc),
66
+ description="Event timestamp in UTC"
67
+ )
68
 
69
+ component: str = Field(
70
+ min_length=1,
71
+ max_length=255,
72
+ description="Component identifier (alphanumeric and hyphens only)"
73
+ )
74
 
75
+ service_mesh: str = Field(
76
+ default="default",
77
+ min_length=1,
78
+ max_length=100
79
+ )
80
 
81
+ # Metrics with proper bounds
82
+ latency_p99: float = Field(
83
+ ge=0,
84
+ lt=300000, # 5 minutes max
85
+ description="P99 latency in milliseconds"
86
+ )
87
+
88
+ error_rate: float = Field(
89
+ ge=0,
90
+ le=1,
91
+ description="Error rate between 0 and 1"
92
+ )
93
+
94
+ throughput: float = Field(
95
+ ge=0,
96
+ description="Requests per second"
97
+ )
98
+
99
+ cpu_util: Optional[float] = Field(
100
+ default=None,
101
+ ge=0,
102
+ le=1,
103
+ description="CPU utilization (0-1)"
104
+ )
105
+
106
+ memory_util: Optional[float] = Field(
107
+ default=None,
108
+ ge=0,
109
+ le=1,
110
+ description="Memory utilization (0-1)"
111
+ )
112
+
113
+ revenue_impact: Optional[float] = Field(
114
+ default=None,
115
+ ge=0,
116
+ description="Estimated revenue impact in dollars"
117
+ )
118
+
119
+ user_impact: Optional[int] = Field(
120
+ default=None,
121
+ ge=0,
122
+ description="Number of affected users"
123
+ )
124
+
125
+ upstream_deps: List[str] = Field(
126
+ default_factory=list,
127
+ description="List of upstream dependencies"
128
+ )
129
+
130
+ downstream_deps: List[str] = Field(
131
+ default_factory=list,
132
+ description="List of downstream dependencies"
133
+ )
134
 
135
  severity: EventSeverity = EventSeverity.LOW
 
136
 
137
+ # FIXED: Frozen model means no mutable fingerprint field
138
+ # Use computed_field instead
139
+
140
+ model_config = ConfigDict(
141
+ frozen=True, # Immutability for data integrity
142
+ validate_assignment=True
143
+ )
144
+
145
+ @field_validator("component")
146
+ @classmethod
147
+ def validate_component_id(cls, v: str) -> str:
148
+ """Validate component ID format (alphanumeric and hyphens only)"""
149
+ if not re.match(r"^[a-z0-9-]+$", v):
150
+ raise ValueError(
151
+ "Component ID must contain only lowercase letters, numbers, and hyphens"
152
+ )
153
+ return v
154
 
155
+ @field_validator("upstream_deps", "downstream_deps")
156
+ @classmethod
157
+ def validate_dependency_format(cls, v: List[str]) -> List[str]:
158
+ """Validate dependency names"""
159
+ for dep in v:
160
+ if not re.match(r"^[a-z0-9-]+$", dep):
161
+ raise ValueError(
162
+ f"Dependency '{dep}' must contain only lowercase letters, numbers, and hyphens"
163
+ )
164
+ return v
165
+
166
+ @computed_field # FIXED: Use computed_field instead of __init__ override
167
+ @property
168
+ def fingerprint(self) -> str:
169
+ """
170
+ Generate deterministic fingerprint for event deduplication
171
+
172
+ SECURITY FIX: Changed from MD5 to SHA-256
173
+ IMPROVEMENT: Removed timestamp from fingerprint for determinism
174
+ """
175
+ components = [
176
+ self.component,
177
+ self.service_mesh,
178
+ f"{self.latency_p99:.2f}",
179
+ f"{self.error_rate:.4f}",
180
+ f"{self.throughput:.2f}"
181
+ ]
182
+
183
+ fingerprint_str = ":".join(components)
184
+
185
+ # SECURITY FIX: SHA-256 instead of MD5
186
+ return hashlib.sha256(fingerprint_str.encode()).hexdigest()
187
+
188
+ def model_post_init(self, __context) -> None:
189
+ """Validate cross-field constraints after initialization"""
190
+ # Check for circular dependencies
191
+ upstream_set = set(self.upstream_deps)
192
+ downstream_set = set(self.downstream_deps)
193
+
194
+ circular = upstream_set & downstream_set
195
+ if circular:
196
+ raise ValueError(
197
+ f"Circular dependencies detected: {circular}. "
198
+ "A component cannot be both upstream and downstream."
199
+ )
200
+
201
 
202
  class HealingPolicy(BaseModel):
203
+ """
204
+ Policy definition for automated healing actions
205
+
206
+ IMPROVEMENT: Changed conditions from Dict[str, Any] to List[PolicyCondition]
207
+ """
208
+
209
+ name: str = Field(
210
+ min_length=1,
211
+ max_length=255,
212
+ description="Policy name"
213
+ )
214
+
215
+ # FIXED: Structured conditions instead of Dict[str, Any]
216
+ conditions: List[PolicyCondition] = Field(
217
+ min_length=1,
218
+ description="List of conditions (all must match)"
219
+ )
220
+
221
+ actions: List[HealingAction] = Field(
222
+ min_length=1,
223
+ description="Actions to execute when policy triggers"
224
+ )
225
+
226
+ priority: int = Field(
227
+ ge=1,
228
+ le=5,
229
+ default=3,
230
+ description="Policy priority (1=highest, 5=lowest)"
231
+ )
232
+
233
+ cool_down_seconds: int = Field(
234
+ ge=0,
235
+ default=300,
236
+ description="Cooldown period between executions"
237
+ )
238
+
239
+ enabled: bool = Field(
240
+ default=True,
241
+ description="Whether policy is active"
242
+ )
243
+
244
+ max_executions_per_hour: int = Field(
245
+ ge=1,
246
+ default=10,
247
+ description="Rate limit: max executions per hour"
248
+ )
249
+
250
+ model_config = ConfigDict(frozen=True)
251
+
252
 
253
  class AnomalyResult(BaseModel):
254
+ """Result from anomaly detection"""
255
+
256
  is_anomaly: bool
257
+ confidence: float = Field(ge=0, le=1)
258
+ anomaly_score: float = Field(ge=0, le=1)
259
+ affected_metrics: List[str] = Field(default_factory=list)
260
+ detection_timestamp: datetime = Field(
261
+ default_factory=lambda: datetime.now(timezone.utc)
262
+ )
263
+
264
+ model_config = ConfigDict(frozen=True)
265
+
266
+
267
+ class ForecastResult(BaseModel):
268
+ """Result from predictive forecasting"""
269
+
270
+ metric: str
271
+ predicted_value: float
272
+ confidence: float = Field(ge=0, le=1)
273
+ trend: Literal["increasing", "decreasing", "stable"]
274
+ time_to_threshold: Optional[float] = Field(
275
+ default=None,
276
+ description="Minutes until threshold breach"
277
+ )
278
+ risk_level: Literal["low", "medium", "high", "critical"]
279
+ forecast_timestamp: datetime = Field(
280
+ default_factory=lambda: datetime.now(timezone.utc)
281
+ )
282
+
283
+ model_config = ConfigDict(frozen=True)