Авторские статьи

Как я сел ваять скрипт и нашёл 2 бага.


Хочу вам рассказать историю, когда обычный банальный скрипт выявил два бага. Один баг официально подтверждён, а второй ещё предстоит понять и оформить с вашей помощью. Кому интересны загадки, которые подбрасывает жизнь админам, прошу к прочтению.

Мы используем систему виртуализации проекта Proxmox VE (Debian GNU/Linux, KVM, Qemu, ...), которая обладает прекрасными для нас свойствами: бесплатна, развитый API, мощный CLI, интуитивный Web GUI. Данная система виртуализации у нас является платформой (фундаментом), поверх которой работают виртуальные машины, обслуживающие запросы пользователей. Всё больше и больше ВМ создаются для различных отделов нашего предприятия и управляются людьми (изнутри ВМ), из числа назначенные быть ответственными. Всегда есть конкретный человек, который "заходит внутрь" с помощью RDP в случае MS Windows Server или с помощью SSH в случае Linux систем, и выполняет там требуемые действия. Чтобы подстраховаться на случай разрушительных действий сотрудника, который не относится к ИТ отделу, кроме создания по ночам резервных копий всех ВМ, решено было создавать в автоматическом режиме снимки (snapshot), чтобы можно было быстро откатиться к рабочему состоянию. Так как пла́той за использование снимков является возросшее занятое место и увеличенное количество операций записи, то решили с ума не сходить и делать не более 3 снимков: 1 день назад (вчера), 2 дня назад (позавчера) и 3 дня назад (позапозавчера).

В основном мои скрипты на bash. Да, они несколько топорны, но просты в понимании, что не маловажно. Скрипт должен создавать каждый день новый снимок и удалять самый старый снимок, чтобы всегда было доступно столько снимков сколько решил администратор для данной ВМ. Нужно предусмотреть ситуацию, что снимки может создавать админ через web интерфейс и нельзя их удалять. Скрипт удаляет только те снимки, что создавал сам.

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

Первая ошибка. Пробел

Создал для скрипта вспомогательный файл vmid_snapshot.txt, в котором строки типа
125 3
124 2
116 3
Цифра VMID виртуальной машины и через пробел желаемое админом количество снимков у данной ВМ.

Циклом while читаем файл

while read vmid days
do
# тело цикла
done < vmid_snapshot.txt

В теле цикла самые важные вещи:
1) через CLI pvesh узнать на какой ноде в данный момент работает конкретная ВМ
2) через CLI pvesh сделать снимок


# узнаем на какой ноде нужная VM
NODE=`/usr/bin/pvesh get /cluster/resources --type vm | jq --raw-output ".[] | select(.vmid == ${vmid}) | .node"`
# создаём снимок
/usr/bin/pvesh create /nodes/${NODE}/qemu/${vmid}/snapshot -snapname "ss_${curdate}" -vmstate false -description "automatically created"

И баг нашёлся там где его совсем не ждали. Снимки будут иметь имя в виде ss_датаВвидеГОДмесяцДЕНЬчасМИНУТА и описание automatically created, чтобы в дальнейшем скрипт мог идентифицировать снимки, созданные им, а не человеком. Скрипт будет трудиться на одной из нод кластера виртуализации и отдавать приказ о создании снимка конкретной ВМ, где бы она не находилась. Так вот скрипт, запущенный на сервер1, не мог создать снимок у ВМ, которая находится не на сервер1!

Вываливалась ошибка типа

400 too many arguments
create nodes/p6/qemu/116/snapshot -snapname  [OPTIONS]
proxy handler failed

Почти интуитивно фраза too many arguments намекнула мне что пробел в описании что-то поломал в недрах кода pvesh. Сделал описание -description "automatically" и всё заработало отлично! Решил запостить ошибку на баг-трекере проекта, но не нашёл как указать сбойный компонент pvesh - его там тупо не было! Поэтому отписался на форуме где опытный товарищ подтвердил и всё-таки попросил создать отчёт. Поискал в каком разделе раньше люди уведомляли об ошибках в pvesh и понял что это компонент pve-common. Создал отчёт под номером 1539

Вторая ошибка. While

Решил добавить в vmid_snapshot.txt ещё строки, чтобы более всесторонне обкатать работу скрипта. Было 3 строки - стало 5! И тут на ровном месте отхватил баг, который просто не ожидал. Цикл while отрабатывал только 3 строки, словно ещё 2 строки не были добавлены. Подумал сначала, что может грешным делом вместо пробела между двумя числами в строке поставил табуляцию. Перепроверил. Поставлен пробел! Подумал, может гипотетически слишком быстро требую от кластера выполнения задания и превышаю некие пороги? Добавил sleep 60. Не помогло.

