惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
博客园 - 【当耐特】
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Recent Announcements
Recent Announcements
D
Docker
GbyAI
GbyAI
酷 壳 – CoolShell
酷 壳 – CoolShell
WordPress大学
WordPress大学
The Cloudflare Blog
雷峰网
雷峰网
A
About on SuperTechFans
小众软件
小众软件
博客园 - Franky
博客园 - 聂微东
F
Full Disclosure
大猫的无限游戏
大猫的无限游戏
C
Check Point Blog
MongoDB | Blog
MongoDB | Blog
G
Google Developers Blog
Microsoft Azure Blog
Microsoft Azure Blog
U
Unit 42
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
V2EX
Engineering at Meta
Engineering at Meta
宝玉的分享
宝玉的分享
aimingoo的专栏
aimingoo的专栏
量子位
P
Proofpoint News Feed
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
罗磊的独立博客
Martin Fowler
Martin Fowler
D
DataBreaches.Net
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
S
Secure Thoughts
Project Zero
Project Zero
L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tailwind CSS Blog
S
Schneier on Security
Blog — PlanetScale
Blog — PlanetScale
The Hacker News
The Hacker News
Spread Privacy
Spread Privacy
Security Latest
Security Latest
NISL@THU
NISL@THU
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
CXSECURITY Database RSS Feed - CXSecurity.com
J
Java Code Geeks

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Расследуем проблемы terraform-provider-ovirt
Михаил Фучко · 2026-06-16 · via Все публикации подряд на Хабре

Средний

9 мин

0

Привет, Хабр. Я Михаил Фучко, технический продакт-менеджер SDN и Terraform в команде zVirt. Я продолжаю серию статей о пути, который мы проделали в процессе разработки собственного провайдера инфраструктуры для Terraform. В предыдущей части мы честно попробовали воспользоваться опенсорс-провайдером Terraform для oVirt и получили неоднозначные результаты (на самом деле вполне однозначные).  

Эта статья посвящена анализу провала проекта terraform-provider-ovirt. Посмотрим на принятые решения, поищем первопричину, оценим, как надо и как не надо делать, и выработаем основные концептуальные решения перед реализацией своего провайдера. 

Эта статья может быть полезна всем, кому предстоит написание своего Terraform-провайдера. Работа с унаследованным API, попытки натянуть одну модель управления ресурсами на абсолютно другую и необходимость предусматривать гораздо больше, чем изначально вложено в систему — все это погубило terraform-provider-ovirt и всего этого следует опасаться любому разработчику подобного решения. 

В предыдущих статьях

В прошлой статье цикла тестирование открытого провайдера terraform-provider-ovirt привело нас к следующим выводам:

  • Оно формально работает, тестовая инфраструктура на нем вполне строится;

  • Очень слабо реализованы механизмы отслеживания состояний объектов;

  • Единая с т.з. пользователя сущность управляется несколькими Terraform-ресурсами;

  • Взаимодействие между этими ресурсами в рамках одного «смысла» тоже фактически сломано. 

Продублирую таблицу из прошлой статьи для демонстрации глубины проблем.

Сценарий

Ожидание

Реальность

Успех?

Имя ВМ изменено

Имя ВМ восстановлено

Имя ВМ восстановлено

Да

ВМ выключена

ВМ включена

ВМ включена

Да

ВМ выключена и запущена на другом кластере

ВМ выключена и запущена на оригинальном кластере

Ресурсы ВМ, подключения ВМ к диску и сети, а также ресурс запуска ВМ должны быть ЗАМЕНЕНЫ. Данные, кроме созданного диска отдельным ресурсом, утеряны

Нет

Увеличено количество ЦП до 4 (1:4:1). Перезагрузка для применения изменений. 

Выключить ВМ. Уменьшить количество ядер. Включить ВМ. 

Нет реакции TF. 

Нет

ВМ прямо прикреплена на запуск к конкретному хосту виртуализации. 

Объект ВМ отредактирован, привязка удалена

Ресурсы ВМ, подключения ВМ к диску и сети, а также ресурс запуска ВМ должны быть ЗАМЕНЕНЫ

Данные, кроме созданного диска отдельным ресурсом, утеряны

Нет

Добавлен еще один сетевой интерфейс

ВМ выключена, интерфейс удален, ВМ включена. 

Нет изменений. 

Нет

Ищем логику

