Централизованная система обновления пакетов в Ubuntu

от автора

Всем привет,

Что делать, если аналоги платные или не адаптированы под наши условия? Конечно, писать самому.

Условие:

  • ~ 50 удаленных клиентских станций, работающих на Ubuntu Desktop (10.04-12.10).

Задача:

  1. Получение информации о доступности обновления пакетов, на удаленных клиентских станциях.
  2. Логирование версий пакетов доступных для обновления.
  3. Удаленное обновление одной/всех клиентских станций.

Варианты решения:

  • Landscape – Отлично, но платно.
  • Spacewalk – Только RHEL и ему подобные.
  • Собственная разработка – этот вариант как раз для нас.

Поскольку мои знания ограничиваются одним языком программирования – bash, реализация будет выполнена именно на нем.
Что нам потребуется:

  • ssh-server на клиентских станциях.
  • Общий пользователь для администрирования.
  • Linux сервер (программа expect должна быть установлена).
  • Сетевая шара (я использовал nfs).

Как будет работать:
ssh adm@IP -> сбор нужной информации -> запись в лог на сервер -> exit.

Как выглядит на практике:

При запуске программы отображается информация о пользователях и доступных пакетах для обновления. Имеется возможность ручного управления через меню. P.s реальные имена и IP заменены в целях анонимности. Далее показан пример выполнения первого пункта:

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

Реализация:
/root/uuman – корневая папка программы.
../uuman/log – папка с лог файлами (она же сетевая шара).
../../log/.menu_log – скрытая папка с краткой информацией о удаленной машине.

setup – файл первоначальной настройки удаленных машин.

#!/usr/bin/expect -f  set IP $argv #Ождиаем каждую команду set timeout -1 spawn ssh adm@$IP #Ождидам запрос на сертификат expect -re "(yes/no)" #Отправляем YES send "yes\r" #Ожидаем запрос на пароль expect -re ":" send "YOU_PASSWORD\r" expect -re "\\$ $" #Обязательноо запросить sudo пароль send "sudo -K\r" expect -re "\\$ $" #Становимся root send "sudo su\r" expect -re "password for" send "YOU_SUDO_PASSWORD\r" expect -re "# $" #Устанавливеам необходимые пакеты(если используете samba шару замените пакет nfs-common на cifs-utils) send "apt-get install -y apt-show-versions nfs-common\r" expect -re "# $" send "exit\r" expect -re "\\$ $" exit 0 

update – файл для сбора информации о доступных пакетах.

#!/usr/bin/expect -f  set IP [lrange $argv 0 0] set USERN [lrange $argv 1 1] set DATES [exec date "+%Y.%m.%d/%H:%M:%S"] #Подключаемся по ssh (adm сменить на своего пользователя) spawn ssh adm@$IP #Ждем ответ консоли expect -re "Password:" #Отправляем пароль send "YOU_PASSWORD\r" #Ждем конец строки $ expect -re "\\$ $" #Требуем обязательно запросить пароль от sudo send "sudo -K\r" expect -re "\\$ $" #Становимся root send "sudo su\r" expect -re "password for" send "YOU_SUDO_PASSWORD\r" #Ждем конец строки # expect -re "# $" #Создаем временную папку, для монтирования шары send "mkdir -m 777 /tmp/share > /dev/null 2>&1\r" expect -re "# $" #Монтируем nfs шару (для samba строка будет выглядить иначе) send "mount.nfs 192.168.0.1:/root/uuman/log /tmp/share\r" expect -re "# $" #Дата в лог файл send "date > /tmp/share/$IP.log\r" expect -re "# $" #Версия ядра, битность системы в лог файл send "uname -a >> /tmp/share/$IP.log\r" expect -re "# $" #Имя пользовтаеля в лог send "echo Username:$USERN >> /tmp/share/$IP.log\r" expect -re "# $" #IP в лог send "echo IP:$IP >> /tmp/share/$IP.log\r" expect -re "# $" #Имя хоста в лог send "(echo -n Hostname:;hostname) >> /tmp/share/$IP.log\r" expect -re "# $" #Версия Ubuntu в лог send "(cat /etc/issue.net) >> /tmp/share/$IP.log\r" expect -re "# $" #Считаем количетсво достпуных пакетов для обновления и записываем в лог файл send "(echo -n Count of packages for update:;apt-show-versions -u | wc -l) >> /tmp/share/$IP.log\r" expect -re "# $" #Пустая строка send "echo >> /tmp/share/$IP.log\r" expect -re "# $" #Расширеный список пакетов для обновления в лог send "apt-show-versions -u | column -t >> /tmp/share/$IP.log\r" expect -re "# $" #Строка для конфигурации денамического меню (ИМЯ пользователя | IP | ubuntu_version | кол-во пакетов | дата запроса) send "(echo -n $USERN;echo -n ' |' $IP '| ';cat /etc/issue.net | sed 's/ /_/g';echo -n AvailableUpdates\:;apt-show-versions -u | wc -l ;echo -n $DATES) > /tmp/share/.menu_log/$IP.log\r" expect -re "# $" #Отмонтируем шару send "umount -f /tmp/share\r" expect -re "# $" #Очищаем историю root send "history -c\r" expect -re "# $" #выходим из root send "exit\r" expect -re "\\$ $" exit 0 

