dayhoff-tools 1.9.0__py3-none-any.whl → 1.9.1__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.
- dayhoff_tools/cli/engine_commands.py +1 -146
- {dayhoff_tools-1.9.0.dist-info → dayhoff_tools-1.9.1.dist-info}/METADATA +1 -1
- {dayhoff_tools-1.9.0.dist-info → dayhoff_tools-1.9.1.dist-info}/RECORD +5 -5
- {dayhoff_tools-1.9.0.dist-info → dayhoff_tools-1.9.1.dist-info}/WHEEL +0 -0
- {dayhoff_tools-1.9.0.dist-info → dayhoff_tools-1.9.1.dist-info}/entry_points.txt +0 -0
@@ -319,87 +319,8 @@ def format_status(state: str, ready: Optional[bool]) -> str:
|
|
319
319
|
return state
|
320
320
|
|
321
321
|
|
322
|
-
# --------------------------------------------------------------------------------
|
323
|
-
# Audit helpers (Phase 1 observability)
|
324
|
-
# --------------------------------------------------------------------------------
|
325
|
-
|
326
|
-
|
327
|
-
def _get_engine_audit_bucket() -> Optional[str]:
|
328
|
-
"""Return the engine audit bucket name from SSM Parameter Store, if configured."""
|
329
|
-
try:
|
330
|
-
ssm = boto3.client("ssm", region_name="us-east-1")
|
331
|
-
resp = ssm.get_parameter(Name="/dev/studio-manager/engine-audit-bucket")
|
332
|
-
return resp["Parameter"]["Value"]
|
333
|
-
except ClientError:
|
334
|
-
return None
|
335
|
-
|
336
|
-
|
337
|
-
def _fetch_last_audit_via_ssm(instance_id: str) -> Optional[Dict]:
|
338
|
-
"""Fetch last shutdown attempt audit from the engine via SSM (fast best-effort)."""
|
339
|
-
try:
|
340
|
-
ssm = boto3.client("ssm", region_name="us-east-1")
|
341
|
-
resp = ssm.send_command(
|
342
|
-
InstanceIds=[instance_id],
|
343
|
-
DocumentName="AWS-RunShellScript",
|
344
|
-
Parameters={
|
345
|
-
"commands": [
|
346
|
-
"cat /var/log/idle-detector/last_shutdown_attempt.json 2>/dev/null || true",
|
347
|
-
],
|
348
|
-
"executionTimeout": ["3"],
|
349
|
-
},
|
350
|
-
)
|
351
|
-
cid = resp["Command"]["CommandId"]
|
352
|
-
time.sleep(1)
|
353
|
-
inv = ssm.get_command_invocation(CommandId=cid, InstanceId=instance_id)
|
354
|
-
if inv["Status"] != "Success":
|
355
|
-
return None
|
356
|
-
content = inv["StandardOutputContent"].strip()
|
357
|
-
if not content:
|
358
|
-
return None
|
359
|
-
return json.loads(content)
|
360
|
-
except Exception:
|
361
|
-
return None
|
362
|
-
|
363
|
-
|
364
|
-
def _fetch_last_audit_via_s3(instance_id: str) -> Optional[Dict]:
|
365
|
-
"""Fetch the newest audit object from S3 if available."""
|
366
|
-
bucket = _get_engine_audit_bucket()
|
367
|
-
if not bucket:
|
368
|
-
return None
|
369
|
-
try:
|
370
|
-
s3 = boto3.client("s3", region_name="us-east-1")
|
371
|
-
paginator = s3.get_paginator("list_objects_v2")
|
372
|
-
newest = None
|
373
|
-
for page in paginator.paginate(
|
374
|
-
Bucket=bucket, Prefix=f"{instance_id}/", MaxKeys=1000
|
375
|
-
):
|
376
|
-
for obj in page.get("Contents", []):
|
377
|
-
lm = obj.get("LastModified")
|
378
|
-
if newest is None or (lm and lm > newest["LastModified"]):
|
379
|
-
newest = obj
|
380
|
-
if not newest:
|
381
|
-
return None
|
382
|
-
obj = s3.get_object(Bucket=bucket, Key=newest["Key"])
|
383
|
-
data = obj["Body"].read().decode("utf-8")
|
384
|
-
return json.loads(data)
|
385
|
-
except Exception:
|
386
|
-
return None
|
387
322
|
|
388
323
|
|
389
|
-
def _summarize_audit(audit: Dict) -> str:
|
390
|
-
"""Return a compact one-line summary for status output."""
|
391
|
-
ts = audit.get("ts", "?")
|
392
|
-
shutdown = audit.get("shutdown", {})
|
393
|
-
result = shutdown.get("result", "?")
|
394
|
-
detach = audit.get("detach", {})
|
395
|
-
num_detached = sum(
|
396
|
-
1 for r in detach.get("results", []) if r.get("status") == "success"
|
397
|
-
)
|
398
|
-
idle = audit.get("idle", {})
|
399
|
-
elapsed = int(idle.get("elapsed_sec", 0))
|
400
|
-
threshold = int(idle.get("threshold_sec", 0))
|
401
|
-
return f"Last shutdown attempt: {ts} result={result} detach={num_detached} idle={elapsed//60}/{threshold//60}m"
|
402
|
-
|
403
324
|
|
404
325
|
def resolve_engine(name_or_id: str, engines: List[Dict]) -> Dict:
|
405
326
|
"""Resolve engine by name or ID with interactive selection."""
|
@@ -1081,15 +1002,7 @@ def engine_status(
|
|
1081
1002
|
status_lines.append(_sensor_line(" IDE ", "IDEConnectionSensor", "🖥"))
|
1082
1003
|
status_lines.append(_sensor_line("Docker", "DockerWorkloadSensor", "🐳"))
|
1083
1004
|
|
1084
|
-
|
1085
|
-
try:
|
1086
|
-
last_audit = _fetch_last_audit_via_ssm(engine["instance_id"])
|
1087
|
-
if last_audit:
|
1088
|
-
status_lines.append("")
|
1089
|
-
status_lines.append("[bold]Shutdown Audit:[/bold]")
|
1090
|
-
status_lines.append(f" • {_summarize_audit(last_audit)}")
|
1091
|
-
except Exception:
|
1092
|
-
pass
|
1005
|
+
|
1093
1006
|
|
1094
1007
|
# Combine top summary and details
|
1095
1008
|
all_lines = top_lines + status_lines
|
@@ -1128,65 +1041,7 @@ def engine_status(
|
|
1128
1041
|
console.print(f"[red]❌ Error fetching log: {e}[/red]")
|
1129
1042
|
|
1130
1043
|
|
1131
|
-
@engine_app.command("why")
|
1132
|
-
def engine_why(
|
1133
|
-
name_or_id: str = typer.Argument(help="Engine name or instance ID"),
|
1134
|
-
raw: bool = typer.Option(False, "--raw", help="Print raw audit JSON"),
|
1135
|
-
):
|
1136
|
-
"""Explain the last idle-detector shutdown attempt for an engine.
|
1137
|
-
|
1138
|
-
Tries SSM (on-instance file) first, then falls back to S3 audit bucket.
|
1139
|
-
"""
|
1140
|
-
check_aws_sso()
|
1141
1044
|
|
1142
|
-
# Resolve engine
|
1143
|
-
response = make_api_request("GET", "/engines")
|
1144
|
-
if response.status_code != 200:
|
1145
|
-
console.print("[red]❌ Failed to fetch engines[/red]")
|
1146
|
-
raise typer.Exit(1)
|
1147
|
-
|
1148
|
-
engines = response.json().get("engines", [])
|
1149
|
-
engine = resolve_engine(name_or_id, engines)
|
1150
|
-
|
1151
|
-
audit = _fetch_last_audit_via_ssm(
|
1152
|
-
engine["instance_id"]
|
1153
|
-
) or _fetch_last_audit_via_s3(engine["instance_id"])
|
1154
|
-
if not audit:
|
1155
|
-
console.print("No audit found (engine may not have attempted shutdown yet).")
|
1156
|
-
raise typer.Exit(0)
|
1157
|
-
|
1158
|
-
if raw:
|
1159
|
-
console.print_json(data=audit)
|
1160
|
-
return
|
1161
|
-
|
1162
|
-
# Pretty summary
|
1163
|
-
status = _summarize_audit(audit)
|
1164
|
-
panel_lines = [
|
1165
|
-
f"[bold]{status}[/bold]",
|
1166
|
-
"",
|
1167
|
-
"[bold]Sensors:[/bold]",
|
1168
|
-
]
|
1169
|
-
for r in audit.get("idle", {}).get("reason_snapshot", []):
|
1170
|
-
active = "✓" if r.get("active") else "-"
|
1171
|
-
reason = r.get("reason") or ""
|
1172
|
-
sensor = r.get("sensor")
|
1173
|
-
panel_lines.append(f" {active} {sensor}: {reason}")
|
1174
|
-
panel_lines.append("")
|
1175
|
-
panel_lines.append("[bold]Detach results:[/bold]")
|
1176
|
-
for res in audit.get("detach", {}).get("results", []):
|
1177
|
-
panel_lines.append(
|
1178
|
-
f" - {res.get('studio_id')}: {res.get('status')} {res.get('error') or ''}"
|
1179
|
-
)
|
1180
|
-
s3_info = audit.get("s3", {})
|
1181
|
-
if s3_info.get("uploaded"):
|
1182
|
-
panel_lines.append("")
|
1183
|
-
panel_lines.append(
|
1184
|
-
f"[dim]S3: s3://{s3_info.get('bucket')}/{s3_info.get('key')}[/dim]"
|
1185
|
-
)
|
1186
|
-
|
1187
|
-
console.print(
|
1188
|
-
Panel("\n".join(panel_lines), title="Idle Shutdown Audit", border_style="blue")
|
1189
|
-
)
|
1190
1045
|
|
1191
1046
|
|
1192
1047
|
@engine_app.command("stop")
|
@@ -3,7 +3,7 @@ dayhoff_tools/chemistry/standardizer.py,sha256=uMn7VwHnx02nc404eO6fRuS4rsl4dvSPf
|
|
3
3
|
dayhoff_tools/chemistry/utils.py,sha256=jt-7JgF-GeeVC421acX-bobKbLU_X94KNOW24p_P-_M,2257
|
4
4
|
dayhoff_tools/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
dayhoff_tools/cli/cloud_commands.py,sha256=33qcWLmq-FwEXMdL3F0OHm-5Stlh2r65CldyEZgQ1no,40904
|
6
|
-
dayhoff_tools/cli/engine_commands.py,sha256=
|
6
|
+
dayhoff_tools/cli/engine_commands.py,sha256=HcMjlfxHWGp8J9ai2pOel6RkT3W-BcaaNk1KBBDWmLg,104796
|
7
7
|
dayhoff_tools/cli/main.py,sha256=LoFs3SI4fdCjP4pdxEAhri-_q0dmNYupmBCRE4KbBac,5933
|
8
8
|
dayhoff_tools/cli/swarm_commands.py,sha256=5EyKj8yietvT5lfoz8Zx0iQvVaNgc3SJX1z2zQR6o6M,5614
|
9
9
|
dayhoff_tools/cli/utility_commands.py,sha256=WQTHOh1MttuxaJjl2c6zMa4x7_JuaKMQgcyotYrU3GA,25883
|
@@ -27,7 +27,7 @@ dayhoff_tools/intake/uniprot.py,sha256=BZYJQF63OtPcBBnQ7_P9gulxzJtqyorgyuDiPeOJq
|
|
27
27
|
dayhoff_tools/logs.py,sha256=DKdeP0k0kliRcilwvX0mUB2eipO5BdWUeHwh-VnsICs,838
|
28
28
|
dayhoff_tools/sqlite.py,sha256=jV55ikF8VpTfeQqqlHSbY8OgfyfHj8zgHNpZjBLos_E,18672
|
29
29
|
dayhoff_tools/warehouse.py,sha256=UETBtZD3r7WgvURqfGbyHlT7cxoiVq8isjzMuerKw8I,24475
|
30
|
-
dayhoff_tools-1.9.
|
31
|
-
dayhoff_tools-1.9.
|
32
|
-
dayhoff_tools-1.9.
|
33
|
-
dayhoff_tools-1.9.
|
30
|
+
dayhoff_tools-1.9.1.dist-info/METADATA,sha256=m7wy7HkaFJon6aQO127tsXQ-GPYiK21NtYFS1FEdh58,2914
|
31
|
+
dayhoff_tools-1.9.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
32
|
+
dayhoff_tools-1.9.1.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
|
33
|
+
dayhoff_tools-1.9.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|