Наигравшись со способами развалить инсталляцию наименее очевидным способом, я решил обратиться к пользовательскому опыту. Провайдер существует давно, им пользуются, схема типового применения выглядит примерно так:

  • Пользователи проходят путь, примерно схожий с описанным выше;

  • Четче формулируют ТЗ, определяют ключевые для бизнес-задачи ресурсы и поля; 

  • Силами разработчика реализуют точечные проверки необходимых полей; 

  • Игнорируют любые другие поля за пределами описанного процесса. 

Давайте поговорим о причинах, так сказать, родовых травмах, которые и сформировали облик terraform-provider-ovirt таким, каким мы его наблюдаем. Начать следует с несколько «аномального» подхода к дизайну ресурсов. ВМ — это один ресурс, диск — второй, а подключение их — третий. 

Сам по себе такой паттерн можно наблюдать, например, в Google Cloud Platform (есть отдельный ресурс для управления подключением), но в целом хорошим тоном является управление связями ВМ из ресурса самой ВМ. Подобные механизмы предоставляют провайдеры и VMware и GCP. Также показательно отсутствие механизмов работы с загрузочным диском — он наследуется из шаблона и существующими механизмами провайдера его не получить. Но почему были выбраны именно такие ресурсы, а не иные? Начнем издалека — из БД. Здесь и далее мы в качестве примера будем исследовать работу с дисками ВМ — разглядывать каплю, в которой отражается море. 

Не видим ничего необычного:

  • ВМ и диски существуют в своих таблицах;

  • Промежуточная таблица описывает связь между ВМ и дисками — какой диск к какой ВМ каким способом с какими опциями. 

Странно, что очень похожую логику мы и видим в итоговом продукте, как будто она в неизменном виде доходит до пользователя. Но ведь между нами и БД находится некий слой кода, обеспечивающий нам доступ, и именно с этим слоем работает разработчик провайдера. Давайте рассмотрим ситуацию с точки зрения REST API.

Вся наша активность будет сосредоточена в эндпоинте /ovirt-engine/api/vms/{vm_id}/diskattachments. Можно заметить иерархичность — объект, описывающий подключения дисков, является подсущностью конкретной ВМ. Это шаг в правильном направлении, мы действительно хотим дать пользователю привычный объект «ВМ со всем обвесом». Попробуем создать диск для конкретной ВМ(API позволяет совместить вызовы создания и прикрепления), возьмем пример из документации. (Подчеркнем особо важные моменты)

# curl \
--cacert '/etc/pki/ovirt-engine/ca.pem' \
--user 'admin@internal:mypassword' \
--request POST \
--header 'Version: 4' \
--header 'Content-Type: application/xml' \
--header 'Accept: application/xml' \
--data '
<disk_attachment>
  <bootable>false</bootable>
  <interface>virtio</interface>
  <active>true</active>
  <disk>
    <description>My disk</description>
    <format>cow</format>
    <name>mydisk</name>
    <provisioned_size>8589934592</provisioned_size>
    <storage_domains>
      <storage_domain>
        <name>mydata</name>
      </storage_domain>
    </storage_domains>
  </disk>
</disk_attachment>
' \
https://myengine.example.com/ovirt-engine/api/vms/007/diskattachments

Разумеется, это POST и он вызывается в контексте конкретной ВМ (007). Нам не приходится дополнительно указывать в теле запроса ID ВМ, он у нас просто есть по факту контекста. Отметим, что можно совместить запросы создания ВМ и прикрепления диска.  Тело запроса будет выглядеть примерно так:

<vm>
    <name>myvm</name>
    <template id="9e7eb3f3-288e-4099-86b9-eac5fe5e1f54"/>
    <cluster>
        <name>SDN</name>
    </cluster>
    <disk_attachments>
        <disk_attachment>
            <disk id="0ed99f7b-e5ed-4754-bff0-1c76d8592dfc">
                <format>cow</format>
                <sparse>false</sparse>
                <storage_domains>
                    <storage_domain id="8cab2b6e-8882-4788-8cd4-97c464f380d6"/>
                </storage_domains>
            </disk>
        </disk_attachment>
    </disk_attachments>
</vm>

Продолжим изыскание и попытаемся получить ВМ со списком дисков. Для этого нам предоставляется механизм follow, с помощью которого мы можем одним запросом получить ВМ и связанную сущность — подключения дисков. Пример из документации:

GET /ovirt-engine/api/vms/123?follow=disk_attachments
<vm id="123" href="/ovirt-engine/api/vms/123">
  ...
  <disk_attachments>
    <disk_attachment id="456" href="/ovirt-engine/api/vms/123/diskattachments/456">
      <active>true</active>
      <bootable>true</bootable>
      <interface>virtio_scsi</interface>
      <pass_discard>false</pass_discard>
      <read_only>false</read_only>
      <uses_scsi_reservation>false</uses_scsi_reservation>
      <disk id="789" href="/ovirt-engine/api/disks/789"/>
    </disk_attachment>
    ...
  </disk_attacments>
  ...
</vm>

Как мы видим, получить одним вызовом цельный объект без дополнительного указания ID у нас тоже получается. Очевидно, при удалении все связанные подсущности аттачей будут тоже удалены — проблемы особой нет. 

Остается вопрос с UPDATE. Следует заметить, что follow по зависимым сущностям не может быть применен в обновлении, он используется строго для операций чтения. Мы можем обновить основной объект ВМ, а затем обновить зависимые подсущности. 

Итого, вспомним логику Terraform в отношении некоей сущности и посмотрим на реализуемость задумки в контексте API oVirt:

  • CREATE — есть возможность создать виртуальную машину сразу с прикрепленным диском — API технически такое позволяет;

  • READ — есть возможность считать объект ВМ вместе с зависимой подсущностью связи с дисками;

  • UPDATE — нет возможности одним вызовом прислать обновленный облик объекта ВМ с подчиненной сущностью, follow такое не позволяет;

  • DELETE — есть возможность удалить сущность с автоматическим сносом всех подчиненных. 

Terraform самостоятельно реализует эту логику — вычисляет дельту и делает запрос с текстом изменений. Попытка наложить это напрямую на REST API не сработает, нет возможности обновить ресурс с зависимыми сущностями. В таком контексте выделение логики аттачей дисков в отдельный ресурс — это решение проблемы CRUD на ВМ, но его нельзя назвать хорошим по комплексу причин:

  • Это неудобно — аргумент слабый для разработчика, но очень сильный для заказчика. У всех в ВМ, а у вас отдельно — это придется объяснять;

  • Смена диска это не просто замена пары строк в БД. Это все же некие действия с процессом виртуальной машины, если она запущена. Не все интерфейсы дисков, которые поддерживает oVirt, допускают замену диска «на горячую»;

На самом деле второй пункт красной линией проходит через весь провайдер. Для выполнения некоторых операций необходимо изменять состояние ВМ, которая является отдельным ресурсом. Итоговый процесс выглядит так:

  1. В манифесте в ресурсе запуска ВМ объявить её остановленной;

  2. Выполнить terraform apply, зарегистрировать выключение ВМ;

  3. Отредактировать подключение диска. Указать ВМ как запущенную в ресурсе запуска ВМ;

  4. Выполнить terraform apply.

Таким, образом часть операций отслеживания состояний берет на себя оператор, что не очень приемлемо. 

Кратко рассмотрим другой интересный процесс — запуск ВМ. Напомню, в terraform-provider-ovirt он реализуется отдельным ресурсом, но в составе объекта ВМ есть поле state. Это прямое следствие REST-модели виртуальной машины — запуск выполняется с помощью метода /start. Не изменением поля state, а вызовом экшена. Для Terraform c его CRUD подходом «было-стало» это серьезный вызов. 

Пытаемся исправить

Первые тесты открытого провайдера в отношении zVirt не показали ничего хорошего. Мы поняли, что для достижения адекватного уровня сервиса подобную связку предстояло чинить. Наша команда занялась этим, и в ходе попыток довести концепцию открытого провайдера до работоспособного состояния мы реализовали самые разные инженерные решения. Обобщенно можно выделить следующие направления работ:

  • Реализация валидаций;

  • Реализация отсутствующей логики;

  • Реализация виртуальных полей; 

  • Изменение состояний ВМ «под задачу».

Валидации призваны решить проблему «ложно положительных» запусков оригинального провайдера. Недостаток предварительных проверок позволяет ему успешно пройти стадию Plan и затем аварийно завершиться уже в процессе, в худшем случае уже создав часть ресурсов и оставив систему в подвисшем состоянии. Не все из них удается выполнить на этапе планирования, не все из них легко вычислимы (хватит ли ресурсов на запуск ВМ в момент apply?), но львиную долю наиболее неприятных опечаток удается отсеять достаточно надежно. Были реализованы проверки на корректность показателей ресурсов, валидность переданных UUID и ссылок и прочее, что позволило «упасть» до начала работы, не оставив после себя противоречивую инфраструктуру. Тем не менее, многие проверки может спокойно выполнить только REST API, и этап начальных валидаций будет пройден с последующей ошибкой от API. Здесь мы добавляем корректную обработку такой ситуации и корректную остановку процесса. 

Реализовать отсутствующую логику — значит прямолинейно и последовательно дорабатывать отдельные поля ресурсов. Заказчик хочет корректное управление оперативной памятью — поля добавляют, для них прописывается логика. В пределах отдельного ресурса задача достаточно тривиальная с поправкой на редкие особенности oVirt REST API. 

Реализация виртуальных полей — это уже шаг в сторону от принятых оригинальными разработчиками архитектурных решений. Заказчик хочет управлять состоянием машины с помощью поля state — указывать stopped, started, rebooted, run_on_create (!) и получать результат без отдельных ресурсов. Здесь мы пришли к реализации виртуального поля, определяемого пользователем и хранимого в контексте Terraform.

В момент исполнения плана происходит обращение к API, интерпретация состояния ВМ и сверка с указанным в плане, в зависимости от результата сравнения вызываются методы запуска\остановки\перезагрузки ВМ. Интересный запрос на тему run_on_create - заказчик хочет только начально запускать ВМ, а если она была внешними средствами остановлена - не запускать при обновлении манифеста. 

А вот попытка реализовать сквозное изменение состояния ВМ для обеспечения работы прочих ресурсов уже стала переломным моментом. Рассмотрим на примере ресурса подключения дисков. Он на вход принимает ID ВМ, ID диска и выполняет соответствующую операцию. Не все типы подключений поддерживают смену «на горячую», например, традиционные, не виртуализированные реализации SATA.

Логичная попытаться выйти из положения так:

  • Обратиться в API и узнать статус данной ВМ. Если не запущена, выполнять аттач;

  • Если запущена, вызвать метод остановки. Дождаться смены статуса;

  • Выполнить аттач;

  • Выполнить запуск ВМ, если она была остановлена, вернуть предыдущий статус. 

Это сработает, диски будут меняться. А теперь попытаемся переподключить два диска, или диск и сетевой интерфейс. Что будет? Правильно — две перезагрузки. И три, и четыре, в зависимости от количества «желающих». Почему? Потому что обработка ресурсов аттача выполняется в собственном контексте, Диск А не знает, что Диск Б тоже планирует перезагружать ВМ. Поэтому каждый из них пройдет по своему алгоритму, и хорошо, если они не начнут устраивать гонку (а они с высокой вероятностью начнут). 

Мы потратили некоторое количество времени на разработку механизмов синхронизации действий и все же сдались. Это не будет работать, по крайней мере не будет работать надежно. 

Итог

Корень проблем открытого провайдера лежит в плохой совместимости REST API oVirt с концепциями Terraform. Можно выделить следующие пункты:

  • Применение action вместо редактирования полей;

  • Разделение объекта на подсущности. 

Применение экшонов не позволяет применять штатные механизмы Terraform по вычислению дельты и применению изменения, заставляя дописывать дополнительную логику и имплементировать виртуальное поле. Разбиение объекта на подсущности, этакая «нормализация, как в БД», выглядит для пользователя контринтуитивно — но это архитектурное решение, которое было принято много лет назад, с этим придется жить. Есть подозрение, что крупные вендоры принимали во внимание существования Terraform и его особенностей при плановом обновлении API во второй половине десятых годов. Имеет смысл, Terraform — это потребитель API №1. Стоит учитывать при проектировании API в своих продуктах. 

Отмечу, что проблему действий на запуск\остановку ВМ удалось решить благодаря виртуальному полю с некоторой логикой «под капотом». Именно этот принцип и был взят за основу при проектировании нашего собственного провайдера — создание виртуальных объектов, которые будут полностью подчиняться логике Terraform и следовать всем рекомендациям HashiCorp. Нам же предстоит реализовать дополнительный слой трансляции виртуального комплексного объекта в примитивы REST API oVirt. Об этом я расскажу в следующей статье.