package – файл для обновления клиентских машин.

#!/usr/bin/expect -f  set IP [lrange $argv 0 0] set USERN [lrange $argv 1 1] set DATES [exec date "+%Y.%m.%d/%H:%M:%S"] #Ждем окончания каждой команды set timeout -1 spawn ssh adm@$IP expect -re "Password:" send "YOU_PASSWORD\r" expect -re "\\$ $" send "sudo -K\r" expect -re "\\$ $" send "sudo su\r" expect -re "password for" send "YOU_SUDO_PASSWORD\r" expect -re "# $" #Обновляем систему send "apt-get -y upgrade\r" expect -re "# $" #Собираем информацию в лог send "mkdir -m 777 /tmp/share > /dev/null 2>&1\r" expect -re "# $" send "mount.nfs 192.168.0.1:/root/uuman/log /tmp/share\r" expect -re "# $" send "date > /tmp/share/$IP.log\r" expect -re "# $" send "uname -a >> /tmp/share/$IP.log\r" expect -re "# $" send "echo IP:$IP >> /tmp/share/$IP.log\r" expect -re "# $" send "(echo -n Hostname:;hostname) >> /tmp/share/$IP.log\r" expect -re "# $" send "(cat /etc/issue.net) >> /tmp/share/$IP.log\r" expect -re "# $" send "(echo -n Count of packages for update:;apt-show-versions -u | wc -l) >> /tmp/share/$IP.log\r" expect -re "# $" send "echo >> /tmp/share/$IP.log\r" expect -re "# $" send "apt-show-versions -u | column -t >> /tmp/share/$IP.log\r" expect -re "# $" send "(echo -n $USERN;echo -n ' |' $IP '| ';cat /etc/issue.net | sed 's/ /_/g';echo -n AvailableUpdates\:;apt-show-versions -u | wc -l ;echo -n $DATES) > /tmp/share/.menu_log/$IP.log\r" expect -re "# $" send "umount /tmp/share\r" expect -re "# $" send "apt-get -y upgrade\r" expect -re "# $" send "history -c\r" expect -re "# $" send "exit\r" expect -re "\\$ $" exit 0 

uuman.sh – файл запуска (основной файл).

