[Из песочницы] Автоматизация обновления темплейтов OpenVZ на Proxmox
Однажды появилась такая необходимость обновить версии темплейтов OpenVZ загруженных в кэш Proxmox’а.
Задача в общем-то простая и тривиальная. Всего то и необходимо, это скачать новый темплейт и заменить им старый. Но в глобальном плане задача мне виделась шире, а именно периодически проверять обновления и загружать по необходимости новые версии темплейтов. То есть постоянно поддерживать их в актуальном состоянии.
Желание вполне естественное и понятное. И было бы глупо выполнять эти рутинные и простые операции каждый раз вручную, а по сему, решение вопроса напрашивалось само собой: сделать некий скрипт, который сам будет всё это делать, в то время как админ будет отдыхать и лишь время от времени проверять работает ли обновление.
Итак, есть желание и пора приступать к его осуществлению. Для начала решим, что же конкретно должен делать наш скрипт.
Постановка задачи
1. Время от времени, через определённые промежутки, скрипт должен запускаться.
2. Подключаться к серверу на котором лежат официальные обновлённые темплейты OpenVZ.
3. Сравнивать темплейты, которые есть на сервере с теми, которые загружены в кэш.
4. Загружать новый темплейт (если он есть), и заменять им старый в том случае, если версия на сервере отличается от локальной.
5. Вести лог того что загружено, а что нет, и когда загружено (обновлено).
Вот собственно и всё.
Реализация
Для написания скрипта мной был выбран старый и надёжный Perl, по причине моей давней с ним дружбы, а так же потому что для этого языка уже написано огромное количество готовых модулей на все случаи жизни для выполнения системных задач и не только.
Пропускаем первый пункт задания, о нём будет написано в самом конце, когда скрипт уже будет готов к работе. Тем более что это задача не относящаяся к самому скрипту и выполняться будет сторонними средствами, а именно, всю ответственность за своевременный запуск нашего скрипта мы возложим на Cron. Это его задача и обязаннось всё планировать и запускать. Мы же двинемся дальше и вернёмся к вопросу планирования позже.
В качестве источника темплейтов будем использовать официальный FTP сервер openvz.org, а для подключения к нему средствами Perl воспользуемся стандартным модулем Net: FTP.
Далее следует описание самого скрипта с пояснениями.
Для начала подключаем модули.
#!/usr/bin/perl -w
use strict;
use Fcntl qw(:DEFAULT :flock);
use Net::FTP;
Теперь объявим необходимые нам переменные, с которыми будет работать скрипт. По сути это константы, которые не будут меняться в ходе выполнения скрипта.
# директория темлейтов OpenVZ на ноде Proxmox
my $vzdir = "/var/lib/vz/template/cache";
# Путь на FTP сервере, по которому нужно искать обновления
my $urldir = "ftp://ftp.openvz.org/template/precreated/";
# Директория в которой будет лежать log-файл с результатами выполнения скрипта
my $logdir ="/var/log/";
# Собственно сам log-файл, его имя
my $logfile = "ovzupdate.log";
Как можно догадаться из имени log-файла, сам скрипт называется ovzupdate. Расширение .pl необязательно, достаточно того, что первой строкой скрипта указан интерпретатор, который обработает этот файл.
Итак, продолжим…
# Открываем log-файл для записи
open(STDOUT, '>>',$logdir.$logfile) or die "Can't open file '$logfile' $!";
# Наводим красоту :) Чтобы всё выглядело прилично, а не выводилось как попало.
print "\n=========================\n".getdate()."\n=========================\n";
print gettime()." * Starting OpenVZ templates update\n";
И наконец-то мы добрались до настоящей работы и подошли к тому, для чего собственно и создавался сам скрипт. Подключаемся к FTP серверу openvz.org.
# Подключение к ftp-серверу
my $server_name = 'ftp.openvz.org';
my $ftp_username = 'anonymous';
my $ftp_password = '';
my $ftp_source_dir = '/template/precreated';
my $ftp = Net::FTP->new($server_name, Debug => 1);
$ftp->login($ftp_username,$ftp_password);
$ftp->cwd($ftp_source_dir);
$ftp->binary();
Теперь объявим переменную, в которой будет храниться список (массив) имён локальных темплейтов и открываем локальную директорию (кэш Proxmox’а) в которой хранятся уже загруженные темплейты для проверки актуальности файлов.
my $file;
opendir(DIR, $vzdir) or die "Can't open $vzdir: $!";
А теперь собственно прописываем сам цикл сравнения файлов. Надо сказать, что мы не будем обновлять и качать все файлы с FTP без разбора, а будем выбирать только те, которые уже находятся в нашем локальном кэше и будем сравнивать их по размеру с файлами на ftp-сервере. Если размер файла в локальном кэше будет отличаться от размера файла с таким же именем на удалённом сервере, то файл в кэше будет удалён, а на его место загружен новый файл с FTP.
# Заносим в переменную список файлов в директории кэша
while( defined ($file = readdir (DIR)) ) {
# Исключаем из списка обработку ссылок на директории находящиеся выше
if ($file ne "." and $file ne "..") {
# Определяем размер локального файла
my $local_file_size = -s $vzdir."/".$file;
# Определяем размер файла с таким же именем, находящегося в директории ftp-сервера
my $remote_file_size = $ftp->size($file);
# Здесь мы просто выводим в log размеры одного и другого файла
# В принципе, эти две строчки можно закоментировать,
# если не хотите перегружать лог лишней информацией
print gettime()." Удалённый файл: $file -> ",$remote_file_size,"\n";
print gettime()." Локальный файл: $file -> ",$local_file_size,"\n";
# Собственно сравнение размеров файлов
if ($local_file_size ne $remote_file_size) {
# Если размер не совпадает, удаляем локальный файл
unlink $vzdir."/".$file;
# и скачиваем актуальную версию файла темплейта
system ("wget -P ".$vzdir." ".$urldir.$file);
# Пишем в log отчёт о том, что файл обновлён
print gettime()." + Загрузка новой версии ".$file."\n";
}
else {
# Иначе, пишем в log что файл является актуальным
print gettime()." Версия ".$file." актуальна\n"
}
}
}
Закрываем директорию, ftp-соединение и стандартный поток вывода.
closedir(DIR);
$ftp->quit;
print gettime()." * Проверка обновлений окончена\n";
close STDOUT;
Ну вот, практически и всё. Сам скрипт содержит три дополнительных функции, которые выполняют всего лишь вспомогательную роль и к сути вопроса не относятся. Готовый скрипт можно посмотреть тут.
Завершение
А теперь, как и обещал, вернёмся к пункту номер один. Скрипт можно положить на сервере Proxmox’а в любое удобное место. И остаётся только прописать его в crontab, чтобы он запускался на выполнение через нужный период времени. Периодичность выбирайте сами, но думаю что нет необходимости запускать его чаще раза в неделю.
Чтобы добавить задание обновления, зайдем в режим редактирования заданий crontab.
crontab -e
И последней добавляем строку, которая и будет выполнять наш скрипт обновления в определённое время и день. Для примера, в приведённой ниже строке указано, что скрипт будет выполняться каждый четверг в три часа ночи.
0 3 * * 4 /root/bin/ovzupdate 2>/dev/null
2>/dev/null — перенаправляет вывод потока STDERR в «чёрную дыру» нигде не сохраняя, но это уже на личное усмотрение, можно тоже сохранять в лог.
Сохраняем сделанные нами изменения и на этом всё, автоматическое обновление настроено. Теперь нам остаётся только время от времени проверять лог /var/log/ovzupdate.log, чтобы знать было или нет обновление. И когда это произойдёт, мы увидим что-то наподобие этого:
=========================
12-03-2015
=========================
03:00:01 * Начало обновления OpenVZ templates
03:00:03 Удалённый файл: debian-7.0-x86_64.tar.gz -> 235043942
03:00:03 Локальный файл: debian-7.0-x86_64.tar.gz -> 235004350
03:01:52 + Загрузка новой версии debian-7.0-x86_64.tar.gz
03:01:52 Удалённый файл: ubuntu-12.04-x86_64.tar.gz -> 131011759
03:01:52 Локальный файл: ubuntu-12.04-x86_64.tar.gz -> 130987444
03:02:48 + Загрузка новой версии ubuntu-12.04-x86_64.tar.gz
03:02:48 Удалённый файл: scientific-6-x86_64.tar.gz -> 221101244
03:02:48 Локальный файл: scientific-6-x86_64.tar.gz -> 219898164
03:03:45 + Загрузка новой версии scientific-6-x86_64.tar.gz
03:03:45 Удалённый файл: centos-7-x86_64.tar.gz -> 211178690
03:03:45 Локальный файл: centos-7-x86_64.tar.gz -> 211139455
...
03:16:31 * Проверка обновлений окончена
Удачного обновления!