<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>Александр Никитин</title><author><name>Александр Никитин</name></author><id>https://teletype.in/atom/ihumster</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/ihumster?offset=0"></link><link rel="alternate" type="text/html" href="https://blog.ihumster.ru/?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=ihumster"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/ihumster?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-04-23T13:02:36.345Z</updated><entry><id>ihumster:nK_4wEHidW8</id><link rel="alternate" type="text/html" href="https://blog.ihumster.ru/nK_4wEHidW8?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=ihumster"></link><title>Не доверяй ИИ</title><published>2025-02-25T18:50:00.354Z</published><updated>2025-02-25T18:50:00.354Z</updated><category term="mysli-vsluh" label="Мысли вслух"></category><summary type="html">Блин, товарищи коллеги. Я с чувством невероятного удовлетворения в очередной раз убеждаюсь что никакой искусственный интеллект нас не заменит. И он херовый помощник тому кто не понимает что он хочет получить в результате и как этот результат должен воспроизводиться.</summary><content type="html">
  &lt;p id=&quot;S12z&quot;&gt;Блин, товарищи коллеги. Я с чувством невероятного удовлетворения в очередной раз убеждаюсь что никакой искусственный интеллект нас не заменит. И он херовый помощник тому кто не понимает что он хочет получить в результате и как этот результат должен воспроизводиться.&lt;/p&gt;
  &lt;p id=&quot;LksA&quot;&gt;Це была фабула. А дальше мой короткий рассказ. 😅&lt;/p&gt;
  &lt;p id=&quot;qkdT&quot;&gt;Понадобилось мне стало быть в логи nginx пихнуть кусок (именно кусок, а не всю строку ибо там чувствительные данные) строки из заголовка http запроса. Пошёл читать документацию. Понял что необходимое мне можно извлечь из одной переменной в другую через директиву map. И вот я два часа &amp;quot;беседовал&amp;quot; с perplexity/openai-o3 которые отчаянно меня убеждали что результат регулярки (match) или именованную группу нельзя использовать как результат map. И действительно приведенный пример конфига nginx не принимал за валидный конфиг. После чтения документации и пары статей на хабре, в комментариях я таки нашел пример, который натолкнул меня на решение моей задачи, которая в итоге решилась и оказалась простой и элегантной (ввиду простенькой регулярочки которую я уже накидал сам).&lt;/p&gt;
  &lt;p id=&quot;FuHX&quot;&gt;В качестве эпилога хочу вам пожелать не верить всяким ИИ, даже если они утверждают что что-то не будет работать. 😁 доверяйте своему инженерному чутью!&lt;/p&gt;
  &lt;p id=&quot;1mDv&quot;&gt;У меня всё.&lt;/p&gt;

</content></entry><entry><id>ihumster:netbox-change-current-config-nbshell</id><link rel="alternate" type="text/html" href="https://blog.ihumster.ru/netbox-change-current-config-nbshell?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=ihumster"></link><title>Управление CurrentConfig через nbshell</title><published>2025-01-13T16:37:14.955Z</published><updated>2025-01-16T14:35:56.904Z</updated><category term="netbox" label="netbox"></category><summary type="html">Опытные пользователи Netbox наверняка сталкивались с таким мощным инструментом как Netbox Shell (построенный на базе Django Shell и представляющий собой интерпретатор iPython с расширенной функциональностью).</summary><content type="html">
  &lt;p id=&quot;LvKh&quot;&gt;Опытные пользователи Netbox наверняка сталкивались с таким мощным инструментом как Netbox Shell (построенный на базе Django Shell и представляющий собой интерпретатор iPython с расширенной функциональностью).&lt;/p&gt;
  &lt;h2 id=&quot;0v70&quot;&gt;Управление в интерактивном режиме&lt;/h2&gt;
  &lt;p id=&quot;xLrY&quot;&gt;Чтобы запустить nbshell необходимо открыть в командной строке папку с дистрибутивом приложения и, активировав virtual environment, выполнить команду &lt;em&gt;python netbox/manage.py nbshell&lt;/em&gt;&lt;/p&gt;
  &lt;p id=&quot;NZaw&quot;&gt;Что бы получить текущую ревизию конфигурации приложения (которая и содержит динамически конфигурируемые параметры) нам понадобится ID текущей (активной) конфигурации, ее можно получить из кэша приложения:&lt;/p&gt;
  &lt;pre id=&quot;EDN4&quot; data-lang=&quot;python&quot;&gt;from django.core.cache import cache

