Spec-Zone.ru › Ansible

Руководство по запуску команд

Введение

Утилита модуля ansible_collections.community.general.plugins.module_utils.cmd_runner предоставляет класс CmdRunner для выполнения внешних команд. Этот класс является оболочкой для стандартного метода AnsibleModule.run_command(), обрабатывающей аргументы команды, настройку локали, обработку вывода, режим проверки и другие функции.

Эта утилита особенно полезна, когда одна команда используется в нескольких модулях: можно определить все параметры в файле утилиты модуля, и каждый модуль будет использовать один и тот же исполнитель с разными аргументами.

Для ясности в этом руководстве, если не указано иное, термин параметр относится к параметрам модуля Ansible, а термин аргумент — к аргументам командной строки внешней команды.

Краткое руководство

CmdRunner определяет команду и набор инструкций в коде о том, как форматировать аргументы командной строки и в каком порядке для конкретного запуска. Для фактического выполнения команды используется ansible.module_utils.basic.AnsibleModule.run_command(). Есть и другие функции — подробности см. далее в этом документе.

Чтобы использовать CmdRunner, сначала необходимо создать объект. Пример ниже представляет собой упрощённую версию фактического кода из community.general.ansible_galaxy_install:

from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt

runner = CmdRunner(
    module,
    command="ansible-galaxy",
    arg_formats=dict(
        type=cmd_runner_fmt.as_func(lambda v: [] if v == 'both' else [v]),
        galaxy_cmd=cmd_runner_fmt.as_list(),
        upgrade=cmd_runner_fmt.as_bool("--upgrade"),
        requirements_file=cmd_runner_fmt.as_opt_val('-r'),
        dest=cmd_runner_fmt.as_opt_val('-p'),
        force=cmd_runner_fmt.as_bool("--force"),
        no_deps=cmd_runner_fmt.as_bool("--no-deps"),
        version=cmd_runner_fmt.as_fixed("--version"),
        name=cmd_runner_fmt.as_list(),
    )
)

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

# Run the command with these arguments, when values exist for them
with runner("type galaxy_cmd upgrade force no_deps dest requirements_file name", output_process=process) as ctx:
    ctx.run(galaxy_cmd="install", upgrade=upgrade)

# version is fixed, requires no value
with runner("version") as ctx:
    dummy, stdout, dummy = ctx.run()

# passes arg 'data' to AnsibleModule.run_command()
with runner("type name", data=stdin_data) as ctx:
    dummy, stdout, dummy = ctx.run()

# Another way of expressing it
dummy, stdout, dummy = runner("version").run()

Обратите внимание: значения аргументов можно передать при вызове run(); в противном случае CmdRunner использует для задания значений аргументов исполнителя параметры модуля с точно такими же именами. Если значение не передано и параметр модуля с указанным именем не найден, возникает исключение, кроме случая, когда для форматирования аргумента используется cmd_runner_fmt.as_fixed, как для version в примере выше. Подробнее об этом см. ниже.

В первом примере значения type, force, no_deps и других берутся непосредственно из модуля, а galaxy_cmd и upgrade передаются явно.

Примечание

Автоматически получить значения вложенных параметров невозможно.

В результате формируется командная строка, подобная следующей (пример взят из вывода интеграционного теста):

[
    "<venv>/bin/ansible-galaxy",
    "collection",
    "install",
    "--upgrade",
    "-p",
    "<collection-install-path>",
    "netbox.netbox",
]

Форматы аргументов

Как видно из примера, CmdRunner ожидает параметр с именем arg_formats, определяющий способ форматирования каждого именованного аргумента CLI. «Формат аргумента» — это функция, преобразующая значение переменной в формат, подходящий для командной строки.

Функция форматирования аргумента

Функция arg_format определяется примерно так:

def func(value):
    return ["--some-param-name", value]

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

Ожидается, что результат будет иметь тип Sequence[str] (чаще всего list[str] или tuple[str]); в противном случае он считается str и преобразуется в list[str]. Полученная последовательность строк добавляется в командную строку, когда используется соответствующий аргумент.

