Проверка аргументов (переменных) для роли Ansible

При написании новой роли задумался о теме данной статьи — как проверять набор минимально необходимых параметров (аргументов, переменных, всё в Ansible сводится к переменным) для выполнения роли и как обычно пошел изучать документацию.

С приятным удивлением обнаружил механизм, который в Ansible появился с релизом ansible-core 2.11 (апрель 2021) и называется он Role argument validation. Исходя из собственного опыта написания модулей Ansible, я предполагаю, что в данном механизме используется тот же самый код, что и при валидации аргументов модуля (по крайней мере, всё на это указывает).

Более того, этот механизм позволяет, помимо валидации аргументов, документировать вашу роль. После добавления файла, описывающего Ваш набор переменных, Вы получаете документированную роль, документацию по которой можно вызвать при помощи ansible-doc.

ansible-doc --type role --roles-path <you roles path> <role_name>

Пример вызова такой роли:

Реализация

Для создания подобной функциональности используется файл meta/argument_specs.yml. Его формат описан в документации и довольно прост. Более того, у Вашей роли может быть не одна точка входа (по умолчанию это main.yml) но и дополнительные, возможно для вызова через ansible.builtin.import_role или ansible.builtin.include_role, набор параметров для каждой точки входа можно описать в этом файле. Также параметры можно пометить обязательными, указать тип данных (набор типов данных, которые умеет проверять AnsibleModule тут), который должен содержаться в параметре, его значение по умолчанию (если оно находится в ../defaults/<entry-point>.yml, например) ну и, конечно же, описание (short_description и description для точки входа и description для каждого параметра).

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

При вызове роли через ansible.builtin.import_role или ansible.builtin.include_role есть возможность отключить проверку аргументов, используя параметр rolespec_validate установленный в false.

Интересный cmdlet из модуля Microsoft.PowerShell.TextUtility

Модуль Microsoft.PowerShell.TextUtility получил новый командлет ConvertFrom-TextTable. Он умеет превращать текстовую таблицу (типичный вывод большинства shell-команд Linux) в массив объектов. И даже больше — он может распарсить значения в столбцах таблицы и сконвертировать их в соответсвующие типы (например Int или Float, Boolean). Ну и наконец вместо объекта массива, командлет может вернуть массив строк в формате JSON.

Установить модуль можно из PowershellGallery:

Install-Module -Name Microsoft.PowerShell.TextUtility -AllowPrerelease

Пример использования:

PS> df | select -first 6 | convertfrom-texttable -ConvertPropertyValue | Format-Table

Filesystem 1K-blocks      Used Available Use% Mounted_on
---------- ---------      ---- --------- ---- ----------
none         8167564         4   8167560 1%   /mnt/wsl
none       498723140 443586308  55136832 89%  /usr/lib/wsl/drivers
none         8167564         0   8167564 0%   /usr/lib/wsl/lib
/dev/sdc   263112772  27894640 221779976 12%  /
rootfs       8164316      1936   8162380 1%   /init

Командлет позовляет указать где определяется строка заголовков или если ее нет просто брать табличные данные и превращать их в массив объектов.

PS> ls -l | select -First 5 | ConvertFrom-TextTable -Skip 1 -NoHeader | ft

Property_01 Property_02 Property_03 Property_04 Property_05 Property_06 Property_07 Property_08 Property_09
----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
drwxr-xr-x  3           ihumster    ihumster    4096        Aug         21          2022        aac-base
drwxr-xr-x  3           ihumster    ihumster    4096        Jul         20          23:48       ansible_collections
drwxr-xr-x  7           ihumster    ihumster    4096        Apr         30          15:43       ansible-module-vcloud-director
drwxr-xr-x  3           ihumster    docker      4096        Jun         14          2022        ansible-netbox-dev

Модуль еще находится в процессе разработки и если он вам понравился и пригодился, но вы нашли какую-либо ошибку, то оставить баг-репорт можно прямо в разделе Issues репозитория.

Нюансы обновления до VCD 10.4.1

После обновления до VCD 10.4.1 одна из Advisores отсылает нас к KB78885 (но да кто их читает, да?), которая опубликована в далеком уже 2021 году и которая напоминает нам о том, что после обновления VCD у нас может пропасть доверненное подключение к различным элементам нашей инфраструктуры. Ужесточение требований к сертификатам в инфраструктуре началось в VCD еще с версии 10.1 (о чём так же отдельно пишется во врезке, на странице документации посвященной обновлению VCD).

