]>
Commit | Line | Data |
---|---|---|
25a66962 MF |
1 | #!/usr/bin/env python3 |
2 | import os | |
3 | import sys | |
3fc75327 | 4 | from subprocess import check_call, CalledProcessError |
25a66962 MF |
5 | import shlex |
6 | import datetime | |
dcc08cd9 | 7 | import configparser |
25a66962 | 8 | |
dcc08cd9 MF |
9 | class Host(): |
10 | def __init__(self, name, config): | |
11 | self.name = name | |
12 | self.config = config | |
13 | if not self.config.has_section('host'): | |
14 | self.config.add_section('host') | |
15 | self.store_dir = self.config['host']['store_dir'] | |
16 | self.host_dir = os.path.join(self.store_dir, name) | |
17 | self.subvol_dir = os.path.join(self.host_dir, "latest") | |
18 | self.keep = int(self.config.get("host", "keep", fallback=-1)) | |
f0a474cc | 19 | |
dcc08cd9 MF |
20 | def backup(self): |
21 | if not os.path.exists(self.host_dir): | |
f0a474cc | 22 | print("New host",host,".") |
dcc08cd9 MF |
23 | os.makedir(self.host_dir) |
24 | if not os.path.exists(self.subvol_dir): | |
f0a474cc | 25 | try: |
dcc08cd9 | 26 | check_call(shlex.split("btrfs subvol create %s"% self.subvol_dir)) |
3fc75327 | 27 | except CalledProcessError as ex: |
f0a474cc MF |
28 | print("Failed to create subvol! Aborting backup.") |
29 | return() | |
3fc75327 MF |
30 | |
31 | command = ("rsync -a --acls --xattrs --whole-file --numeric-ids --delete --delete-excluded --human-readable --inplace ") | |
dcc08cd9 | 32 | excludes = " --exclude " + " --exclude ".join(self.config.get("host", "exclude").split(',')) #FIXME |
f0a474cc | 33 | try: |
dcc08cd9 MF |
34 | print(command + excludes + " root@%s:/ "%(self.name) + self.subvol_dir) |
35 | check_call(shlex.split(command + excludes + " root@%s:/ "%(self.name) + self.subvol_dir)) | |
3fc75327 | 36 | except CalledProcessError as ex: |
dcc08cd9 MF |
37 | if ex.returncode not in (12, 30): |
38 | print("Rsync did not transfer anything from %s, skipping snapshot."%self.name) | |
f0a474cc MF |
39 | return() |
40 | todays_date = datetime.datetime.now().date().strftime("%F") | |
dcc08cd9 | 41 | if os.path.exists(os.path.join(self.host_dir, todays_date)): |
3fc75327 MF |
42 | #There is a snapshot for today, removing it and creating a new one |
43 | try: | |
dcc08cd9 | 44 | check_call(shlex.split("btrfs subvol delete %s"%(os.path.join(self.host_dir, todays_date)))) |
3fc75327 MF |
45 | except CalledProcessError as ex: |
46 | pass | |
f0a474cc | 47 | try: |
dcc08cd9 | 48 | check_call(shlex.split("btrfs subvol snapshot -r %s %s"%(self.subvol_dir,os.path.join(self.host_dir, todays_date)))) |
3fc75327 | 49 | except CalledProcessError as ex: |
f0a474cc | 50 | pass |
25a66962 | 51 | |
dcc08cd9 MF |
52 | def prune_snapshots(self): |
53 | if self.keep == -1: | |
54 | print("No keep specified for %s, keeping all"%self.name) | |
55 | return | |
56 | ||
57 | snaps = sorted([snap for snap in os.listdir(self.host_dir) if not snap == "latest" ], reverse=True) | |
58 | while len(snaps) > self.keep: | |
59 | snap = snaps.pop() | |
60 | try: | |
61 | check_call(shlex.split("btrfs subvol delete %s"%(os.path.join(self.host_dir, snap)))) | |
62 | except CalledProcessError as ex: | |
63 | pass | |
64 | ||
65 | class BackupRunner(): | |
66 | def __init__(self, config_dir, dest_dir): | |
67 | self.config_dir = config_dir | |
68 | self.dest_dir = dest_dir | |
69 | if not os.path.exists(self.config_dir): | |
70 | print("No config found", self.config_dir) | |
71 | sys-exit(-1) | |
72 | ||
73 | def run(self): | |
74 | self.hosts = os.listdir(self.config_dir) | |
75 | ||
76 | for host in self.hosts: | |
77 | if host == 'default.cfg': | |
78 | continue | |
79 | try: | |
80 | config = configparser.ConfigParser(strict=False) | |
81 | config.read_file(open(os.path.join(self.config_dir, 'default.cfg'),'r')) | |
82 | config.read(os.path.join(self.config_dir, host)) | |
83 | except BaseException as ex: | |
84 | print("Config error for %s. Skipping host."%host) | |
85 | continue | |
86 | h = Host(host, config) | |
87 | h.prune_snapshots() | |
88 | h.backup() | |
89 | ||
25a66962 MF |
90 | if __name__ == "__main__": |
91 | if os.geteuid() != 0: | |
92 | print("You need to be root. Otherwise all permissions will be lost.") | |
93 | sys.exit(-1) | |
f0a474cc MF |
94 | br = BackupRunner("/etc/butterbackup", "/mnt/data2") |
95 | br.run() | |
25a66962 | 96 | sys.exit(0) |