Python на примере демона уведомления о новых коммитах Git
Работая в команде я люблю быть в курсе активности участников. Поэтому было решено написать демона наблюдающего за поступлением новых коммитов в репозиторий git’а. Так как я работаю в Ubuntu, то уведомление было реализовано встроенным способом — библиотекой libnotify.
Язык — Python!
В статье упоминается:
1. Демон на Python;
2. Логирование на Python;
3. Хранение конфигурационных файлов программ на Python;
4. Работа с командами ОС из скриптов Python;
5. Получения списка последних изменений из git’а;
6. Стандартные всплывающие уведомления Ubuntu.
Для реализации задачи был выбран язык Python (высокоуровневый, интерпретируемый, объектно-ориентированный и расширяемый язык программирования), так как я его не знаю.
Для начала мне очень помогли два источника:
— официальная документация: http://docs.python.org/tutorial/index.html;
— цикл статей IBM на русском языке: https://www.ibm.com/developerworks/ru/library/l-python_part_1/.
Во время изучения основ, приступаем к написанию программы.
1. Демон
В сети встречается много реализаций демонов, выбрал один из готовых с положительными отзывами и привлекательным названием: http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/. У этого демона были проблемы с завершением работы командой «daemon.py stop» вот в этом месте:
Это как видно возникло это из-за русской локали, фраза «No such process» в моей системе возвращалась на русском языке. Простой способ исправить — удалить эту проверку:
2. Ведение логов
Простейшим средством оповещение о процессе работы программы является использование функции print(). Но эта программа будет запускаться в качестве демона и не предполагает вывод информации о своем состоянии в консоль запуска. Удобным вариантом в этом случае является запись лога в файл. В Python’е есть встроенный способ логирования входящий в стандартную библиотеку — модуль logging (http://docs.python.org/library/logging.html).
Модуль имеет множество вариантов ведения лога (handlers, http://docs.python.org/library/logging.handlers.html): StreamHandler, FileHandler, WatchedFileHandler, RotatingFileHandler, TimedRotatingFileHandler, SocketHandler, DatagramHandler, SysLogHandler, NTEventLogHandler, SMTPHandler, MemoryHandler, HTTPHandler. Для контроля работы демона использовался FileHandler:
3. Хранение конфигурации программ на Python
Для хранения конфигурации приложений используется ini файл и встроенный модуль работы с ними ConfigParser (http://docs.python.org/library/configparser.html):
Получение значения параметров функциями (в данном случае получение integer значения):
4. Работа с командам ОС из скриптов Python
Для выполнения системных команд используется метод check_output() модуля subprocess (http://docs.python.org/library/subprocess.html):
Также можно использовать методы модуля os:
Документация рекомендует использовать subprocess.
Создать демона на python 2.7 ?
Всегда использую supervisord. Просто отличная штука, ещё и на питоне написанная. Гибкие конфиги, перезапуск при падении. и другие плюшки.
UPD
Как использовать:
1) Ставишь его общесистемно sudo apt-get install supervisor (можно и через пип поставить, но настраивать надо побольше будет).
2) Надо настроить сам супервизор: откуда он будет брать конфиги, какие права нужны и т.д. Если у тебя убунта, то настройки самого супервизора будут в порядке. Они хранятся в /etc/supervisor/supervisord.conf. В этом файле прописано, что настройки для демонов будут читаться из папки /etc/supervisor/conf.d
Если у тебя не убунта, или ты ставил супервизор через пип, то вот стандартный конфиг. Настройки там очевидные
3) Когда супервизор поставлен и настроен, запускай его:
4) Теперь надо сделать конфиг для запуска твоего скрипта. Создаёшь файл в папке с конфигами такого содержания:
Всё! Управлять твоими демонами можно через команду supervisorctl. Сперва надо перепрочитать конфиг, потом стартануть приложение:
Можно опустить ОТОБРАЖАЕМОЕ_ИМЯ, тогда команда будет применена ко всем.
Также ты можешь запустить CLI, если введёшь supervisorctl без параметров. Там по табу смотри команды.
Читай этот раздел, для понимания конфигов запускаемых приложений.
>И работатет в фоновом режиме без терминала и прочего. Тоесть только показивет собщение ?
Извените, а куда он будет вам показывать сообщение ? Только если в лог писать.
Или вы в X выводите инфомацию ?
Для запуска при старте системы надо написать скрипт для старта вашей программы. В теории можно и на bash забацать демона. Если вам интересен этот вариант, я дам пример скрипта.
Давайте я сделаю небольшой итог.
сделать свой скрипт нормальным демоном или использовать слд варианты в linux.
Использовать /etc/rc.local для запуска программы в фоновом режиме (&) с перенаправленными потоками ввода/вывода. Можно воспользоваться командой nohup.
Использовать inittab и указать уровень запуска программы (не рекомендую)
Написать скрипт на bash, позволяющий запускать/останавливать/перезапускать программу как демона, а также получать информацию о её состоянии.
@icCE: Не, я имею ввиду, к чему такая сложность в запуске самого демона. Вот есть готовый питоновский скрипт. Его надо демонизировать. Самый (на мой взгляд) простой и одновременно надёжный вариант — использовать supervisord.
Он позволяет с лёгкостью запускать всякие скрипты, сервера приложений (для питона, для руби, для любого другого языка). Очень прост в использовании. Позволяет настраивать логирование, запуск от определённого пользователя, переменные окружения (для virtualenv, например), перезапуск при падении, запуск и мониторинг сразу нескольких задач, автостарт и многое другое.
Т.е. отпадает необходимость вручную писать баш-скрипты, которые хранят пид, запускают программу, перенаправляют вывод и т.д. Это всё просто автоматизированно и очень удобно. Ты просто описываешь конфиг для каждой задачи, требующей демонизации, читаешь его (для supervisord есть удобный CLI), запускаешь. Далее, ты можеш остановить отдельную задачу, посмотреть статус, и т.д.
В техническом смысле демоном считается процесс, который не имеет управляющего терминала.
Это с вики. Можешь ещё на английской прочитать, как демоны создавать. В некоторых дистрах есть start-stop-daemon.
Почему я в скрипте это «зациклел»
Ты зациклил только потому, что сама программа, которую ты запускаешь, делает какое-то действие и выходит. По-сути, ты написал новую программу на баше, которая в цикле выполняет какой-то процесс, анализирует его вывод и спит некоторое время, после чего по-новой.
Так вот, если сама программа сама запускается в бесконечном цикле, то вручную зацикливать не нужно. Если просто запустить такую программу, то терминал будет занят, в нём будет вывод такой программы. Если запустить в фоне, то процесс всё равно будет привязан к терминалу, поэтому при закрытии терминала такой процесс будет завершён. Если же отсоединить такой процесс, то получится как раз демон.
почему у многих программ есть отдельный режим работы демоном ?
потому что обычно такие программы просто запускают бесконечный цикл, т.н. loop. Это может быть луп от GUI-фреймворка (например, в qt вызов метода QApplication::exec запускает такой бесконечный цикл), может быть ioloop какого-нибудь сетевого фреймворка (например, в питоне 3.4 это asyncio.get_event_loop().run_forever()). Т.е. сама программа выполняется «бесконечно». И такие программы демонизируются внешне: обычно это простой скрипт в init.d. Т.е. как такового режиме демона нет. Вот простой пример: монгоДБ. Она предоставляет 2 команды: mongod и mongo. Первая запускается в бесконечном цикле. Если отдельно её не демонизировать, то процесс при закрытии терминала умрёт. Демонизация происходит в init.d. Да, некоторый программы сразу могут запускаться как демон, но за кулисами там точно такой же алгоритм.
и как бы мне помог ваш супервизор в данной ситуации.
Очень просто: весь твой бесконечный цикл надо вынести в bash-скрипт, а в супервизоре в настройках надо просто указать нужную команду.
Легко настраиваемый python daemon
В какой-то момент мне потребовалось демонизировать некоторое действие на python. В сети валялась куча примеров подобной деятельности разной степени собранности. Так как в дальнейшем предполагалось использовать код демонизации в дальнейшей деятельности я решил разнести на разные части настройки и собственно демона.
В итоге файлов получилось три:
- Родительские классы — с небольшим изменением классы взятые из интернет статей
- Классы настройки — реакция на сигналы, реакция на команды и набор статических настроек для запуска
- Скрипт запуска — собирает первые два в собственно демона
Дальше я попробую описать логику работы всех трех.
Сразу скажу, что все есть на Гитхабе. Потому как если вы легко читаете питон — читать мой весьма неумелый текст может оказаться гораздо сложнее.
Собственно в первом файлике описывать толком нечего: Это почти неизменные три класса, взятые из этой статьи. Из изменений там только то, что к самому демону был прикреплен класс обработчик сигналов, и добавление сигналов в список обрабатываемых было вкручено в собственно процедуру демонизации.
Вторая часть будет чуть интереснее. Там присутствует три класса:
1) SigFunctionsCon — содержит реакцию на сигналы. При инициализации получает экземпляр демона, чтобы уметь обращаться к его методам. Каждый метод должен соответствовать сигналу, который он обрабатывает названием. Например так:
Внутренние методы и поля могут быть какими угодно.
2)ReactFunctionCon — содержит реакцию на консольные команды. При инициализации так же получает демона. Каждый метод по названию должен соответствовать команде на которую он будет реагировать и может принимать аргументы (то, что собственно идет за командой в командной строке). Например:
3)StatCon — содержит всякие статические настройки демона. На данный момент выглядит так:
Соответственно —
Хелп строка, выводимая при неправильной передаче аргументов в какую-либо функцию (Возможно следует сделать команду хелп по умолчанию, которая выводит это сообщение?).
Метод run — собственно то, для чего все затевалось — то, что демон делает.
Адрес pid файла — для хранения процесса и все такое.
Ввод, вывод, ошибки — логгирование и прочее. По умолчанию отсылается в /dev/null
Центровой скрипт представляет интерес исключительно кодом. В общем говоря он наследует класс демона, собирает все настройки с предыдущего файла и раскладывает их по демону, и принимает команды.
Ну и собственно вопросы:
Что не так, что не очень так?
Как по вашему следует ли как-то приписывать к этому GPL, или не стоит больгеносить, и все это слишком несерьезно?
Достаточно ли адекватно я указал предыдущих авторов?