v8.2: Rework backup handling to use server_id instead of server

Refactored code to use `server_id` in place of `server` across various modules to improve clarity and consistency. Updated database migrations, schema definitions, API documentation, and templates accordingly. Additionally, fixed related issues in the backup creation and management logic.
pull/399/head
Aidaho 2 months ago
parent ddaa3bf2dd
commit bf242a8eb2

@ -622,6 +622,33 @@ def update_db_v_8_0_2_1():
print("Updating... DB has been updated to version 8.0.2-1")
def update_db_v_8_1():
try:
migrate(
migrator.rename_column('backups', 'server', 'server_id')
)
except Exception as e:
if e.args[0] == 'no such column: "server"' or str(e) == '(1060, no such column: "server")':
print("Updating... DB has been updated to version 8.2")
elif e.args[0] == "'bool' object has no attribute 'sql'":
print("Updating... DB has been updated to version 8.2")
else:
print("An error occurred:", e)
def update_db_v_8_1_0_1():
try:
migrate(
migrator.rename_column('s3_backups', 'server', 'server_id')
)
except Exception as e:
if e.args[0] == 'no such column: "server"' or str(e) == '(1060, no such column: "server")':
print("Updating... DB has been updated to version 8.2")
elif e.args[0] == "'bool' object has no attribute 'sql'":
print("Updating... DB has been updated to version 8.2")
else:
print("An error occurred:", e)
def update_ver():
try:
Version.update(version='8.1.0').execute()
@ -650,4 +677,6 @@ def update_all():
update_db_v_8()
update_db_v_8_0_2()
update_db_v_8_0_2_1()
update_db_v_8_1()
update_db_v_8_1_0_1()
update_ver()

