====== Отчеты в 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 ====
Отчет
Отчет
Volume Name
File Name
Job Name
Job ID
Media Type
Start Time
End Time
Pool ID
{% for row in reports %}
{{ row[0] }}
{{ row[1] }}
{{ row[2] }}
{{ row[3] }}
{{ row[4] }}
{{ row[5] }}
{{ row[6] }}
{{ row[7] }}
{% endfor %}
Вернуться назад
==== Содержимое файла 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}}
На этом все, мы проделали отличную работу по созданию интерфейса для отчетности.\\
Его можно расширять если вам необходимы еще какие-то данные из базы визуализировать