Как стать автором
Обновить
Veeam Software
Продукты для резервного копирования информации

Бэкап для Linux не пишет писем

Время на прочтение9 мин
Количество просмотров7.3K
Всем привет!

Сегодня хочу поведать о том, как управлять Veeam Agent for Linux с помощью командной строки, и о том, какие возможности она открывает в умелых руках программиста.

На написание статьи меня подтолкнул комментарий к предыдущей статье. Перефразирую удивление пользователя: «Ну как же так? Cервер не пишет писем о том, что он забэкапился!». Причём, со слов аналитиков, он не один такой, иначе бы не появился тред на форуме. А раз люди пишут — значит, это кому нибудь нужно!

В статье я поясню, почему этой функции в продукте нет. Но на этом мы не остановимся, мы эту функцию добавим! Мы ж программисты, так что напишем письмо и сгенерируем отчёт в виде html страницы.


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

Приготовьтесь: много кода, картинок нет.

Для начала ответим на вопрос: «Почему Veeam Agent for Linux не пишет писем?»

Ответы вам могут не понравиться, не обессудьте. А дело в том, что более-менее крупным enterprise пользователям это не нужно, и вот почему:

  • Во-первых, для работы с почтой нужно либо поставить smpt-сервер на локальную машину, либо пользоваться каким-то внутри сети. При самой простой реализации (команда mail) потребуется ставить пакет mailutils. А многие системные администраторы не захотят создавать на своём продакшн сервере потенуциальную уязвимость в виде сервиса, который может слать куда бы то ни было письма. Да и возможности может не быть по причине закрытости портов, независимости подсетей и прочее...
  • Во-вторых, так как пакета mailutils очень может не быть на системе (по первой причине), нет смысла и пытаться его использовать. Иначе можем получить функцию, которая вроде есть, но «из коробки» не работает, а значит, будет тред на форуме на тему типа: «Как настроить сервер так, чтобы письма-таки отсылались.»
  • Ну и в-третьих, вообще никакая дополнительная нотификация не нужна, так как более-менее крупные enterprise-заказчики используют Veeam Backup & Replication. Его консоль собирает информацию о всех бэкапах, которые были произведены на известные репозитории. Убедитесь сами.

В версии Veeam Backup & Replication 9.5 Update 4 есть возможность пользоваться этим продуктом бесплатно, но с ограничением по обслуживаемым виртуальным/физическим машинам.
Если у вас до 3-х (включительно) физических серверов — бесплатных функций VBR вам будет более чем достаточно.

Если же у вас машин больше 3-х, платить за ПО нет возможности, а централизованно производить мониторинг своих серверов всё же хочется, то предлагаю дописать немного скриптов самостоятельно. Люблю потешить себя на python после рабочего для на С/С++.

Скриптами мы будем оборачивать вызов команды veeamconfig. Команда veeamconfig обеспечивает доступ ко всему функционалу продукта. Бесспорно, псевдографический интерфейс, созданный с помощью библиотеки ncurses, намного приятнее для глаз, однако если нужно связать программы во что-то новое, то CLI — наше всё.

Описание команд Veeam Agent for Linux справедливо для версии 3.0. На предыдущих версиях не проверял, так что могут быть отличия.

CLI-интерфейс в Veeam Agent for Linux довольно удобный и неплохо документирован. Достаточно ввести veeamconfig --help, и вы получите список доступных команд:

$sudo veeamconfig --help
Veeam Agent for Linux
(c) Veeam Software AG

  Usage: veeamconfig [command]

Commands:
  repository               - Backup repositories management
  vbrserver                - Veeam Backup and Replication servers management
  job                      - Backup jobs management
  backup                   - Backups management
  point                    - Restore points management
  license                  - License management
  agreement                - End User License Agreement management
  config                   - Import/export configuration
  schedule                 - Jobs schedule configuration
  cloud                    - Cloud provider management
  mode                     - Operation mode
  session                  - Sessions management
  ui                       - User interface
  aap                      - Application-aware processing
  version, --version, -v   - Product version
  help, --help, -h         - Short help