В 10.4.1 ужесточили еще один аспект инфраструктуры, а именно подключение к ESXi хостам. В результате, после обновления до 10.4.1 (и автоматическом переходе на новую консоль, старую выпилили таки, и слава богу), в VCD перестанут работать операции требующие прямого доступа к хосту, а именно доступ к консоли ВМ, ипорту OVF/ISO, Guest Customization.

В логах это может отобразиться в виде следующих сообщений:

[ 88fc5c17-f0c7-4021-922b-5dfe1b84b7cd ] PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 - PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 - PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 - unable to find valid certification path to requested target
 2023-02-24 02:59:01,050 | ERROR    | pool-jetty-81             | ServerWebSocket                | Connecting to ESX esx01.lab.ihumster.ru [server: [L=/192.168.168.25:443 R=/192.168.168.30:60004]] [client: [id: 0x13240ef6, L:/192.168.168.25:52056 ! R:esx01.lab.ihumster.ru/192.168.168.101:443]] |
io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Решение

Самый простой вариант — это запустить повторно CMT trust-infra-certs:

./cell-management-tool trust-infra-certs --vsphere --unattended
Downloading certificates for 2 host(s), including 1 vCenter(s):
        vc-mng.lab.ihumster.ru                                      [Download: SUCCESS] (VMCA)
        vc-mng.lab.ihumster.ru                                      [Download: SUCCESS] (VMCA)
        vc-mng.lab.ihumster.ru                                      [Download: SUCCESS]
        nsx.lab.ihumster.ru                                         [Download: SUCCESS]
Downloaded certificates for 3/2 host(s).
Trusting certificates for 2 host(s):
        vc-mng.lab.ihumster.ru                                       [Already Trusted: SUCCESS]
        nsx.lab.ihumster.ru                                          [Already Trusted: SUCCESS]
Trusting VMCA certificates for 1 vCenter(s):
        vc-mng.lab.ihumster.ru                                       [Add Trusted: SUCCESS] (VMCA) - alias=vc-mng.lab.ihumster.ru_vmca_2023-02-24-11-05-13
Trusted 3/3 downloaded certificates.

Или самостоятельно скачать с vCenters их сертификаты VMCA (<vcenter>/certs/download.zip) и вручную добавить VMCA сертификаты в Trusted Certificates вашего VCD.

Для VCD с большим количеством vCenters (или организаций с высокими требованиями к безопасности инфраструктурных компонентов) самым правильным путём, по моему мнению, будет вариант с заменой сертификата VMCA на vCenters на подписанный с внутреннего или внешнего CA и последующей регенерацией сертификатов ESXi.

VMware Cloud Director Availability Initial Setup API

Встала задача по автоматизации развертывания VCDA для инсталляций VCD где VCDA еще не развернут. Начав изучение вопроса и прочтя внимательно документацию по VCDA с удивлением понял что для развертывания самих аплайнсов есть способ по автоматизации процесса, т.к. аплайнсы поставляются в формате OVF с обычными, для аплайнсов VMware, возможностями деплоя их как с помощью UI, так и с помощью OVFTool ну и прочими средствами (например, community.vmware.vmware_deploy_ovf из коллекции ansible). Но для дальнейшей первоначальной настройки нет иного официального пути, кроме как пойти в UI и потыкать кнопки, заполняя поля в Initial Setup Wizard.

Вооружившись DevTools я за несколько итераций записал все необходимые API вызовы, которые происходят при выполнении этапов Initial Setup Wizard и после, проверив возможность их воспроизведения сторонними средствами (воспроизводил в Postman) решил что хочу записать их в виде документации OpenAPI 3.0 что бы любой мог импортировать ее в Postman и ознакомиться с деталями запросов.

По этому спешу представить Вам первую версию документации к VCDA Initial Setup API. В репозиторий добавлена также готовая коллекция для Postman (на случай если вы не умеете генерировать коллекцию из OpenAPI спецификации).

В коллекции все запросы пронумерованы в порядке выполнения их в Wizard. Отдельно не пронумерован один Get запрос — он используется для проверки доступности той или иной системы (VCD, vCenter server, ReplicatorAppliance, Tunnel Appliance) из VCDA Manager, а также возвращает SSL-сертификат используемый системой (сам сертефикат в PEM-encoded формате, его thumbprint — используются в остальных запросах).

Конфигурация VCDA которую я изучал — это рекомендуемая в продуктивной среде конфигурация из отдельных «ролевых» аплайнсов — manager, replicator, tunnel. Для combined и vc_combined версии, последовательность и структура запросов скорее всего иная (в vc_combined например не используется VCD).

