Files

112 lines
5.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Общий цикл поллинга операции Nubes — ждём завершения или таймаута.
Поллинг — это периодический опрос API:
GET /instanceOperations/{opUid}?fields=dtFinish,isSuccessful,errorLog,duration,stages,svc
Каждые 5 секунд проверяем не появился ли dtFinish (дата завершения).
Как только dtFinish есть → операция завершена → возвращаем результат.
Используется в двух режимах:
- _finish_op (api_test.py) — асинхронно, в threading.Thread.
Запускается после возврата HTTP-ответа пользователю.
Пользователь видит "RUNNING" и поллит через /api/test/status/<opUid>.
- run_scenario (scenario.py) — синхронно, в основном потоке сценария.
Сценарий ждёт завершения шага перед переходом к следующему.
"""
import time
def poll_until_done(client, op_uid, timeout=1800):
"""Поллинг операции до завершения или таймаута.
Алгоритм:
1. Запоминаем время старта (t0).
2. В цикле: GET /instanceOperations/{opUid} с нужными полями.
3. Если dtFinish есть и не пустой → операция завершена.
- isSuccessful=True → status="OK"
- isSuccessful=False → status="FAIL"
4. Если dtFinish нет → sleep(5) и повторяем.
5. Если вышли за deadline → status="TIMEOUT".
Args:
client: HttpClient
op_uid: str — UUID операции (instanceOperationUid)
timeout: int — максимальное время ожидания в секундах.
По умолчанию 1800 (30 минут).
Столько может длиться create сложного сервиса.
Returns:
dict с ВСЕГДА одинаковыми ключами:
{status, is_successful, error_log, stages, duration, svc}
status ∈ {"OK", "FAIL", "TIMEOUT"}
OK — операция завершилась успешно
FAIL — операция завершилась с ошибкой
TIMEOUT — не дождались за timeout секунд
is_successful: bool — True только для OK
error_log: str — текст ошибки из API (или "TIMEOUT after ...")
stages: list — этапы выполнения [{stage, dtStart, dtFinish, isSuccessful, duration}, ...]
duration: float — реальное время поллинга в секундах (округлено до 0.1)
svc: str — имя сервиса (из ответа API)
"""
# Фиксируем время начала поллинга
t0 = time.time()
deadline = t0 + timeout # крайний срок
# Основной цикл поллинга
while time.time() < deadline:
try:
# GET с fields — запрашиваем только нужные поля (экономия трафика)
# dtFinish — ключевое: если есть → операция завершена
# isSuccessful — true/false
# errorLog — текст ошибки если FAIL
# duration — сколько заняла операция по версии API
# stages — этапы (Terraform: plan → apply → ...)
# svc — имя сервиса (может отличаться от того что знаем)
data = client.get(
f"/instanceOperations/{op_uid}"
"?fields=dtFinish,isSuccessful,errorLog,duration,stages,svc"
)
except Exception:
# Сетевая ошибка или API временно недоступен — не падаем.
# Ждём 5 секунд и пробуем снова.
# Это нормально: Nubes API иногда отдаёт 502 при высокой нагрузке.
time.sleep(5)
continue
# Ответ API: {"instanceOperation": {...}}
op = data.get("instanceOperation", {})
dt_finish = op.get("dtFinish")
# dtFinish может быть None, пустой строкой, или датой.
# Проверяем что dtFinish не пустой:
# str(dt_finish).strip() — отсекаем None→"None" и пустые строки
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", ""),
}
# Операция ещё не завершена — ждём 5 секунд
time.sleep(5)
# Вышли из while по deadline — таймаут.
# 30 минут истекли, операция всё ещё выполняется.
# Это редкий случай (обычно create занимает 2-10 минут).
return {
"status": "TIMEOUT",
"is_successful": False,
"error_log": f"TIMEOUT after {timeout}s",
"stages": [],
"duration": round(time.time() - t0, 1),
"svc": "",
}