Например, если func возвращает:

  • ["nee", 2, "shruberries"], в командную строку добавляются аргументы "nee" "2" "shruberries".
  • 2 == 2, в командную строку добавляется аргумент True.
  • None, в командную строку добавляется аргумент None.
  • [], для этого аргумента в командную строку не добавляется ни одного аргумента.

Вспомогательные методы форматирования

В том же модуле, что и CmdRunner, находится класс cmd_runner_fmt, предоставляющий набор вспомогательных методов, которые возвращают функции форматирования для распространённых случаев. В первом фрагменте кода раздела Краткое руководство показан импорт этого класса:

from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt

В том же примере показано, как использовать некоторые из этих методов при создании объекта CmdRunner. Ниже приведено описание каждого доступного вспомогательного метода и примеры его использования. В этих описаниях value обозначает единственный параметр, передаваемый функции форматирования.

  • cmd_runner_fmt.as_list()

    Этот метод не принимает параметров; функция возвращает value без изменений.

    • Создание:

      cmd_runner_fmt.as_list()

    • Примеры:

      Значение

      Результат

      ["foo", "bar"]

      ["foo", "bar"]

      "foobar"

      ["foobar"]

  • cmd_runner_fmt.as_bool()

    Этот метод принимает два разных параметра: args_true и args_false, причём второй параметр необязателен. Если результат булевой оценки value — True, функция форматирования возвращает args_true. Если результат булевой оценки — False, функция возвращает args_false, если он был задан, или [] в противном случае.

    • Создание (один аргумент):

      cmd_runner_fmt.as_bool("--force")

    • Примеры:

      Значение

      Результат

      True

      ["--force"]

      False

      []

    • Creation (two args, None treated as False):

      cmd_runner_fmt.as_bool("--relax", "--dont-do-it")

    • Примеры:

      Значение

      Результат

      True

      ["--relax"]

      False

      ["--dont-do-it"]

      ["--dont-do-it"]

    • Creation (two args, None is ignored):

      cmd_runner_fmt.as_bool("--relax", "--dont-do-it", ignore_none=True)

    • Примеры:

      Значение

      Результат

      True

      ["--relax"]

      False

      ["--dont-do-it"]

      []

  • cmd_runner_fmt.as_bool_not()

    Этот метод принимает один параметр, который функция возвращает, если булева оценка value равна False.

    • Создание:

      cmd_runner_fmt.as_bool_not("--no-deps")

    • Примеры:

      Значение

      Результат

      True

      []

      False

      ["--no-deps"]

  • cmd_runner_fmt.as_optval()

    Этот метод принимает один параметр arg; функция возвращает конкатенацию строк arg и value.

    • Создание:

      cmd_runner_fmt.as_optval("-i")

    • Примеры:

      Значение

      Результат

      3

      ["-i3"]

      foobar

      ["-ifoobar"]

  • cmd_runner_fmt.as_opt_val()

    Этот метод принимает один параметр arg; функция возвращает [arg, value].

    • Создание:

      cmd_runner_fmt.as_opt_val("--name")

    • Примеры:

      Значение

      Результат

      abc

      ["--name", "abc"]

  • cmd_runner_fmt.as_opt_eq_val()

    Этот метод принимает один параметр arg; функция возвращает строку вида {arg}={value}.

    • Создание:

      cmd_runner_fmt.as_opt_eq_val("--num-cpus")

    • Примеры:

      Значение

      Результат

      10

      ["--num-cpus=10"]

  • cmd_runner_fmt.as_fixed()

    Этот метод задаёт один или несколько фиксированных аргументов, которые возвращаются созданной функцией независимо от того, передан ли ей value.

    Этот метод принимает аргументы в одном из трёх форматов:

    • один скалярный параметр arg, который функция вернёт как [arg], либо
    • один параметр-последовательность, например список, arg, который функция вернёт как arg[0], либо
    • несколько параметров args, которые функция вернёт непосредственно как args.

    Примеры каждого из этих форматов приведены ниже. Обратите внимание: созданная функция не ожидает value; если он передан, то игнорируется.

    • Создание (один скалярный аргумент):
      • cmd_runner_fmt.as_fixed("--version")
    • Примеры:

      Значение

      Результат

      • ["--version"]

      57

      • ["--version"]
    • Создание (один аргумент-последовательность):
      • cmd_runner_fmt.as_fixed(["--list", "--json"])
    • Примеры:

      Значение

      Результат

      • ["--list", "--json"]

      True

      • ["--list", "--json"]
    • Создание (несколько аргументов):
      • cmd_runner_fmt.as_fixed("--one", "--two", "--three")
    • Примеры:

      Значение

      Результат

      • ["--one", "--two", "--three"]

      False

      • ["--one", "--two", "--three"]
    • Примечание:

      Это единственный особый случай, когда значение для функции форматирования может отсутствовать. Первый пример взят из кода раздела Краткое руководство. В этом случае в модуле есть код, определяющий версию команды, чтобы проверить её совместимость. Для этого аргумента CLI не нужно передавать никакое значение.

  • cmd_runner_fmt.as_map()

    Этот метод принимает один параметр arg, который должен быть словарём, и необязательный параметр default. Функция возвращает результат вычисления arg[value]. Если value not in arg, то она возвращает default, если он задан, или [] в противном случае.

    • Создание:

      cmd_runner_fmt.as_map(dict(a=1, b=2, c=3), default=42)

    • Примеры:

      Значение

      Результат

      "b"

      ["2"]

      "yabadabadoo"

      ["42"]

    • Примечание:

      Если default не задан, недопустимые значения возвращают пустой список, то есть молча игнорируются.

  • cmd_runner_fmt.as_func()

    Этот метод принимает один параметр arg, который сам является функцией форматирования и должен соответствовать описанным выше правилам.

    • Создание:

      cmd_runner_fmt.as_func(lambda v: [] if v == 'stable' else ['--channel', '{0}'.format(v)])

    • Примечание:

      Результат полностью зависит от функции, предоставленной разработчиком.

