Перейти к содержимому

Автоматизация через API: сервер скриптом

Создать сервер с защитой от дублей, дождаться IP, обработать 402/409/429 и удалить сервер — на bash и Python.

Уровень
Продвинутый
Время чтения
4 мин чтения
Обновлено
Содержание
  1. Защита от двойного заказа
  2. Bash + curl + jq
  3. Python
  4. Действия с сервером
  5. Удаление
  6. Частые ошибки

Сценарий: скрипт заказывает сервер, ждёт, пока тот получит IP, выполняет первичную настройку и при необходимости удаляет сервер. Нужен ключ с правами «Управление» и деньги на балансе — стоимость срока списывается при создании.

Защита от двойного заказа

Сеть может оборвать ответ, и скрипт не узнает, создан ли сервер. Чтобы повтор не создал второй, передавайте заголовок Idempotency-Key — любую уникальную строку, например UUID. Повтор с тем же ключом в течение суток вернёт первый ответ с заголовком Idempotent-Replayed: true.

Bash + curl + jq

bash
$ sudo apt install -y curl jq uuid-runtime
create-server.sh
$ #!/usr/bin/env bash$ set -euo pipefail$ API=https://qocloud.tech/api/v1$ AUTH="Authorization: Bearer $QOCLOUD_TOKEN"$ KEY=$(uuidgen)$ $ resp=$(curl -sS -w '\n%{http_code}' -X POST "$API/servers" \  -H "$AUTH" -H "Content-Type: application/json" -H "Idempotency-Key: $KEY" \  -d '{"plan":"2c-4g-60","location":"nl","os":"ubuntu2404","period":"1m","name":"web-02"}')$ code=$(tail -n1 <<<"$resp"); body=$(sed '$d' <<<"$resp")$ $ case "$code" in$   201) id=$(jq -r .server.id <<<"$body") ;;$   402) echo "Не хватает $(jq -r .error.need <<<"$body") ₽ на балансе" >&2; exit 1 ;;$   *)   echo "Ошибка $code: $(jq -r .error.message <<<"$body")" >&2; exit 1 ;;$ esac$ $ for i in $(seq 1 60); do$   s=$(curl -sS "$API/servers/$id" -H "$AUTH")$   status=$(jq -r .server.status <<<"$s"); ip=$(jq -r '.server.ip // empty' <<<"$s")$   if [ "$status" = active ] && [ -n "$ip" ]; then echo "$ip"; exit 0; fi$   if [ "$status" = failed ]; then echo "Создать не удалось" >&2; exit 1; fi$   sleep 10$ done$ echo "Сервер не готов за 10 минут" >&2; exit 1

Скрипт печатает IP готового сервера — его удобно передать дальше, например в Ansible. Пароль root можно получить методом GET /servers/{id}/password, каждый такой запрос записывается в журнал безопасности. Надёжнее сразу после создания добавить на сервер SSH-ключ и дальше работать по ключу.

Python

bash
$ python3 -m venv .venv && . .venv/bin/activate$ pip install requests
create_server.py
import os, time, uuid, requestsAPI = "https://qocloud.tech/api/v1"s = requests.Session()s.headers["Authorization"] = f"Bearer {os.environ['QOCLOUD_TOKEN']}"def call(method, path, **kw):    """Запрос с повтором при 429: ждём, сколько просит Retry-After."""    while True:        r = s.request(method, API + path, timeout=30, **kw)        if r.status_code != 429:            return r        time.sleep(int(r.headers.get("Retry-After", "60")))order = {"plan": "2c-4g-60", "location": "nl", "os": "ubuntu2404", "period": "1m", "name": "web-02"}r = call("POST", "/servers", json=order, headers={"Idempotency-Key": str(uuid.uuid4())})if r.status_code == 402:    raise SystemExit(f"Не хватает {r.json()['error']['need']} ₽")r.raise_for_status()server_id = r.json()["server"]["id"]for _ in range(60):    srv = call("GET", f"/servers/{server_id}").json()["server"]    if srv["status"] == "active" and srv["ip"]:        print(srv["ip"])        break    if srv["status"] == "failed":        raise SystemExit("Создать не удалось")    time.sleep(10)else:    raise SystemExit("Сервер не готов за 10 минут")

Действия с сервером

Действия вызываются методом POST /servers/{id}/actions/{action}: start, stop, reboot, reinstall (нужно поле os), reset-password, change-ip (платно), renew (продление на месяц), upgrade (нужно поле plan, доплата за остаток срока). Ответ 202 значит, что операция принята; статус меняется за секунды или минуты.

bash
$ curl -sS -X POST "$API/servers/$id/actions/reboot" -H "$AUTH"

Ответ 409 conflict означает, что действие сейчас невозможно: например, сервер ещё создаётся или остановлен. Подождите и проверьте статус, прежде чем повторять.

Удаление

Удаление необратимо, поэтому API требует заголовок X-Confirm-Name с именем сервера. Без него или с чужим именем вернётся 400.

bash
$ curl -sS -X DELETE "$API/servers/$id" -H "$AUTH" -H "X-Confirm-Name: web-02"

Частые ошибки

  • Новый Idempotency-Key на каждый повтор — так защита не работает. Ключ генерируется один раз на заказ.
  • Ожидание только status = active. IP может появиться чуть позже — проверяйте оба поля.
  • Ключ «Только чтение» для скрипта, который создаёт серверы, — ответ 403 forbidden.
  • Имя сервера с пробелами, подчёркиванием или кириллицей. Допустимы латиница, цифры и дефис, 2–60 символов.

Дочитайте до конца — статья засчитается в обучении автоматически.

  • API
  • автоматизация
  • скрипт
  • Python
  • curl
  • jq

Статья помогла?