Для того, чтобы посмотреть, что каждая команда позволяет сделать, достаточно вызвать veeamconfig config --help. Получим:

Veeam Agent for Linux
(c) Veeam Software AG

  Usage: veeamconfig config [command]

Commands:
  import             - Import repositories and jobs into database
  export             - Export repositories and jobs from database
  grabLogs           - Collect support logs bundle
  patchiso           - Create custom Veeam Recovery Media adding all hardware drivers from this system
  help, --help, -h   - Short help

Тут, кстати, мы можем увидеть команду сбора логов grabLogs. Она позволит быстро собрать все необходимые логи для саппорта. Это на случай, если что-то пойдёт не так.

Есть ещё интересная команда, которая появилась в версии 3.0:

$ sudo veeamconfig agreement --help
Veeam Agent for Linux
(c) Veeam Software AG

  Usage: veeamconfig agreement [command]

Commands:
  acceptEula                 - Accept Veeam End User License Agreements
  acceptThirdPartyLicenses   - Accept Veeam 3rd party License Agreement
  show                       - Show End User License Agreements acceptance status
  help, --help, -h           - Short help

Дело в том, что начиная с версии 3.0 от пользователя требуется явно дать согласие с лицензионными соглашениями. Выглядит это примерно так:

$ sudo veeamconfig job list
I accept Veeam Software End User License Agreement:
/usr/share/doc/veeam/EULA
(yes/no | y/n):
yes
I accept the terms of the following 3rd party software components license agreements:
/usr/share/doc/veeam/3rd_party
(yes/no | y/n):

Соответственно, работа ваших скриптов может быть нарушена. Чтобы не заходить на каждую машину и не выполнять эту процедуру вручную, были предусмотрены команды:

veeamconfig agreement acceptEula
veeamconfig agreement acceptThirdPartyLicenses

Они позволяют принять лицензионные соглашения без лишних вопросов.

Но мы отклонились от темы написания письма.

Для задачи мониторинга состояния сервера нам потребуется команда veeamconfig session list. Выводит она что-то типа:

Job name    Type    ID                                      State    Started at        Finished at     
bj-home     Backup  {dbe48e88-3df7-4712-a472-09af8fed4e80}  Success  2018-12-05 15:43  2018-12-05 15:44
bj-home     Backup  {c178a799-2935-4bd6-883b-b11278000076}  Success  2018-12-05 16:26  2018-12-05 16:26
bj-home     Backup  {3405dad3-0016-4a00-933e-60ef66b30324}  Success  2018-12-06 06:00  2018-12-06 06:00

Отлично, тут есть информация, когда сервер бэкапился и каков был успех. В принципе, уже можно собирать «выхлоп» в файл и слать письмом. Однако уже за год письмо может подрасти примерно на 365 строк. А выискивать в тексте State с ошибками может показаться утомительным. Поэтому распарсим этот «выхлоп» и получим нормальный список, с которым можно уже что-то делать.

Целиком код смотреть тут

class CSession:
    @staticmethod
    def List():
return subproccall( ["veeamconfig", "session", "list"] )

class CSessionInfoList(object):
    def __init__(self, list):
        self.list = list

    def List(self):
        return self.list

    @staticmethod
    def Get():
        text = CSession.List()
        lines = text.split("\n")
        list = [] # session info list
        for line in lines:
            if len(line) == 0:
                continue

            words = line.split()
            if len(words) == 0:
                continue
            if words[0] == "Job":
                continue
            if words[0] == "Total":
                continue
            try:
                jobName = words[0]
                type = words[1]
                id = words[2]
                state = words[3]
                startTime = words[4] + " " + words[5]
                finishTime = words[6] + " " + words[7]
                list.append(CSessionInfo(id, type, jobName, state, startTime, finishTime))
            except:
                print "Failed to parse [", line, "]"
        return CSessionInfoList(list)

Ну а теперь сделаем письмо и отправим его себе.