Другие возможности форматирования аргументов

Некоторые дополнительные возможности доступны в виде декораторов:

  • cmd_runner_fmt.unpack args()

    Этот декоратор распаковывает входящий value в список элементов.

    Например, в ansible_collections.community.general.plugins.module_utils.puppet он используется так:

    @cmd_runner_fmt.unpack_args
    def execute_func(execute, manifest):
        if execute:
            return ["--execute", execute]
        else:
            return [manifest]
    
    runner = CmdRunner(
        module,
        command=_prepare_base_cmd(),
        path_prefix=_PUPPET_PATH_PREFIX,
        arg_formats=dict(
            # ...
            _execute=cmd_runner_fmt.as_func(execute_func),
            # ...
        ),
    )
    

    Затем в community.general.puppet он используется следующим образом:

    with runner(args_order) as ctx:
        rc, stdout, stderr = ctx.run(_execute=[p['execute'], p['manifest']])
    
  • cmd_runner_fmt.unpack_kwargs()

    В отличие от предыдущего, этот декоратор распаковывает входящий value как объект типа dict.

  • cmd_runner_fmt.stack()

    Этот декоратор предполагает, что value является последовательностью, и объединяет результаты применения обёрнутой функции к каждому элементу последовательности.

    Например, в community.general.django_check формат аргумента для database определяется так:

    arg_formats = dict(
        # ...
        database=cmd_runner_fmt.stack(cmd_runner_fmt.as_opt_val)("--database"),
        # ...
    )
    

    При получении списка ["abc", "def"] результат будет следующим:

    ["--database", "abc", "--database", "def"]
    

Исполнитель команд

