Решили мы однажды заняться автоматизацией наших рутинных рабочих моментов. Создать у себя ансамбль(ansible) или что-нибудь в этом роде. Я полез на сайт ансамбля, посмотрел как он работает … подключается к удаленному серверу через ssh и выполняет какие-то там скрипты... Стоп, подумал я, разве для этого нужен ансамбль? Я и без ансамбля, сам ..., один… могу это сделать! А чтобы не только лишь я это мог, решил написать вэб приложение. Приложение назвали Update Server, сокращенно UpS.

Ворнинг! Под катом много картинок и пара гифок.

Приложение я решил делать при помощи питона и джанги. Тут хватает специалистов и «специалистов» и по тому и по другому. Поэтому я не буду сейчас мериться количеством строк кода. Желающие могут заглянуть сюда секретов там нет, а большинство команд захардкожены специализированы под наш проект. Расскажу(и покажу) только о том, какой функционал получилось реализовать в приложении. Так вот. Взял я в руки свой питон(python), к слову, питон у меня не очень большой, обычный, среднестатистический 2.7. Достал для него новенькую(по тем временам) джангу 1.10 и начал их соединять. И так и эдак соединял. Сверху и снизу заходил, по-всякому пробовал. И получилась в итоге вот такая штука.


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


Кликнув в джумботрон Projects мы попадаем на страничку выбора проектов.


Выбрав проект попадаем на основную страницу со списком серверов, скриптов и т.д.


Перейдем к подробностям. В приложении обозначилось несколько сущностей: сервер(server), скрипт(script), пакет обновления(update), задача в кроне(cron job), событие в истории(event), проект(project) и не совсем сущьность дамп(dump).

Небольшое лирическое отступление
Играем с сыном в Titan quest, вернее я играю, а он смотрит) Там из монстров выпадают некие артефакты, в русской версии называющиеся сущностями. Вот в один прекрасный день выпала мне какая-то сущность Ахилеса чего-то там. На картинке что-то другое, но из той же оперы)
Сын увидел и кричит:
— пап, гляди суЧность Ахилеса!)

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


Тут мы видим доступные скрипты и серверы(серверы доступны на всех закладках, т.к. нужны практически всегда). Скрипты можно загружать из файла или создавать в интерфейсе кнопкой Add. Поддерживаются SH, PY, YML и SQL скрипты. В панели скрипта есть описание(description), кусочек кода(preview), автор и дата. Для SH и PY скриптов предусмотрена возможность добавлять опции(поле options). Выбирается скрипт(и любой другой объект) методом клика на любой части панельки(кроме «быстрых» кнопок). Клик на скрипт(второй, третий и т.д.), клик на сервер(второй, третий и т.д.) и клик на Run script. Выбранный скрипт или скрипты будут выполнены на выбранном сервере или серверах. Если нужно «по быстренькому» выполнить один скриптик, можно воспользоваться «быстрой» кнопкой на панельке скрипта, клик на сервер(второй, третий и т.д.) и клик на Run нужного скрипта. Пока писал статью появились изменения. Отделил SQL скрипты от остальных, добавил для выполнения эскуелей отдельную команду и пермишон. Это позволило давать, например, только права на выполнение эскуэлей, т.к. SH и PY гораздо мощней, они дают практически неограниченные возможности. Но переделывать скриншоты я не стал) Скриптами можно было и ограничиться, практически все можно сделать ими, но остановиться уже было трудно).
Идем дальше, пакеты обновлений.


На этой закладке можно добавлять новые апдейты, копировать их на серверы, выполнять обновление.На самом деле это могут быть любые файлы, как в этом примере в качестве тестового апдейта используется обычный txt файл. Закачиваешь на сервер файл(чаще всего это zip) и как-то используешь его при помощи скриптов. У апдейтов тоже есть описание, автор, дата и показан размер.
Дальше идут дампы.