def SendMailsessions():
    print "---"
    print "Sending statistic to administrator:"

    sessions = veeamlpb.session.CSessionInfoList.Get()

    recipient = "dear.admin@company.com"
    subject = "VAL status notification"
    text = "Statistic:\n"

    inx = 0;
    successCount = 0
    warningCount = 0
    errorCount = 0

    for sessionInfo in sessions.List():
        if (sessionInfo.State() == "Success"):
            successCount += 1
        elif (sessionInfo.State() == "Warning"):
            warningCount += 1
        else:
            errorCount += 1
    text += str(successCount)+"/"+str(warningCount)+"/"+str(errorCount)+" Success/Warning/Error\n"

    text += "Last 10 session:\n"
    for sessionInfo in reversed(sessions.List()):
        if inx == 10:
            text += "...\n"
            break;

        text += str(inx)+" | "+sessionInfo.State()+" | "+sessionInfo.JobName()+" | "+sessionInfo.StartTime()+" / "+sessionInfo.FinishTime() + "\n"
        #text += 
        inx += 1

    text += "\n"
    text += "--------------------------------------------------------------------------------\n"
    text += "  Yours sincerely, Veeam Agent for Linux Monitor\n"

    print text
    os.system("echo '"+text+"' | mail -s '"+subject+"' "+recipient)

В результате после установки mailutils можем получить письмо вида:

Statistic:
3/0/0 Success/Warning/Error
Last 10 session:
0 | Success | bj-home | 2018-12-06 06:00 / 2018-12-06 06:00
1 | Success | bj-home | 2018-12-05 16:26 / 2018-12-05 16:26
2 | Success | bj-home | 2018-12-05 15:43 / 2018-12-05 15:44

--------------------------------------------------------------------------------
  Yours sincerely, Veeam Agent for Linux Monitor

В письме выводятся только последние 10 сессий. При этом в самом начале письма выводится информация о числе успешных и не очень сессий. Достаточно глянуть на циферки в письме в начале рабочего дня, проверяя почту и потягивая кофеёк, чтобы понять, что ночные бэкапы прошли успешно.

Если же вам нужно что-то понагляднее — можно запросить информацию о сессиях в xml-формате и передать её на свой сервер. Там объединить полученные данные в единую сводную таблицу, которая отобразит всю необходимую информацию в удобном или посильном для вас формате.
XML-ку получаем парой строк:

sessionList = veeamlpb.session.CSessionList()
text = sessionList.ToXml()

Сохраняем полученное в файлик

sessionListFileName = "session_list.xml"
print "Store XML to file: ",sessionListFileName
sessionListXmlFile = open(sessionListFileName, "w")
sessionListXmlFile.write(text)
sessionListXmlFile.close()

Далее полученную XML-ку отправляем на сервер. Возможен и альтернативный вариант — сервер собирает XML-ки с машин, которые бэкапятся. Кто инициатор — нам пока не важно. Важно, что на сервере собираются XML-ки со списками сессий со всех машин. Я выбрал певый вариант:

hostname = os.uname()[1]
target = "user@admin-desktop:/home/user"
os.system("scp ./"+sessionListFileName+" "+target+"/backups/"+hostname+"/session_list.xml")

Теперь на стороне сервера осталось обработать принятые данные и сделать красивую html-страничку.

import veeamlpb
import os
import datetime
import xml.etree.ElementTree as xml