@ -2,11 +2,17 @@ from app.modules.db.db_model import Backup, S3Backup, GitSetting
from app.modules.db.common import out_error
from app.modules.roxywi.exception import RoxywiResourceNotFound
models = {
'fs': Backup,
's3': S3Backup,
'git': GitSetting,
}
def insert_backup_job(server, rserver, rpath, backup_type, time, cred, description):
def insert_backup_job(server_id, rserver, rpath, backup_type, time, cred, description):
try:
return Backup.insert(
server=server, rhost=rserver, rpath=rpath, type=backup_type, time=time,
server_id=server_id, rhost=rserver, rpath=rpath, type=backup_type, time=time,
cred_id=cred, description=description
).execute()
except Exception as e:
@ -20,32 +26,31 @@ def insert_s3_backup_job(**kwargs):
out_error(e)
def update_backup(server, rserver, rpath, backup_type, time, cred, description, backup_id):
backup_update = Backup.update(
server=server, rhost=rserver, rpath=rpath, type=backup_type, time=time,
cred_id=cred, description=description
).where(Backup.id == backup_id)
def update_s3_backup_job(backup_id: int, model: str, **kwargs):
model = models[model]
try:
backup_update.execute()
return model.update(**kwargs).where(model.id == backup_id).execute()
except Exception as e:
out_error(e)
def delete_backups(backup_id: int) -> None:
def update_backup(server_id, rserver, rpath, backup_type, time, cred, description, backup_id):
backup_update = Backup.update(
server_id=server_id, rhost=rserver, rpath=rpath, type=backup_type, time=time,
cred_id=cred, description=description
).where(Backup.id == backup_id)
try:
Backup.delete().where(Backup.id == backup_id).execute()
backup_update.execute()
except Exception as e:
out_error(e)
def delete_s3_backups(backup_id: int) -> bool:
def delete_backup(backup_id: int, model: str) -> None:
model = models[model]
try:
S3Backup.delete().where(S3Backup.id == backup_id).execute()
model.delete().where(model.id == backup_id).execute()
except Exception as e:
out_error(e)
return False
else:
return True
def insert_new_git(server_id, service_id, repo, branch, period, cred, description) -> int:
@ -58,13 +63,6 @@ def insert_new_git(server_id, service_id, repo, branch, period, cred, descriptio
out_error(e)
def delete_git(git_id: int) -> None:
try:
GitSetting.delete().where(GitSetting.id == git_id).execute()
except Exception as e:
out_error(e)
def select_gits(**kwargs):
if kwargs.get("server_id") is not None and kwargs.get("service_id") is not None:
query = GitSetting.select().where(
@ -81,8 +79,8 @@ def select_gits(**kwargs):
def select_backups(**kwargs):
if kwargs.get("server") is not None and kwargs.get("rserver") is not None:
query = Backup.select().where((Backup.server == kwargs.get("server")) & (Backup.rhost == kwargs.get("rserver")))
if kwargs.get("backup_id") is not None:
query = Backup.select().where(Backup.id == kwargs.get("backup_id"))
else:
query = Backup.select().order_by(Backup.id)
@ -110,21 +108,10 @@ def select_s3_backups(**kwargs):
return query_res
def check_exists_backup(server: str) -> bool:
try:
backup = Backup.get(Backup.server == server)
except Exception:
pass
else:
if backup.id is not None:
return True
else:
return False
def check_exists_s3_backup(server: str) -> bool:
def check_exists_backup(server_id: int, model: str) -> bool:
model = models[model]
try:
backup = S3Backup.get(S3Backup.server == server)
backup = model.get(model.server_id == server_id)
except Exception:
pass
else:
@ -135,11 +122,6 @@ def check_exists_s3_backup(server: str) -> bool:
def get_backup(backup_id: int, model: str) -> Backup:
models = {
'fs': Backup,
's3': S3Backup,
'git': GitSetting,
}
model = models[model]
try:
return model.get(model.id == backup_id)

@ -210,7 +210,7 @@ class Cred(BaseModel):
class Backup(BaseModel):
id = AutoField()
server = CharField()
server_id = CharField()
rhost = CharField()
rpath = CharField()
type = CharField(column_name='type')
@ -224,7 +224,7 @@ class Backup(BaseModel):
class S3Backup(BaseModel):
id = AutoField()
server = CharField()
server_id = CharField()
s3_server = CharField()
bucket = CharField()
secret_key = CharField()

@ -4,7 +4,7 @@ from typing import Optional, Annotated, Union, Literal, Any, Dict, List
from shlex import quote
from pydantic_core import CoreSchema, core_schema
from pydantic import BaseModel, Base64Str, StringConstraints, IPvAnyAddress, GetCoreSchemaHandler
from pydantic import BaseModel, Base64Str, StringConstraints, IPvAnyAddress, GetCoreSchemaHandler, AnyUrl
DomainName = Annotated[str, StringConstraints(pattern=r"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z0-9-]{0,61}[a-z0-9]$")]
@ -251,21 +251,21 @@ class SettingsRequest(BaseModel):
class BackupRequest(BaseModel):
cred_id: int
server: Union[IPvAnyAddress, DomainName]
server_id: int
rserver: Optional[Union[IPvAnyAddress, DomainName]] = None
description: Optional[EscapedString] = None
rpath: Optional[EscapedString] = None
type: Optional[EscapedString] = None
time: Optional[EscapedString] = None
type: Literal['backup', 'synchronization'] = None
time: Literal['hourly', 'daily', 'weekly', 'monthly'] = None
class S3BackupRequest(BaseModel):
server: Union[IPvAnyAddress, DomainName]
s3_server: Optional[Union[IPvAnyAddress, DomainName]] = None
server_id: int
s3_server: Optional[Union[IPvAnyAddress, AnyUrl]] = None
bucket: EscapedString
secret_key: Optional[EscapedString] = None
access_key: Optional[EscapedString] = None
time: Optional[EscapedString] = None
time: Literal['hourly', 'daily', 'weekly', 'monthly'] = None
description: Optional[EscapedString] = None

@ -17,7 +17,7 @@ import app.modules.db.ha_cluster as ha_sql
import app.modules.roxy_wi_tools as roxy_wi_tools
from app.modules.roxywi.class_models import ErrorResponse
from app.modules.roxywi.exception import RoxywiResourceNotFound, RoxywiGroupMismatch, RoxywiGroupNotFound, \
RoxywiPermissionError
RoxywiPermissionError, RoxywiConflictError
get_config_var = roxy_wi_tools.GetConfigVar()
@ -337,5 +337,7 @@ def handler_exceptions_for_json_data(ex: Exception, main_ex_mes: str) -> tuple[d
return handle_json_exceptions(ex, 'Resource not found in group'), 404
elif isinstance(ex, RoxywiPermissionError):
return handle_json_exceptions(ex, 'You cannot edit this resource'), 403
elif isinstance(ex, RoxywiConflictError):
return handle_json_exceptions(ex, 'Conflict'), 429
else:
return handle_json_exceptions(ex, main_ex_mes), 500

@ -19,10 +19,10 @@ error_mess = common.error_mess
get_config = roxy_wi_tools.GetConfigVar()
def return_ssh_keys_path(server_ip: str, **kwargs) -> dict:
def return_ssh_keys_path(server_ip: str, cred_id: int = None) -> dict:
ssh_settings = {}
if kwargs.get('id'):
sshs = cred_sql.select_ssh(id=kwargs.get('id'))
if cred_id:
sshs = cred_sql.select_ssh(id=cred_id)
else:
sshs = cred_sql.select_ssh(serv=server_ip)

@ -1,4 +1,3 @@
from docutils.parsers.rst.directives import body
from flask import render_template
import app.modules.db.sql as sql
@ -10,15 +9,17 @@ import app.modules.server.ssh as ssh_mod
import app.modules.roxywi.common as roxywi_common
import app.modules.service.installation as installation_mod
from app.modules.roxywi.class_models import BackupRequest, IdResponse, IdDataResponse, BaseResponse, S3BackupRequest, GitBackupRequest
from app.modules.roxywi.exception import RoxywiConflictError
def create_backup_inv(json_data: BackupRequest, del_id: int = 0) -> None:
ssh_settings = ssh_mod.return_ssh_keys_path(str(json_data.server), id=json_data.cred_id)
server = server_sql.get_server_by_id(json_data.server_id)
ssh_settings = ssh_mod.return_ssh_keys_path(server.ip, json_data.cred_id)
inv = {"server": {"hosts": {}}}
server_ips = []
inv['server']['hosts'][str(json_data.server)] = {
inv['server']['hosts'][server.ip] = {
'HOST': str(json_data.rserver),
"SERVER": str(json_data.server),
"SERVER": server.ip,
"TYPE": json_data.type,
"TIME": json_data.time,
"RPATH": json_data.rpath,
@ -26,7 +27,7 @@ def create_backup_inv(json_data: BackupRequest, del_id: int = 0) -> None:
"USER": ssh_settings['user'],
"KEY": ssh_settings['key']
}
server_ips.append(str(json_data.server))
server_ips.append(str(server.ip))
try:
installation_mod.run_ansible(inv, server_ips, 'backup')
@ -35,9 +36,10 @@ def create_backup_inv(json_data: BackupRequest, del_id: int = 0) -> None:
def create_s3_backup_inv(data: S3BackupRequest, tag: str) -> None:
server = server_sql.get_server_by_id(data.server_id)
inv = {"server": {"hosts": {}}}
inv["server"]["hosts"]["localhost"] = {
"SERVER": str(data.server),
"SERVER": server.hostname,
"S3_SERVER": str(data.s3_server),
"BUCKET": data.bucket,
"SECRET_KEY": data.secret_key,
@ -54,7 +56,7 @@ def create_s3_backup_inv(data: S3BackupRequest, tag: str) -> None:
def create_git_backup_inv(data: GitBackupRequest, server_ip: str, service: str, del_job: int = 0) -> None:
service_config_dir = sql.get_setting(service + '_dir')
ssh_settings = ssh_mod.return_ssh_keys_path(server_ip, id=data.cred_id)
ssh_settings = ssh_mod.return_ssh_keys_path(server_ip, data.cred_id)
inv = {"server": {"hosts": {}}}
inv["server"]["hosts"][server_ip] = {
"REPO": data.repo,
@ -74,43 +76,44 @@ def create_git_backup_inv(data: GitBackupRequest, server_ip: str, service: str,
def create_backup(json_data: BackupRequest, is_api: bool) -> tuple:
if backup_sql.check_exists_backup(json_data.server):
raise Exception(f'warning: Backup job for {json_data.server} already exists')
if backup_sql.check_exists_backup(json_data.server_id, 'fs'):
raise RoxywiConflictError('FS backup for this server already exists')
create_backup_inv(json_data)
last_id = backup_sql.insert_backup_job(json_data.server, json_data.rserver, json_data.rpath, json_data.type,
last_id = backup_sql.insert_backup_job(json_data.server_id, json_data.rserver, json_data.rpath, json_data.type,
json_data.time, json_data.cred_id, json_data.description)
roxywi_common.logging('backup ', f' a new backup job for server {json_data.server} has been created', roxywi=1, login=1)
roxywi_common.logging('backup ', f'A new backup job for server {json_data.server_id} has been created', roxywi=1, login=1)
if is_api:
return IdResponse(id=last_id).model_dump(mode='json'), 201
else:
data = render_template(
'ajax/new_backup.html',
backups=backup_sql.select_backups(server=json_data.server, rserver=json_data.rserver),
sshs=cred_sql.select_ssh()
backups=backup_sql.select_backups(backup_id=last_id),
sshs=cred_sql.select_ssh(),
servers=roxywi_common.get_dick_permit(virt=1, disable=0, only_group=1),
)
return IdDataResponse(data=data, id=last_id).model_dump(mode='json'), 201
def delete_backup(json_data: BackupRequest, backup_id: int) -> tuple:
create_backup_inv(json_data, backup_id)
backup_sql.delete_backups(backup_id)
roxywi_common.logging('backup ', f' a backup job for server {json_data.server} has been deleted', roxywi=1, login=1)
create_backup_inv(json_data, 1)
backup_sql.delete_backup(backup_id, 'fs')
roxywi_common.logging('backup ', f'A backup job for server {json_data.server_id} has been deleted', roxywi=1, login=1)
return BaseResponse().model_dump(mode='json'), 204
def update_backup(json_data: BackupRequest, backup_id: int) -> tuple:
create_backup_inv(json_data)
backup_sql.update_backup(json_data.server, json_data.rserver, json_data.rpath, json_data.type,
backup_sql.update_backup(json_data.server_id, json_data.rserver, json_data.rpath, json_data.type,
json_data.time, json_data.cred_id, json_data.description, backup_id)
roxywi_common.logging('backup ', f' a backup job for server {json_data.server} has been updated', roxywi=1, login=1)
roxywi_common.logging('backup ', f'A backup job for server {json_data.server_id} has been updated', roxywi=1, login=1)
return BaseResponse().model_dump(mode='json'), 201
def create_s3_backup(data: S3BackupRequest, is_api: bool) -> tuple:
if backup_sql.check_exists_s3_backup(data.server):
raise Exception(f'Backup job for {data.server} already exists')
if backup_sql.check_exists_backup(data.server_id, 's3'):
raise RoxywiConflictError('S3 backup for this server already exists')
try:
create_s3_backup_inv(data, 'add')
@ -119,7 +122,7 @@ def create_s3_backup(data: S3BackupRequest, is_api: bool) -> tuple:
try:
last_id = backup_sql.insert_s3_backup_job(**data.model_dump(mode='json'))
roxywi_common.logging('backup ', f'a new S3 backup job for server {data.server} has been created', roxywi=1, login=1)
roxywi_common.logging('backup ', f'A new S3 backup job for server {data.server_id} has been created', roxywi=1, login=1)
except Exception as e:
raise Exception(e)
@ -133,8 +136,8 @@ def create_s3_backup(data: S3BackupRequest, is_api: bool) -> tuple:
def delete_s3_backup(data: S3BackupRequest, backup_id: int) -> None:
try:
create_s3_backup_inv(data, 'delete')
backup_sql.delete_s3_backups(backup_id)
roxywi_common.logging('backup ', f'a S3 backup job for server {data.server} has been deleted', roxywi=1, login=1)
backup_sql.delete_backup(backup_id, 's3')
roxywi_common.logging('backup ', f'The S3 backup job for server {data.server_id} has been deleted', roxywi=1, login=1)
except Exception as e:
raise e
@ -180,7 +183,7 @@ def delete_git_backup(data: GitBackupRequest, backup_id: int) -> tuple:
raise Exception(e)
try:
backup_sql.delete_git(backup_id)
backup_sql.delete_backup(backup_id, 'git')
except Exception as e:
raise Exception(e)

@ -8,19 +8,15 @@
path: "{{ RPATH }}/roxy-wi-configs-backup/configs"
state: directory
owner: "{{ ansible_user }}"
when: not DELJOB
- hosts: localhost
become: yes
become_method: sudo
gather_facts: no
tasks:
when: not DELJOB and ansible_host != "localhost"
- name: Creates backup jobs
cron:
name: "Roxy-WI Backup configs for server {{ SERVER }} {{ item }}"
special_time: "{{ TIME }}"
job: "rsync -arv {{ TYPE }} /var/lib/roxy-wi/configs/{{ item }}/{{ SERVER }}* {{ ansible_user }}@{{ HOST }}:{{ RPATH }}/roxy-wi-configs-backup/configs/{{ item }} -e 'ssh -i {{ KEY }} -o StrictHostKeyChecking=no' --log-file=/var/www/haproxy-wi/log/backup.log"
job: "rsync -arv {{ TYPE }} /var/lib/roxy-wi/configs/{{ item }}/{{ SERVER }}* {{ USER }}@{{ HOST }}:{{ RPATH }}/roxy-wi-configs-backup/configs/{{ item }} -e 'ssh -i {{ KEY }} -o StrictHostKeyChecking=no' --log-file=/var/www/haproxy-wi/log/backup.log"
when: not DELJOB
delegate_to: localhost
with_items:
- kp_config
- hap_config
@ -32,6 +28,7 @@
name: "Roxy-WI Backup configs for server {{ SERVER }} {{ item }}"
state: absent
when: DELJOB
delegate_to: localhost
with_items:
- kp_config
- hap_config

@ -139,7 +139,7 @@ function addBackup(dialog_id) {
valid = valid && checkLength($('#backup-credentials'), "backup credentials", 1);
if (valid) {
let jsonData = {
"server": $('#backup-server').val(),
"server_id": $('#backup-server').val(),
"rserver": $('#rserver').val(),
"rpath": $('#rpath').val(),
"type": $('#backup-type').val(),
@ -175,8 +175,8 @@ function addS3Backup(dialog_id) {
valid = valid && checkLength($('#s3_access_key'), "S3 access key", 1);
if (valid) {
let json_data = {
"s3_server": $('#s3-backup-server').val(),
"server": $('#s3_server').val(),
"s3_server": $('#s3_server').val(),
"server_id": $('#s3-backup-server').val(),
"bucket": $('#s3_bucket').val(),
"secret_key": $('#s3_secret_key').val(),
"access_key": $('#s3_access_key').val(),
@ -332,7 +332,7 @@ function removeBackup(id) {
$("#backup-table-" + id).css("background-color", "#f2dede");
let jsonData = {
"cred_id": $('#backup-credentials-' + id).val(),
"server": $('#backup-server-' + id).text(),
"server_id": $('#backup-server-id-' + id).val(),
}
$.ajax({
url: api_prefix + "/server/backup/fs/" + id,
@ -360,7 +360,7 @@ function removeS3Backup(id) {
$("#backup-table-s3-" + id).css("background-color", "#f2dede");
let jsonData = {
"bucket": $('#bucket-' + id).text(),
"server": $('#backup-s3-server-' + id).text(),
"server_id": $('#backup-s3-server-' + id).text(),
}
$.ajax({
url: api_prefix + "/server/backup/s3/" + id,
@ -419,7 +419,7 @@ function updateBackup(id) {
toastr.error('All fields must be completed');
} else {
let jsonData = {
"server": $('#backup-server-' + id).text(),
"server_id": $('#backup-server-id-' + id).val(),
"rserver": $('#backup-rserver-' + id).val(),
"rpath": $('#backup-rpath-' + id).val(),
"type": $('#backup-type-' + id).val(),

@ -1,77 +1,54 @@
{% for b in backups %}
<tr class="newbackup" id="backup-table-{{b.id}}">
<td class="padding10 first-collumn">
<span id="backup-server-{{b.id}}">{{ b.server }}</span>
</td>
<td>
<input type="text" id="backup-rserver-{{b.id}}" value="{{b.rhost}}" size="14" class="form-control">
</td>
<td>
<input type="text" id="backup-rpath-{{b.id}}" value="{{b.rpath}}" class="form-control">
</td>
<td>
{% set values = {'backup':'backup','synchronization':'synchronization'} %}
<select name="backup-type-{{b.id}}" id="backup-type-{{b.id}}" class="force_close">
{% for v, des in values|dictsort(false, 'value') %}
{% if v == b.backup_type %}
<option value="{{v}}" selected>{{des}}</option>
{% else %}
<option value="{{v}}">{{des}}</option>
{% endif %}
{% endfor %}
</select>
</td>
<td>
{% set values = {'hourly':'hourly','daily':'daily','weekly':'weekly', 'monthly':'monthly'} %}
<select name="backup-time-{{b.id}}" id="backup-time-{{b.id}}" class="force_close">
{% for v, des in values|dictsort(false, 'value') %}
{% if v == b.time %}
<option value="{{v}}" selected>{{des}}</option>
{% else %}
<option value="{{v}}">{{des}}</option>
{% endif %}
{% endfor %}
</select>
</td>
<td>
<select id="backup-credentials-{{b.id}}" required>
<option disabled selected>Choose credentials</option>
{% for ssh in sshs %}
{% if ssh.key_enabled == 1 %}
{% if ssh.id == b.cred %}
<option value="{{ssh.id}}" selected="selected">{{ssh.name}}</option>
{% else %}
<option value="{{ssh.id}}">{{ssh.name}}</option>
{% endif %}
{% endif %}
{% endfor %}
</select>
</td>
<td>
{% if b.description != 'None' %}
<input type="text" id="backup-description-{{b.id}}" value="{{b.description.replace("'", "")}}" class="form-control">
{% else %}
<input type="text" id="backup-description-{{b.id}}" class="form-control">
{% endif %}
</td>
<td>
<a class="add" onclick="cloneBackup({{b.id}})" id="clone-backup{{b.id}}" title="Clone {{b.server}}" style="cursor: pointer;"></a>
</td>
<td>
<a class="delete" onclick="confirmDeleteBackup({{b.id}})" title="Delete backup {{b.server}}" style="cursor: pointer;"></a>
</td>
</tr>
<script>
$( function() {
$("#backup-time-{{ b.id}}" ).selectmenu({
width: 100
});
$("#backup-type-{{b.id}}" ).selectmenu({
width: 130
});
$("#backup-credentials-{{b.id}}" ).selectmenu({
width: 150
});
});
</script>
{% endfor %}
{% for s in servers %}
{% if b.server_id|string() == s.0|string() %}
<tr id="backup-table-{{b.id}}">
<td class="padding10 first-collumn">
{% set id = 'backup-server-' + b.id|string() %}
{{ input('backup-server-id-'+b.id|string(), value=s.0, type='hidden') }}
{{ copy_to_clipboard(id=id, value=s.1) }}
</td>
<td>
{{ input('backup-rserver-'+b.id|string(), value=b.rhost, size='14') }}
</td>
<td>
{{ input('backup-rpath-'+b.id|string(), value=b.rpath) }}
</td>
<td>
{% set values = {'backup':'backup','synchronization':'synchronization'} %}
{{ select('backup-type-'+b.id|string(), values=values, selected=b.type, required='required', class='force_close') }}
</td>
<td>
{% set values = {'hourly':'hourly','daily':'daily','weekly':'weekly', 'monthly':'monthly'} %}
{{ select('backup-time-'+b.id|string(), values=values, selected=b.time, required='required', class='force_close') }}
</td>
<td>
<select id="backup-credentials-{{b.id}}" required>
<option disabled selected>------</option>
{% for ssh in sshs %}
{% if ssh.key_enabled == 1 %}
{% if ssh.id == b.cred_id %}
<option value="{{ssh.id}}" selected="selected">{{ssh.name}}</option>
{% else %}
<option value="{{ssh.id}}">{{ssh.name}}</option>
{% endif %}
{% endif %}
{% endfor %}
</select>
</td>
<td>
{% if b.description is not none %}
{{ input('backup-description-'+b.id|string(), value=b.description.replace("'", "")) }}
{% else %}
{{ input('backup-description-'+b.id|string()) }}
{% endif %}
</td>
<td>
<a class="add" onclick="cloneBackup({{b.id}})" id="clone-backup{{b.id}}" title="{{lang.words.clone|title()}} {{b.server_id}}" style="cursor: pointer;"></a>
</td>
<td>
<a class="delete" onclick="confirmDeleteBackup({{b.id}})" title="{{lang.words.delete|title()}} {{lang.words.backup}} {{b.server_id}}" style="cursor: pointer;"></a>
</td>
</tr>
{% endif %}
{% endfor %}
{% endfor %}

@ -7,7 +7,7 @@
<select autofocus required name="backup-server" id="backup-server">
<option disabled>------</option>
{% for s in servers %}
<option value="{{ s.2 }}">{{ s.1 }}</option>
<option value="{{ s.0 }}">{{ s.1 }}</option>
{% endfor %}
</select>
</td>

@ -7,7 +7,7 @@
<select autofocus required name="s3-backup-server" id="s3-backup-server">
<option disabled>------</option>
{% for s in servers %}}
<option value="{{ s.2 }}">{{ s.1 }}</option>
<option value="{{ s.0 }}">{{ s.1 }}</option>
{% endfor %}
</select>
</td>

@ -60,59 +60,7 @@
<td style="margin-left: 5px;"></td>
<td></td>
</tr>
{% for b in backups %}
{% for s in servers %}
{% if b.server in s.2 %}
<tr id="backup-table-{{b.id}}">
<td class="padding10 first-collumn">
{% set id = 'backup-server-' + b.id|string() %}
{{ copy_to_clipboard(id=id, value=b.server) }}
</td>
<td>
{{ input('backup-rserver-'+b.id|string(), value=b.rhost, size='14') }}
</td>
<td>
{{ input('backup-rpath-'+b.id|string(), value=b.rpath) }}
</td>
<td>
{% set values = {'backup':'backup','synchronization':'synchronization'} %}
{{ select('backup-type-'+b.id|string(), values=values, selected=b.type, required='required', class='force_close') }}
</td>
<td>
{% set values = {'hourly':'hourly','daily':'daily','weekly':'weekly', 'monthly':'monthly'} %}
{{ select('backup-time-'+b.id|string(), values=values, selected=b.time, required='required', class='force_close') }}
</td>
<td>
<select id="backup-credentials-{{b.id}}" required>
<option disabled selected>------</option>
{% for ssh in sshs %}
{% if ssh.key_enabled == 1 %}
{% if ssh.id == b.cred_id %}
<option value="{{ssh.id}}" selected="selected">{{ssh.name}}</option>
{% else %}
<option value="{{ssh.id}}">{{ssh.name}}</option>
{% endif %}
{% endif %}
{% endfor %}
</select>
</td>
<td>
{% if b.description is not none %}
{{ input('backup-description-'+b.id|string(), value=b.description.replace("'", "")) }}
{% else %}
{{ input('backup-description-'+b.id|string()) }}
{% endif %}
</td>
<td>
<a class="add" onclick="cloneBackup({{b.id}})" id="clone-backup{{b.id}}" title="{{lang.words.clone|title()}} {{b.server}}" style="cursor: pointer;"></a>
</td>
<td>
<a class="delete" onclick="confirmDeleteBackup({{b.id}})" title="{{lang.words.delete|title()}} {{lang.words.backup}} {{b.server}}" style="cursor: pointer;"></a>
</td>
</tr>
{% endif %}
{% endfor %}
{% endfor %}
{% include 'ajax/new_backup.html' %}
</table>
<br /><span class="add-button" title="{{lang.words.add|title()}} {{lang.words.w_a}} {{lang.words.new}} {{lang.words.backup}} {{lang.words.job}}" id="add-backup-button">+ {{lang.words.add|title()}} {{lang.words.backup}}</span>
<br /><br />
@ -132,10 +80,10 @@
<tbody id="tbody-s3">
{% for b in s3_backups %}
{% for s in servers %}
{% if b.server in s.2 %}
{% if b.server_id|string() == s.0|string() %}
<tr id="s3-backup-table-{{b.id}}">
<td class="padding10 first-collumn">
<span id="backup-s3-server-{{b.id}}" style="display: none">{{ b.server }}</span>
<span id="backup-s3-server-{{b.id}}" style="display: none">{{ s.0 }}</span>
{{s.1}}
</td>
<td>
@ -155,10 +103,10 @@
{% endif %}
</td>
<td>
<a class="add" onclick="cloneS3Backup({{b.id}})" id="clone-s3-backup{{b.id}}" title="{{lang.words.clone|title()}} S3 {{b.server}}" style="cursor: pointer;"></a>
<a class="add" onclick="cloneS3Backup({{b.id}})" id="clone-s3-backup{{b.id}}" title="{{lang.words.clone|title()}} S3 {{s.2}}" style="cursor: pointer;"></a>
</td>
<td>
<a class="delete" onclick="confirmDeleteS3Backup({{b.id}})" title="{{lang.words.delete|title()}} S3 {{lang.words.backup}} {{b.server}}" style="cursor: pointer;"></a>
<a class="delete" onclick="confirmDeleteS3Backup({{b.id}})" title="{{lang.words.delete|title()}} S3 {{lang.words.backup}} {{s.2}}" style="cursor: pointer;"></a>
</td>
</tr>
{% endif %}

@ -89,14 +89,14 @@ class BackupView(MethodView):
- cred_id
- rhost
- rpath
- server
- server_id
- rserver
- time
- type
properties:
server:
server_id:
type: 'string'
description: 'The server to be backed up'
description: 'The server ID to be backed up'
rserver:
type: 'string'
description: 'The remote server where backup files should be stored'
@ -153,14 +153,14 @@ class BackupView(MethodView):
- cred_id
- rhost
- rpath
- server
- server_id
- rserver
- time
- type
properties:
server:
type: 'string'
description: 'The server to be backed up'
server_id:
type: 'integer'
description: 'The server ID to be backed up'
rserver:
type: 'string'
description: 'The remote server where backup files should be stored'
@ -214,9 +214,9 @@ class BackupView(MethodView):
schema:
type: 'object'
properties:
server:
type: 'string'
description: 'The server to be backed up'
server_id:
type: 'integer'
description: 'The server ID to be backed up'
cred_id:
type: 'string'
description: 'Credentials ID for the backup task'
@ -276,9 +276,9 @@ class S3BackupView(MethodView):
secret_key:
type: 'string'
description: 'The secret key for S3 access'
server:
type: 'string'
description: 'The server that was backed up'
server_id:
type: 'integer'
description: 'The server ID that was backed up'
time:
type: 'string'
description: 'The timing for the S3 backup task'
@ -308,7 +308,7 @@ class S3BackupView(MethodView):
type: 'object'
required:
- s3_server
- server
- server_id
- bucket
- secret_key
- access_key
@ -317,9 +317,9 @@ class S3BackupView(MethodView):
s3_server:
type: 'string'
description: 'The S3 server where the backup should be stored'
server:
type: 'string'
description: 'The server to be backed up'
server_id:
type: 'integer'
description: 'The server ID to be backed up'
bucket:
type: 'string'
description: 'The S3 bucket where the backup should be stored'
@ -347,6 +347,67 @@ class S3BackupView(MethodView):
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot create S3 backup')
@validate(body=S3BackupRequest)
def put(self, backup_id: int, body: S3BackupRequest):
"""
Update the S3 backup.
---
tags:
- S3 Backup
parameters:
- in: path
name: backup_id
type: 'integer'
required: true
description: The ID of the specific S3 backup
- name: config
in: body
required: true
description: The configuration for S3 backup service
schema:
type: 'object'
required:
- s3_server
- server_id
- bucket
- secret_key
- access_key
- time
properties:
s3_server:
type: 'string'
description: 'The S3 server where the backup should be stored'
server_id:
type: 'integer'
description: 'The server ID to be backed up'
bucket:
type: 'string'
description: 'The S3 bucket where the backup should be stored'
secret_key:
type: 'string'
description: 'The secret key for S3 access'
access_key:
type: 'string'
description: 'The access key for S3'
time:
type: 'string'
description: 'The timing for the S3 backup task'
enum: [hourly, daily, weekly, monthly]
description:
type: 'string'
description: 'Description for the S3 backup configuration'
responses:
201:
description: Successful operation
default:
description: Unexpected error
"""
try:
backup_mod.create_s3_backup_inv(body, 'add')
backup_sql.update_s3_backup_job(backup_id, 's3')
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot update S3 backup')
@validate(body=S3BackupRequest)
def delete(self, backup_id: int, body: S3BackupRequest):
"""
@ -370,9 +431,9 @@ class S3BackupView(MethodView):
bucket:
type: 'string'
description: 'The S3 bucket where the backup is stored'
server:
type: 'string'
description: 'The server that was backed up'
server_id:
type: 'integer'
description: 'The server ID that was backed up'
responses:
200:
description: Successful operation

@ -182,6 +182,7 @@ class ServiceView(MethodView):
return ErrorResponse(error='Keepalived service not found').model_dump(mode='json'), 404
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot get version')
data['id'] = f'{server_id}-{service}'
return jsonify(data)
@staticmethod

Loading…
Cancel
Save