activeRevision = ConfigRevision.objects.get(pk=cache.get(&amp;#x27;config_version&amp;#x27;))&lt;/pre&gt;
  &lt;p id=&quot;30h2&quot;&gt;Объект activeRevision будет содержать объект типа ConfigRevision, являющийся активной конфигурацией в данный момент (&amp;quot;Curren configuration&amp;quot;). Объект простой, в поле data содержит словарь с динамически конфигурируемыми параметрами (список всех параметров и их значение можно подсмотреть в &lt;a href=&quot;https://netboxlabs.com/docs/netbox/en/stable/configuration/&quot; target=&quot;_blank&quot;&gt;официальной документации&lt;/a&gt;). Однако просто изменить текущую конфигурацию нельзя, конфигурация будет применена только к новой ревизии конфигурации, а значит нам нужно ее создать.&lt;/p&gt;
  &lt;p id=&quot;hCHh&quot;&gt;Однако создавать мы ее будем не с нуля, а на базе предыдущей активной конфигурации.&lt;/p&gt;
  &lt;p id=&quot;eXTB&quot;&gt;Для примера мы включим параметр MAINTENANCE_MODE, переведя таким образом приложение в режим обслуживания.&lt;/p&gt;
  &lt;pre id=&quot;iPQn&quot; data-lang=&quot;python&quot;&gt;# Хорошим тоном будет записать смысл новой ревизии конфига в комментарий
# Заполняем все параметры из текущей конфигурации
newRevsion = ConfigRevision(comment=&amp;#x27;Enable MM&amp;#x27;, data=activeRevision.data)
# Изменяем нужный нам параметр
newRevsion.data[&amp;#x27;MAINTENANCE_MODE&amp;#x27;] = True
# Валидируем нашу модель конфигурации на соответствие модели данных,
# заложенных разработчиком
newRevsion.full_clean()
# Сохраняем новую конфигурацию (она автоматически станет активной)
newRevsion.save()&lt;/pre&gt;
  &lt;h2 id=&quot;1PWO&quot;&gt;Управление в режиме скрипта&lt;/h2&gt;
  &lt;p id=&quot;zv8h&quot;&gt;Данный код можно склеить в одну строку, разделив строки точкой с запятой &lt;strong&gt;; &lt;/strong&gt;и передать одним аргументом для nbshell.&lt;/p&gt;
  &lt;pre id=&quot;2ju7&quot; data-lang=&quot;shell&quot;&gt;python netbox/manage.py nbshell -c &amp;quot;from django.core.cache import cache; activeRevision = ConfigRevision.objects.get(pk=cache.get(&amp;#x27;config_version&amp;#x27;)); newRevsion = ConfigRevision(comment=&amp;#x27;Enable MM&amp;#x27;, data=activeRevision.data); newRevsion.data[&amp;#x27;MAINTENANCE_MODE&amp;#x27;] = True; newRevsion.full_clean(); newRevsion.save();&amp;quot;&lt;/pre&gt;
  &lt;h2 id=&quot;gQqO&quot;&gt;Для мультинодовых конфигураций&lt;/h2&gt;
  &lt;section style=&quot;background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;cDdW&quot;&gt;не забывайте что проделывать манипуляции с конфигурациями придется для каждой ноды отдельно. &lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;pqJ0&quot;&gt;UPD: Работа с существующей конфигурацией&lt;/h2&gt;
  &lt;p id=&quot;AxhH&quot;&gt;Если же хочется изменить какую-то текущую конфигурацию или просто сделать ее активной (например, откатиться на предыдущую), то сделать это можно через встроенный метод класса ConfigRevision - activate()&lt;/p&gt;
  &lt;pre id=&quot;9Fkq&quot; data-lang=&quot;python&quot;&gt;revision = ConfigRevision.object.get(pk=revisionNumber)
revision.activate()&lt;/pre&gt;

</content></entry><entry><id>ihumster:netbox-keycloak-custom-pipeline</id><link rel="alternate" type="text/html" href="https://blog.ihumster.ru/netbox-keycloak-custom-pipeline?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=ihumster"></link><title>Авторизация в Netbox через Keycloak (кастомный pipeline)</title><published>2025-01-04T20:53:43.008Z</published><updated>2026-04-08T13:27:15.924Z</updated><category term="netbox" label="netbox"></category><summary type="html">В сети есть хороший мануал по настройке авторизации в Netbox через Keycloak, однако он хорошо подходит для новых инсталляций, когда у вас чистая БД, ну или размер таблицы с пользователями и группами не очень большой и вам не составит труда все переделать.</summary><content type="html">
  &lt;p id=&quot;tlDW&quot;&gt;В сети есть хороший мануал по настройке &lt;a href=&quot;https://itdraft.ru/2023/05/17/sso-avtorizacija-v-netbox-cherez-keycloak/&quot; target=&quot;_blank&quot;&gt;авторизации в Netbox через Keycloak&lt;/a&gt;, однако он хорошо подходит для новых инсталляций, когда у вас чистая БД, ну или размер таблицы с пользователями и группами не очень большой и вам не составит труда все переделать.&lt;/p&gt;
  &lt;p id=&quot;1JPj&quot;&gt;Сложность, с которой я столкнулся, заключалась в том, что используемая нами инсталляция Netbox имеет множество активных пользователей импортированных из Active Directory, активную ролевую модель основанную на группах из Active Directory и потерять все эти ассоциации очень не хотелось бы.&lt;/p&gt;
  &lt;p id=&quot;s8FO&quot;&gt;Изучая документацию на модуль &lt;a href=&quot;https://python-social-auth.readthedocs.io/en/latest/&quot; target=&quot;_blank&quot;&gt;python-social-auth&lt;/a&gt; (а также код самого модуля и его бэкэнд к Keycloak) я наткнулся на раздел &lt;a href=&quot;https://python-social-auth.readthedocs.io/en/latest/pipeline.html#extending-the-pipeline&quot; target=&quot;_blank&quot;&gt;Extending the Piplene&lt;/a&gt; и понял что весь конвеер авторизации легко настраивается и изменяется, однако придётся немного по-питонить.&lt;/p&gt;
  &lt;h2 id=&quot;1xG1&quot;&gt;Проблемы с которыми я столкнулся&lt;/h2&gt;
  &lt;p id=&quot;1qCy&quot;&gt;Собственно их было две:&lt;/p&gt;
  &lt;ol id=&quot;pGKM&quot;&gt;
    &lt;li id=&quot;0xr1&quot;&gt;Дефолтный Pipeline падает при наличии в Netbox пользователей с одинаковыми email (это специфический случай, который характерен для нашей организации и с которым вы можете никогда не столкнуться. Для снятия лишних вопросов скажу что, у нас есть правило записывать в поле email соответствующий email владельца технической учётной записи)&lt;/li&gt;
    &lt;li id=&quot;ybtp&quot;&gt;Дефолтный Pipeline не обновляет членство в группах (а вот это уже критично для ролевой модели доступа основанной на группах).&lt;/li&gt;
  &lt;/ol&gt;
  &lt;h2 id=&quot;Jxpc&quot;&gt;Дефолтный Pipeline&lt;/h2&gt;
  &lt;p id=&quot;1T0x&quot;&gt;Дефолтный pipeline для Netbox определяется в модуле &amp;lt;your-netbox-folder&amp;gt;/netbox/netbox/settings.py и выглядит так:&lt;/p&gt;
  &lt;pre id=&quot;brZJ&quot; data-lang=&quot;python&quot;&gt;SOCIAL_AUTH_PIPELINE = (
    &amp;#x27;social_core.pipeline.social_auth.social_details&amp;#x27;,
    &amp;#x27;social_core.pipeline.social_auth.social_uid&amp;#x27;,
    &amp;#x27;social_core.pipeline.social_auth.social_user&amp;#x27;,
    &amp;#x27;social_core.pipeline.user.get_username&amp;#x27;,
    &amp;#x27;social_core.pipeline.social_auth.associate_by_email&amp;#x27;,
    &amp;#x27;social_core.pipeline.user.create_user&amp;#x27;,
    &amp;#x27;social_core.pipeline.social_auth.associate_user&amp;#x27;,
    &amp;#x27;netbox.authentication.user_default_groups_handler&amp;#x27;,
    &amp;#x27;social_core.pipeline.social_auth.load_extra_data&amp;#x27;,
    &amp;#x27;social_core.pipeline.user.user_details&amp;#x27;,
)&lt;/pre&gt;
  &lt;p id=&quot;02l5&quot;&gt;Он содержит функции из дефолтного, для модуля python-social-auth, конвеера, за исключением функции &lt;strong&gt;&lt;em&gt;user_default_groups_handler&lt;/em&gt;&lt;/strong&gt; из модуля &amp;lt;your-netbox-folder&amp;gt;/netbox/netbox/authentication.py (строка &amp;#x27;netbox.authentication.user_default_groups_handler&amp;#x27;).&lt;/p&gt;
  &lt;h2 id=&quot;pVXd&quot;&gt;Решение проблемы №1&lt;/h2&gt;
  &lt;p id=&quot;q9Vv&quot;&gt;В первых трех функциях Pipeline происходит получение и декодирование JWT access_token из которого извлекаются различные поля, которые передаются клиенту (в нашем случае Netbox) от Keycloak, и помещаются в переменную details (dict содержащий данные о пользователе, на базе которых пайплайн должен будет создать или обновить данные пользователя Netbox).&lt;/p&gt;
  &lt;p id=&quot;sRsK&quot;&gt;Функция get_username создает на базе совокупности различных настроек приложения и модуля python-social-auth имя пользователя, а функция associate_by_email пытается по email найти в БД Netbox соответствующего пользователя.&lt;/p&gt;
  &lt;p id=&quot;VdsC&quot;&gt;Функция associate_by_email выглядит так:&lt;/p&gt;
  &lt;pre id=&quot;jz60&quot; data-lang=&quot;python&quot;&gt;def associate_by_email(backend, details, user=None, *args, **kwargs):
    &amp;quot;&amp;quot;&amp;quot;
    Associate current auth with a user with the same email address in the DB.

    This pipeline entry is not 100% secure unless you know that the providers
    enabled enforce email verification on their side, otherwise a user can
    attempt to take over another user account by using the same (not validated)
    email address on some provider.  This pipeline entry is disabled by
    default.
    &amp;quot;&amp;quot;&amp;quot;
    if user:
        return None

    email = details.get(&amp;quot;email&amp;quot;)
    if email:
        # Try to associate accounts registered with the same email address,
        # only if it&amp;#x27;s a single object. AuthException is raised if multiple
        # objects are returned.
        users = list(backend.strategy.storage.user.get_users_by_email(email))
        if len(users) == 0:
            return None
        elif len(users) &amp;gt; 1:
            raise AuthException(
                backend, &amp;quot;The given email address is associated with another account&amp;quot;
            )
        else:
            return {&amp;quot;user&amp;quot;: users[0], &amp;quot;is_new&amp;quot;: False}&lt;/pre&gt;
  &lt;p id=&quot;1GMd&quot;&gt;Очевидно, что при наличии в в БД нескольких пользователей в одинаковым email эта функция упадёт.&lt;/p&gt;
  &lt;p id=&quot;pvzo&quot;&gt;Чтобы решить эту проблему я создал новый модуль и переписал эту функцию так, чтобы использовать для ассоциации не только поле email но username.&lt;/p&gt;
  &lt;pre id=&quot;Cx86&quot; data-lang=&quot;python&quot;&gt;import re
from social_core.exceptions import AuthException


def associate_by_email_and_username(backend, details, user=None, *args, **kwargs):
    &amp;quot;&amp;quot;&amp;quot;
    Associate current auth with a user with the same email address and username in the DB.
    &amp;quot;&amp;quot;&amp;quot;
    if user:
        return None
    email = details.get(&amp;quot;email&amp;quot;)
    username = details.get(&amp;quot;username&amp;quot;)
    # If username in upn format, get username part
    if m:=re.match(r&amp;quot;^(?:(?P&amp;lt;username&amp;gt;[^@]+)@(?P&amp;lt;domain&amp;gt;.+))quot;, username):
        username = m.group(&amp;#x27;username&amp;#x27;)
    if email:
        users = list(backend.strategy.storage.user.get_users_by_email(email))
        if len(users) == 0:
            return None
        elif len(users) &amp;gt; 1:
            for u in users:
                if u.username == username:
                    return {&amp;quot;user&amp;quot;: u, &amp;quot;is_new&amp;quot;: False}
            raise AuthException(
                backend, &amp;quot;The given email address is associated with another account&amp;quot;
            )
        else:
            return {&amp;quot;user&amp;quot;: users[0], &amp;quot;is_new&amp;quot;: False}
    else:
        raise AuthException(backend, &amp;quot;No email address returned&amp;quot;)&lt;/pre&gt;
  &lt;p id=&quot;Kckm&quot;&gt;Функция извлекает из словаря details username, если он в формате userPrincipalName - извлекает из него username часть и при наличии в БД Netbox пользователей с одинаковым email проводит дополнительную проверку по полю username.&lt;/p&gt;
  &lt;p id=&quot;TEyg&quot;&gt;Остается только собрать пакет из модуля, импортировать его в venv окружение, в котором работает Netbox, и переопределить в настройках приложения Pipeline (об этом расскажу ниже).&lt;/p&gt;
  &lt;h2 id=&quot;hScU&quot;&gt;Решение проблемы №2&lt;/h2&gt;
  &lt;p id=&quot;lo41&quot;&gt;Изучив встроенную функцию netbox.authentication.user_default_groups_handler я понял что она назначает пользователю только группы определенные в переменной REMOTE_AUTH_DEFAULT_GROUPS из configuration.py. Я принял решение заменить эту функцию в конвеере совместив с функциональностью обновления групп, полученных от Keycloak.&lt;/p&gt;
  &lt;p id=&quot;9DKE&quot;&gt;Код этой функции будет выглядеть так:&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;Dert&quot;&gt;&lt;strong&gt;Важно!&lt;/strong&gt; Для того чтобы эта функция корректно работала access_token, который возвращает ваш Keycloak после авторизации, должен содержать поле groups с массивом имён групп. Если этого поля нет или оно содержит пустой массив - пользователю Netbox будут сброшены все связи с группами, а новых связей создано не будет.&lt;/p&gt;
  &lt;/section&gt;
  &lt;pre id=&quot;KU4p&quot; data-lang=&quot;python&quot;&gt;import logging
from django.conf import settings
from django.contrib.auth.models import Group

def update_user_groups(backend, response, user=None, *args, **kwargs):
    &amp;quot;&amp;quot;&amp;quot;
    Custom pipline handler with adds remote auth users to extist groups or
    create non-exist groups
    &amp;quot;&amp;quot;&amp;quot;
    logger = logging.getLogger(
        &amp;quot;netbox-social-auth-custom.auth_username.update_user_groups_handler&amp;quot;
    )
    if not user:
        return None
    else:
        user_groups = []
        auth_groups = response.get(&amp;quot;groups&amp;quot;, [])
        user.groups.clear()

        # Add or Create DEFAULT USER GROUPS defined in configuration.py
        for name in settings.REMOTE_AUTH_DEFAULT_GROUPS:
            group, created = Group.objects.get_or_create(name=name)
            user_groups.append(group)

        # Add or Create Social Auth user groups
        # groups should be added to access token as array (list) of names
        for name in auth_groups:
            group, created = Group.objects.get_or_create(name=name)
            user_groups.append(group)

        if user_groups:
            user.groups.add(*user_groups)
        else:
            logger.info(
                f&amp;quot;No any groups assignments for {user}. May be REMOTE_AUTH_DEFAULT_GROUPS incorrectly set\
                  or not returned from OAuth provider&amp;quot;
            )&lt;/pre&gt;
  &lt;h2 id=&quot;gy80&quot;&gt;Переопределение Pipeline&lt;/h2&gt;
  &lt;p id=&quot;V8qc&quot;&gt;Итак, обе функции помещены в файл, который я назвал auth_username.py, сложены в папки netbox-social-auth-custom/pipeline (вторая папка больше для красоты в названии - обозначения что это часть pipeline по аналогии с модулем social_core) и из этого собран python package (как собирать python-пакеты я описывать не буду, т.к. для этого есть куча разного инструментария и соответствующих гайдов - &lt;a href=&quot;https://packaging.python.org/en/latest/tutorials/packaging-projects/&quot; target=&quot;_blank&quot;&gt;например такой&lt;/a&gt;, разве что подскажу, что не плохо бы при сборке указать зависимость от social-auth-core&amp;gt;=4.5).&lt;/p&gt;
  &lt;p id=&quot;9IYN&quot;&gt;Пакет установлен в окружении venv нашего Netbox и остается только переопределить собственно сам Pipline. Для этого в файле configuration.py переопределяется переменная SOCIAL_AUTH_PIPELINE в виде вот такого массива:&lt;/p&gt;
  &lt;pre id=&quot;yOXy&quot; data-lang=&quot;python&quot;&gt;SOCIAL_AUTH_PIPELINE = (
    &amp;#x27;social_core.pipeline.social_auth.social_details&amp;#x27;,
    &amp;#x27;social_core.pipeline.social_auth.social_uid&amp;#x27;,
    &amp;#x27;social_core.pipeline.social_auth.social_user&amp;#x27;,
    &amp;#x27;social_core.pipeline.user.get_username&amp;#x27;,
    &amp;#x27;netbox_social_auth_custom.pipeline.auth_username.associate_by_email_and_username&amp;#x27;,
    &amp;#x27;social_core.pipeline.user.create_user&amp;#x27;,
    &amp;#x27;social_core.pipeline.social_auth.associate_user&amp;#x27;,
    &amp;#x27;social_core.pipeline.social_auth.load_extra_data&amp;#x27;,
    &amp;#x27;social_core.pipeline.user.user_details&amp;#x27;,
    &amp;#x27;netbox_social_auth_custom.pipeline.auth_username.update_user_groups&amp;#x27;,
)&lt;/pre&gt;
  &lt;p id=&quot;670e&quot;&gt;Собственно на этом всё. Спасибо что дочитали, надеюсь это кому-то пригодится.&lt;/p&gt;
  &lt;p id=&quot;HjuR&quot;&gt;UPD: Отладка Pipline&lt;/p&gt;
  &lt;p id=&quot;bBA8&quot;&gt;Забыл написать про отладку вашего (или вообще) конвеера авторизации. Чтобы в увидеть в логах или dev-консоли сервера Netbox &lt;/p&gt;
  &lt;h2 id=&quot;HjuR&quot;&gt;UPD: Отладка Pipline&lt;/h2&gt;
  &lt;p id=&quot;bBA8&quot;&gt;Забыл написать про отладку вашего (или вообще) конвеера авторизации. Чтобы в увидеть в логах или dev-консоли сервера Netbox отладочную информацию добавьте в конец (или перед падающей функцией конвеера) строку&lt;/p&gt;
  &lt;pre data-lang=&quot;python&quot; id=&quot;KfhA&quot;&gt;&amp;#x27;social_core.pipeline.debug.debug&amp;#x27;,&lt;/pre&gt;

</content></entry><entry><id>ihumster:ansible-role-argument-validation</id><link rel="alternate" type="text/html" href="https://blog.ihumster.ru/ansible-role-argument-validation?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=ihumster"></link><title>Проверка аргументов (переменных) для роли Ansible</title><published>2024-09-23T07:09:41.534Z</published><updated>2024-09-23T07:09:41.534Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/77/af/77af2f0a-4923-49e1-a776-2a27b8081401.png"></media:thumbnail><category term="ansible" label="Ansible"></category><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/8e/0d/8e0d343a-84fc-40d4-8871-4cb5b536e99d.png&quot;&gt;При написании новой роли задумался о теме данной статьи - как проверять набор минимально необходимых параметров (аргументов, переменных, всё в Ansible сводится к переменным) для выполнения роли и как обычно пошел изучать документацию.</summary><content type="html">
  &lt;p id=&quot;RhUn&quot;&gt;При написании новой роли задумался о теме данной статьи - как проверять набор минимально необходимых параметров (аргументов, переменных, всё в Ansible сводится к переменным) для выполнения роли и как обычно пошел изучать &lt;a href=&quot;https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html#role-argument-validation&quot; target=&quot;_blank&quot;&gt;документацию&lt;/a&gt;.&lt;/p&gt;
  &lt;p id=&quot;esCJ&quot;&gt;С приятным удивлением обнаружил механизм, который в Ansible появился с релизом ansible-core 2.11 (апрель 2021) и называется он &lt;strong&gt;Role argument validation&lt;/strong&gt;. Исходя из собственного опыта написания модулей Ansible, я предполагаю, что в данном механизме используется тот же самый код, что и при валидации аргументов модуля (по крайней мере, всё на это указывает).&lt;/p&gt;
  &lt;p id=&quot;yOuk&quot;&gt;Более того, этот механизм позволяет, помимо валидации аргументов, документировать вашу роль. После добавления файла, описывающего Ваш набор переменных, Вы получаете документированную роль, документацию по которой можно вызвать при помощи ansible-doc.&lt;/p&gt;
  &lt;pre id=&quot;jSJS&quot;&gt;ansible-doc --type role --roles-path &amp;lt;you roles path&amp;gt; &amp;lt;role_name&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;vzG2&quot;&gt;Пример вызова такой роли:&lt;/p&gt;
  &lt;figure id=&quot;HjXW&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/8e/0d/8e0d343a-84fc-40d4-8871-4cb5b536e99d.png&quot; width=&quot;1339&quot; /&gt;
  &lt;/figure&gt;
  &lt;h2 id=&quot;2zph&quot;&gt;Реализация&lt;/h2&gt;
  &lt;p id=&quot;zEUs&quot;&gt;Для создания подобной функциональности используется файл meta/argument_specs.yml. Его формат описан в &lt;a href=&quot;https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html#specification-format&quot; target=&quot;_blank&quot;&gt;документации &lt;/a&gt;и довольно прост. Более того, у Вашей роли может быть не одна точка входа (по умолчанию это main.yml) но и дополнительные, возможно для вызова через &lt;strong&gt;&lt;em&gt;ansible.builtin.import_role&lt;/em&gt;&lt;/strong&gt; или &lt;strong&gt;&lt;em&gt;ansible.builtin.include_role&lt;/em&gt;&lt;/strong&gt;, набор параметров для каждой точки входа можно описать в этом файле. Также параметры можно пометить обязательными, указать тип данных (набор типов данных, которые умеет проверять AnsibleModule &lt;a href=&quot;https://docs.ansible.com/ansible/latest/dev_guide/developing_program_flow_modules.html#argument-spec&quot; target=&quot;_blank&quot;&gt;тут&lt;/a&gt;), который должен содержаться в параметре, его значение по умолчанию (если оно находится в ../defaults/&amp;lt;entry-point&amp;gt;.yml, например) ну и, конечно же, описание (short_description и description для точки входа и description для каждого параметра).&lt;/p&gt;
  &lt;p id=&quot;ND6b&quot;&gt;В дальнейшем роль, имеющая такое описание параметров при вызове будет проверять набор необходимых параметров автоматически отдельным таском.&lt;/p&gt;
  &lt;figure id=&quot;03d1&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/ff/f7/fff71be9-1138-49ed-bdd4-d63c1e1eef11.png&quot; width=&quot;1314&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Dgii&quot;&gt;При вызове роли через &lt;strong&gt;&lt;em&gt;ansible.builtin.import_role&lt;/em&gt;&lt;/strong&gt; или &lt;strong&gt;&lt;em&gt;ansible.builtin.include_role&lt;/em&gt;&lt;/strong&gt; есть возможность отключить проверку аргументов, используя параметр &lt;strong&gt;rolespec_validate&lt;/strong&gt; установленный в &lt;strong&gt;false&lt;/strong&gt;.&lt;/p&gt;

</content></entry><entry><id>ihumster:interesting-cmdlet-from-the-microsoft-powershell</id><link rel="alternate" type="text/html" href="https://blog.ihumster.ru/interesting-cmdlet-from-the-microsoft-powershell?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=ihumster"></link><title>Интересный cmdlet из модуля Microsoft.PowerShell.TextUtility</title><published>2024-09-23T07:04:41.097Z</published><updated>2024-09-23T07:06:43.117Z</updated><summary type="html">Модуль Microsoft.PowerShell.TextUtility получил новый командлет ConvertFrom-TextTable. Он умеет превращать текстовую таблицу (типичный вывод большинства shell-команд Linux) в массив объектов. И даже больше - он может распарсить значения в столбцах таблицы и сконвертировать их в соответсвующие типы (например Int или Float, Boolean). Ну и наконец вместо объекта массива, командлет может вернуть массив строк в формате JSON.</summary><content type="html">
  &lt;p id=&quot;RuMN&quot;&gt;Модуль Microsoft.PowerShell.TextUtility получил новый командлет ConvertFrom-TextTable. Он умеет превращать текстовую таблицу (типичный вывод большинства shell-команд Linux) в массив объектов. И даже больше - он может распарсить значения в столбцах таблицы и сконвертировать их в соответсвующие типы (например Int или Float, Boolean). Ну и наконец вместо объекта массива, командлет может вернуть массив строк в формате JSON.&lt;/p&gt;
  &lt;p id=&quot;rEHO&quot;&gt;Установить модуль можно из PowershellGallery:&lt;/p&gt;
  &lt;pre id=&quot;ll2v&quot;&gt;Install-Module -Name Microsoft.PowerShell.TextUtility -AllowPrerelease&lt;/pre&gt;
  &lt;p id=&quot;lwli&quot;&gt;Пример использования:&lt;/p&gt;
  &lt;pre id=&quot;jz9M&quot; data-lang=&quot;powershell&quot;&gt;PS&amp;gt; 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&lt;/pre&gt;
  &lt;p id=&quot;JwjP&quot;&gt;Командлет позовляет указать где определяется строка заголовков или если ее нет просто брать табличные данные и превращать их в массив объектов.&lt;/p&gt;
  &lt;pre id=&quot;PgiV&quot; data-lang=&quot;powershell&quot;&gt;PS&amp;gt; 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&lt;/pre&gt;
  &lt;p id=&quot;lrr2&quot;&gt;Модуль еще находится в процессе разработки и если он вам понравился и пригодился, но вы нашли какую-либо ошибку, то оставить баг-репорт можно прямо в разделе &lt;a href=&quot;https://github.com/PowerShell/TextUtility/issues&quot; target=&quot;_blank&quot;&gt;Issues &lt;/a&gt;репозитория.&lt;/p&gt;

</content></entry><entry><id>ihumster:vcd-upgrade-10-4-1-nuanses</id><link rel="alternate" type="text/html" href="https://blog.ihumster.ru/vcd-upgrade-10-4-1-nuanses?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=ihumster"></link><title>Нюансы обновления до VCD 10.4.1</title><published>2024-09-23T07:03:35.187Z</published><updated>2024-09-23T07:03:35.187Z</updated><category term="cloud-director" label="Cloud Director"></category><summary type="html">После обновления до VCD 10.4.1 одна из Advisores отсылает нас к KB78885 (но да кто их читает, да?), которая опубликована в далеком уже 2021 году и которая напоминает нам о том, что после обновления VCD у нас может пропасть доверненное подключение к различным элементам нашей инфраструктуры. Ужесточение требований к сертификатам в инфраструктуре началось в VCD еще с версии 10.1 (о чём так же отдельно пишется во врезке, на странице документации посвященной обновлению VCD).</summary><content type="html">
  &lt;p id=&quot;fvYh&quot;&gt;После обновления до VCD 10.4.1 одна из Advisores отсылает нас к &lt;a href=&quot;https://kb.vmware.com/s/article/78885&quot; target=&quot;_blank&quot;&gt;KB78885&lt;/a&gt; (но да кто их читает, да?), которая опубликована в далеком уже 2021 году и которая напоминает нам о том, что после обновления VCD у нас может пропасть доверненное подключение к различным элементам нашей инфраструктуры. Ужесточение требований к сертификатам в инфраструктуре началось в VCD еще с версии 10.1 (о чём так же отдельно пишется во врезке, на странице документации &lt;a href=&quot;https://docs.vmware.com/en/VMware-Cloud-Director/10.4/VMware-Cloud-Director-Install-Configure-Upgrade-Guide/GUID-6EC093C1-4CE7-4435-9FC5-F801EA618D91.html&quot; target=&quot;_blank&quot;&gt;посвященной обновлению&lt;/a&gt; VCD).&lt;/p&gt;
  &lt;p id=&quot;hDWo&quot;&gt;В 10.4.1 ужесточили еще один аспект инфраструктуры, а именно подключение к ESXi хостам. В результате, после обновления до 10.4.1 (и автоматическом переходе на новую консоль, старую выпилили таки, и слава богу), в VCD перестанут работать операции требующие прямого доступа к хосту, а именно &lt;strong&gt;доступ к консоли ВМ, ипорту OVF/ISO, Guest Customization&lt;/strong&gt;.&lt;/p&gt;
  &lt;p id=&quot;B8Zk&quot;&gt;В логах это может отобразиться в виде следующих сообщений:&lt;/p&gt;
  &lt;pre id=&quot;yf6H&quot;&gt;[ 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&lt;/pre&gt;
  &lt;pre id=&quot;O4lr&quot;&gt; 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
&lt;/pre&gt;
  &lt;h2 id=&quot;UaNd&quot;&gt;Решение&lt;/h2&gt;
  &lt;p id=&quot;HDwa&quot;&gt;Самый простой вариант - это запустить повторно &lt;strong&gt;CMT trust-infra-certs&lt;/strong&gt;:&lt;/p&gt;
  &lt;pre id=&quot;ydsi&quot;&gt;./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.&lt;/pre&gt;
  &lt;p id=&quot;BxPL&quot;&gt;Или самостоятельно скачать с vCenters их сертификаты VMCA (&amp;lt;vcenter&amp;gt;/certs/download.zip) и вручную добавить VMCA сертификаты в Trusted Certificates вашего VCD.&lt;/p&gt;
  &lt;p id=&quot;TYoj&quot;&gt;Для VCD с большим количеством vCenters (или организаций с высокими требованиями к безопасности инфраструктурных компонентов) самым правильным путём, по моему мнению, будет вариант с &lt;a href=&quot;https://kb.vmware.com/s/article/2097936#use_of_replacing_vmca_certificate_with_custom_ca_certificate&quot; target=&quot;_blank&quot;&gt;заменой сертификата VMCA&lt;/a&gt; на vCenters на подписанный с внутреннего или внешнего CA и последующей регенерацией сертификатов ESXi.&lt;/p&gt;

</content></entry><entry><id>ihumster:vcda-initial-setup-api</id><link rel="alternate" type="text/html" href="https://blog.ihumster.ru/vcda-initial-setup-api?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=ihumster"></link><title>VMware Cloud Director Availability Initial Setup API</title><published>2024-09-23T07:02:53.641Z</published><updated>2024-09-23T07:02:53.641Z</updated><summary type="html">Встала задача по автоматизации развертывания VCDA для инсталляций VCD где VCDA еще не развернут. Начав изучение вопроса и прочтя внимательно документацию по VCDA с удивлением понял что для развертывания самих аплайнсов есть способ по автоматизации процесса, т.к. аплайнсы поставляются в формате OVF с обычными, для аплайнсов VMware, возможностями деплоя их как с помощью UI, так и с помощью OVFTool ну и прочими средствами (например, community.vmware.vmware_deploy_ovf из коллекции ansible). Но для дальнейшей первоначальной настройки нет иного официального пути, кроме как пойти в UI и потыкать кнопки, заполняя поля в Initial Setup Wizard.</summary><content type="html">
  &lt;p id=&quot;025Y&quot;&gt;Встала задача по автоматизации развертывания VCDA для инсталляций VCD где VCDA еще не развернут. Начав изучение вопроса и прочтя внимательно документацию по VCDA с удивлением понял что для развертывания самих аплайнсов есть способ по автоматизации процесса, т.к. аплайнсы поставляются в формате OVF с обычными, для аплайнсов VMware, возможностями деплоя их как с помощью UI, так и с помощью OVFTool ну и прочими средствами (например, community.vmware.vmware_deploy_ovf из коллекции ansible). Но для дальнейшей первоначальной настройки нет иного официального пути, кроме как пойти в UI и потыкать кнопки, заполняя поля в Initial Setup Wizard.&lt;/p&gt;
  &lt;p id=&quot;KBLT&quot;&gt;Вооружившись DevTools я за несколько итераций записал все необходимые API вызовы, которые происходят при выполнении этапов Initial Setup Wizard и после, проверив возможность их воспроизведения сторонними средствами (воспроизводил в Postman) решил что хочу записать их в виде документации OpenAPI 3.0 что бы любой мог импортировать ее в Postman и ознакомиться с деталями запросов.&lt;/p&gt;
  &lt;p id=&quot;lLZN&quot;&gt;По этому спешу представить Вам первую версию &lt;a href=&quot;https://github.com/ihumster/VCDAInitialSetupAPI&quot; target=&quot;_blank&quot;&gt;документации &lt;/a&gt;к VCDA Initial Setup API. В репозиторий добавлена также готовая коллекция для Postman (на случай если вы не умеете генерировать коллекцию из OpenAPI спецификации).&lt;/p&gt;
  &lt;p id=&quot;fOv6&quot;&gt;В коллекции все запросы пронумерованы в порядке выполнения их в Wizard. Отдельно не пронумерован один Get запрос - он используется для проверки доступности той или иной системы (VCD, vCenter server, ReplicatorAppliance, Tunnel Appliance) из VCDA Manager, а также возвращает SSL-сертификат используемый системой (сам сертефикат в PEM-encoded формате, его thumbprint - используются в остальных запросах).&lt;/p&gt;
  &lt;p id=&quot;qZTY&quot;&gt;Конфигурация VCDA которую я изучал - это рекомендуемая в продуктивной среде конфигурация из отдельных &amp;quot;ролевых&amp;quot; аплайнсов - manager, replicator, tunnel. Для combined и vc_combined версии, последовательность и структура запросов скорее всего иная (в vc_combined например не используется VCD).&lt;/p&gt;
  &lt;p id=&quot;3gNR&quot;&gt;Также, если вы используете несколько replicator аплайнсов то соответствующие запросы необходимо будет проделать для каждого аплайнса последовательно.&lt;/p&gt;
  &lt;p id=&quot;VRaF&quot;&gt;Для работы с этим API вам необходимы минимум 3 задеплоенных удобным вам способом аплайнса.&lt;/p&gt;

</content></entry><entry><id>ihumster:vcd-api-with-postman-2</id><link rel="alternate" type="text/html" href="https://blog.ihumster.ru/vcd-api-with-postman-2?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=ihumster"></link><title>Комфортная работа в Postman с VMware Cloud Director (2)</title><published>2024-09-23T07:02:07.305Z</published><updated>2024-09-23T07:02:07.305Z</updated><category term="cloud-director" label="Cloud Director"></category><summary type="html">В процессе работы с OpenAPI разных версий VMware Cloud Director понял что меня раздражает постоянно следить за версией API, которая указывается в Header 'Accept'. Чтобы автоматизировать актуализацию версии API в моих запросах я модифицировал свой pre-request Script из предыдущей статьи.</summary><content type="html">
  &lt;p id=&quot;ZQaK&quot;&gt;В процессе работы с OpenAPI разных версий VMware Cloud Director понял что меня раздражает постоянно следить за версией API, которая указывается в Header &amp;#x27;Accept&amp;#x27;. Чтобы автоматизировать актуализацию версии API в моих запросах я модифицировал свой pre-request Script из предыдущей статьи.&lt;/p&gt;
  &lt;p id=&quot;qTQ5&quot;&gt;Добавьте следующую строку между запросами версии и получением access_token, либо после них:&lt;/p&gt;
  &lt;pre id=&quot;CXUc&quot;&gt;pm.request.headers.get(&amp;#x27;Accept&amp;#x27;).replace(&amp;#x27;application\/json;version=(?&amp;lt;version&amp;gt;\d{2}\.\d{1})&amp;#x27;,&amp;#x27;application/json;version={{apiVersion}}&amp;#x27;)&lt;/pre&gt;

</content></entry><entry><id>ihumster:regex-url-rfc</id><link rel="alternate" type="text/html" href="https://blog.ihumster.ru/regex-url-rfc?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=ihumster"></link><title>Regexp для парсинга URL по RFC</title><published>2024-09-23T07:01:22.567Z</published><updated>2024-09-23T07:12:24.942Z</updated><summary type="html">Не претендуя на истину в последней инстанции, однако, хотелось закрыть для себя этот вопрос на всегда. Частенько в скриптах posh\python и т.п. приходится на входе принимать в качестве параметра строку с URL и быть уверенным что это:</summary><content type="html">
  &lt;p id=&quot;S2TW&quot;&gt;Не претендуя на истину в последней инстанции, однако, хотелось закрыть для себя этот вопрос на всегда. Частенько в скриптах posh\python и т.п. приходится на входе принимать в качестве параметра строку с URL и быть уверенным что это:&lt;/p&gt;
  &lt;ol id=&quot;nAC9&quot;&gt;
    &lt;li id=&quot;LP3p&quot;&gt;точно URL&lt;/li&gt;
    &lt;li id=&quot;Lxy1&quot;&gt;иметь возможность легко получить его составляющие (схему\fqdn\query\etc)&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;oogw&quot;&gt;Подсматривая в интернет, получилось написать вот такую &amp;quot;красивую&amp;quot; регулярку, которая входящую строку бъет на следующие группы:&lt;/p&gt;
  &lt;ol id=&quot;MrcK&quot;&gt;
    &lt;li id=&quot;qd3v&quot;&gt;url (собственно если строка соответствует паттерну url - в этой группе будет отображаться полный url&lt;/li&gt;
    &lt;li id=&quot;lOZ5&quot;&gt;scheme - схема URL http:// | ftp:// | anycustomscheme://&lt;/li&gt;
    &lt;li id=&quot;YA5Q&quot;&gt;hostname - FQDN&lt;/li&gt;
    &lt;li id=&quot;xay0&quot;&gt;port&lt;/li&gt;
    &lt;li id=&quot;SAQ9&quot;&gt;path&lt;/li&gt;
    &lt;li id=&quot;xAdb&quot;&gt;query&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;RAx1&quot;&gt;Как работает этот regexp вы можете увидеть ниже&lt;/p&gt;
  &lt;figure id=&quot;sYYw&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/5d/26/5d264052-16e5-47cc-91f8-8eee91e3a63b.png&quot; width=&quot;1940&quot; /&gt;
  &lt;/figure&gt;
  &lt;pre id=&quot;7Yum&quot; data-lang=&quot;regex&quot;&gt;(?&amp;lt;url&amp;gt;(?:(?&amp;lt;scheme&amp;gt;[a-zA-Z]+:\/\/)?(?&amp;lt;hostname&amp;gt;(?:[-a-zA-Z0-9@%_\+~#=]{1,256}\.){1,256}(?:[-a-zA-Z0-9@%_\+~#=]{1,256})))(?::(?&amp;lt;port&amp;gt;[[:digit:]]+))?(?&amp;lt;path&amp;gt;(?:\/[-a-zA-Z0-9!$&amp;amp;&amp;#x27;()*+,\\\/:;=@\[\]._~%]*)*)(?&amp;lt;query&amp;gt;(?:(?:\#|\?)[-a-zA-Z0-9!$&amp;amp;&amp;#x27;()*+,\\\/:;=@\[\]._~]*)*))&lt;/pre&gt;
  &lt;p id=&quot;ZtPB&quot;&gt;На Python именованные группы нужно обозначать через символ P, поэтому для Python regex будет выглядеть чуть иначе:&lt;/p&gt;
  &lt;pre id=&quot;zrZv&quot; data-lang=&quot;regex&quot;&gt;(?P&amp;lt;url&amp;gt;(?:(?P&amp;lt;scheme&amp;gt;[a-zA-Z]+:\/\/)?(?P&amp;lt;hostname&amp;gt;(?:[-a-zA-Z0-9@%_\+~#=]{1,256}\.){1,256}(?:[-a-zA-Z0-9@%_\+~#=]{1,256})))(?::(?P&amp;lt;port&amp;gt;[[:digit:]]+))?(?P&amp;lt;path&amp;gt;(?:\/[-a-zA-Z0-9!$&amp;amp;&amp;#x27;()*+,\\\/:;=@\[\]._~%]*)*)(?P&amp;lt;query&amp;gt;(?:(?:\#|\?)[-a-zA-Z0-9!$&amp;amp;&amp;#x27;()*+,\\\/:;=@\[\]._~]*)*))&lt;/pre&gt;
  &lt;p id=&quot;lBCT&quot;&gt;Ну и пример кода на Powershell:&lt;/p&gt;
  &lt;pre id=&quot;Vi4h&quot; data-lang=&quot;powershell&quot;&gt;$URL = &amp;quot;https://vcloud.lab.ihumster.ru/api/query?type=adminOrgVdcStorageProfile&amp;quot;
$URL -match &amp;quot;(?&amp;lt;url&amp;gt;(?:(?&amp;lt;scheme&amp;gt;[a-zA-Z]+:\/\/)?(?&amp;lt;hostname&amp;gt;(?:[-a-zA-Z0-9@%_\+~#=]{1,256}\.){1,256}(?:[-a-zA-Z0-9@%_\+~#=]{1,256})))(?::(?&amp;lt;port&amp;gt;[[:digit:]]+))?(?&amp;lt;path&amp;gt;(?:\/[-a-zA-Z0-9!$&amp;amp;&amp;#x27;()*+,\\\/:;=@\[\]._~%]*)*)(?&amp;lt;query&amp;gt;(?:(?:\#|\?)[-a-zA-Z0-9!$&amp;amp;&amp;#x27;()*+,\\\/:;=@\[\]._~]*)*))&amp;quot;
$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&lt;/pre&gt;
  &lt;p id=&quot;6Rkz&quot;&gt;Операнд -math возвращает boolean результат соответствия строки в $URL нашему регулярному выражению и если он True, то автопеременная $Matches будет содержать key-value словарь в котором ключами будут имена групп с соответствующими values.&lt;/p&gt;
  &lt;p id=&quot;4RV8&quot;&gt;Спасибо что дочитали до конца, надеюсь это будет вам полезно.&lt;/p&gt;

</content></entry><entry><id>ihumster:vcd-api-with-postman</id><link rel="alternate" type="text/html" href="https://blog.ihumster.ru/vcd-api-with-postman?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=ihumster"></link><title>Комфортная работа в Postman с VMware Cloud Director</title><published>2024-09-23T07:00:31.766Z</published><updated>2024-09-24T15:07:56.595Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img1.teletype.in/files/0e/87/0e875be6-6656-4439-872f-7f9aaa2ae8e1.png"></media:thumbnail><category term="cloud-director" label="Cloud Director"></category><summary type="html">&lt;img src=&quot;https://blog.ihumster.ru/wp-content/uploads/2022/07/image-2.png&quot;&gt;В последний раз что-то полезное на эту тему писал Tomas Fojta в, далеком уже, 2018 году. С тех пор в VCD появились новые интересные методы API и в частности в 10.3.1 появились Cloud Director API Token.</summary><content type="html">
  &lt;p id=&quot;XjFW&quot;&gt;В последний раз что-то полезное на эту &lt;a href=&quot;https://fojta.wordpress.com/2018/10/18/postman-and-vcloud-director-9-5-access-token-authentication/&quot; target=&quot;_blank&quot;&gt;тему писал&lt;/a&gt; Tomas Fojta в, далеком уже, 2018 году. С тех пор в VCD появились новые интересные методы API и в частности в 10.3.1 появились &lt;a href=&quot;https://blogs.vmware.com/cloudprovider/2022/03/cloud-director-api-token.html&quot; target=&quot;_blank&quot;&gt;Cloud Director API Token&lt;/a&gt;.&lt;/p&gt;
  &lt;p id=&quot;3GdV&quot;&gt;По этому я решил слегка модернизировать подход к работе с VCD API в Postman с использованием токенов.&lt;/p&gt;
  &lt;p id=&quot;UV4l&quot;&gt;Если вам интересно - добро пожаловать под cut.&lt;/p&gt;
  &lt;p id=&quot;zjLZ&quot;&gt;Подход к работе с токенами/авторизацией и вообще разными инстансами VCD в Postman будет основан на использовании всей доступной в Postman функциональности &lt;strong&gt;Workspaces&lt;/strong&gt;, &lt;strong&gt;Collections&lt;/strong&gt;, &lt;strong&gt;Variables &lt;/strong&gt;и &lt;strong&gt;Scripts&lt;/strong&gt;.&lt;/p&gt;
  &lt;p id=&quot;NtrJ&quot;&gt;Первым делом создадим для работы с VCD отдельный &lt;strong&gt;Workspace &lt;/strong&gt;и дадим ему имя (например, внезапно, VMware Cloud Director).&lt;/p&gt;
  &lt;figure id=&quot;t7SK&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/db/9f/db9fe05e-914a-4003-a1dd-21a089ccc63d.png&quot; width=&quot;363&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure id=&quot;EJ9g&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/34/6b/346b017c-8e95-416e-9ff9-da2eeeaf463e.png&quot; width=&quot;572&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;DNU8&quot;&gt;Создадим коллекцию запросов назвав ее, ну например, VCD API.&lt;/p&gt;
  &lt;figure id=&quot;BTON&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/0f/57/0f574e7a-e544-4b69-8782-130e08e76aaf.png&quot; width=&quot;382&quot; /&gt;
  &lt;/figure&gt;
  &lt;h2 id=&quot;tb7B&quot;&gt;Переменные окружения и глобальные переменные&lt;/h2&gt;
  &lt;p id=&quot;L2jp&quot;&gt;Откроем блок глобальных переменных и создадим там следующие переменные:&lt;/p&gt;
  &lt;figure id=&quot;NQBF&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/00/17/0017722a-f478-44e7-aa56-620be57d5c5b.png&quot; width=&quot;730&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure id=&quot;36J2&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/fe/e6/fee6fbcd-5a66-4a9c-9d62-73d3ab38c8bd.png&quot; width=&quot;1060&quot; /&gt;
  &lt;/figure&gt;
  &lt;pre id=&quot;GDRJ&quot;&gt;apiVersion
accept-vcloud-xml: application/*+xml;version={{apiVersion}}
accept-vcloud-json: application/*+json;version={{apiVersion}}&lt;/pre&gt;
  &lt;p id=&quot;uul3&quot;&gt;Это будут переменные общего назначения, и наиболее часто используемые, т.к. именно через поле заголовка Accept в запросе передается используемая версия API.&lt;/p&gt;
  &lt;p id=&quot;FrcI&quot;&gt;Не забудьте сохранить глобальные переменные (кнопка Save).&lt;/p&gt;
  &lt;p id=&quot;UCfc&quot;&gt;Следующим этапом будет создание переменных окружения. Наборы переменных окружения позволяют нам использовать одни и те же запросы к разным инстансам VCD. Выбор текущего набора переменных окружения позволит вам работать с конкретным инстансом VCD.&lt;/p&gt;
  &lt;figure id=&quot;7SC5&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/b3/eb/b3eb4dba-cf84-417c-99a3-35d812552e27.png&quot; width=&quot;732&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure id=&quot;fwg0&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/65/84/65843d98-2c08-4158-99dc-a59825fcf672.png&quot; width=&quot;1064&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;HIKM&quot;&gt;В окружении &amp;quot;ihumster lab&amp;quot; (дайте своему окружению имя, позволяющее конкретно идентифицировать инстанс с котороым будет вестись работа) будет 3 переменных:&lt;/p&gt;
  &lt;p id=&quot;pntO&quot;&gt;&lt;strong&gt;vcd-url&lt;/strong&gt; - базовый url вашего инстанса VCD (я намеренно не указываю url вида https://vcd-fqdn/api т.к. у VCD с некоторых пор 2 API - классический XML-RPC и новый OpenAPI)&lt;/p&gt;
  &lt;p id=&quot;FljG&quot;&gt;&lt;strong&gt;bearer&lt;/strong&gt; - переменная типа secret, ее initial значение пустое, т.к. получать значение переменная будет в процессе работы&lt;/p&gt;
  &lt;p id=&quot;qIgK&quot;&gt;&lt;strong&gt;user_token&lt;/strong&gt; - переменная типа secret, содержащая токен вашего пользователя, позволяющий получить bearer токен без аутентификации с логином и паролем (как получить этот токен описано в &lt;a href=&quot;https://blogs.vmware.com/cloudprovider/2022/03/cloud-director-api-token.html&quot; target=&quot;_blank&quot;&gt;статье VMware&lt;/a&gt;).&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;h4 id=&quot;523R&quot;&gt;Важно!&lt;/h4&gt;
  &lt;/section&gt;
  &lt;p id=&quot;EjZA&quot;&gt;Имейте ввиду, с использованием этого API Token вы не сможете через API:&lt;/p&gt;
  &lt;ul id=&quot;l3oo&quot;&gt;
    &lt;li id=&quot;gYRg&quot;&gt;Менять пароль пользователя&lt;/li&gt;
    &lt;li id=&quot;LUzn&quot;&gt;Управлять пользователями&lt;/li&gt;
    &lt;li id=&quot;HEFB&quot;&gt;Создавать новые API Tokens&lt;/li&gt;
    &lt;li id=&quot;yhr6&quot;&gt;Видеть и отзывать текущие API Tokens&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;PKrE&quot;&gt;А также, при использовании API Token, для следующих ресурсов будет иметься доступ только для чтения:&lt;/p&gt;
  &lt;ul id=&quot;A8uq&quot;&gt;
    &lt;li id=&quot;jUNy&quot;&gt;User&lt;/li&gt;
    &lt;li id=&quot;iMcN&quot;&gt;Group&lt;/li&gt;
    &lt;li id=&quot;T3g7&quot;&gt;Roles&lt;/li&gt;
    &lt;li id=&quot;RxLp&quot;&gt;Global roles&lt;/li&gt;
    &lt;li id=&quot;nou7&quot;&gt;Rights bundles&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;ZW0u&quot;&gt;Не забудьте сохранить ваши переменные (кнопка Save). Итого ваше окружение будет выглядеть так:&lt;/p&gt;
  &lt;figure id=&quot;X1qn&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/85/e6/85e6ac7c-59e2-4140-8a07-e3f41ac42661.png&quot; width=&quot;730&quot; /&gt;
  &lt;/figure&gt;
  &lt;h2 id=&quot;AH8q&quot;&gt;Настройки коллекции&lt;/h2&gt;
  &lt;p id=&quot;vLfp&quot;&gt;Для нашей созданной ранее коллекции мы будем настраивать следующие свойства:&lt;/p&gt;
  &lt;p id=&quot;Eyz7&quot;&gt;&lt;strong&gt;Authorization&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;h6z4&quot;&gt;Выбераем тип &lt;strong&gt;Bearer Token&lt;/strong&gt; и в свойство Token вписываем переменную &lt;strong&gt;{{bearer}}&lt;/strong&gt;.&lt;/p&gt;
  &lt;p id=&quot;cwoK&quot;&gt;Теперь любой созданный в коллекции запрос будет использовать наследуемый тип авторизации через токен.&lt;/p&gt;
  &lt;figure id=&quot;z8el&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/3f/4d/3f4d206a-9769-4e9a-9c5b-165cc0df70db.png&quot; width=&quot;760&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure id=&quot;E5wt&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/22/95/229528ca-a58b-401f-9564-809e2b012cf7.png&quot; width=&quot;1326&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;pre-request-script&quot;&gt;&lt;strong&gt;Pre-request Script&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;1Bb7&quot;&gt;Самая важная часть всей затеи - скрипт, который будет делать две важные вещи:&lt;/p&gt;
  &lt;ol id=&quot;rMr1&quot;&gt;
    &lt;li id=&quot;nYUS&quot;&gt;Получать последнюю версию API (т.е. текущую для нашего инстанса VCD) и сохранять ее в глобальную переменную &lt;strong&gt;apiVersion&lt;/strong&gt;.&lt;/li&gt;
    &lt;li id=&quot;Q8A2&quot;&gt;Получать токен доступа от VCD с использованием нашего &lt;strong&gt;user_token&lt;/strong&gt; и сохранение его в переменную окружения &lt;strong&gt;bearer&lt;/strong&gt;.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;pre id=&quot;mnxd&quot; data-lang=&quot;javascript&quot;&gt;// Get last version of VCD and save it to &amp;#x27;apiVersion&amp;#x27; global variable
let urlVer = pm.environment.get(&amp;quot;vcd-url&amp;quot;) + &amp;quot;/api/versions&amp;quot;
const urpRequest = {
    url: urlVer,
    method: &amp;#x27;GET&amp;#x27;,
    header: {
        &amp;#x27;Accept&amp;#x27;: &amp;#x27;application/*+json&amp;#x27;,
        &amp;#x27;Content-Type&amp;#x27;: &amp;#x27;application/json&amp;#x27;
    }
}
pm.sendRequest(urpRequest, (error,repsponse) =&amp;gt;{
    if (error){
        console.log(error)
    } else {
        pm.globals.set(&amp;#x27;apiVersion&amp;#x27;, repsponse.json().versionInfo.last().version)
    }
})

// Get access token using env var &amp;#x27;user_token&amp;#x27; and transform it to Bearer token data 
// with save to &amp;#x27;bearer&amp;#x27; env var
let urlTok = pm.environment.get(&amp;quot;vcd-url&amp;quot;) + &amp;quot;/oauth/provider/token&amp;quot;
let body = &amp;#x27;grant_type=refresh_token&amp;amp;refresh_token=&amp;#x27; + pm.environment.get(&amp;#x27;user_token&amp;#x27;)
const postRequest = {
  url: urlTok,
  method: &amp;#x27;POST&amp;#x27;,
  header: {
    &amp;#x27;Accept&amp;#x27;: &amp;#x27;application/json&amp;#x27;,
    &amp;#x27;Content-Type&amp;#x27;: &amp;#x27;application/x-www-form-urlencoded&amp;#x27;
  },
  body: {
    mode: &amp;#x27;raw&amp;#x27;,
    raw: body
  }
};

pm.sendRequest(postRequest, (error, response) =&amp;gt;{
    if (error){
        console.log(error)
    } else {
        pm.environment.set(&amp;#x27;bearer&amp;#x27;, response.json().access_token)
    }
})&lt;/pre&gt;
  &lt;p id=&quot;BANo&quot;&gt;Остается только проверить работу этого скрипта. Давайте попробуем получить ответ на запрос GET /api/admin/ (надо понимать что ваш user_token взят у пользователя из организации system и имеет права для запросов из раздела /api/admin/).&lt;/p&gt;
  &lt;p id=&quot;fRCN&quot;&gt;Создадим запрос типа GET, в url впишем конечный адрес с использованием переменной &lt;strong&gt;{{vcd-url}}&lt;/strong&gt;, в разделе Headers добавим заголовок &lt;strong&gt;Accept &lt;/strong&gt;с value &lt;strong&gt;{{accept-vcloud-json}}&lt;/strong&gt;, и нажмем &lt;strong&gt;Send&lt;/strong&gt;.&lt;/p&gt;
  &lt;p id=&quot;ux4e&quot;&gt;В результате ваш запрос выполнит сначала Pre-request Script (т.е. получит поддерживаемую версию API и получит access token для использования в запросе).&lt;/p&gt;
  &lt;figure id=&quot;OBmb&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/f6/16/f616cd05-9166-4657-bf46-066eda3ed93c.png&quot; width=&quot;1024&quot; /&gt;
  &lt;/figure&gt;
  &lt;h2 id=&quot;2Fv7&quot;&gt;Использование данного подхода к OpenAPI&lt;/h2&gt;
  &lt;p id=&quot;CKaF&quot;&gt;Залогиньтесь в провайдерскую консоль вашего VCD и откройте его API Exporer (Help-&amp;gt; API Exporer) и сохраните на локальный диск его Swagger файл (cloudapi.json)&lt;/p&gt;
  &lt;figure id=&quot;GkB8&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/1b/7d/1b7d162c-dbdb-4c82-8a10-e2fb2714fc7f.png&quot; width=&quot;659&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;tdXQ&quot;&gt;Перейдите в Postman в раздел APIs и нажмте + после чего в разделе Import выберите ранее скаченный файл.&lt;/p&gt;
  &lt;p id=&quot;Rb12&quot;&gt;Postman покажет вам тип API (Swagger 2.0) и предложит сгенерировать новую коллекцию запросов из этого API.&lt;/p&gt;
  &lt;figure id=&quot;3Mzd&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/58/a8/58a86a01-0670-4ff4-9830-1f351a9c24c2.png&quot; width=&quot;555&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;PkSb&quot;&gt;Имейте ввиду, после процесса импорта окно не закрывается, а остается активным с вновь доступной кнопкой Import. Нажимать ее повторно не нужно - просто закройте окно крестиком.&lt;/p&gt;
  &lt;p id=&quot;U749&quot;&gt;В коллекциях у вас появится новая коллекция VMware Cloud Director OpenAPI с набором всех доступных методов и их описанием (документацией) - это очень удобно (требуется лишь изредка обновлять коллекцию при выходе новых версий API).&lt;/p&gt;
  &lt;p id=&quot;uDt6&quot;&gt;Что бы использовать эту коллекцию с всеми удобствами созданных нами ранее переменных и скриптов проделаем следующее:&lt;/p&gt;
  &lt;ol id=&quot;1js5&quot;&gt;
    &lt;li id=&quot;491t&quot;&gt;Изменим тип авторизации с API Key на Bearer Token и вставим в поле Token &lt;strong&gt;{{bearer}}&lt;/strong&gt;.&lt;/li&gt;
    &lt;li id=&quot;zdyl&quot;&gt;В разделе Pre-request Script вставим &lt;a href=&quot;#pre-request-script&quot;&gt;аналогичный код&lt;/a&gt;&lt;/li&gt;
    &lt;li id=&quot;xmAj&quot;&gt;В разделе Variables значение переменной baseUrl замените с /cloudapi на&lt;strong&gt; {{vcd-url}}&lt;/strong&gt;/cloudapi для Initial и Current&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;Xb9i&quot;&gt;После чего можно проверить новую коллекцию с помощью запроса &lt;strong&gt;Get base navigation links&lt;/strong&gt;. В настройки запроса достаточно добавить только заголовок Accept с переменной &lt;strong&gt;{{accept-vcloud-json}}&lt;/strong&gt;&lt;/p&gt;
  &lt;figure id=&quot;YeWK&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/31/f6/31f6ff19-df0c-415d-a793-c1a95cda83c3.png&quot; width=&quot;1024&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;L0nE&quot;&gt;Спасибо что дочитали до конца!&lt;/p&gt;

</content></entry></feed>