Pull to refresh

Comments 8

Про эту проблему есть долговисящий proposal который совсем не спешат реализовывать.

Спасибо за наводку, отметился в нём.

Меня ваш код сильно смутил. Если вы пишите в сокет, а из этого сокета никто не читает, то процесс блокируется. Более того, ничего, из того, что вы сделали, не требует участия ансибла — вы command можете ровно так же завернуть в любой враппер (включая, например, socat) и спокойно читать, не привнося неожиданностей в работу самого ансибла.


Если же говорить про решение силами ансибла, то async для этого и придуман. stdout при этом брать тяжело (потому что он зарезервирован под результаты выполнения), но простейший вариант будет просто писать статистику локально, а poll брать статистику с удалённой машины, до тех пор, пока задача всё ещё работает, и показывать через debug или прямую запись в stdout ансибла на контроллере.

Я просто текст из своего поста в чате здесь оставлю:
[В ответ на Timur Gadiev]
Ну смотри, сейчас это в виде MVP.
Соответственно, есть следующие проблемы:
1. Аргументы для ssh захардкожены — для передачи выбран кастомный порт, который, возможно, будет занят
2. Вся обработка приёма потоков stdout на контроллере как таковая отсутствует
3. Нет разграничения потоков от разных команд
4. На целевой тачке никак не обрабатывается ситуация, когда порт на контроллере по какой-то причине никто не слушает (error 32 — Broken pipe)

Время поста — 13:38 MSK. Sapienti sat.

Понимаете, фокус в том, что критиковать могут «не только лишь все», а вот делать… В общем, с удовольствием почитаю вашу статью с продакшн-качества кодом, который будет решать какую-нибудь из давних болячек Ansible (конкретно эта датирована январём 2018).

Я не уверен, что я хочу нырять в ansible-core. Но у меня есть идея, которые я, надеюсь, сделает жизнь людям лучше. Одна из них — это remote testinfra, т.е. запуск тестов testinfra на удалённой машине (с правильно заполненным host в localhost). Это будет, к сожалению, action plugin, потому что в виде модуля оно не даст правильного контроля за копированием теста.


Алсо, вот эту штуку (которая ссылка) я бы сделал чуть-чуть по-другому. Я бы предоставлял всем subprocess'ам на ансибле дополнительный fd для логгинга (echo hello >3), который бы писался в соответствующее поле register, а при async'е позволял бы получить для выполняемого в бэкграунде задания.


Но это тектонические изменения для того, чтобы кому-то нарисовать прогресс-бар.


Качество моего кода под ансибл можете посмотреть тут — https://github.com/amarao/collection_ip

Ну вообще-то статья была не о том, чтобы качеством кода меряться, а о самом концепте.

Концепт не предусматривает какое-либо вмешательство в аргументы вызовов чего бы то ни было по одной простой причине: все модули Ansible, которые существуют в 2.9 (о коллекциях не говорю), не поддерживают такой способ работы.

А если всё же о коде, то вот чего: код парсит экранный вывод утилит командной строки. О том, что это bad practice, в интернетах писалось 100500 раз, и это перебивает любое качество кода.

вы про коллекцию ip?


  1. У ip есть обещанный правильный вывод (-o).
  2. Между ip и ядром есть netlink (man 7 netlink), но реализация своего клиента netlink'а гарантировано будет хуже, чем ip.
  3. Пользователь, которому вернули ошибку уровня netlink ничего не сможет сделать или понять, а для ip — сможет воспроизвести руками и понять что там не так.

iproute2 — это не совсем "утилита", примерно как udev.

Я решал похожую задачу для долгоиграющих процессов, остановился на screen и передаче аутпута через файл:


  • с процессом можно взаимодействовать вручную на машине, если нужно через screen
  • не нужен отдельный порт – вывод передается через tail основным ansible-каналом
  • минус в том, что интеррапт ансибла не прерывает сам процесс (но может быть это и плюс), а так же если процесс ждет ввода ансибл об этом не знает и тоже ждет
  • приходится костылить для получения exit code

            self._shell(" && ".join([
                "echo -e 'logfile %s\\nlogfile flush %d' >> %s" % (logfile, delay, config_path),
                "sudo touch %s" % logfile,
                "sudo chmod a+rw %s" % logfile
            ]))
            exit_code = "EXIT CODE "

            started = datetime.datetime.now()
            self._shell("screen -L -c %s -S %s -d -m sh -c $'(%s); echo %s$?'" %
                        (config_path, task_id, command.replace("'", "\\'"), exit_code))

            self._display.display("Command started in screen -r %s" % task_id)
            self._display.display("> %s\n" % command, color=C.COLOR_VERBOSE)

            offset = 1
            rc = None
            while True:
                status = self._raw("screen -S %s -Q info" % task_id)
                alive = status["rc"] == 0

                chunk = self._shell("tail -c +%d %s" % (offset, logfile))["stdout"]
                chunk_len = len(to_bytes(chunk))
                offset = offset + chunk_len

                last = chunk.rfind(exit_code)
                if last > -1:
                    rc = chunk[last + len(exit_code):]
                    rc = int(rc) if rc.isnumeric() else None

                if chunk_len > 0:
                    self._display.display(chunk[2:] if chunk.startswith("\r\n") else chunk)
Sign up to leave a comment.

Articles