Тут отображается список файлов, полученных командой Get dump или закачанные сюда с компа. У дампов описания нет, есть только имя, размер и время создания. Наверное не только лишь все хоть раз(два, три) грохали продуктовую базу? Я вывел эту возможность на новый уровень) Send dump эта команда разворачивает базу сервера из выбранного дампа. Чувствуете возможности?(слышится треск пуканов) Да в неумелых руках эта команда может убить ВСЕ базы на ВСЕХ серверах буквально несколькими кликами! Именно поэтому кнопка этой(и подобных) команды закрашена красненьким, нажимать ее следует крайне аккуратно. Для объемных дампов конечно проблематично использовать, а для маленьких тестовых баз очень удобная штука. Крон.


Все это безобразие можно запускать через крон. На этой закладке отображаются активные задачи. У каждой задачи есть уникальный идентификатор(crontab id), он необходим для последующих манипуляций с ней. В описании указано, что задача будет делать. В примере выполнить скрипт test2.sh на сервере с адресом jboss@localhost. Время начала задания указано в поле Start date в формате YYYY-MM-DD HH:MM Обозначен автор и время создания. Можно отменить задачу, изменить время, сделать задачу постоянной(будет выполнятся каждый день в указанное время) или наоборот одноразовой.
Логи выполняющихся команд появляются тут.


С логами ничего делать нельзя, все уже сделано, сиди и смотри что получилось) Клик на табличку лога открывает его в отдельном окне. Если выполняется несколько процессов появляется кнопка All logs, открывает все логи в одном окне(я забыл сделать этот скриншот).

Серверы. Как я уже говорил, список серверов доступен на всех закладках. У серверов отображается адрес(ssh address), можно записывать в формате user@address, но лучше пользоваться ssh алиасами(~/.ssh/config) и задавать какие-то осмысленные имена. HTTP adress — это урл приложения развернутого на сервере. Рабочий каталог(working folder) и порт(server bind port), это папка, в которой находится приложение и порт, который оно открывает соответственно. Описание, автор, дата. На скриншоте засветилась пара новых команд, добавленных во время написания статьи: Prop и Full. У нас есть два основных конфигурационных файла, эти команды позволяют их редактировать, удобно. Остальные команды. Сначала красненькие:
Maintenance ON\OFF — включить\отключить страницу «Регламентные работы» на время регламентных работ O_o или если что-то пошло не так(после send dump например).
Reload config — перезагружает конфиг jboss'а(наш проект использует jboss).
Restart\start\stop\kill jboss — остановка, запуск, перезапуск jboss'а и жестокое убийство если уже ничего не помогает)
Дальше разные полезные штуки:
System info — показывает информацию о сервере: память, диск, процессор, пользователи, процессы.
Check health — быстрая проверка работоспособности приложения, ищет процесс приложения, проверяет открыт ли порт и пытается курлом в этот порт постучаться.
Check conf — показывает конфигурационный файл приложения.
Check logs — показывает логи приложения.
Check GC log — показывает лог гарбаж колектора.
Get day\week\month\all logs — позволяет скачать логи за определенное время одним архивом.
Backup base\system\full — создает резервную копию базы, файлов приложения, делает и то и другое.
Copy utils — копирует всякие разные скриптики на сервер.
Peep passwords — позволяет подсмотреть пароль админа.
Check updates — показывает какие файлы обновлений закачаны на сервер.
Create tunnel — делает проброс порта приложения, с сервера приложения на UpS'а (у нас ведь ssh). Можно подключиться к порту приложения напрямую. Полезно для проверки работоспособности в случае отсутствия доступа(http\s) по основному адресу.
Команды System info, Check health, Check conf, Check logs, Peep passwords, Check updates и Create tunnel имеют «быстрые» аналоги непосредственно на панели каждого сервера.
В верхней панели есть ссылка History, в истории записывается результат выполнения всех(почти) команд. На просмотр истории и выполнение команд необходимы пермишоны. Пермишоны навешиваются стандартной джанговской админкой. Единственное что я тут добавил, прикрутил библиотеку джанго-гардиан, которая позволяет навешивать пермишоны на объекты, мне это показалось очень удобным, а джанга не умеет делать это из коробки.

