diff --git a/truenas_tools.py b/truenas_tools.py index 84e5c9b..495bfd9 100644 --- a/truenas_tools.py +++ b/truenas_tools.py @@ -33,6 +33,13 @@ class Tools: "Authorization": f"Bearer {self.valves.TRUENAS_API_KEY}", "Content-Type": "application/json", } + self._last_error = "нет данных (запросы ещё не выполнялись)" + try: + import urllib3 + + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + except Exception: + pass def clear_cache(self): self._cache.clear() @@ -53,10 +60,71 @@ class Tools: if self.valves.CACHE_TTL > 0: self._cache[cache_key] = (now, s) return s + self._last_error = f"HTTP {r.status_code} на {endpoint}: {r.text[:300]}" return None - except Exception: + except Exception as e: + self._last_error = f"Исключение на {endpoint}: {type(e).__name__}: {e}" return None + def get_debug_info(self) -> str: + """ + Диагностика соединения с TrueNAS: проверяет базовый URL, HTTP/HTTPS, + код ответа и текст ошибки для system/info. Полезно, когда API + 'недоступен' и нужно понять, на каком этапе рвётся запрос. + """ + lines = [] + base = self._base + lines.append(f"🔧 TRUENAS_URL из настроек: {base}") + lines.append( + "🔑 API-ключ задан: " + + ("да" if self.valves.TRUENAS_API_KEY else "❌ НЕТ — заполни Valves.TRUENAS_API_KEY") + ) + + candidates = [base] + if base.startswith("http://"): + candidates.append("https://" + base[len("http://"):]) + elif base.startswith("https://"): + candidates.append("http://" + base[len("https://"):]) + + for url in candidates: + lines.append(f"\n▶️ Проверка: {url}/api/v2.0/system/info") + try: + r = requests.get( + f"{url}/api/v2.0/system/info", + headers=self._headers, + timeout=10, + verify=False, + ) + lines.append(f" Код ответа: {r.status_code}") + if r.status_code == 200: + lines.append( + " ✅ Успех! Обнови TRUENAS_URL на этот вариант, если он отличается от текущего." + ) + elif r.status_code in (401, 403): + lines.append( + " 🔑 Ошибка авторизации — проверь API-ключ (Settings → API Keys) и что он активен." + ) + else: + lines.append(f" ⚠️ Тело ответа (первые 300 симв.): {r.text[:300]}") + except requests.exceptions.SSLError as e: + lines.append(f" 🔒 Ошибка SSL: {e}") + except requests.exceptions.ConnectionError as e: + lines.append( + f" 🔌 Ошибка соединения (сервер недоступен / порт закрыт / неверный IP): {e}" + ) + except requests.exceptions.Timeout: + lines.append( + " ⏱️ Таймаут — сервер не отвечает за 10 секунд (перегружен или сеть медленная)." + ) + except Exception as e: + lines.append(f" ❓ Прочая ошибка: {type(e).__name__}: {e}") + + lines.append( + "\nℹ️ Последняя зафиксированная ошибка внутренних запросов: " + + getattr(self, "_last_error", "нет данных (запросы ещё не выполнялись)") + ) + return "\n".join(lines) + def _try_endpoints(self, cache_key: str, endpoints: list) -> str: for ep in endpoints: result = self._rest_get(f"{cache_key}_{ep.replace('/', '_')}", ep) @@ -248,4 +316,4 @@ class Tools: "--- ВИРТУАЛЬНЫЕ МАШИНЫ ---\n" + self.get_vms_status() + "\n\n" "--- КОНТЕЙНЕРЫ (virt/incus) ---\n" + self.get_containers_status() + "\n\n" "--- АЛЕРТЫ ---\n" + self.get_active_alerts() - ) + ) \ No newline at end of file