• R/O
  • SSH

Commit

Tags
Keine Tags

Frequently used words (click to add to your profile)

javaandroidc++linuxc#objective-ccocoa誰得qtrubypythonwindowsphpgamebathyscaphegui翻訳comegattwitterframeworkbtronvb.net計画中(planning stage)testdomarduinodirectxpreviewerゲームエンジン

rsync wrapper for pushing incremental backups


Commit MetaInfo

Revision7c0782056095c3f700d78b8b3cd6c3e8d5b8d728 (tree)
Zeit2019-11-06 14:01:48
AutorFrank Tobin <ftobin@neve...>
CommiterFrank Tobin

Log Message

initial commit

Ändern Zusammenfassung

Diff

diff -r 000000000000 -r 7c0782056095 rsnappush
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rsnappush Wed Nov 06 00:01:48 2019 -0500
@@ -0,0 +1,78 @@
1+#!/usr/bin/env python3
2+
3+"""
4+rsnappush: Perform file-based incremental rsync push backup with hard-links
5+
6+Author: Frank Tobin
7+Email: ftobin@neverending.org
8+License: Eclipse Public License 2.0 (https://opensource.org/licenses/EPL-2.0)
9+"""
10+
11+import os
12+import re
13+import sys
14+import time
15+import argparse
16+import subprocess
17+
18+parser = argparse.ArgumentParser(description="Perform file-based incremental rsync push backup with hard-links")
19+
20+parser.add_argument("source", metavar="SOURCE_PATH", help="directory to backup")
21+parser.add_argument("dest", metavar="[ACCOUNT:]DEST_PATH", help="Where to store the backups. ACCOUNT can be anything ssh will accept. Example: user@host:backups/")
22+
23+parser.add_argument("-r", "--rsync-opt", action="append", default=[],
24+ help="pass-thru options to rsync. Use '=' syntax, and include prefixing dashes. Example: --rsync-opt=--partial-dir=/home/user/rsync-partial")
25+parser.add_argument("--quiet", "-q", action="store_true", default=False,
26+ help="emit less output")
27+parser.add_argument("--backup-prefix", default="backup-",
28+ help="Prefix used in directory snapshots; default: 'backup-'")
29+parser.add_argument("--compare-n-backups", type=int, default=20,
30+ help="How many previous backups to consider hard-linking against; default: 20 (rsync max)")
31+
32+args = parser.parse_args()
33+
34+match = re.search("(.+)?:(.*)", args.dest)
35+
36+if match:
37+ dest_account = match.group(1)
38+ dest_path = match.group(2)
39+else:
40+ dest_account = None
41+ dest_path = args.dest
42+
43+mkdir_cmd = ["mkdir", "-p", dest_path]
44+if dest_account:
45+ mkdir_cmd = ['ssh', dest_account] + mkdir_cmd
46+subprocess.run(mkdir_cmd, check=True)
47+
48+# check what backups already exist
49+check_backups_cmd = ["ls", f"{dest_path}/"]
50+if dest_account:
51+ check_backups_cmd = ['ssh', dest_account] + check_backups_cmd
52+proc = subprocess.run(check_backups_cmd,
53+ stdout=subprocess.PIPE, encoding="utf-8", check=True)
54+
55+link_dests = proc.stdout.splitlines()
56+link_dests = list(filter(lambda x: re.search(args.backup_prefix, x), link_dests))
57+link_dests = link_dests[-args.compare_n_backups:]
58+link_dest_args = list(map(lambda x: f"--link-dest=../{x}", link_dests))
59+
60+my_dest = time.strftime(f'{args.backup_prefix}%Y%m%d-%H:%M')
61+
62+cmd = ["rsync", "--human-readable", "-a", "--hard-links",
63+ "--delete", "--compress-level", "9",
64+ "-e", "ssh"
65+]
66+
67+if not args.quiet:
68+ cmd.extend(["-v", "--progress"])
69+
70+cmd.extend(args.rsync_opt)
71+cmd.extend(link_dest_args)
72+
73+cmd.extend([os.path.normpath(f"{args.source}") + "/", # just look inside the directory
74+ os.path.join(args.dest, my_dest)])
75+
76+if not args.quiet:
77+ print(*cmd)
78+os.execvp(cmd[0], cmd)
diff -r 000000000000 -r 7c0782056095 setup.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/setup.py Wed Nov 06 00:01:48 2019 -0500
@@ -0,0 +1,12 @@
1+#!/usr/bin/python3
2+
3+import distutils.core
4+
5+distutils.core.setup(name='rsnappush',
6+ version='1.0',
7+ license = "EPL 2.0",
8+ author = "Frank Tobin",
9+ author_email = "ftobin@neverending.org",
10+ platforms = "POSIX",
11+ scripts=["rsnappush"]
12+)