====== Отчеты в Bareos ====== В данной системе бэкапирования к сожалению нет удобного визуального контроля за тем какие файлы и в какой момент были записаны на ленточное хранилище.\\ И я решил сам сделать такой функционал т.к. смотреть через **bconsole** не очень удобно из-за привязки записанных файлов к заданию **job**\\ Я изучил структуру базы данных и зависимости таблиц между собой, что бы понять какие данные мне нужно включить в отчетность и что бы ничего лишнего не попало туда. ===== Кратко описываю что мне нужно извлечь из базы ===== Есть таблицы **job**, **file**, **media**\\ в таблице **job** есть столбец **name**, из него мы считываем только содержимое с названием **BackupOnTape**,\\ так же есть столбец **jobid** и он идентичен в таблице **file**\\ Теперь в таблице **file** сопоставив **jobid** мы должны вытащить из столбца **name** который в таблице **file** название\\ В таблице **media** есть столбец **poolid** он понадобится для понимания какой используется пул\\ Но мне из таблицы **media** более важно знать что в столбце **volumename** и выводить эту информацию Еще в таблице **media** есть вот такой столбец **mediatype**, мне важно что бы в финальном отчете он тоже фигурировал, но при условии что в **mediatype** будет исключительно содержимое с названием **LTO**\\ И из таблицы **job** нужны два столбца **starttime** и **endtime**, которые отвечают за начало и конец записи на ленту.\\ Таким образом резюмируем\\ Мне нужно сформировать отчет в котором будет: * **volumename**,**poolid**,**mediatype** из таблицы **media** * **name** из таблицы **file** * **name**,**jobid**,**starttime**,**endtime** из таблицы **job** У меня получился вот такой SQL запрос SELECT m.volumename AS volumename, f.name AS file_name, j.name AS job_name, j.jobid AS jobid, m.mediatype AS mediatype, j.starttime AS starttime, j.endtime AS endtime, m.poolid AS poolid FROM public.job j JOIN public.file f ON j.jobid = f.jobid JOIN public.media m ON m.poolid IS NOT NULL WHERE j.name = 'BackupOnTape' AND m.mediatype = 'LTO' AND m.poolid = 2; Для проверки можно сохранить в файл вывод запроса COPY ( SELECT m.volumename AS volumename, f.name AS file_name, j.name AS job_name, j.jobid AS jobid, m.mediatype AS mediatype, j.starttime AS starttime, j.endtime AS endtime, m.poolid AS poolid FROM public.job j JOIN public.file f ON j.jobid = f.jobid JOIN public.media m ON m.poolid IS NOT NULL WHERE j.name = 'BackupOnTape' AND m.mediatype = 'LTO' AND m.poolid = 2 ) TO '/ВАШ ПУТЬ/report.txt' WITH (FORMAT csv, HEADER); Но это все сырые данные и так мы только убедились что все работает, теперь нужно создать веб интерфейс и прикрутить кнопку в основной веб интерфейс bareos ===== Создание веб интерфейса ===== Создаем такую структуру /var/www/html/ └── reports_bareos ├── reports_bareos.py ├── static │   └── style.css └── templates ├── index.html └── report.html ==== Содержимое файла index.html ==== Форма для отчета

Форма для отчета

==== Содержимое файла report.html ==== Отчет

Отчет

{% for row in reports %} {% endfor %}
Volume Name File Name Job Name Job ID Media Type Start Time End Time Pool ID
{{ row[0] }} {{ row[1] }} {{ row[2] }} {{ row[3] }} {{ row[4] }} {{ row[5] }} {{ row[6] }} {{ row[7] }}