def main():

    hosts = []
    backupsDirectory = "/home/user/backups"
    for item in os.listdir(backupsDirectory):
        if item in [".", ".."]:
            continue
        if os.path.isdir(os.path.join(backupsDirectory,item)):
            hosts.append(item)
            print "item: ",item

    if len(hosts) == 0:
        return 0

    backupSessionMap = {}
    for host in hosts:
        print "found host: ", host
        sessionInfoFile = os.path.join(os.path.join(backupsDirectory,host), "session_list.xml")
        sessionList = veeamlpb.session.CSessionInfoList.FromXmlFile(sessionInfoFile)
        backupSessionMap[host] = sessionList

        for sessionInfo in sessionList.List():
            print "Session:",sessionInfo.ToString()

    html = xml.Element("html")
    body = xml.SubElement(html, "body", {"style":"background-color: #00b336;"})

    xml.SubElement(body,"h1").text = "Report at "+datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    xml.SubElement(body,"h2").text = "Statistic:"
    for host in hosts:
        sessionList = backupSessionMap[host]
        success=0
        warning=0
        error=0
        if len(sessionList.List()) == 0:
            continue
        
        for sessionInfo in sessionList.List():
            if sessionInfo.State() == "Success":
                success +=1
            elif sessionInfo.State() == "Warning":
                warning +=1
            else:
                error +=1

        latestSessionInfo = sessionList.List()[-1]
        attr = {}
        if latestSessionInfo.State() == "Success":
            #attr["style"] = "background-color: #00b336;"
            attr["style"] = "background-color: #005f4b; color: white;"
        elif latestSessionInfo.State() == "Warning":
            attr["style"] = "background-color: #93ea20;"
        else:          
            attr["style"] = "background-color: #ba0200; color: white;"
                    
        xml.SubElement(xml.SubElement(body,"p"),"span", attr).text = \
            host + " - "+str(success)+"/"+str(warning)+"/"+str(error)+" Success/Warning/Error"

            
    for host in hosts:
        sessionList = backupSessionMap[host]
        
        xml.SubElement(body,"h2").text = host+":"

        tableStyle =xml.SubElement(body,"style")
        tableStyle.attrib["type"] = "text/css"
        tableStyle.text = "TABLE {border: 1px solid green;} TD{ border: 1px solid green; padding: 4px;}" 
        
        table = xml.SubElement(body,"table")
        thead = xml.SubElement(table, "thead")
        xml.SubElement(thead, "th").text = "Number"
        xml.SubElement(thead, "th").text = "State"
        xml.SubElement(thead, "th").text = "Job name"
        xml.SubElement(thead, "th").text = "Start at"
        xml.SubElement(thead, "th").text = "Complete at"
        
        tbody = xml.SubElement(table, "tbody")
        inx = 0
        for sessionInfo in reversed(sessionList.List()):
            if inx == 10:
                break;
            tr = xml.SubElement(tbody,"tr")
            xml.SubElement(tr, "td").text = str(inx)

            attr ={}
            if sessionInfo.State() == "Success":
                pass
            elif sessionInfo.State() == "Warning":
                attr["style"] ="background-color: #93ea20;"
            else:  
                attr["style"] ="background-color: #ba0200; color: white;" 
            xml.SubElement(tr, "td", attr).text = sessionInfo.State()
            
            xml.SubElement(tr, "td").text = sessionInfo.JobName()
            xml.SubElement(tr, "td").text = sessionInfo.StartTime()
            xml.SubElement(tr, "td").text = sessionInfo.FinishTime()

            inx += 1

    xml.ElementTree(html).write("summary.html", encoding='utf-8', method='html')
    return 0

exit(main())

В результате отчёт готов:



У меня не было задачи сделать красивый продукт. Задача была показать, что вопрос сбора статистики можно решить на скриптах за один-два дня.

В принципе, если развить изложенные здесь идеи, то можно сделать «Open Backup Monitor for Veeam Agent for Linux». На мой взгляд, хорошая тема для курсовой по Python, или, может, даже для диплома, или просто повод потренироваться в программировании над opensource-проектом. Согласитесь, лучше потренироваться в программировании, чем становиться эльфом 80-го уровня.

Весь код можно найти на http://www.github.com/CodeImp/veeampy/. Качайте, пользуйтесь, дополняйте и форкайте на здоровье.

Учтите, код распространяется по GPL-2 лицензии, может содержать ошибки и прочее. Всё как обычно в мире opensource. Так что прежде чем применять в продакшн — не забудьте погонять на тестовой лабе.
Теги:
Хабы:
Всего голосов 12: ↑12 и ↓0+12
Комментарии0

Публикации

Информация

Сайт
veeam.com
Дата регистрации
Дата основания
Численность
1 001–5 000 человек
Местоположение
Швейцария

Истории