#!/bin/bash  #Дата для логов DATE=`date "+%Y.%m.%d/%H:%M:%S"` #Папка лог файлов LOCAL="/root/uuman/log" #Рабочая папка скрипта WORKD="/root/uuman"  #[\]Идикатор работы spinner() {     local pid=$1     local delay=0.25     while [ $(ps -eo pid | grep $pid) ]; do         for i in \| / - \\; do             printf ' [%c]\b\b\b\b' $i             sleep $delay         done     done     printf '\b\b\b\b' }  #Функция для обновления информации о хостах UPDATEA() {     #2012_11_23     DATEFN=`date "+%Y_%m_%d"`     #Задаем права на папку с логами. Данная строка используется для nfs     chown -R nfsnobody:nfsnobody $LOCAL     i=0     #Обрабатываем каждую строку файла     cat $WORKD/ip.txt | while read line; do     USERN=`echo $line`     #Получаем IP из строки 	IP=`echo $line | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}'`     #Доступность хоста     if ping -c 1 -s 1 -W 1 $IP > /dev/null 2>&1; then 	     i=`expr $i + 1` 	     #Для каждого хоста отдельный screen          screen -A -m -d -S "upd$i" expect $WORKD/update $IP $USERN          #Строка для начальной настройки хостов 	     #screen -A -m -d -S "upd$i" expect $WORKD/setup $IP     else 	     #Если хост не доступен пишем в логи соответсующий вывод          #Данные в лог ошибок 	     echo "$DATE" >> $LOCAL/err.txt          echo "$USERN - not connected" >> $LOCAL/err.txt 	     echo >> $LOCAL/err.txt 	     #Данные в общий лог 	     echo "$DATE | $USERN - not connected" >> $LOCAL/upd.txt 	     echo >> $LOCAL/upd.txt 	     #Данные в лог текущей даты 	     echo "$DATE | $USERN - not connected" >> $LOCAL/UPD-$DATEFN.txt 	     echo >> $LOCAL/UPD-$DATEFN.txt 	     #Если хост когда-либо был в сети, не ставить статус not connected в меню 	     NT=`cat $LOCAL/.menu_log/$IP.log | grep "AvailableUpdates"` 	     if [ -z "$NT" ]; then 	          echo -n "$USERN | Unknown | NotConnected | $DATE" > $LOCAL/.menu_log/$IP.log 	     fi     fi     done     #Информация по хостам в общий лог     ls $LOCAL | grep .log | while read TXT; do          paste $LOCAL/$TXT >> $LOCAL/upd.txt          echo >> $LOCAL/upd.txt 	     #В лог на текущую дату 	     paste $LOCAL/$TXT >> $LOCAL/UPD-$DATEFN.txt 	     echo >> $LOCAL/UPD-$DATEFN.txt     done }  #Функция для обновления всех хостов PACKAGEA() {     #2012_11_23     DATEFN=`date "+%Y_%m_%d"`     #Задаем права на папку с логами. Данная строка используется для nfs     chown -R nfsnobody:nfsnobody $LOCAL     i=0     #Обрабатываем каждую строку файла     cat $WORKD/ip.txt | while read line; do     USERN=`echo $line`     #Получаем IP из строки     IP=`echo $line | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}'`     #Доступность хоста     if ping -c 1 -s 1 -W 1 $IP > /dev/null 2>&1; then          i=`expr $i + 1`          #Для каждого хоста отдельный screen          screen -A -m -d -S "upd$i" expect $WORKD/package $IP $USERN      else          #Если хост не доступен пишем в логи соответсующий вывод          #Данные в лог ошибок          echo "$DATE" >> $LOCAL/err.txt          echo "$USERN - not connected" >> $LOCAL/err.txt          echo >> $LOCAL/err.txt          #Данные в общий лог          echo "$DATE | $USERN - not connected" >> $LOCAL/upd.txt          echo >> $LOCAL/upd.txt          #Данные в лог текущей даты          echo "$DATE | $USERN - not connected" >> $LOCAL/UPD-$DATEFN.txt          echo >> $LOCAL/UPD-$DATEFN.txt          #Если хост когда-либо был в сети, не ставить статус not connected в меню          NT=`cat $LOCAL/.menu_log/$IP.log | grep "AvailableUpdates"`          if [ -z "$NT" ]; then               echo -n "$USERN | Unknown | NotConnected | $DATE" > $LOCAL/.menu_log/$IP.log          fi     fi     done     #Информация по хостам в общий лог     ls $LOCAL | grep .log | while read TXT; do          paste $LOCAL/$TXT >> $LOCAL/upd.txt          echo >> $LOCAL/upd.txt          #В лог на текущую дату          paste $LOCAL/$TXT >> $LOCAL/UPD-$DATEFN.txt          echo >> $LOCAL/UPD-$DATEFN.txt     done }  #Функция генерация общего мини-отчета по хостам (Меню) MAIN() {     rm -rf $LOCAL/MENU.txt     ls $LOCAL/.menu_log | grep .log | while read MENU; do 	     #Все для красивого вывода (возврат строки и замена символов)	          TEXT=`paste -s -d '|' $LOCAL/.menu_log/$MENU | sed 's/|/ | /g;s/-/|/g'` 	     echo $TEXT >> $LOCAL/MENU.txt     done     #Очистка экрана и табличный вывод файла     clear && cat $LOCAL/MENU.txt | column -t }  #Меню скрипта MENU()  {     echo     echo "1.Get update-information for Ubuntu-host (by custom IP-address)"     echo "2.Get update-information for all Ubuntu-hosts (uses file with IP-addresses)"     echo "3.Update packages for Ubuntu-host (by custom IP-address)"     echo "4.Update packages for all hosts (uses file with IP-addresses)"     echo "5.View error-connection log"     echo "6.Refresh"     echo "7.Exit"     read SELECT      case $SELECT in     1) 	     #Информация о конкретном IP          echo -n "Enter IP:"          read IP          #Задаем права на папку с логами. Данная строка используется для nfs     	 chown -R nfsnobody:nfsnobody $LOCAL 	     #Доступен ли хост          if ping -c 1 -s 1 -W 1 $IP > /dev/null 2>&1; then 	          #Находим пользовтеля по IP и получаем его имя 	          USERN=`cat $WORKD/ip.txt | grep "$IP" | sed 's/ .*//'`               $WORKD/update $IP $USERN > /dev/null 2>&1 & spinner $!               cat $LOCAL/$IP.log 	     else 	          #Уведомляем и записываем в лог ошибок               echo "Computer is not online"               echo "$DATE" >> $LOCAL/err.txt               echo "$IP - not connected" >> $LOCAL/err.txt               echo >> $LOCAL/err.txt          fi 	     MENU          ;;     2) 	     #Обновить информацию о всех хостах          UPDATEA > /dev/null 2>&1 & spinner $!          #Не все хосты успевают быстро обработать информацию, можно выставить задержку перед показом меню 	     #sleep 5s 	     MAIN 	     MENU 	     ;;     3) 	     #Обновить конкретный IP          echo "Enter IP:"          read IP          #Задаем права на папку с логами. Данная строка используется для nfs     	 chown -R nfsnobody:nfsnobody $LOCAL 	     if ping -c 1 -s 1 -W 1 $IP > /dev/null 2>&1; then 	          #Находим пользовтеля по IP и получаем его имя               USERN=`cat $WORKD/ip.txt | grep "$IP" | sed 's/ .*//'`               $WORKD/package $IP $USERN > /dev/null 2>&1 & spinner $! 	          cat $LOCAL/$IP.log 	     else 	          echo "Computer is not online" 	          echo "$DATE" >> $LOCAL/err.txt               echo "$IP - not connected" >> $LOCAL/err.txt               echo >> $LOCAL/err.txt 	     fi 	     MENU          ;;     4) 	     #Обновить все хосты          PACKAGEA > /dev/null 2>&1 & spinner $!          #Не все хосты успевают быстро обработать информацию, можно выставить задержку перед показом меню 	     #sleep 5s 	     MAIN 	     MENU          ;;     5) 	     #Показать лог ошибок          cat $LOCAL/err.txt 	     MENU          ;;     6) 	     #Обновить          MAIN          MENU 	     ;;     7)          exit 0 	     ;;     *) 	     MENU    esac }  #Запуск скрипта с параметрами #Получаем параметр ARG=$1    case $ARG in     #Обновить информацию о всех хостах     check)          UPDATEA          exit 0          ;;     #Обновить все хосты     update)          PACKAGEA          exit 0          ;;     *)          MAIN          MENU          ;;    esac 