Вернуться назад
==== Содержимое файла style.css ==== body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 20px; } h1 { text-align: center; color: #333; } form { max-width: 600px; margin: auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } label { display: block; margin-bottom: 8px; color: #555; } input[type="text"], input[type="date"] { width: 100%; padding: 10px; margin-bottom: 15px; border: 1px solid #ccc; border-radius: 4px; } input[type="submit"] { background-color: #5cb85c; color: white; border: none; padding: 10px 15px; border-radius: 4px; cursor: pointer; font-size: 16px; transition: background-color 0.3s; } input[type="submit"]:hover { background-color: #4cae4c; } /* Стили для таблицы */ table { width: 100%; border-collapse: collapse; margin-top: 20px; } table, th, td { border: 1px solid #ccc; } th { background-color: #5cb85c; color: white; padding: 10px; } td { padding: 10px; text-align: center; } /* Стили для ссылки */ a { display: block; text-align: center; margin-top: 20px; color: #5cb85c; text-decoration: none; } a:hover { text-decoration: underline; } ===== Создаем логику работы (backend) ===== Логику будем описывать с помощью **Python** и фреймворка **Flask** ==== Содержимое файла reports_bareos.py ==== from flask import Flask, request, render_template import psycopg2 app = Flask(__name__) def get_db_connection(): conn = psycopg2.connect(dbname='ВАША БАЗА', user='ВАШ ПОЛЬЗОВАТЕЛЬ', password='ВАШ ПАРОЛЬ', host='localhost') return conn @app.route('/') def index(): return render_template('index.html') @app.route('/report', methods=['GET']) def report(): volumename = request.args.get('volumename') start_date = request.args.get('start_date') end_date = request.args.get('end_date') conn = get_db_connection() cur = conn.cursor() query = ''' SELECT m.volumename AS volumename, f.name AS file_name, j.name AS job_name, j.jobid AS jobid, m.mediatype AS mediatype, j.starttime AS starttime, j.endtime AS endtime, m.poolid AS poolid FROM public.job j JOIN public.file f ON j.jobid = f.jobid JOIN public.media m ON m.poolid IS NOT NULL WHERE j.name = 'BackupOnTape' AND m.mediatype = 'LTO' AND m.poolid = 2 ''' if volumename: query += ' AND m.volumename = %s' cur.execute(query, (volumename,)) else: cur.execute(query) reports = cur.fetchall() cur.close() conn.close() # Фильтруем строки, у которых file_name пустая reports = [row for row in reports if row[1]] # row[1] - это file_name return render_template('report.html', reports=reports) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True) ===== Тестируем ===== Откройте порт 5000 в вашем firewall если это необходимо\\ Далее нужно вручную запустить приложение командой python /var/www/html/reports_bareos.py Теперь идем в браузер http://ВАШ IP:5000 И вы увидите вот эту форму\\ {{:poleznosti:pasted:20240925-093950.png?nolink}}\\ Значит все идет как нужно. Но запускать таким образом приложение не удобно, нужно сделать его отдельным демоном в системе ===== Создаем демона для reports_bareos.py ===== Открываем nano /etc/systemd/system/reports_bareos.service Содержимое будет примерно такое [Unit] Description=Flask Application for Reports Bareos After=network.target [Service] User=bareos Group=bareos WorkingDirectory=/var/www/html/reports_bareos ExecStart=/var/lib/bareos/.local/bin/gunicorn -w 4 -b 0.0.0.0:5000 reports_bareos:app Restart=always RestartSec=10 Environment="FLASK_ENV=development" Environment="PATH=/var/lib/bareos/.local/bin:$PATH" StandardOutput=append:/var/log/reports_bareos.log StandardError=append:/var/log/reports_bareos.log [Install] WantedBy=multi-user.target Запускаем демона и проверяем его systemctl daemon-reload systemctl enable reports_bareos.service systemctl start reports_bareos.service systemctl status reports_bareos.service {{:poleznosti:pasted:20240925-094718.png?nolink}}\\ Теперь идем в браузер http://ВАШ IP:5000 И вы увидите вот эту форму\\ {{:poleznosti:pasted:20240925-093950.png?nolink}}\\ Значит все получилось. ===== Прикручиваем кнопку отчетов в bareos-webui ===== Тут уже все индивидуально и куда вам будет удобнее, туда и прикручивайте, я покажу свой пример как сделал я\\ Внешний вид интерфейса вот такой по умолчанию\\ {{:poleznosti:pasted:20240925-095308.png?nolink}}\\ Я решил в пункте **Analytics** сделать кнопку отчетности {{:poleznosti:pasted:20240925-095436.png?nolink}}\\ Открываем файл nano /usr/share/bareos-webui/module/Analytics/view/analytics/analytics/index.phtml Ищем в нем где упоминается **Stored Data** как видно на скрине выше это такой раздел\\ И туда отдельным пунктом добавляем кнопку перехода в наш созданный интерфейс
  • Отчетность
  • Визуально выглядит вот так {{:poleznosti:pasted:20240925-100458.png?nolink}} На этом все, мы проделали отличную работу по созданию интерфейса для отчетности.\\ Его можно расширять если вам необходимы еще какие-то данные из базы визуализировать