104 lines
4.9 KiB
Python
104 lines
4.9 KiB
Python
"""
|
|
Общие утилиты: извлечение UUID из ответов API Nubes.
|
|
|
|
Проблема: Nubes API возвращает UUID инстанса/операции в разных форматах
|
|
в зависимости от эндпоинта и фазы жизненного цикла:
|
|
- POST /instances → {"instanceUid": "..."} (прямой ключ)
|
|
- POST /instances → Location: "./uuid" (заголовок)
|
|
- POST /instanceOperations → {"instanceOperationUid": "..."} (другой ключ)
|
|
- GET /instanceOperations → {"instanceOperation": {"instanceOperationUid": "..."}} (вложенный)
|
|
|
|
Эти две функции — единый способ достать UUID из ЛЮБОГО ответа.
|
|
Раньше каждая точка вызова имела свою реализацию _find_uid / _uid_from_location.
|
|
Теперь всё здесь — один источник правды.
|
|
|
|
Используется: executor.py, api_test.py, scenario.py.
|
|
"""
|
|
|
|
|
|
def find_uid(resp):
|
|
"""Извлечь instanceUid или instanceOperationUid из dict-ответа API.
|
|
|
|
Стратегия поиска (в порядке приоритета):
|
|
1. Верхний уровень — прямые ключи (стиль api_test.py):
|
|
instanceOperationUid → instanceUid → uid → Uid
|
|
2. Вложенные dict-значения (стиль scenario.py):
|
|
Для каждого значения-словаря проверяем instanceOperationUid → instanceUid
|
|
|
|
Почему такой порядок:
|
|
- instanceOperationUid приоритетнее — это ответ на POST /instanceOperations
|
|
- instanceUid — ответ на POST /instances или GET /instances
|
|
- uid / Uid — legacy-форматы, почти не встречаются
|
|
|
|
Args:
|
|
resp: dict — распарсенный JSON-ответ API (или что угодно).
|
|
|
|
Returns:
|
|
str или None — UUID (36 символов) или None если не нашли.
|
|
"""
|
|
# Защита: если передали не dict (например, list или str) — сразу None
|
|
if not isinstance(resp, dict):
|
|
return None
|
|
|
|
# ── Уровень 1: прямые ключи на верхнем уровне ──
|
|
# Пример: {"instanceUid": "abc-123", ...}
|
|
# Проверяем по порядку: сначала специфичные, потом общие
|
|
for key in ("instanceOperationUid", "instanceUid", "uid", "Uid"):
|
|
v = resp.get(key)
|
|
# isinstance(str) — отсекаем числа, None, пустые строки
|
|
if isinstance(v, str) and v:
|
|
return v
|
|
|
|
# ── Уровень 2: вложенные dict-значения ──
|
|
# Пример: {"instanceOperation": {"instanceOperationUid": "abc-123"}}
|
|
# Перебираем ВСЕ значения resp, ищем вложенные словари
|
|
for v in resp.values():
|
|
if isinstance(v, dict):
|
|
# Внутри вложенного dict — те же ключи что на верхнем уровне
|
|
uid = v.get("instanceOperationUid") or v.get("instanceUid")
|
|
if uid:
|
|
return uid
|
|
|
|
# Ничего не нашли
|
|
return None
|
|
|
|
|
|
def uid_from_location(loc):
|
|
"""Извлечь UUID из Location-заголовка HTTP-ответа.
|
|
|
|
Location — это HTTP-заголовок, который Nubes API возвращает при создании
|
|
ресурса (201 Created). Форматы:
|
|
- "./uuid" (относительный)
|
|
- "/api/v1/svc/instances/uuid" (абсолютный путь)
|
|
- "/api/v1/svc/instanceOperations/uuid" (полный путь)
|
|
|
|
Алгоритм:
|
|
1. Убираем trailing slash.
|
|
2. Split по "/" — берём ПОСЛЕДНИЙ сегмент.
|
|
3. Проверяем что это похоже на UUID (>= 32 символа) и не ".".
|
|
|
|
Args:
|
|
loc: str или None — значение заголовка Location.
|
|
|
|
Returns:
|
|
str или None — UUID или None если не нашли.
|
|
"""
|
|
# Пустой Location → нечего извлекать
|
|
if not loc:
|
|
return None
|
|
|
|
# Убираем trailing slash (напр. "./uuid/" → "./uuid")
|
|
# Split по "/" → [".", "uuid"] или ["", "api", "v1", "svc", "instances", "uuid"]
|
|
parts = str(loc).rstrip("/").split("/")
|
|
|
|
# Последний сегмент — это UUID
|
|
uid = parts[-1]
|
|
|
|
# Проверки:
|
|
# uid != "." — Location "./" даст пустой последний сегмент
|
|
# len >= 32 — UUID всегда 36 символов (8-4-4-4-12), но на всякий случай >= 32
|
|
if uid and uid != "." and len(uid) >= 32:
|
|
return uid
|
|
|
|
return None
|