Конструктору CmdRunner можно передать следующие параметры:

  • module: AnsibleModule

    Экземпляр модуля. Обязательный параметр.

  • command: str | list[str]

    Команда для выполнения. Это может быть одна строка — имя исполняемого файла — или список строк, в котором первый элемент является именем исполняемого файла, а последующие элементы, если они есть, — фиксированными параметрами. Эти параметры используются при каждом запуске исполнителя. Исполняемый файл, указанный этим параметром (сам параметр, если str, или его первый элемент, если list), обрабатывается с помощью AnsibleModule.get_bin_path(), если только он не является абсолютным путём и не содержит символ /.

  • arg_formats: dict

    Соответствие имён аргументов функциям форматирования.

  • default_args_order: str

    Как следует из названия, порядок аргументов по умолчанию. Если он задан, контекст можно создать, не указывая args_order. По умолчанию имеет значение ().

  • check_rc: bool

    Если True, а код возврата команды не равен нулю, модуль завершает работу с ошибкой. По умолчанию имеет значение False.

  • path_prefix: list[str]

    Если выполняемая команда установлена в нестандартный каталог, для поиска исполняемого файла можно указать дополнительные пути. По умолчанию имеет значение None.

  • environ_update: dict

    Дополнительные переменные среды, которые нужно установить на время выполнения команды. По умолчанию имеет значение None.

  • force_lang: str

    Обычно важно принудительно задать конкретную локаль, чтобы ответы были единообразными и, следовательно, пригодными для разбора. Обратите внимание: использование этого параметра (включённого по умолчанию) перезаписывает переменные среды LANGUAGE и LC_ALL. Чтобы отключить этот механизм, задайте для параметра значение None. В community.general 9.1.0 для этого параметра было введено специальное значение auto, при котором CmdRunner пытается определить наиболее подходящую для разбора локаль среды выполнения. В будущем это значение должно стать значением по умолчанию, но пока значение по умолчанию — C.

При создании контекста вызову можно передать следующие дополнительные параметры:

  • args_order: str

    Задаёт порядок отображения аргументов в командной строке. Этот параметр обязателен, если default_args_order не был задан для экземпляра исполнителя.

  • output_process: func

    Функция для преобразования вывода исполняемого файла в другие значения или форматы. Примеры см. в следующем разделе.

  • check_mode_skip: bool

    Следует ли пропускать фактическое выполнение команды, когда модуль находится в режиме проверки. По умолчанию имеет значение False.

  • check_mode_return: any

    Если check_mode_skip=True, вместо этого возвращается указанное значение.

  • valid named arguments to AnsibleModule.run_command()

    Помимо args, при настройке контекста запуска можно передать любой допустимый аргумент для run_command(). Например, data можно использовать для передачи данных в стандартный ввод команды. А cwd позволяет запустить команду в заданном рабочем каталоге.

Кроме того, можно передать любые другие допустимые параметры для AnsibleModule.run_command(), однако переопределение параметров, уже заданных в исполнителе или при создании его контекста, может привести к неожиданному поведению. Используйте с осторожностью.

Обработка результатов

Как уже упоминалось, CmdRunner использует AnsibleModule.run_command() для выполнения внешней команды и передаёт вызывающему коду значение, возвращённое этим методом. Это означает, что по умолчанию результатом будет кортеж (rc, stdout, stderr).

Если необходимо преобразовать или обработать этот вывод, контексту можно передать функцию в качестве параметра output_process. Она должна иметь примерно такой вид:

def process(rc, stdout, stderr):
    # do some magic
    return processed_value    # whatever that is

В этом случае результатом run() будет processed_value, возвращённый функцией.

PythonRunner

Класс PythonRunner — это специализированная версия CmdRunner, предназначенная для выполнения сценариев Python. В его конструкторе есть два дополнительных взаимоисключающих параметра: python и venv:

from ansible_collections.community.general.plugins.module_utils.python_runner import PythonRunner
from ansible_collections.community.general.plugins.module_utils.cmd_runner import cmd_runner_fmt

runner = PythonRunner(
    module,
    command=["-m", "django"],
    arg_formats=dict(...),
    python="python",
    venv="/path/to/some/venv",
)

Значением python по умолчанию является строка python, а значением venv — None.

Командная строка, создаваемая такой командой с python="python3.12", выглядит примерно так:

/usr/bin/python3.12 -m django <arg1> <arg2> ...

Командная строка для venv="/work/venv" выглядит так:

/work/venv/bin/python -m django <arg1> <arg2> ...

Значение аргумента command можно указать строкой (в этом случае строка используется как имя сценария) или списком; элементы списка должны быть допустимыми аргументами интерпретатора Python, как в примере выше. Подробнее см. в разделе Командная строка и среда.

Если параметр python является абсолютным путём или содержит разделители каталогов, например /, он используется как есть; в противном случае среда выполнения PATH выполняет поиск команды с таким именем.

В остальном всё работает так же, как в CmdRunner.

Добавлено в версии 4.8.0.

© 2012–2018 Michael DeHaan
© 2018–2025 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/projects/ansible/latest/collections/community/general/docsite/guide_cmdrunner.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API