ip.txt – база содержащая User – IP.

User1 — 192.168.0.1
User2 — 192.168.0.2
User3 — 192.168.0.3
User4 — 192.168.0.4

Как пользоваться:
Если Вы использовали свои пути, поправьте следующие переменные в uuman.sh:

#Папка лог файлов LOCAL="/root/uuman/log" #Рабочая папка скрипта WORKD="/root/uuman" 

Скрипт setup установит необходимые пакеты для работы программы на клиентских станциях. Чтобы использовать скрипт, в файле uuman.sh закомментируйте строку:

screen -A -m -d -S "upd$i" expect $WORKD/update $IP $USERN 

И раскомментируйте:

#screen -A -m -d -S "upd$i" expect $WORKD/setup $IP  

Автоматический режим:
$WORKD/uuman.sh check — чек клиентских станций из файла ip.txt на доступность обновлений.
$WORKD/uuman.sh update — обновление клиентских станций, доступных из файла ip.txt.

Итог:

Мы получаем клиент-серверную систему позволяющую в автоматическом или ручном режиме получать, хранить и обрабатывать информацию об актуальности установленных программ, пакетов безопасности и при необходимости производить обновление на клиентских станциях под управлением Ubuntu Linux.

ссылка на оригинал статьи http://habrahabr.ru/post/160579/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *