Раздел переписан по факту поведения платформы, а не по документации. - номер сети лежит в resource_id, а не id, добавлен vpc_id_of() - список сетей приходит голым массивом - при создании принимается только имя, регион и участники только для чтения - подключение сервера асинхронно и блокирует сервер, добавлен vpc_wait_member() - отключение участника и удаление сети настойчивые, ждут снятия блокировки Добавлен пример private_network.py. Claude-Session: https://claude.ai/code/session_01Loz5stgQZH6ofBdMqTxiJu
193 строки
8.3 KiB
Markdown
193 строки
8.3 KiB
Markdown
# regcloud-api
|
|
|
|
Клиент к API облака REG.RU (Рег.облако), точка входа `https://api.cloudvps.reg.ru`.
|
|
|
|
Полное покрытие API: серверы и все действия над ними, снимки, образы, тарифы, регионы,
|
|
SSH-ключи, IP-адреса и обратные записи, приватные сети, kubeconfig кластеров,
|
|
история операций и биллинг.
|
|
|
|
## Установка
|
|
|
|
```bash
|
|
pip install git+https://git.stackworks.ru/viktor/regcloud-api.git
|
|
```
|
|
|
|
Токен берётся в личном кабинете, в разделе настроек, и кладётся в `REGRU_CLOUD_TOKEN`.
|
|
|
|
## Осмотреться
|
|
|
|
```python
|
|
from regcloud_api import RegCloud
|
|
|
|
api = RegCloud()
|
|
|
|
for r in api.regions(): # где можно размещаться
|
|
print(r["slug"], r["name"])
|
|
|
|
for i in api.distributions(): # операционные системы
|
|
print(i["slug"], i["distribution"])
|
|
|
|
for i in api.applications(): # готовые сборки: панели, докер, гитлаб
|
|
print(i["slug"])
|
|
|
|
for s in api.sizes(): # тарифы
|
|
print(s["slug"], s["vcpus"], "ядер", s["price"], "р/час")
|
|
```
|
|
|
|
Во второй версии API образов заметно больше и они разложены по регионам.
|
|
|
|
```python
|
|
for i in api.images_v2("openstack-spb1", type="distribution"):
|
|
print(i["id"], i["name"])
|
|
```
|
|
|
|
## Тарифы
|
|
|
|
Slug собирается по шаблону `c<ядра>-m<память>-d<диск>-<линейка>`. Клиент умеет
|
|
собирать его сам и заодно проверять, что такое сочетание вообще существует.
|
|
|
|
```python
|
|
from regcloud_api import build_size, parse_size
|
|
|
|
RegCloud.make_size(4, 8, 120, "hp") # 'c4-m8-d120-hp'
|
|
build_size(4, 5, 120, "hp") # ValueError, при 4 ядрах памяти 5 ГБ не бывает
|
|
|
|
parse_size("c8-m32-d128-g1-a4000-gpu")
|
|
# {'gpu': 'A4000 16 ГБ', 'vcpus': 8, 'memory_gb': 32, 'disk_gb': 128, 'price_hour': 37.8}
|
|
```
|
|
|
|
Линейки: `std` стандартный, `stdp` стандартный плюс, `hp` производительный,
|
|
`hps` производительный ФЗ-152, `ddc` выделенный CPU, `high` высокочастотный.
|
|
|
|
## Жизненный цикл сервера
|
|
|
|
```python
|
|
key = api.add_ssh_key("deploy", "~/.ssh/id_ed25519.pub") # можно путём к файлу
|
|
|
|
srv = api.create_server(
|
|
size="c2-m4-d60-hp",
|
|
image="ubuntu-24-04-amd64",
|
|
name=api.random_name(), # платформа умеет придумывать имена
|
|
region="openstack-msk1",
|
|
ssh_keys=[key["fingerprint"]],
|
|
)
|
|
|
|
info = api.wait_active(srv["id"])
|
|
print("адрес:", RegCloud.ip_of(info))
|
|
```
|
|
|
|
Действия над сервером.
|
|
|
|
```python
|
|
api.stop(id); api.start(id); api.reboot(id)
|
|
api.resize(id, "c4-m8-d120-hp") # сменить тариф
|
|
api.rebuild(id, "debian-12-amd64") # переустановить систему
|
|
api.reset_password(id)
|
|
api.vnc_link(id) # консоль, когда по ssh уже не зайти
|
|
api.set_backups(id, True)
|
|
api.clone(id, "копия")
|
|
```
|
|
|
|
Снимки.
|
|
|
|
```python
|
|
api.snapshot(id, "base", offline=True) # платформа сама остановит сервер
|
|
snap = api.wait_snapshot("base")
|
|
api.create_server(size="c2-m4-d60-hp", image=snap["id"], region="openstack-msk1")
|
|
api.servers_from_snapshot(snap["id"]) # кто уже поднят из этого снимка
|
|
api.delete_snapshot(snap["id"])
|
|
```
|
|
|
|
Удаление.
|
|
|
|
```python
|
|
api.destroy_server(id) # дождётся снятия блокировки
|
|
```
|
|
|
|
## Адреса
|
|
|
|
```python
|
|
api.ips(server_id)
|
|
api.add_ip(server_id, ipv4=1)
|
|
api.set_ptr("1.2.3.4", "mail.example.com")
|
|
api.delete_ip("1.2.3.4")
|
|
```
|
|
|
|
## Приватные сети
|
|
|
|
Серверы в одной приватной сети видят друг друга напрямую, минуя интернет. Трафик внутри
|
|
не тарифицируется, а машине-исполнителю можно не выдавать публичный адрес вовсе.
|
|
|
|
```python
|
|
net = api.create_vpc("backoffice") # кроме имени платформа ничего не принимает
|
|
net_id = RegCloud.vpc_id_of(net) # номер лежит в resource_id, а не в id
|
|
|
|
api.vpc_attach(net_id, server_id) # действие асинхронное
|
|
api.vpc_wait_member(net_id, server_id) # дожидаемся, пока сервер реально войдёт
|
|
|
|
api.vpc_members(net_id) # список номеров серверов
|
|
api.rename_vpc(net_id, "новое имя")
|
|
|
|
api.vpc_detach(net_id, server_id)
|
|
api.delete_vpc(net_id)
|
|
```
|
|
|
|
Что выяснено на живом API и чего нет в документации.
|
|
|
|
Номер сети лежит в поле `resource_id`, а не `id`. Список сетей приходит голым массивом,
|
|
без обёртки. При создании принимается только имя, а регион, участники и `resource_id`
|
|
объявлены только для чтения и проставляются платформой. Подключение сервера к сети
|
|
асинхронно, и пока оно не доиграет, сервер заблокирован, из-за чего не выйдет ни отключить
|
|
его, ни удалить сеть. Поэтому `vpc_detach()` и `delete_vpc()` тут настойчивые, повторяют
|
|
попытки, пока блокировка не снимется.
|
|
|
|
## Деньги
|
|
|
|
```python
|
|
api.money_left() # сколько на счету
|
|
api.burn_rate() # сколько капает в час, в сутки и в месяц
|
|
api.billing_history()
|
|
api.history(limit=50) # что и когда делали с ресурсами
|
|
api.prices()
|
|
```
|
|
|
|
## Ошибки
|
|
|
|
Все ошибки платформы разложены по смыслу, чтобы ловить прицельно.
|
|
|
|
```python
|
|
from regcloud_api import NotFound, Locked, ValidationError, InsufficientFunds
|
|
|
|
try:
|
|
api.destroy_server(123)
|
|
except NotFound:
|
|
pass # его уже нет, и хорошо
|
|
except Locked:
|
|
pass # занят другой операцией
|
|
```
|
|
|
|
## Особенности платформы, обойдённые внутри
|
|
|
|
Собрано на живых серверах, чтобы вы на это не наступали.
|
|
|
|
**Адрес сервера лежит в поле `ip`.** Поле `networks` из документации всегда пустое.
|
|
`RegCloud.ip_of()` смотрит в оба места.
|
|
|
|
**Снимок снимается только с работающего сервера.** У остановленного платформа отвечает
|
|
`SERVER_IS_STOPPED`, останавливать его должна она сама по параметру `offline`.
|
|
|
|
**После снимка сервер сидит в `locked`** и на удаление отвечает `RESOURCE_DELETE_ERROR`.
|
|
Отпускает примерно через полминуты, поэтому `destroy_server()` повторяет попытки.
|
|
Забытый сервер продолжает тарифицироваться, молча сдаваться тут нельзя.
|
|
|
|
**`sizes()` возвращает не все линейки.** Тарифы с видеокартами в выдачу не попадают,
|
|
хотя создание с ними работает. Их список лежит в `gpu_sizes()` и в [docs_slugs.md](docs_slugs.md).
|
|
|
|
**Тарификация идёт от часа.** Двадцать минут работы списываются как полный час.
|
|
|
|
## Рядом
|
|
|
|
- [docs_slugs.md](docs_slugs.md) — тарифы, образы, регионы, правила составления slug'ов
|
|
- [openapi_v1.json](openapi_v1.json) — спецификация API, снятая с сервера платформы
|
|
- [examples/](examples/) — примеры
|