Руководство по запуску команд
Введение
Утилита модуля 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