v1.2.0: unified executor + flexible instance refs + params-render.js

Phase 1 — New modules:
- api/utils.py: find_uid(), uid_from_location() (replaces 2 duplicates)
- operations/poll.py: poll_until_done() (shared sync/async polling)
- operations/executor.py: execute_operation() (single CREATE/non-CREATE flow)

Phase 2 — Format + validation:
- routes/api_scenario_defs.py: _validate_steps with output/instance_ref/instance_uid
- operations/scenario.py: resolve instance_uid > instance_ref > service_id,
  use executor + poll_until_done, persist instance_bindings

Phase 3 — Migration:
- routes/api_test.py: create/non-create through executor, _finish_op through poll_until_done
- db/init_db.py: startup cleanup of stuck scenario_runs (>1h)

Phase 4 — UI shared module:
- static/js/params-render.js: renderParamRow, renderMapFixedRow, collectParams
- static/js/operations.js: use params-render.js (remove duplicates)
- templates/index.html: include params-render.js
- app.py: bump 1.1.57 → 1.2.0
This commit is contained in:
2026-07-31 08:55:06 +04:00
parent 38f58ddbd7
commit 91e97f6f75
11 changed files with 429 additions and 272 deletions
+51
View File
@@ -0,0 +1,51 @@
"""
Общий цикл поллинга операции Nubes.
Используется:
- _finish_op (api_test.py) — асинхронно в threading.Thread
- run_scenario (scenario.py) — синхронно в while
"""
import time
def poll_until_done(client, op_uid, timeout=1800):
"""Поллинг операции до завершения или таймаута.
Возвращает dict:
{status, is_successful, error_log, stages, duration, svc}
status ∈ {OK, FAIL, TIMEOUT}
"""
t0 = time.time()
deadline = t0 + timeout
while time.time() < deadline:
try:
data = client.get(
f"/instanceOperations/{op_uid}"
"?fields=dtFinish,isSuccessful,errorLog,duration,stages,svc"
)
except Exception:
time.sleep(5)
continue
op = data.get("instanceOperation", {})
dt_finish = op.get("dtFinish")
if dt_finish and str(dt_finish).strip():
is_ok = op.get("isSuccessful")
return {
"status": "OK" if is_ok else "FAIL",
"is_successful": is_ok,
"error_log": op.get("errorLog") or "",
"stages": op.get("stages", []),
"duration": round(time.time() - t0, 1),
"svc": op.get("svc", ""),
}
time.sleep(5)
# Таймаут
return {
"status": "TIMEOUT",
"is_successful": False,
"error_log": f"TIMEOUT after {timeout}s",
"stages": [],
"duration": round(time.time() - t0, 1),
"svc": "",
}