Во всех примерах токен берётся из переменной окружения DOBRY_TOKEN.
bash: перезагрузить все серверы с тегом vpn
#!/usr/bin/env bash
set -euo pipefail
API=https://panel.dobry.host/api
H="Authorization: Bearer $DOBRY_TOKEN"
for id in $(curl -sf "$API/servers" -H "$H" | jq -r '.servers[] | select(.tags | index("vpn")) | .id'); do
key="cron-$(date +%Y%m%d%H)-$id"
curl -sf -X POST "$API/servers/$id/actions" -H "$H" -H "Content-Type: application/json" \
-d "{\"action\":\"reboot\",\"idempotency_key\":\"$key\"}" | jq -c '{id: .operation.id, status: .operation.status}'
done
Ключ cron-ГГГГММДДЧЧ-id гарантирует, что при повторном запуске в тот же час сервер не перезагрузится второй раз.
Python: клиент с обработкой ошибок и лимитов
import os, time, requests
class Dobry:
def __init__(self, token=None, base="https://panel.dobry.host/api"):
self.s = requests.Session()
self.s.headers["Authorization"] = "Bearer " + (token or os.environ["DOBRY_TOKEN"])
self.base = base
def call(self, method, path, **kw):
for attempt in range(3):
r = self.s.request(method, self.base + path, timeout=20, **kw)
if r.status_code == 429: # лимит 120/мин — подождать
time.sleep(5 * (attempt + 1)); continue
data = r.json()
if not data.get("ok"):
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
return data
raise RuntimeError("rate limit")
def servers(self):
return self.call("GET", "/servers")["servers"]
def action(self, sid, action, key):
return self.call("POST", f"/servers/{sid}/actions", json={"action": action, "idempotency_key": key})["operation"]
def metrics(self, sid, rng="24h"):
return self.call("GET", f"/servers/{sid}/metrics", params={"range": rng})["metrics"]
api = Dobry()
for s in api.servers():
m = api.metrics(s["id"], "1h")
last = m["points"][-1] if m["points"] else None
print(s["name"], s["ip"], "cpu:", last["cpu"] if last else "нет агента")
Python: экспорт метрик за сутки в CSV
import csv
api = Dobry()
with open("metrics.csv", "w", newline="") as f:
w = csv.writer(f); w.writerow(["server", "time", "cpu", "mem_gb", "disk_gb", "rx_mbit", "tx_mbit"])
for s in api.servers():
for p in api.metrics(s["id"], "24h")["points"]:
w.writerow([s["name"], p["time"], p["cpu"], p["memory"], p["disk"], p["rx"], p["tx"]])
Python: продлить нельзя, но напомнить себе — можно
import time
api = Dobry()
soon = [s for s in api.servers() if 0 < s["paid_until"] - time.time() < 3 * 86400 and not s["auto_renew"]]
for s in soon:
print(f"⚠️ {s['name']} истекает {time.strftime('%d.%m', time.localtime(s['paid_until']))} — продлите в кабинете")
Node.js: перезагрузка, если сервер не отвечает
const API = "https://panel.dobry.host/api";
const H = { Authorization: "Bearer " + process.env.DOBRY_TOKEN, "Content-Type": "application/json" };
const api = (path, init = {}) => fetch(API + path, { ...init, headers: H }).then((r) => r.json());
const { servers } = await api("/servers/sparklines");
for (const [id, s] of Object.entries(servers)) {
if (s.status !== "down") continue; // агент молчит > 4 минут
const key = `watchdog-${id}-${Math.floor(Date.now() / 3600000)}`; // не чаще раза в час
const r = await api(`/servers/${id}/actions`, { method: "POST", body: JSON.stringify({ action: "reboot", idempotency_key: key }) });
console.log(id, r.ok ? r.operation.status : r.error.message);
}
Grafana / Prometheus
Простейший экспортёр — cron раз в минуту пишет sparklines в textfile-коллектор node_exporter:
curl -sf https://panel.dobry.host/api/servers/sparklines -H "Authorization: Bearer $DOBRY_TOKEN" \
| jq -r '.servers | to_entries[] | "dobry_cpu{server=\"\(.key)\"} \(.value.cpu[-1] // 0)\ndobry_up{server=\"\(.key)\"} \(if .value.status=="up" then 1 else 0 end)"' \
> /var/lib/node_exporter/textfile/dobry.prom.$$ && mv /var/lib/node_exporter/textfile/dobry.prom.$$ /var/lib/node_exporter/textfile/dobry.prom
Что учесть
- После
reboot/shutdownживой статус в карточке меняется через 10–30 с — не опрашивайте чаще. needs_review— не ошибка и не успех: проверьтеlive.statusчерез минуту.- Токен с правом
powerдержите отдельно от токена мониторинга (read) и ограничьте по IP. - Если скрипт работает с сервера, который выдан вам другом, права ограничены тем, что разрешил владелец (
shared.perms).