Трансформировал чтение файла к виду - cat vmid_snapshot.txt | while read vmid days. Бестолку. Отсекая строку за строкой вышел на команду, которая запрашивает количество снимков у данной ВМ перед процедурой удаления старого снимка.

# сколько снимков "automatically" у данной VM ?
countss=`/usr/bin/pvesh get /nodes/${NODE}/qemu/${vmid}/snapshot | jq --raw-output "sort_by(.snaptime)[] | select(.description == \"automatically\") | .name" | wc -l`

Если строку заремарить, то цикл while отработает 5 строк из vmid_snapshot.txt, если строка есть, то только 3. В данном месте я понял, что ничего не понял.

Сделал минимальный тестовый скрипт, чтобы понять что не брежу. Комментирую строку kolvo= и получаю отработанных 5 строк! Убираю комментарий у kolvo= - 3 строки!

cat vmid_snapshot.txt | while IFS=: read -r -e vmid days
do
   # узнаём на какой ноде нужная VM
   NODE=`/usr/bin/pvesh get /cluster/resources --type vm | jq --raw-output ".[] | select(.vmid == ${vmid}) | .node"`
   # дай JSON ответ - сколько у ВМ снапшотов?
   kolvo=`/usr/bin/pvesh get /nodes/${NODE}/qemu/${vmid}/snapshot`
   echo ${kolvo}
done

И вот тут вынужден признаться, что не понимаю механизма влияния pvesh на цикл while. Как после отработки 3 строк сломаться? Нашёл на просторах Интернета такого же бедалагу, которому никто не помог - pvesh stops "Bash loop".

Не раскрыв суть бага, ибо моих знаний не хватает, и уповая на вашу помощь, тупо заменил while на for и получил рабочую версию скрипта photographer.sh
Оцените мой тонкий юмор Фотограф делает снимки.

#!/bin/bash

curdate=`/bin/date "+%Y%m%d%H%M"`

for x in $(cat vmid_snapshot.txt)
do
    vmid=`echo $x | cut -d \: -f 1`
    days=`echo $x | cut -d \: -f 2`

    # узнаём на какой ноде нужная VM
    NODE=`/usr/bin/pvesh get /cluster/resources --output-format json --type vm | jq --raw-output ".[] | select(.vmid == ${vmid}) | .node"`
    echo "-------- ${NODE} -- ${vmid} ------- ${days} --------"
    result=`/usr/bin/pvesh create /nodes/${NODE}/qemu/${vmid}/snapshot -snapname "ss${curdate}" -vmstate false -description "automatically"`
    echo ${result}

    # сколько снимков "automatically" у данной VM ?
    countss=`/usr/bin/pvesh get /nodes/${NODE}/qemu/${vmid}/snapshot --output-format json | jq --raw-output "sort_by(.snaptime)[] | select(.description == \"automatically\") | .name" | wc -l` 

    # пока снимков больше чем нужно - удаляем
    COUNTER=${days}
    while [ ${COUNTER} -lt ${countss} ]; do
        /bin/sleep 15
	namess=`/usr/bin/pvesh get /nodes/${NODE}/qemu/${vmid}/snapshot --output-format json  | jq --raw-output "sort_by(.snaptime)[] | select(.description == \"automatically\") | .name" | head -1` 
        echo "Count snapshot="${countss}" daysaves="${days}" VMID="${vmid}" name snapshot="${namess}
	result=`/usr/bin/pvesh delete /nodes/${NODE}/qemu/${vmid}/snapshot/${namess}`
        echo ${result}
        # сколько снимков "automatically" у данной VM ?
        countss=`/usr/bin/pvesh get /nodes/${NODE}/qemu/${vmid}/snapshot --output-format json | jq --raw-output "sort_by(.snaptime)[] | select(.description == \"automatically\") | .name" | wc -l` 
    done
    echo "--------------------------------"
done

exit 0

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

Дата последней правки: 2023-12-26 17:37:46

RSS vasilisc.com   


Разделы

Главная
Новости
Ворох бумаг
Видео Linux
Игры в Linux
Безопасность
Статьи об Astra Linux
Статьи о FreeBSD
Статьи об Ubuntu
Статьи о Snappy
Статьи об Ubuntu Phone
Статьи о Kubuntu
Статьи о Xubuntu
Статьи о Lubuntu
Статьи об Open Source
Карта сайта