Browse code

Sane option parsing, dry-run switch

Lorenz Hüdepohl authored on23/04/2015 15:25:41
Showing1 changed files
... ...
@@ -3,18 +3,58 @@
3 3
 import sys
4 4
 import datetime
5 5
 from subprocess import Popen, PIPE, check_call
6
+import argparse
6 7
 
7
-tagname, = sys.argv[1:]
8
+parser = argparse.ArgumentParser(
9
+            formatter_class=argparse.RawTextHelpFormatter,
10
+            description='''
11
+  Use with cron to make periodic ZFS snapshots, with the option of keeping
12
+  only a limited number of old snapshots.
8 13
 
9
-zfs_props = Popen(["/usr/sbin/zfs", "get", "-t", "filesystem", "autosnapshot:{0}".format(tagname), "-H"],
14
+  For every ZFS filesystem that has the user property 'autosnapshot:TAGNAME'
15
+  a snapshot is created. The value must be a positive number or the string
16
+  "forever", which is used as the number of old snapshots to keep. If there
17
+  are more, the oldest are destroyed.
18
+
19
+  Example:
20
+
21
+   Setup properties:
22
+
23
+    #> zfs set autosnapshot:5min=12        dpool/fs
24
+    #> zfs set autosnapshot:hourly=24      dpool/fs
25
+    #> zfs set autosnapshot:daily=7        dpool/fs
26
+    #> zfs set autosnapshot:weekly=forever dpool/fs
27
+
28
+   Use with this crontab:
29
+
30
+    @weekly     zfs_auto_snapshot weekly
31
+    @daily      zfs_auto_snapshot daily
32
+    @hourly     zfs_auto_snapshot hourly
33
+    */5 * * * * zfs_auto_snapshot 5min
34
+''')
35
+
36
+parser.add_argument('tagname', metavar='TAGNAME', type=str)
37
+
38
+parser.add_argument('-n', '--dry-run', action="store_true",
39
+		help='Only echo the commands that would be issued,\ndo not actually do anything.')
40
+
41
+args = parser.parse_args()
42
+
43
+if args.dry_run:
44
+    zfs = ["echo", "/usr/sbin/zfs"]
45
+else:
46
+    zfs = ["/usr/sbin/zfs"]
47
+
48
+zfs_props = Popen(["/usr/sbin/zfs", "get", "-t", "filesystem", "autosnapshot:{0}".format(args.tagname), "-H"],
10 49
         stdout=PIPE)
11 50
 
12 51
 for line in zfs_props.stdout:
13 52
     fs, prop, number, source = line.split("\t")
14 53
     if number != "-":
15 54
         # Create new snapshot
16
-        check_call(["/usr/sbin/zfs", "snapshot", "{0}@autosnapshot-{1}-{2}".format(fs, tagname,
17
-            datetime.datetime.now().strftime("%Y-%m-%d-%H:%M"))])
55
+        new_snapshot = "{0}@autosnapshot-{1}-{2}".format(fs, args.tagname,
56
+                            datetime.datetime.now().strftime("%Y-%m-%d-%H:%M"))
57
+        check_call(zfs + ["snapshot", new_snapshot])
18 58
 
19 59
         # Delete oldest snapshots
20 60
         if number == "forever":
... ...
@@ -23,7 +63,7 @@ for line in zfs_props.stdout:
23 63
             number = int(number)
24 64
 
25 65
         if number <= 0:
26
-            raise Exception("Invalid number for property 'autosnapshot:{0}' on filesystem {1}: {2}".format(tagname, fs, number))
66
+            raise Exception("Invalid number for property 'autosnapshot:{0}' on filesystem {1}: {2}".format(args.tagname, fs, number))
27 67
 
28 68
         snapshots = []
29 69
 
... ...
@@ -31,13 +71,16 @@ for line in zfs_props.stdout:
31 71
 
32 72
         for line_snapshots in zfs_snapshots.stdout:
33 73
             snapname, rest = line_snapshots.split("\t", 1)
34
-            if snapname.startswith("{0}@autosnapshot-{1}-".format(fs, tagname)):
74
+            if snapname.startswith("{0}@autosnapshot-{1}-".format(fs, args.tagname)):
35 75
                 snapshots.append(snapname)
36 76
 
77
+        if args.dry_run:
78
+            snapshots.append(new_snapshot)
79
+
37 80
         to_delete = snapshots[:-number]
38 81
 
39 82
         for d in to_delete:
40 83
             # Delete this old snapshot
41
-            if not snapname.startswith("{0}@autosnapshot-{1}-".format(fs, tagname)):
42
-                raise Exception("Invalid snapshot name '{0}', does not start with '{1}@autosnapshot-{2}-'".format(snapname, fs, tagname))
43
-            check_call(["/usr/sbin/zfs", "destroy", d])
84
+            if not snapname.startswith("{0}@autosnapshot-{1}-".format(fs, args.tagname)):
85
+                raise Exception("Invalid snapshot name '{0}', does not start with '{1}@autosnapshot-{2}-'".format(snapname, fs, args.tagname))
86
+            check_call(zfs + ["destroy", d])