41 KiB
Работа со стендом
Этот документ — для оператора, который уже развернул стенд (см. SETUP.md) и теперь использует его в повседневной работе: добавляет адреса на проверку, следит за очередью, разбирается в результатах и реагирует на проблемы. Прямые вызовы API описаны в API.md — здесь мы используем их только как инструмент, не углубляясь в протокол.
Содержание
- Как устроена работа с системой
- Добавление новых IP в очередь
- Наблюдение за очередью
- Значения полей IP
- Как читать итоговый результат (pass/partial/fail)
- Просмотр деталей и истории по конкретному адресу
- Управление валидаторами
- Управление площадками (проберами)
- Управление типами проверок пробера
- Управление целями проверки
- Повторная проверка адреса
- Принудительная остановка проверки
- Удаление адресов из очереди
- Пауза перед self-check (fip_settle_seconds)
- Частые проблемы и что с ними делать
Как устроена работа с системой
Оператор не взаимодействует с валидаторами и проберами напрямую — вся
работа идёт через control-api. Цикл жизни одного IP-адреса:
- Адрес встаёт в очередь (
queued). - Control-api сам находит свободный валидатор, привязывает адрес к нему как Floating IP.
- Валидатор проверяет, что действительно вышел в интернет именно через этот адрес (self-check), затем прогоняет исходящие проверки (HTTPS, ICMP, опционально SSH до заданных внешних целей).
- Одновременно три внешние площадки проверяют, что этот адрес доступен снаружи (входящие TCP-подключения на 22/80/443/8080 и ICMP) — это ловит блокировки/чёрные списки на конкретных внешних сетях.
- Как только все источники (валидатор + 3 площадки) отчитались — или истекло время ожидания — control-api подводит итог и освобождает адрес (отвязывает Floating IP).
Всё это происходит автоматически, без участия оператора. Задача оператора — положить адреса в очередь и снять с них результат.
Добавление новых IP в очередь
Основной способ — API, без перезапуска процесса:
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips \
-d '{"addresses": ["203.0.113.10", "203.0.113.11"]}'
{"added": ["203.0.113.10", "203.0.113.11"], "requeued": [], "reordered": [], "skipped_in_progress": []}
Адреса обрабатываются в порядке, в котором перечислены в addresses —
именно в этом порядке они и встанут в очередь друг за другом. Метод
идемпотентен относительно уже идущих проверок: адрес, который сейчас
активно проверяется, в ответе окажется в skipped_in_progress и не будет
тронут (см. «Повторная проверка адреса»
ниже — тот же метод форсирует перепроверку уже завершённых адресов).
Также по-прежнему можно добавить адреса через ip_addresses в
/etc/cloud-ip-validator/control-api.yaml и перезапустить control-api —
при каждом старте control-api доливает в очередь только новые адреса из
этого списка (уже обработанные ранее не сбрасываются и повторно не
проверяются). Держать control-api.yaml под версионным контролем (git)
по-прежнему полезно как журнал того, что изначально ставилось в очередь
при разворачивании стенда — но для повседневного добавления адресов проще
и быстрее пользоваться API выше.
Наблюдение за очередью
Общая сводка:
curl -s http://<control-api>:8080/api/v1/admin/status | python3 -m json.tool
{
"total_ips": 25,
"ips_by_state": {"queued": 10, "awaiting_self_check": 1, "checking": 3, "done": 10, "failed": 1},
"total_validators": 4
}
ips_by_state — сколько адресов в каждом состоянии прямо сейчас. Если
хотите наблюдать за прогрессом в реальном времени:
watch -n 2 'curl -s http://<control-api>:8080/api/v1/admin/status | python3 -m json.tool'
Полный список всех адресов со всеми полями:
curl -s http://<control-api>:8080/api/v1/admin/ips | python3 -m json.tool
Только финальные результаты (уже готовые адреса), с помощью jq:
curl -s http://<control-api>:8080/api/v1/admin/ips \
| jq '[.[] | select(.State=="done" or .State=="failed") | {IPAddress, State, OverallResult}]'
Значения полей IP
| Поле | Значение |
|---|---|
IPAddress |
Проверяемый адрес |
Sequence |
Позиция в очереди (порядок постановки — из конфига при первом старте либо из последнего вызова POST /api/v1/admin/ips) |
State |
Текущий этап: queued, assigning_fip, awaiting_self_check, checking, aggregating, done, failed |
OwnerValidatorID |
Какой валидатор сейчас (или последним) занимался этим адресом |
FIPID |
Идентификатор Floating IP в OpenStack, к которому привязан адрес (пусто, если ещё/уже не привязан) |
AttemptNumber |
Номер попытки — растёт при каждом requeue (сбой привязки, сбой self-check, реклейм по таймауту) |
RetryCount |
Сколько раз адрес уже переставлялся в очередь заново |
EgressComplete |
Валидатор закончил исходящие проверки |
OverallResult |
Итог: pass, partial, fail, cancelled (принудительно остановлена, см. «Принудительная остановка проверки»), либо пусто, пока проверка не завершена |
AssignedAt / AggregatedAt / FIPReleasedAt |
Метки времени соответствующих этапов |
Завершённость входящих проверок по каждой конкретной площадке в этом списке не отображается (площадок теперь может быть сколько угодно, а не фиксированные три) — детали по конкретной площадке смотрите в массиве
checksответаGET /api/v1/admin/ips/{ip}(Source: "inbound-site-N").
Как читать итоговый результат (pass/partial/fail)
pass— прошли все проверки: все исходящие, плюс все площадки по всем портам и ICMP — если площадки вообще настроены (sitesв конфиге control-api может быть пустым, см. «Управление площадками» — тогда учитываются только исходящие). Адрес можно считать пригодным к повторной выдаче.partial— часть проверок прошла, часть — нет (например, площадка site-2 не смогла достучаться по 8080/tcp, но остальное в порядке). Означает частичную деградацию — например, адрес заблокирован в отдельном сегменте сети/у отдельного провайдера. Требует решения оператора: годится ли адрес для данного случая использования.fail— либо ни одна проверка не прошла, либо адрес вообще не дошёл до стадии проверок (например, self-check не подтвердился — трафик валидатора не пошёл через назначенный FIP — и попытки исчерпались). Смотритеeventsпо этому адресу (см. ниже), чтобы понять, на каком шаге и почему.cancelled— проверку остановил оператор черезPOST /api/v1/admin/ips/{ip}/cancel(Stateпри этом —failed), а не система по итогам проверок. Отличать от обычногоfailполезно, чтобы не путать «адрес не прошёл проверку» с «проверку прервали вручную».
Отсутствие ответа от источника (площадка не прислала результат до
истечения checking_window_seconds) засчитывается как провал — это
управляется настройкой aggregation.missing_counts_as_fail в конфиге
control-api (по умолчанию включено).
Просмотр деталей и истории по конкретному адресу
curl -s http://<control-api>:8080/api/v1/admin/ips/203.0.113.10 | python3 -m json.tool
Ответ содержит три части:
ip— те же поля, что и в спискеadmin/ips, но для одного адреса;checks— все отдельные проверки текущей попытки: кто проверял (Source:egressилиinbound-site-N), что именно (CheckType,Target), результат (Success), задержка (LatencyMS), и человекочитаемая деталь (Detail, например текст ошибки при отказе);events— журнал аудита по этому адресу в хронологическом порядке (регистрация, привязка FIP, self-check, агрегация и т.д.) — полезен, чтобы восстановить точную последовательность событий при разборе инцидента.
Пример: почему адрес получил fail?
curl -s http://<control-api>:8080/api/v1/admin/ips/203.0.113.10 \
| jq '.checks[] | select(.Success==false)'
Управление валидаторами
Список валидаторов и их текущее состояние:
curl -s http://<control-api>:8080/api/v1/admin/validators | python3 -m json.tool
Состояния валидатора: unregistered (в конфиге есть, агент ещё не
подключался), idle (свободен, готов взять адрес), assigned/checking
(занят), unreachable (пропустил heartbeat дольше
orchestrator.heartbeat_timeout_seconds).
Добавление нового валидатора (без перезапуска control-api):
- Поднимите новую ВМ в сервисном проекте облака, узнайте её Neutron
port_id. - Зарегистрируйте валидатора через API:
curl -s -X POST http://<control-api>:8080/api/v1/admin/config/validators \ -d '{"validator_id": "validator_05", "os_port_id": "port-abc123"}' - Разверните и запустите
validator-agentна новой ВМ с тем жеvalidator_idв его конфиге (см. SETUP.md).
Сменить os_port_id уже существующего валидатора (например, после
пересоздания ВМ) — PUT /api/v1/admin/config/validators/{id} с телом
{"os_port_id": "новый-port-id"}.
Полный список зарегистрированных валидаторов — GET /api/v1/admin/config/validators (в отличие от GET /api/v1/admin/validators, отдаёт snake_case и без текущего IP —
только конфигурационные поля).
Вывод валидатора из эксплуатации: остановите на нём
validator-agent (systemctl stop validator-agent). Он перестанет
получать новые задания после того, как закончит текущее (если оно было);
если он был убит посреди работы — control-api сам заберёт у него
незавершённый адрес обратно в очередь по истечении
orchestrator.lease_ttl_seconds. Удалять регистрацию валидатора не
обязательно — просто выключенный агент не будет ничего забирать. Если всё
же нужно убрать валидатора из системы совсем:
curl -s -X DELETE http://<control-api>:8080/api/v1/admin/config/validators/validator_05
Возвращает 409, если валидатор прямо сейчас владеет каким-то IP —
дождитесь освобождения (или принудительно остановите его проверку, см.
«Принудительная остановка проверки»)
перед удалением.
Правки через
validators[]вcontrol-api.yamlтоже поддерживаются, но только как bootstrap пустой базы данных при самом первом старте — как только в БД есть хотя бы один валидатор, YAML для этой секции игнорируется при всех последующих рестартах (см. SETUP.md). Для стенда, который уже хоть раз запускался, используйте API выше.
Управление площадками (проберами)
Входящие (inbound/prober) проверки полностью опциональны, а число
площадок не ограничено. Список sites в конфиге control-api и есть
переключатель: пусто — inbound-проверки выключены целиком, итоговый
результат считается только по исходящим (egress) проверкам, и агрегация
не ждёт вообще ни одного пробера. Указано N слотов — ждём именно их.
Явного отдельного флага "включить/выключить" нет — самого списка sites
достаточно.
Чтобы добавить площадку (без перезапуска control-api) — назначьте
site_id любому свободному слоту (index — любое целое >= 1, слотов
может быть сколько угодно) через API:
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/sites/1 \
-d '{"site_id": "site-1"}'
и разверните на площадке prober с тем же site_id. Чтобы отключить
конкретную площадку — освободите слот:
curl -s -X DELETE http://<control-api>:8080/api/v1/admin/config/sites/1
процесс prober на ней можно не останавливать (он просто перестанет
получать назначения — POST /api/v1/probers/register для отвязанного
site_id начнёт отвечать 400). Текущее распределение слотов — GET /api/v1/admin/config/sites.
Правки через
sites[]вcontrol-api.yamlтоже поддерживаются, но только как bootstrap пустой базы данных при самом первом старте — как только в БД есть хотя бы одна площадка, YAML для этой секции игнорируется при всех последующих рестартах. Для стенда, который уже хоть раз запускался, используйте API выше.
Состояния площадки
По аналогии с валидаторами (см. «Управление
валидаторами» выше), у каждой площадки есть
состояние подключения — видно и в GET /api/v1/admin/config/sites
(поля hostname/state/last_heartbeat_at), и на странице /sites
дашборда бейджем:
unregistered— слот сконфигурирован, но процессproberна этой площадке ещё ни разу не подключался (не вызывалPOST /api/v1/probers/register).idle— площадка на связи:proberзарегистрирован и присылает heartbeat (POST /api/v1/probers/{site_id}/heartbeat) на каждом опросе.unreachable— площадка пропустила heartbeat дольшеorchestrator.heartbeat_timeout_seconds(тот же параметр, что и для валидаторов) — вероятно, процессproberупал или потерял сеть до control-api.
В отличие от валидатора, у площадки нет состояний assigned/checking
— пробер не привязан к одному IP, а на каждом опросе обрабатывает сразу
весь активный набор.
Управление типами проверок пробера
Список TCP-портов и флаг ICMP, которые prober проверяет на каждой
настроенной площадке — единый глобальный набор, общий для всех площадок
сразу (не то же самое, что список sites выше: sites решает, сколько
точек его применяют, а этот набор — что именно они проверяют).
Посмотреть текущий набор:
curl -s http://<control-api>:8080/api/v1/admin/config/inbound-checks | python3 -m json.tool
Изменить набор портов и/или ICMP (без перезапуска control-api — новое значение сразу видно и следующему опросу пробера, и уже идущей агрегации):
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/inbound-checks \
-d '{"ports": [22, 80, 443, 8080], "icmp": true}'
Порты должны быть в диапазоне 1..65535 и не повторяться — иначе 400.
Пустой список портов вместе с "icmp": false — штатный способ временно
отключить inbound-проверки целиком, не трогая список площадок:
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/inbound-checks \
-d '{"ports": [], "icmp": false}'
То же самое — на странице /settings дашборда, второй формой рядом с
паузой перед self-check (см. ниже): текстовое поле с портами через запятую
и чекбокс ICMP.
Порты 22 и 443 в этом списке трактуются особо: помимо базового
TCP-connect (tcp-22/tcp-443) пробер дополнительно выполняет настоящий
обмен SSH-банером (ssh) и настоящий TLS-хендшейк (tls-443) —
голого открытого TCP-порта недостаточно, чтобы считать SSH/HTTPS
рабочими. Отдельного переключателя для этих доп.проверок нет: они
включаются и выключаются вместе с самим портом в ports. Если на порту
22/443 у площадки на самом деле слушает что-то, кроме SSH/HTTPS,
доп.проверка будет закономерно проваливаться — заведите для такого сервиса
другой порт.
Правки через
orchestrator.inbound_checksвcontrol-api.yamlтоже поддерживаются, но только как bootstrap пустой базы данных при самом первом старте — как только в БД есть эта настройка (а она появляется сразу же при первом старте, значение по умолчанию — из YAML), YAML для этой секции игнорируется при всех последующих рестартах. Для стенда, который уже хоть раз запускался, используйте API выше.
Управление целями проверки
Набор egress-целей (targets) и типов проверок (check_types,
привязывающих тип — https/icmp/ssh — к одной или нескольким группам
целей) управляется через API так же, как валидаторы и площадки —
изменения подхватываются немедленно, следующим же назначением от
оркестратора, без перезапуска.
Посмотреть текущий набор:
curl -s http://<control-api>:8080/api/v1/admin/config/targets | python3 -m json.tool
curl -s http://<control-api>:8080/api/v1/admin/config/check-types | python3 -m json.tool
Создать/заменить группу целей и включить тип проверки, ссылающийся на неё:
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/targets/web \
-d '{"targets": ["https://hub.docker.com", "https://github.com"]}'
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/check-types/ssh \
-d '{"enabled": true, "targets": ["web"]}'
Отключить тип проверки, не удаляя его (значения целей сохраняются):
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/check-types/ssh \
-d '{"enabled": false, "targets": ["web"]}'
Удалить группу целей можно только если на неё не ссылается ни один
check_type (иначе — 409); удалить сам check_type можно в любой
момент (DELETE /api/v1/admin/config/check-types/{name}).
Важное следствие принятого компромисса: если конфигурация меняется
ровно в момент, когда чей-то IP уже находится в checking (self-check
уже пройден, проверки уже назначены агенту), агрегация этой конкретной
попытки посчитает уже изменённую конфигурацию, а не ту, что была на
момент выдачи задания. На практике это узкое окно в несколько секунд;
деградирует безопасно — через aggregation.missing_counts_as_fail худший
исход для одной попытки — partial вместо pass, самоисправляется на
следующей попытке (в том числе через принудительный повтор, см. ниже).
Правки через
targets/check_typesвcontrol-api.yamlтоже поддерживаются, но только как bootstrap пустой базы данных при самом первом старте — см. примечание в разделах выше.
Повторная проверка адреса
Если адрес завершился с failed или partial, а вы хотите перепроверить
его ещё раз (например, после устранения блокировки на стороне сети) —
отправьте его тем же методом, что используется для постановки новых
адресов в очередь:
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips \
-d '{"addresses": ["203.0.113.10"]}'
{"added": [], "requeued": ["203.0.113.10"], "reordered": [], "skipped_in_progress": []}
Адрес в requeued означает, что он был в терминальном состоянии
(done/failed) и его перезапустили: AttemptNumber увеличился,
RetryCount обнулён, предыдущий OverallResult сброшен, адрес снова
queued и будет обработан на общих основаниях. Никакой особой обработки
для уже проверенных адресов не требуется — тот же вызов безопасно
принимает список из новых и уже проверенных адресов одновременно;
единственное, что метод не сделает — не запустит вторую параллельную
проверку адреса, который прямо сейчас уже проверяется (такой адрес
вернётся в skipped_in_progress, см.
«Добавление новых IP в очередь»).
Принудительная остановка проверки
Если проверка адреса зависла дольше ожидаемого либо просто больше не
актуальна, не дожидайтесь истечения checking_window_seconds —
остановите её сразу:
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips/203.0.113.10/cancel
Работает из любого состояния, кроме уже терминального (done/failed
вернут 409 — отменять нечего). Если на момент отмены был привязан
Floating IP — он отвязывается (best-effort, как и при обычном завершении
проверки), владевший валидатор освобождается и снова становится idle.
Итог записывается как OverallResult: "cancelled" (в State: "failed"),
и виден в истории адреса наравне с обычными результатами:
curl -s http://<control-api>:8080/api/v1/admin/ips/203.0.113.10 | python3 -m json.tool
Чтобы позже всё же проверить этот адрес — используйте «Повторную проверку адреса» выше, она одинаково работает и для отменённых, и для обычно завершённых адресов.
Удаление адресов из очереди
Отличие от отмены (cancel) выше: удаление безвозвратно. Cancel
переводит адрес в failed/cancelled и сохраняет запись как историю —
её видно в очереди и в деталях адреса. Delete физически стирает строку
ip_queue и всю её историю проверок и событий: адрес полностью исчезает,
восстановить его нельзя. Если нужно просто остановить зависшую проверку,
но сохранить её результат в истории — используйте
«Принудительную остановку проверки»
выше, а не удаление.
Удалить один адрес (работает из любого состояния, включая активно проверяемое — Floating IP при этом отвязывается, а владевший валидатор освобождается, точно так же, как при cancel):
curl -s -X DELETE http://<control-api>:8080/api/v1/admin/ips/203.0.113.10
Удалить список адресов одним вызовом (неизвестные адреса просто
попадают в not_found, не считаются ошибкой):
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips/delete \
-d '{"addresses": ["203.0.113.10", "203.0.113.11"]}'
Полностью очистить очередь — самая опасная операция, удаляет вообще всё, включая адреса, которые прямо сейчас проверяются:
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips/clear
В admin-dashboard то же самое доступно на странице /ips: чекбоксы у
каждой строки + кнопка «Удалить выбранные» для точечного/массового
удаления, кнопка «Удалить» в каждой строке, и отдельная кнопка «Очистить
всё» — каждая с подтверждением, явно предупреждающим о необратимости
(см. DASHBOARD.md).
Пауза перед self-check (fip_settle_seconds)
Как только Floating IP привязывается к валидатору, control-api по умолчанию сразу же позволяет агенту начать self-check — а data plane OpenStack может не успеть в этот же момент реально начать пропускать трафик через только что привязанный адрес, из-за чего self-check ложно проваливается по причине, не связанной с самой привязкой. Если это наблюдается на вашем стенде, задайте паузу между привязкой FIP и началом self-check:
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/orchestrator \
-d '{"fip_settle_seconds": 5}'
То же самое — на странице /settings дашборда. 0 (по умолчанию) — без
паузы. Пока пауза не истекла, адрес уже в состоянии
awaiting_self_check, но GET /assignment агенту продолжает отдавать
204 (агент просто ждёт следующего опроса, доработок на его стороне не
требуется); в дашборде на /ips такой адрес в это время помечен бейджем
«прогрев FIP» вместо обычного статуса.
Значение обязано оставлять запас внутри лизинга адреса:
fip_settle_seconds + self_check_timeout_seconds должно быть меньше
orchestrator.lease_ttl_seconds — иначе пауза плюс сам self-check не
влезут в лизинг, адрес не успеет пройти self-check до истечения
lease_ttl_seconds и будет вечно возвращаться в очередь через
sweepExpiredLeases. Попытка задать такое значение отклоняется 400, не
обрезается молча.
Частые проблемы и что с ними делать
Валидатор долго висит в unreachable.
Проверьте сетевую связность ВМ-валидатора до control-api (порт из
server.listen_addr) и что процесс validator-agent вообще запущен
(systemctl status validator-agent, journalctl -u validator-agent).
Адрес не выходит из awaiting_self_check (статус не меняется вообще).
Если это длится всего несколько секунд и на стенде настроена
пауза перед self-check
(fip_settle_seconds) — это ожидаемое поведение, не сбой: адрес
сознательно придерживается, прежде чем агенту разрешат начать проверку.
Проблема — если статус не меняется значительно дольше этой паузы. Тогда
дело обычно в self-check: он запрашивает внешние (вне облака) сервисы из
self_check.ip_echo_urls в конфиге валидатора (по умолчанию
api.ipify.org, ifconfig.me) — если у ВМ-валидатора нет исходящего
доступа в интернет к этим адресам, запрос не проходит вообще, и агент
даже не может сообщить результат control-api (ни успешный, ни
неуспешный) — тогда статус реально зависает до истечения
orchestrator.lease_ttl_seconds, после чего адрес возвращается в
queued и цикл повторяется. Проверьте связность до
self_check.ip_echo_urls прямо с ВМ-валидатора (curl -s https://api.ipify.org) и логи journalctl -u validator-agent на предмет
ip echo request failed.
Адрес постоянно проваливает self-check (не зависает, а именно
возвращается в очередь снова и снова).
Смотрите events по адресу (GET /api/v1/admin/ips/{ip}) — в детали
события self_check_result будет указан обнаруженный исходящий адрес.
Если он не совпадает с ожидаемым — вероятно, на ВМ-валидаторе есть другой
маршрут наружу (не через назначенный Floating IP), либо привязка FIP на
стороне OpenStack не применилась. Проверьте вручную в OpenStack
(openstack floating ip show <адрес>), что port_id совпадает с портом
валидатора. Обратите внимание: адрес для сравнения обязан быть вне
облака (см. self_check.ip_echo_urls) — запрос к чему-либо внутри
проекта (в том числе к самому control-api, если он в той же внутренней
сети) покажет приватный адрес валидатора независимо от того, правильно
ли привязан FIP, и всегда будет давать ложный провал.
Площадка (site-N) никогда не отчитывается по конкретному IP.
Сперва проверьте статус самой площадки — GET /api/v1/admin/config/sites, поле state. unregistered или
unreachable означает, что prober на этой площадке вообще не на связи
с control-api (см. «Состояния площадки» выше) —
проверьте, что процесс запущен и его site_id в конфиге совпадает с
site_id в конфиге control-api, и что площадка имеет сетевой доступ до
control-api. Если статус idle (площадка на связи), а конкретный IP
всё равно не получает отметку о завершении — проверьте отдельно
связность до проверяемого адреса (входящий трафик на 22/80/443/8080 +
ICMP — это отдельная связность от связи с control-api, см.
SETUP.md).
Много адресов зависло в checking дольше ожидаемого.
Это нормально, если ещё не истёк orchestrator.checking_window_seconds —
агрегация ждёт либо полного набора ответов, либо истечения окна. Если
адрес завис заметно дольше окна — проверьте, что фоновый цикл control-api
вообще работает (смотрите journalctl -u control-api на предмет ошибок
в sweep checking window).
Нужно посмотреть на данные "из первых рук", в обход API.
control-api использует SQLite, файл — по пути database.path из
конфига. Можно (только для чтения, на той же машине, где крутится
control-api) открыть его sqlite3 в режиме WAL — это безопасно для
чтения параллельно с работающим процессом:
sqlite3 /var/lib/cloud-ip-validator/control-api.db \
"select ip_address, state, overall_result from ip_queue order by sequence"
Не редактируйте эту базу вручную во время работы control-api — это может рассинхронизировать состояние с реальными привязками Floating IP в OpenStack.