Я конечно не уи гуру, но постарался сделать управление легким и не принужденным) Прокликиваем необходимые объекты, выбираем команду и ой всё. Нажми на кнопку, получишь результат. Как-то так.


Пара слов о том, как это все работает. Питон и джанга, как я уже говорил, вьювс, моделс, урлс, формс, вот это вот все и есть такой файлик commands.py. В этом файлике описываются доступные команды(пока все логично, да?). Выглядит это как-то так:
class CommandClass:
	"""Класс команды"""
	def __init__(
		self,
		permission='run_command',  # Permission needed to run this command.
		position=1,                # Position in commands list
		section='',                # Section command will be placed to.
		style='',                  # Class assigned to a command button
		title='',                  # Pop up help message(via title)
		short='',                  # Short name for commands in quick section
		menu='',                   # Command name in UI
		name='',                   # Command name(an internal command name)
		run='',                    # Prevent CRONing
		his=True,                  # Command log will be saved to history
		fst=False,                 # Add command to quick section
		dgr='false',               # If true will show confirmation window
		job='false',               # Check if some cron jobs selected
		srv='false',               # Check if some servers selected
		upd='false',               # Check if some updates selected
		scr='false',               # Check if some scripts selected
		dmp='false',               # Check if some dumps selected
	):
		self.permission = permission
		self.position = position
		self.section = section
		self.style = style
		self.title = title
		self.short = short
		self.name = name
		self.menu = menu
		self.run = run
		self.his = his
		self.fst = fst
		self.srv = srv
		self.upd = upd
		self.job = job
		self.scr = scr
		self.dmp = dmp
		self.dgr = dgr

И все существующие команды описываются в словарике commandick. Название словарика это некая игра слов command(команда) и dictionary(словарь), знатоки английского наверное найдут еще какой-нибудь скрытый смысл. Словарик:
commandick = {

	# Cron submenu }-----------------------------------
	'cancel_job': CommandClass(
		permission='run_cron',
		position=4,
		section='cron',
		title='Cancel selected cron job(s).',
		name='cancel_job',
		menu='Remove job',
		job='true',
		run="run_or_cron('RUN');",
	),

	'permanent_job': CommandClass(
		permission='run_cron',
		position=3,
		section='cron',
		title='Make selected cron job(s) permanent.',
		name='permanent_job',
		menu='Run everyday',
		job='true',
		run="run_or_cron('RUN');",
	),

    ...

	'tunnel': CommandClass(
		permission='tunnel',
		position=80,
		section='server',
		title='Make ssh tunnel to the bind port of selected server(s).',
		short='Tunnel',
		name='tunnel',
		menu='Create tunnel',
		run="run_or_cron('RUN');",
		srv='true',
		his=False,
		fst=True,
	),

	'test_ssh': CommandClass(
		permission='tunnel',
		position=90,
		section='server',
		title='Test ssh connection.',
		short='Tunnel',
		name='test_ssh',
		menu='Test ssh',
		run="run_or_cron('RUN');",
		srv='true',
		his=False,
	),
}

Каждая команда это отдельный модуль в папке modules. Запускаются команды отдельным процессом, если выбрано несколько серверов, то для каждого создается свой процесс скрипт starter.py, он импортирует и выполняет нужный модуль(команду). Придумал новую фичу? Добавляешь файлик в папку modules и описываешь в словарике, ой все. Вот такое получилось приложение. Вангую массовые комменты про велосипедостроение. На ваши выс… сказывания я хотел бы ответить вот что. Почему люди продолжают делать новых людей? Ведь людей итак уже до… до определенной степени много. Потому что процесс нравится) Вот и мне нравится процесс, чего и вам желаю, творите, выдумывайте, пробуйте)

