truenas_tools.py v2.1.2: автоматический fallback POST→GET при 405 + новые пути API
This commit is contained in:
+45
-61
@@ -1,64 +1,48 @@
|
|||||||
# -----------------------------------------------------------------
|
def _rest_request(self, method: str, endpoint: str, payload: Optional[dict] = None, timeout: Optional[int] = None) -> Optional[Any]:
|
||||||
# Каталоги и приложения (v2.1+)
|
method = method.upper()
|
||||||
# -----------------------------------------------------------------
|
if method not in ("POST", "PUT", "DELETE", "PATCH", "GET"):
|
||||||
def discover_app_endpoints(self) -> str:
|
self._last_error = f"_rest_request: неподдерживаемый метод {method}"
|
||||||
"""
|
return None
|
||||||
Диагностический метод: проверяет разные эндпоинты API каталога приложений
|
url = f"{self._base}/api/v2.0/{endpoint}"
|
||||||
и сообщает, какие из них реально работают на этой версии TrueNAS.
|
t = timeout if timeout is not None else self.valves.request_timeout
|
||||||
Полезно при обновлении truenas_tools.py или при ошибках в list_app_catalog.
|
|
||||||
"""
|
|
||||||
candidates_get = [
|
|
||||||
"app/available", "app/available_apps", "app/list_apps",
|
|
||||||
"chart/release/names", "chart/release", "chart/release/list",
|
|
||||||
"catalog", "catalog?label=TRUENAS", "catalog/items",
|
|
||||||
"catalogs", "app/catalog",
|
|
||||||
]
|
|
||||||
candidates_post = [
|
|
||||||
("app/available", {"catalog_name": "TRUENAS", "retrieve_all_trains": True}),
|
|
||||||
("app/available", {}),
|
|
||||||
("app.available", {"catalog_name": "TRUENAS"}),
|
|
||||||
("chart/release/names", {"catalog_name": "TRUENAS"}),
|
|
||||||
("chart.release.names", {}),
|
|
||||||
("chart_release/names", {"catalog_name": "TRUENAS"}),
|
|
||||||
("catalog/items", {"label": "TRUENAS"}),
|
|
||||||
]
|
|
||||||
lines = ["🔍 ДИАГНОСТИКА ЭНДПОИНТОВ КАТАЛОГА", ""]
|
|
||||||
lines.append("=== GET ===")
|
|
||||||
for ep in candidates_get:
|
|
||||||
url = f"{self._base}/api/v2.0/{ep}"
|
|
||||||
try:
|
try:
|
||||||
r = requests.get(url, headers=self._headers, timeout=self.valves.request_timeout, verify=self.valves.verify_ssl)
|
r = requests.request(method, url, headers=self._headers, json=payload if payload is not None else {}, timeout=t, verify=self.valves.verify_ssl)
|
||||||
if r.status_code == 200:
|
# v2.1.2: автоматический fallback POST → GET при 405 (Method Not Allowed).
|
||||||
body = r.text[:120].replace("\n", " ")
|
# Это часто встречается в TrueNAS 25.10: некоторые эндпоинты каталога
|
||||||
lines.append(f"✅ {ep:35s} HTTP 200 | {body}")
|
# исторически были POST, но в новых версиях стали GET.
|
||||||
elif r.status_code == 404:
|
if r.status_code == 405 and method == "POST":
|
||||||
lines.append(f"❌ {ep:35s} HTTP 404")
|
|
||||||
elif r.status_code in (401, 403):
|
|
||||||
lines.append(f"🔑 {ep:35s} HTTP {r.status_code} (нет прав)")
|
|
||||||
else:
|
|
||||||
lines.append(f"⚠️ {ep:35s} HTTP {r.status_code}")
|
|
||||||
except Exception as e:
|
|
||||||
lines.append(f"💥 {ep:35s} ошибка: {type(e).__name__}")
|
|
||||||
lines.append("")
|
|
||||||
lines.append("=== POST ===")
|
|
||||||
for ep, payload in candidates_post:
|
|
||||||
url = f"{self._base}/api/v2.0/{ep}"
|
|
||||||
try:
|
try:
|
||||||
r = requests.post(url, headers=self._headers, json=payload, timeout=self.valves.request_timeout, verify=self.valves.verify_ssl)
|
r2 = requests.get(url, headers=self._headers, params=payload or {}, timeout=t, verify=self.valves.verify_ssl)
|
||||||
if r.status_code == 200:
|
if r2.status_code in (200, 201, 202, 204):
|
||||||
body = r.text[:120].replace("\n", " ")
|
if not r2.text:
|
||||||
lines.append(f"✅ {ep:35s} HTTP 200 | {body}")
|
return {"status": "ok", "http_code": r2.status_code, "via": "GET-fallback"}
|
||||||
elif r.status_code == 404:
|
try:
|
||||||
lines.append(f"❌ {ep:35s} HTTP 404")
|
data = r2.json()
|
||||||
elif r.status_code in (401, 403):
|
if isinstance(data, dict):
|
||||||
lines.append(f"🔑 {ep:35s} HTTP {r.status_code} (нет прав)")
|
data["_via"] = "GET-fallback"
|
||||||
else:
|
return data
|
||||||
lines.append(f"⚠️ {ep:35s} HTTP {r.status_code}")
|
except Exception:
|
||||||
|
return {"status": "ok", "text": r2.text, "http_code": r2.status_code, "_via": "GET-fallback"}
|
||||||
|
except Exception:
|
||||||
|
pass # вернёмся к исходной ошибке ниже
|
||||||
|
if r.status_code in (200, 201, 202, 204):
|
||||||
|
if not r.text:
|
||||||
|
return {"status": "ok", "http_code": r.status_code}
|
||||||
|
try:
|
||||||
|
return r.json()
|
||||||
|
except Exception:
|
||||||
|
return {"status": "ok", "text": r.text, "http_code": r.status_code}
|
||||||
|
self._last_error = f"{method} {endpoint} → HTTP {r.status_code}: {r.text[:400]}"
|
||||||
|
return None
|
||||||
|
except requests.exceptions.SSLError as e:
|
||||||
|
self._last_error = f"SSL-ошибка {method} {endpoint}: {e}"
|
||||||
|
return None
|
||||||
|
except requests.exceptions.ConnectionError as e:
|
||||||
|
self._last_error = f"Соединение {method} {endpoint}: {e}"
|
||||||
|
return None
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
self._last_error = f"Таймаут {method} {endpoint} (> {t}с)"
|
||||||
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
lines.append(f"💥 {ep:35s} ошибка: {type(e).__name__}")
|
self._last_error = f"{method} {endpoint}: {type(e).__name__}: {e}"
|
||||||
lines.append("")
|
return None
|
||||||
lines.append("ℹ️ Также можно посмотреть Swagger-документацию API:")
|
|
||||||
lines.append(f" {self._base}/api/docs/")
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
def list_catalogs(self) -> str:
|
|
||||||
Reference in New Issue
Block a user