Свой велосипед для бэкапов
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
5.3 KiB

#!/usr/local/bin/python3.8
import os
from dataclasses import dataclass
from datetime import datetime
import yaml
import argparse
from typing import List
from rich import print
from rich.prompt import Prompt
from rich.padding import Padding
from rich.panel import Panel
from rich.text import Text
from rich.table import Table
from rich.console import Console
from rich import box
@dataclass
class Job:
name: str
dst_dir: str
action: str
active: bool = False
# Добавить обозначения {warn} {good} {error} {nice}, менять их на
# символы и красить вывод консоли
def run_job(job: Job, console: Console) -> None:
"""Запусить задачу бэкапа."""
date = datetime.today().strftime("%Y-%m-%d")
time = datetime.now().strftime("%H%M")
path = job.dst_dir.replace("{date}", date).replace("{time}", time)
if not os.path.isdir(path):
os.makedirs(path)
# BOLDYELLOW="\e[1;33m[!]\e[0m"
# ENDCOLOR="\e[0m"
cmd = (
job.action.replace("{name}", job.name)
.replace("{date}", date)
.replace("{time}", time)
.replace("{warn}", "\033[33m[!]\033[0m")
)
with console.status(
f"[bold yellow]\[..][/bold yellow] [bold]{job.name}[/bold] начинаю ..."
):
os.system(cmd)
print(f"[bold green]\[OK][/bold green] [bold]{job.name}[/bold] выполнено")
def ask_about_backups(jobs: List[Job], force_all: bool) -> List[Job]:
"""Проверить список задач.
Проходит в цикле по всем задачам, спрашивая нужно ли ее выполнять.
Если нужно, ставит флаг `job.active` в True.
Parameters
----------
jobs : List[Job]
Список задач. Состоит из объектов Job.
force_all : bool
Если True, то всем задачам будет установлен флаг
`active`=True. Вопросы пользователю заданы не будут.
Returns
-------
jobs : List[Job]
Список задач после опроса пользователя по каждой из них. В
соответствии с решением пользователя поле `active` будет либо
изменено на True (задача активна), либо установленно False
(задача не активна).
"""
if force_all:
for job in jobs:
job.active = True
return jobs
print(Panel(Text("Составляю список задач", justify="center")))
for job in jobs:
answer = Prompt.ask(
f"Архивируем [bold]{job.name}[/bold]?",
choices=["y", "n"],
default="y",
).lower()
if answer == "y":
job.active = True
print("[bold green]\[+] Задача добавлена[/bold green]")
elif answer == "n":
job.active = False
print("[bold red]\[-] Пропуск задачи[/bold red]")
print(Panel(Text("Список задач составлен, начинаю бэкап", justify="center")))
return jobs
def yaml_loader(file: str) -> List[Job]:
jobs: list = []
with open(file, "r") as conf:
for task in yaml.safe_load_all(conf):
try:
jobs.append(
Job(
name=task["name"],
dst_dir=task["dst_dir"],
action=task["action"],
)
)
except AttributeError:
print(
"Ваш список задач говно, попробуйте не ставить --- после последнего элемента"
)
return jobs
def main() -> None:
parser = argparse.ArgumentParser(description="Скрипт для бэкапа")
required_args = parser.add_argument_group("Обязательные аргументы")
required_args.add_argument(
"--file", type=str, required=True, help="файл с задачами в формате YAML"
)
optional_args = parser.add_argument_group("Необязательные аргументы")
optional_args.add_argument(
"--forceall",
action="store_true",
help="выполнить все задачи из списка без запроса подтверждения",
)
args = parser.parse_args()
console = Console()
jobs = yaml_loader(os.path.abspath(args.file))
jobs = ask_about_backups(jobs, args.forceall)
for j in jobs:
if j.active:
run_job(j, console)
# table = Table(title="Результаты бэкапов", box=box.SIMPLE)
# table.add_column("Задача", justify="right", style="cyan", no_wrap=True)
# table.add_column("Архив", style="magenta")
# table.add_column("Статус", justify="right", style="green")
# table.add_row(
# "Archives криппи и прочее",
# "/back/Archives/Archives-{date}-{time}",
# "ОК",
# )
# table.add_row(
# "Archivebox-emacs",
# "/back/Archives/Archivebox-emacs",
# "Пропуск",
# )
# console.print(table, justify="center")
if __name__ == "__main__":
main()