Резервные копии по расписанию: каждый день или раз в неделю, все машины или отмеченные, хранятся последние N автокопий; копии делает служба virtuality-backup, а не панель
Этот коммит содержится в:
@@ -0,0 +1,109 @@
|
||||
{% extends "base.html" %}
|
||||
{% from "_ui.html" import icon, badge, alert %}
|
||||
|
||||
{% block page_header %}
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<div class="eyebrow"><a href="/backups">{{ icon('arrow-left', 'icon-sm') }} Резервные копии</a></div>
|
||||
<h1>Расписание копий</h1>
|
||||
<p class="lead">Сервер сам делает копии машин в выбранное время и хранит только последние, чтобы копии не заняли весь диск.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block alerts %}
|
||||
{% if error %}{{ alert('danger', error) }}{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="grid grid-main">
|
||||
<article class="card">
|
||||
<form method="post" action="/backups/schedule" class="schedule-form">
|
||||
<div class="card-body form-grid">
|
||||
<label class="switch-row" style="cursor:pointer">
|
||||
<span class="switch-text"><strong>Делать копии автоматически</strong><span>Выключите, чтобы приостановить расписание. Уже сделанные копии останутся.</span></span>
|
||||
<input type="checkbox" name="enabled" value="1" {% if form.enabled %}checked{% endif %} style="width:20px;height:20px;accent-color:var(--brand)">
|
||||
</label>
|
||||
|
||||
<div class="field">
|
||||
<span class="label">Как часто</span>
|
||||
<div class="options" style="grid-template-columns:repeat(auto-fit,minmax(200px,1fr))">
|
||||
<label class="option">
|
||||
<input type="radio" name="frequency" value="daily" {% if form.frequency == 'daily' %}checked{% endif %}>
|
||||
<span class="option-body"><span class="option-top"><span class="option-icon">{{ icon('calendar') }}</span></span><span class="option-title">Каждый день</span><span class="option-text">Для машин, в которых что-то меняется каждый день: сайты, базы, файлы.</span></span>
|
||||
</label>
|
||||
<label class="option">
|
||||
<input type="radio" name="frequency" value="weekly" {% if form.frequency == 'weekly' %}checked{% endif %}>
|
||||
<span class="option-body"><span class="option-top"><span class="option-icon">{{ icon('calendar-days') }}</span></span><span class="option-title">Раз в неделю</span><span class="option-text">Если машины меняются редко или места под копии немного.</span></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-2" style="align-items:start">
|
||||
<div class="field" data-when="weekly">
|
||||
<label for="weekday">День недели</label>
|
||||
<select class="select" id="weekday" name="weekday">
|
||||
{% for day in weekdays %}<option value="{{ loop.index0 }}" {% if form.weekday == loop.index0 %}selected{% endif %}>{{ day }}</option>{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="time">Время</label>
|
||||
<input class="input" id="time" type="time" name="time" value="{{ form.time }}" required>
|
||||
<span class="hint">По часам сервера. Лучше ночью, когда машинами никто не пользуется.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<span class="label">Какие машины копировать</span>
|
||||
<div class="options" style="grid-template-columns:repeat(auto-fit,minmax(200px,1fr))">
|
||||
<label class="option">
|
||||
<input type="radio" name="machines" value="all" {% if form.all_vms %}checked{% endif %}>
|
||||
<span class="option-body"><span class="option-title">Все машины</span><span class="option-text">Новые машины тоже попадут в копии сами.</span></span>
|
||||
</label>
|
||||
<label class="option">
|
||||
<input type="radio" name="machines" value="chosen" {% if not form.all_vms %}checked{% endif %}>
|
||||
<span class="option-body"><span class="option-title">Только отмеченные</span><span class="option-text">Например, без тестовых машин, которые не жалко.</span></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="stack-sm" data-when="chosen">
|
||||
{% for name in vm_names %}
|
||||
<label class="check"><input type="checkbox" name="vms" value="{{ name }}" {% if name in form.vms %}checked{% endif %}><span><strong>{{ name }}</strong></span></label>
|
||||
{% else %}
|
||||
<p class="subtle small">Машин на сервере пока нет.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field" style="max-width:280px">
|
||||
<label for="keep">Сколько копий хранить</label>
|
||||
<input class="input" id="keep" type="number" name="keep" value="{{ form.keep }}" min="1" max="{{ keep_max }}" required>
|
||||
<span class="hint">Для каждой машины. Более старые автоматические копии удаляются сами.</span>
|
||||
</div>
|
||||
|
||||
{{ alert('info', 'На время копирования работающая машина аккуратно выключается, а потом запускается снова. Машины копируются по очереди, так что каждая стоит только пока копируется она сама.') }}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a class="btn" href="/backups">Отмена</a>
|
||||
<button type="submit" class="btn btn-primary">{{ icon('check') }}Сохранить</button>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<aside class="stack">
|
||||
<article class="card">
|
||||
<div class="card-header"><h2>Как это работает</h2></div>
|
||||
<div class="checklist">
|
||||
{% set steps = [
|
||||
('Старые копии удаляются сами', 'После удачной новой копии у машины остаются последние копии по расписанию, сколько вы указали. Копии, сделанные вручную, не удаляются.'),
|
||||
('Неудачная копия ничего не удаляет', 'Если места не хватило или машина не выключилась, прежние копии остаются. Итог виден на странице «Резервные копии».'),
|
||||
('Если сервер был выключен', 'Пропущенную копию сервер сделает в течение двух часов после включения. Если включили позже, копия будет в следующий раз.'),
|
||||
('Обновления не мешают', 'Копии делает отдельная служба сервера, а не панель. Перезапуск панели их не прерывает.'),
|
||||
] %}
|
||||
{% for title, text in steps %}
|
||||
<div class="check-item success">{{ icon('circle-check') }}<div><strong>{{ title }}</strong><span>{{ text }}</span></div></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</article>
|
||||
</aside>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -39,6 +39,53 @@
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<article class="card mt-3" id="schedule">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<span class="vm-avatar">{{ icon('clock') }}</span>
|
||||
<div>
|
||||
<h2>Автоматические копии {{ badge('включены', 'success', cls='hide-sm') if schedule.enabled else badge('выключены', 'neutral', False, cls='hide-sm') }}</h2>
|
||||
{% if schedule.enabled %}
|
||||
<p>{{ schedule.when|capitalize }} · {{ 'все машины' if schedule.all_vms else schedule.vms|join(', ') }} · хранить {{ schedule.keep }} {{ plural(schedule.keep, 'последнюю копию', 'последние копии', 'последних копий') }}</p>
|
||||
{% else %}
|
||||
<p>Сервер может сам копировать машины каждую ночь или раз в неделю и удалять старые копии.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-sm" href="/backups/schedule">{{ icon('settings', 'icon-sm') }}{{ 'Изменить' if schedule.enabled else 'Настроить' }}</a>
|
||||
</div>
|
||||
{% if schedule.enabled or schedule.last %}
|
||||
<div class="list">
|
||||
{% if schedule.enabled %}
|
||||
<div class="list-item">
|
||||
<span class="list-icon brand">{{ icon('clock') }}</span>
|
||||
<div class="grow"><div class="title">Следующая копия: {{ schedule.next }}</div><div class="meta">Работающие машины на время копирования выключаются и потом запускаются снова</div></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if schedule.missed %}
|
||||
<div class="list-item">
|
||||
<span class="list-icon info">{{ icon('info') }}</span>
|
||||
<div class="grow"><div class="title">Копия {{ schedule.missed }} пропущена</div><div class="meta">В это время сервер был выключен, копия будет сделана в следующий раз</div></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if schedule.last %}
|
||||
{% set last = schedule.last %}
|
||||
<div class="list-item">
|
||||
<span class="list-icon {{ 'info' if last.running else 'danger' if last.failed else 'success' }}">{{ icon('activity' if last.running else 'triangle-alert' if last.failed else 'circle-check') }}</span>
|
||||
<div class="grow">
|
||||
<div class="title">
|
||||
{% if last.running %}Идёт копирование по расписанию {{ last.when }}: готово {{ last.count }}
|
||||
{% elif not last.count %}Запуск {{ last.when }}: машин для копирования не было
|
||||
{% else %}Запуск {{ last.when }}: скопировано {{ last.ok }} из {{ last.count }} {{ plural(last.count, 'машины', 'машин', 'машин') }}{% endif %}
|
||||
</div>
|
||||
{% for item in last.failed %}<div class="meta"><strong>{{ item.vm }}</strong>: {{ item.message }}</div>{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
{% if groups %}
|
||||
<div class="stack mt-3">
|
||||
{% for g in groups %}
|
||||
@@ -75,7 +122,7 @@
|
||||
<article class="card">
|
||||
<div class="card-body stack-sm">
|
||||
<div class="row">{{ icon('info') }}<h3>Как это работает</h3></div>
|
||||
<p class="muted small">Работающая машина на время копирования аккуратно выключается и потом запускается снова. При восстановлении машина создаётся выключенной: под новым названием — с новыми сетевыми адресами, чтобы не мешать оригиналу; при замене — с прежними. Копии хранятся в папке <code>/var/lib/virtuality/backups</code>.</p>
|
||||
<p class="muted small">Работающая машина на время копирования аккуратно выключается и потом запускается снова. При восстановлении машина создаётся выключенной: под новым названием — с новыми сетевыми адресами, чтобы не мешать оригиналу; при замене — с прежними. Копии хранятся в папке <code>/var/lib/virtuality/backups</code>. Копии по расписанию сервер удаляет сам, оставляя последние. Копии, сделанные вручную, лежат, пока вы их не удалите.</p>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
@@ -70,6 +70,17 @@
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
<article class="card">
|
||||
<div class="card-body stack-sm">
|
||||
<div class="row">{{ icon('history') }}<h3>Автоматические копии</h3></div>
|
||||
{% if scheduled %}
|
||||
<p class="muted small">Машина копируется {{ schedule.when }}, сервер хранит {{ schedule.keep }} {{ plural(schedule.keep, 'последнюю копию', 'последние копии', 'последних копий') }}. Следующая: {{ schedule.next }}.</p>
|
||||
{% else %}
|
||||
<p class="muted small">Эта машина не копируется автоматически.</p>
|
||||
{% endif %}
|
||||
<a class="btn btn-sm" href="/backups/schedule">{{ icon('settings', 'icon-sm') }}Расписание копий</a>
|
||||
</div>
|
||||
</article>
|
||||
<article class="card">
|
||||
<div class="card-body stack-sm">
|
||||
<div class="row">{{ icon('clock') }}<h3>Сколько это займёт</h3></div>
|
||||
|
||||
Ссылка в новой задаче
Block a user