другие поделки
В заключение хочу упомянуть еще парочку своих поделок. Для самых маленьких раскраска. Скриптик упрощающий процесс раскрашивания других скриптиков:
#--------------------------------------------------------------------+
#Color picker, usage: printf $BLD$CUR$RED$BBLU'Hello World!'$DEF     |
#-------------------------+--------------------------------+---------+
#       Text color        |       Background color         |         |
#-----------+-------------+--------------+-----------------+         |
# Base color|Lighter shade| Base color   | Lighter shade   |         |
#-----------+-------------+--------------+-----------------+         |
BLK='\e[30m'; blk='\e[90m'; BBLK='\e[40m'; bblk='\e[100m' #| Black   |
RED='\e[31m'; red='\e[91m'; BRED='\e[41m'; bred='\e[101m' #| Red     |
GRN='\e[32m'; grn='\e[92m'; BGRN='\e[42m'; bgrn='\e[102m' #| Green   |
YLW='\e[33m'; ylw='\e[93m'; BYLW='\e[43m'; bylw='\e[103m' #| Yellow  |
BLU='\e[34m'; blu='\e[94m'; BBLU='\e[44m'; bblu='\e[104m' #| Blue    |
MGN='\e[35m'; mgn='\e[95m'; BMGN='\e[45m'; bmgn='\e[105m' #| Magenta |
CYN='\e[36m'; cyn='\e[96m'; BCYN='\e[46m'; bcyn='\e[106m' #| Cyan    |
WHT='\e[37m'; wht='\e[97m'; BWHT='\e[47m'; bwht='\e[107m' #| White   |
#----------------------------------------------------------+---------+
# Effects                                                            |
#--------------------------------------------------------------------+
DEF='\e[0m'   #Default color and effects                             |
BLD='\e[1m'   #Bold\brighter                                         |
DIM='\e[2m'   #Dim\darker                                            |
CUR='\e[3m'   #Italic font                                           |
UND='\e[4m'   #Underline                                             |
INV='\e[7m'   #Inverted                                              |
COF='\e[?25l' #Cursor Off                                            |
CON='\e[?25h' #Cursor On                                             |
#--------------------------------------------------------------------+
# Text positioning, usage: XY 10 10 'Hello World!'                   |
XY   () { printf "\e[$2;${1}H$3"; } #                                |
#--------------------------------------------------------------------+
# Print line, usage: line - 10 | line -= 20 | line 'Hello World!' 20 |
line () { printf -v LINE "%$2s"; printf "${LINE// /$1}"; } #         |
#--------------------------------------------------------------------+

Для ребят постарше, проводящих много времени в консоли, симпатичная информационная панель.
image

Для тех, кому уже тесно в рамках локалхоста, есть скриптик, создающий меню(dialog) из вашего ~/.ssh/config. Все знают, зачем нужен ~/.ssh/config?) Выглядит это так:
image

И конечно же ПИУ-ПИУ!
Пиу-пиу многим понравилась(больше 550 звезд на гитхабе), спасибо всем за добрые слова и за помощь! Не слышали про пиу-пиу? Скорей читайте тут, тут и тут. Хочу развить сюжет дальше и сделать игру больше, есть идеи по оптимизации но катастрофически не хватает времени. Новогодние праздники как нельзя кстати, надеюсь получится поработать над игрой. Кстати, пиу-пиу можно установить apt'ом:
sudo apt install -y piu-piu

image

С наступающим!)

