Pull to refresh

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

Reading time9 min
Views21K
Всем привет,

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

Условие:

  • ~ 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-copy-id -i /root/.ssh/id_rsa.pub adm@$IP
expect -re "(yes/no)"
#Отправляем YES
send "yes\r"
#Ждем строки ввода пароля
expect -re "Password:"
#Вводим пароль
send "YOU_PASSWORD\r"
#Подтверждение добовления ключа
expect -re "expecting."
#Заходим по ssh
spawn ssh adm@$IP
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 "\\$ $"
#Требуем обязательно запросить пароль от 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 "\\$ $"
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.

UPD:
Авторизация теперь по ssh-key.
Генерируем ключ
ssh-keygen -t rsa

С помощью файла setup, размещаем его на клиентских станциях.
Tags:
Hubs:
Total votes 47: ↑35 and ↓12+23
Comments36

Articles