Также, если вы используете несколько replicator аплайнсов то соответствующие запросы необходимо будет проделать для каждого аплайнса последовательно.

Для работы с этим API вам необходимы минимум 3 задеплоенных удобным вам способом аплайнса.

Комфортная работа в Postman с VMware Cloud Director (2)

В процессе работы с OpenAPI разных версий VMware Cloud Director понял что меня раздражает постоянно следить за версией API, которая указывается в Header ‘Accept’. Чтобы автоматизировать актуализацию версии API в моих запросах я модифицировал свой pre-request Script из предыдущей статьи.

Добавьте следующую строку между запросами версии и получением access_token, либо после них:

pm.request.headers.get('Accept').replace('application\/json;version=(?<version>\d{2}\.\d{1})','application/json;version={{apiVersion}}')

Regexp для парсинга URL по RFC

Не претендуя на истину в последней инстанции, однако, хотелось закрыть для себя этот вопрос на всегда. Частенько в скриптах posh\python и т.п. приходится на входе принимать в качестве параметра строку с URL и быть уверенным что это:

  1. точно URL
  2. иметь возможность легко получить его составляющие (схему\fqdn\query\etc)

Подсматривая в интернет, получилось написать вот такую «красивую» регулярку, которая входящую строку бъет на следующие группы:

  1. url (собственно если строка соответствует паттерну url — в этой группе будет отображаться полный url
  2. scheme — схема URL http:// | ftp:// | anycustomscheme://
  3. hostname — FQDN
  4. port
  5. path
  6. query

Как работает этот regexp вы можете увидеть ниже

(?<url>(?:(?<scheme>[a-zA-Z]+:\/\/)?(?<hostname>(?:[-a-zA-Z0-9@%_\+~#=]{1,256}\.){1,256}(?:[-a-zA-Z0-9@%_\+~#=]{1,256})))(?::(?<port>[[:digit:]]+))?(?<path>(?:\/[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~%]*)*)(?<query>(?:(?:\#|\?)[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~]*)*))

На Python именованные группы нужно обозначать через символ P, поэтому для Python regex будет выглядеть чуть иначе:

(?P<url>(?:(?P<scheme>[a-zA-Z]+:\/\/)?(?P<hostname>(?:[-a-zA-Z0-9@%_\+~#=]{1,256}\.){1,256}(?:[-a-zA-Z0-9@%_\+~#=]{1,256})))(?::(?P<port>[[:digit:]]+))?(?P<path>(?:\/[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~%]*)*)(?P<query>(?:(?:\#|\?)[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~]*)*))

Ну и пример кода на Powershell:

$URL = "https://vcloud.lab.ihumster.ru/api/query?type=adminOrgVdcStorageProfile"
$URL -match "(?<url>(?:(?<scheme>[a-zA-Z]+:\/\/)?(?<hostname>(?:[-a-zA-Z0-9@%_\+~#=]{1,256}\.){1,256}(?:[-a-zA-Z0-9@%_\+~#=]{1,256})))(?::(?<port>[[:digit:]]+))?(?<path>(?:\/[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~%]*)*)(?<query>(?:(?:\#|\?)[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~]*)*))"
$Matches

True

Name                           Value
----                           -----
path                           /api/query
hostname                       vcloud.lab.ihumster.ru
url                            https://vcloud.lab.ihumster.ru/api/query?type=adminOrgVdcStorageProfile
scheme                         https://
query                          ?type=adminOrgVdcStorageProfile
0                              https://vcloud.lab.ihumster.ru/api/query?type=adminOrgVdcStorageProfile

Операнд -math возвращает boolean результат соответствия строки в $URL нашему регулярному выражению и если он True, то автопеременная $Matches будет содержать key-value словарь в котором ключами будут имена групп с соответствующими values.

Спасибо что дочитали до конца, надеюсь это будет вам полезно.

Комфортная работа в Postman с VMware Cloud Director

В последний раз что-то полезное на эту тему писал Tomas Fojta в, далеком уже, 2018 году. С тех пор в VCD появились новые интересные методы API и в частности в 10.3.1 появились Cloud Director API Token.

По этому я решил слегка модернизировать подход к работе с VCD API в Postman с использованием токенов.

Если вам интересно — добро пожаловать под cut.

Читать далее

Ansible Cheat Sheet или как не стрелять себе в ногу при написании ролей и плейбуков

Так как я в настоящее время плотно погружаюсь в «чудесный» мир Ansible, то много читаю (и официальную документацию, да) статей по теме.

Недавно наткнулся на хороший цикл из трех статей от @amarao на Habr:

Они безусловно стоят того чтобы их прочесть внимательно (а также прочесть комментарии — там тоже изложены ценные мысли). Я выписал себе отдельно из них несколько тезисов которые планирую держать перед глазами при написании своих плейбуков и ролей.

Плейбуки и роли

  • Не используй tasks и roles одновременно в play. Если play с ролями — для тасков лучше использовать pre_tasks или post_tasks.
  • Роль — не функция! В нее нельзя передать параметры (не путать с переменными) и получить на выходе результат.
  • Если у переменных роли должны быть дефолтные значениея — определяй их в defaults роли
  • Избегай when — ansible task должен быть декларативным (и следовательно не опираться на предыдущие register)
  • Если можно что-то красиво (без выкрутас) написать без хэндлеров лучше делать без них. Если красиво не получается — лучше с ними.
  • Чем меньше if‘ов (явных или декларативных — в форме when или форме include_vars по набору переменных), тем лучше роль.
  • Хотите строгой логики (программирования) — выносите ее в плагины, модули, библиотеки

Инвентори

  • При работе с SSH, всегда (кроме спецслучаев) использовать ansible_host внутри которого IP-адрес. (Самый спорный тезис, имхо)
  • Перменные в инвентори. Основной вопрос, который надо себе задавать: «почему эта переменная должна быть в инвентори»? Другими словами, инвентори — это специальное место для перменных, и вам нужны специальные причины записывать их туда.

Переменные

  • Переменные разименовываются в момент интерполяции их в Jinja
  • Не интерполируются ключи словарей на любом уровне вложенности yaml. Нельзя использовать шаблоны в именах модулей, ключах словарей в переменных, именах переменных (которые по сути те же ключи).
  • Никогда не создавай перекрытие по именам между групповыми переменными (инвентори и плейбук), никогда не создавай перекрытия между hostvars , между инвентори и плейбукой.

Ну и, на последок, из того же источкника

Разумная таблица приоритетов переменных

  • role/defaults — всегда проигрывает. Идеальное место для размещения переменных, которые надо переопределять. Дефолты ролей видны в соответствующих pre/post tasks.
  • group_vars/all — переменные группы all наименее приоритетные
  • group_vars/other_groups
  • host_vars
  • результат выполнения gather_facts: true (host_facts)
  • Переменные уровня play
  • Переменные уровня block
  • Переменные уровня task
  • set_fact/register. set_fact ещё и живёт дольше, чем плей (т.е. может передавать данные между разными play).
  • -e побеждает всех и всегда.

Ну и хорошее напутствие — «Пишите просто, пишите красиво. Сложно и некрасиво не пишите» 🙂

[Расследование] VCD Error: No row with the given identifier exists

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

[ 2c728624-3b93-463e-8f99-cf5386f089bf ] Internal Server Error - No row with the given identifier exists: [com.vmware.vcloud.common.model.net.LogicalNetworkModel#40bbadee-76dc-4760-bac8-0293a79a595f]

Никаких внятных сообщений в логах VCD (vcloud-container-debug.log) я не обнаружил. Там конечно присутвовали простыни Java логов об ошибке но понять причину было нельзя, отсылка была лишь к NetworkServiceImpl, а значит искать надо было что-то связанное с сетями.

Читать далее

[VCD V2T] Проверка перед миграцией

Решил что стоит поделиться своими проверками Org vDC перед миграцией V2T. Эти проверки позволят избежать части падений процесса миграции, о которых этап —preCheck не уведомляет.

[ x ] Проверить использование Non Latin символов в дескрипшенах Org, Org vDC
[ x ] Проверить сети на Shared — отключить если сеть не шарится между vDC (позволит не создавать Data Сenter Group там где это не нужно)
[ x ] Проверить правила SNAT в EGS — если в source содержится адрес, а не сеть, валидация пройдет успешно, а этап переноса правила упадет
[ x ] Проверить наличие пустого IP Set в NSX-v EGS (раздел Grouping Objects) — его наличие сломает этап переноса правил Gateway
[ x ] Проверить настройки DHCP — с ними много странного, быть готовым что с DHCP что-то пойдет не так. Если можно его отключить — отключите
[ x ] Проверить Storage Policy у каждой ВМ — если есть не VM default Storage Policy на дисках — перенос vApp в котором есть такая ВМ не пройдет (vcdNSXMigrator 1.3.1 не умеет детектить кастомные SP и как следствие не добавляет их в api запрос на moveVApp)