Комментарии (11)


  1. staticmain
    28.12.2018 00:18

    подключается к удаленному серверу через ssh и выполняет какие-то там скрипты… Стоп, подумал я, разве для этого нужен ансамбль? Я и без ансамбля, сам ..., один… могу это сделать!


    Мы изначально тоже хотели сделать web-морду, потом передумали, чтобы лишние дырки не крутить. Поэтому сделали примерно так:
    function terminal_open1()
    {
        $TERMINAL               --geometry="$TERM1_POS"                                          --title="LOGS"                                                   --command="sh -ic \"$1\""
    }
    
    function terminal_open3()
    {
        $TERMINAL               --geometry="$TERM1_POS"                                          --title="DIRECTORY"                                              --command="sh -ic \"$1; exec bash\""                    --window --geometry="$TERM2_POS"                                          --title="LOGS"                                                   --command="sh -ic \"$2; exec bash\""                    --window --geometry="$TERM3_POS"                                          --title="MYSQL"                                                  --command="sh -ic \"$3; exec bash\""
    }

    function visual_work()
    {
        terminal_open3 "expect -c 'spawn /usr/bin/ssh -o StrictHostKeyChecking=no xxx@yyy.ru; "`
                                 `"expect \\\"xxxxxx@yyyyy\\\"; "`
                                 `"send \\\"ssh $ip\n\\\"; "`
                                 `"send \\\"cd /tmp/ygg-$DATETIME && ./scripts/build.sh debug && cd bin && export LD_LIBRARY_PATH=.\n\\\";"`
                                 `"interact;'"                    "expect -c 'spawn /usr/bin/ssh -o StrictHostKeyChecking=no xxx@yyy.ru; "`
                                 `"expect \\\"xxxxx@yyyyy\\\"; "`
                                 `"send \\\"ssh $ip\n\\\"; "`
                                 `"send \\\"cd /tk/dd24/log/ctms && tail -f hall_*.log\n\\\";"`
                                 `"interact;'"                    "expect -c 'spawn /usr/bin/ssh -o StrictHostKeyChecking=no xxx@yyy.ru; "`
                                 `"expect \\\"xxxxx@yyyyy\\\"; "`
                                 `"send \\\"ssh $ip\n\\\"; "`
                                 `"send \\\"cda && bin/mysql\n\\\";"`
                                 `"interact;'"
    }
    Так как у нас jump-сервер для входа на узлы пришлось поизвращаться с тремя уровнями expect.


    1. gecube
      28.12.2018 00:24

      ProxyJump в OpenSSH (вроде с версии 7.3) пробовали?
      Очень крутая опция!!!


      1. staticmain
        28.12.2018 00:30

        Насколько я помню, когда мы это делали из доступных опций была только -tt, но у нее есть проблема — некорректно переадресуются stdout\stderr. Так что когда-то сделали как сделали.

        P.S. Сейчас проверил на своей xU16.04 — такой опции (-J) нет.


        1. gecube
          28.12.2018 00:37

          Ну, мне ради нее пришлось обновиться с
          7.2 на

          gaal@5580-gaal:~> ssh -V
          OpenSSH_7.9p1, OpenSSL 1.0.2j-fips 26 Sep 2016

          Не жалею. Оно стоит того.
          Так что когда-то сделали как сделали.

          больше костылей и велосипедов!!!


    1. vaniacer Автор
      28.12.2018 10:42

      Expect у меня тоже используется чтобы сказать "yes" при первом подключении.


  1. gecube
    28.12.2018 00:45

    мне вспоминается, как коллеги показывали какую-то самодельную систему для автоматизации выполнения тасков на серверах. Тот еще велосипед. К сожалению, ссылку потерял. Точно помню, что у нее было какое-то очень смешное название и вроде бы она была на php или python…


    1. gecube
      28.12.2018 01:37

      habr.com/company/croc/blog/318532
      Нашел. Вот он — эталонный велосипед для автоматизации инфраструктуры. А не то что «без ансамбля»


      1. vaniacer Автор
        28.12.2018 08:28

        По смыслу похоже, спасибо за ссылку.


  1. sanakess
    28.12.2018 10:43

    Есть же awx или ansible tower


    1. vaniacer Автор
      28.12.2018 10:43

      Ага, но какбэ, без ансамбля)


  1. PAE
    28.12.2018 14:17

    NIH — что только люди не придумают, лишь бы не использовать уже созданное. Jenkins мог бы решить почти все описанные кейсы.

    Само собой, дело ваше — решает задачи и хорошо.