Skip to content

Commit 2ea960e

Browse files
committed
feat(models): merging PreExecutionSignature and PostExecutionSignature into ExecutionSignature (#284)
rational: pre/post metadata is now more managed by ExecutionDetails bonus: adding post_execution_update functions to both ExecutionSignature and ExecutionDetails
1 parent 85db25b commit 2ea960e

1 file changed

Lines changed: 65 additions & 37 deletions

File tree

pyoaev/signatures/models.py

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,33 @@
2020
from pyoaev.signatures.types import ExpectationType, InjectExecutionActions
2121

2222

23+
class ToolErrorInfo(BaseModel):
24+
"""Crash report. Non-zero exit code and a timestamp if the tool left one behind."""
25+
26+
model_config = ConfigDict(extra="allow")
27+
28+
exit_code: int = 0
29+
crash_timestamp: str | None = None
30+
31+
32+
class ToolTimeoutInfo(BaseModel):
33+
"""Timeout report. Whatever partial loot was rescued before the kill signal."""
34+
35+
model_config = ConfigDict(extra="allow")
36+
37+
partial_results: list[str] = []
38+
39+
40+
class ToolOutput(BaseModel):
41+
"""Whatever the tool spat out: status, error info, timeout info, or injector extras."""
42+
43+
model_config = ConfigDict(extra="allow")
44+
45+
status: str | None = None
46+
error_info: ToolErrorInfo | None = None
47+
timeout_info: ToolTimeoutInfo | None = None
48+
49+
2350
class SignatureValue(BaseModel):
2451
"""One signature observation: a type and the value it carries."""
2552

@@ -152,6 +179,25 @@ def execution_duration(self) -> float:
152179
except:
153180
return 0.0
154181

182+
def post_execution_udpate(self, tool_output: ToolOutput, now: datetime) -> None:
183+
"""
184+
Update execution-related metadata according to tool output and now timestamp
185+
"""
186+
self.end_time = now
187+
188+
if tool_output.error_info and tool_output.error_info.exit_code != 0:
189+
self.execution_status = "failed"
190+
if tool_output.error_info.crash_timestamp:
191+
self.end_time = tool_output.error_info.crash_timestamp
192+
elif tool_output.timeout_info:
193+
self.execution_status = "timeout"
194+
elif tool_output.status == "partial":
195+
self.execution_status = "partial"
196+
else:
197+
self.execution_status = "success"
198+
199+
self.execution_action = "complete"
200+
155201

156202
class SignatureCallbackPayload(BaseModel):
157203
"""Outer POST envelope validated by ``SignatureApiManager`` before wire transmission."""
@@ -188,13 +234,21 @@ def build_from_models(
188234
)
189235

190236

191-
class PreExecutionSignature(BaseModel):
192-
"""Pre-execution data dump. Field set varies by category: network, cloud."""
237+
class ExecutionSignature(BaseModel):
238+
"""
239+
Execution signature data. Field set varies by category: network, cloud. Plus outcome, end_time, and any partial results.
240+
"""
193241

194242
model_config = ConfigDict(extra="allow")
195243

196244
# Timing always emitted at call time.
197-
start_time: str | None = None
245+
start_time: str = Field(
246+
default_factory=lambda: datetime.now(timezone.utc).strftime(
247+
"%Y-%m-%dT%H:%M:%SZ"
248+
)
249+
)
250+
end_time: str | None = None
251+
partial_results: list[str] | None = None
198252

199253
# Network identity
200254
source_ipv4: str | None = None
@@ -209,40 +263,15 @@ class PreExecutionSignature(BaseModel):
209263
cloud_region: str | None = None
210264
target_service: str | None = None
211265

266+
def post_execution_udpate(self, tool_output: ToolOutput, now: datetime) -> None:
267+
""" """
268+
self.end_time = now.strftime("%Y-%m-%dT%H:%M:%SZ")
212269

213-
class PostExecutionSignature(PreExecutionSignature):
214-
"""Post-execution view: pre-execution fields plus outcome, end_time, and any partial results."""
215-
216-
end_time: str | None = None
217-
execution_status: str | None = None
218-
partial_results: list[str] | None = None
219-
220-
221-
class ToolErrorInfo(BaseModel):
222-
"""Crash report. Non-zero exit code and a timestamp if the tool left one behind."""
223-
224-
model_config = ConfigDict(extra="allow")
225-
226-
exit_code: int = 0
227-
crash_timestamp: str | None = None
228-
270+
if tool_output.error_info and tool_output.error_info.crash_timestamp:
271+
self.end_time = tool_output.error_info.crash_timestamp
229272

230-
class ToolTimeoutInfo(BaseModel):
231-
"""Timeout report. Whatever partial loot was rescued before the kill signal."""
232-
233-
model_config = ConfigDict(extra="allow")
234-
235-
partial_results: list[str] = []
236-
237-
238-
class ToolOutput(BaseModel):
239-
"""Whatever the tool spat out: status, error info, timeout info, or injector extras."""
240-
241-
model_config = ConfigDict(extra="allow")
242-
243-
status: str | None = None
244-
error_info: ToolErrorInfo | None = None
245-
timeout_info: ToolTimeoutInfo | None = None
273+
if tool_output.timeout_info and tool_output.timeout_info.partial_results:
274+
self.partial_results = tool_output.timeout_info.partial_results
246275

247276

248277
class NetworkInjectorConfig(BaseModel):
@@ -339,8 +368,7 @@ def build_network_configs(
339368
"TargetSignatures",
340369
"SignaturePayload",
341370
"SignatureCallbackPayload",
342-
"PreExecutionSignature",
343-
"PostExecutionSignature",
371+
"ExecutionSignature",
344372
"ToolErrorInfo",
345373
"ToolTimeoutInfo",
346374
"ToolOutput",

0 commit comments

Comments
 (0)