diff --git a/examples/tunasync.ini b/examples/tunasync.ini index 90a77d4..2558ca4 100644 --- a/examples/tunasync.ini +++ b/examples/tunasync.ini @@ -13,7 +13,7 @@ max_retry = 2 [btrfs] service_dir = {mirror_root}/{mirror_name}/_current working_dir = {mirror_root}/{mirror_name}/_working -tmp_dir = {mirror_root}/{mirror_name}/_tmp +gc_dir = {mirror_root}/{mirror_name}/_gc_{timestamp} # rmirror:archlinux] diff --git a/systemd/tunasync-snapshot-gc@.service b/systemd/tunasync-snapshot-gc@.service new file mode 100644 index 0000000..6bb4fb1 --- /dev/null +++ b/systemd/tunasync-snapshot-gc@.service @@ -0,0 +1,11 @@ +[Unit] +Description=Delete garbage subvolumes generated by tunasync +Requires = network.target +After = network.target + +[Service] +Type=simple +ExecStart=/home/tuna/.virtualenvs/tunasync/bin/python -u /home/tuna/tunasync/tunasync_snapshot_gc.py %i + +[Install] +WantedBy = multi-user.target diff --git a/systemd/tunasync-snapshot-gc@.timer b/systemd/tunasync-snapshot-gc@.timer new file mode 100644 index 0000000..57ec5ab --- /dev/null +++ b/systemd/tunasync-snapshot-gc@.timer @@ -0,0 +1,9 @@ +[Unit] +Description=TUNAsync GC every 10 minutes + +[Timer] +OnUnitActiveSec=10min +Unit=tunasync-snapshot-gc@%i.service + +[Install] +WantedBy=multi-user.target diff --git a/systemd/tunasync.service b/systemd/tunasync.service new file mode 100644 index 0000000..df5e902 --- /dev/null +++ b/systemd/tunasync.service @@ -0,0 +1,13 @@ +[Unit] +Description = TUNA mirrors sync daemon +Requires = network.target +After = network.target + +[Service] +ExecStart = /home/tuna/.virtualenvs/tunasync/bin/python -u /home/tuna/tunasync/tunasync.py -c /etc/tunasync.ini +KillSignal = SIGTERM +ExecReload = /bin/kill -SIGUSR1 $MAINPID +Environment = "HOME=/home/tuna" + +[Install] +WantedBy = multi-user.target diff --git a/tunasync/btrfs_snapshot.py b/tunasync/btrfs_snapshot.py index 87a3162..a5288f8 100644 --- a/tunasync/btrfs_snapshot.py +++ b/tunasync/btrfs_snapshot.py @@ -2,6 +2,7 @@ # -*- coding:utf-8 -*- import sh import os +from datetime import datetime from .hook import JobHook @@ -11,10 +12,10 @@ class BtrfsVolumeError(Exception): class BtrfsHook(JobHook): - def __init__(self, service_dir, working_dir, tmp_dir): + def __init__(self, service_dir, working_dir, gc_dir): self.service_dir = service_dir self.working_dir = working_dir - self.tmp_dir = tmp_dir + self.gc_dir = gc_dir def before_job(self): self._create_working_snapshot() @@ -44,13 +45,15 @@ class BtrfsHook(JobHook): def _commit_changes(self): self._ensure_subvolume() self._ensure_subvolume() - out = sh.mv(self.service_dir, self.tmp_dir) + gc_dir = self.gc_dir.format(timestamp=datetime.now().strftime("%s")) + + out = sh.mv(self.service_dir, gc_dir) assert out.exit_code == 0 and out.stderr == "" out = sh.mv(self.working_dir, self.service_dir) assert out.exit_code == 0 and out.stderr == "" # print("btrfs subvolume delete {}".format(self.tmp_dir)) - sh.sleep(3) - out = sh.btrfs("subvolume", "delete", self.tmp_dir) - assert out.exit_code == 0 and out.stderr == "" + # sh.sleep(3) + # out = sh.btrfs("subvolume", "delete", self.tmp_dir) + # assert out.exit_code == 0 and out.stderr == "" # vim: ts=4 sw=4 sts=4 expandtab diff --git a/tunasync/tunasync.py b/tunasync/tunasync.py index 1b10921..f08ccac 100644 --- a/tunasync/tunasync.py +++ b/tunasync/tunasync.py @@ -113,11 +113,11 @@ class MirrorConfig(object): mirror_root=parent.mirror_root, mirror_name=self.name ) - tmp_dir = parent.btrfs_tmp_dir_tmpl.format( + gc_dir = parent.btrfs_gc_dir_tmpl.format( mirror_root=parent.mirror_root, mirror_name=self.name ) - hooks.append(BtrfsHook(service_dir, working_dir, tmp_dir)) + hooks.append(BtrfsHook(service_dir, working_dir, gc_dir)) return hooks @@ -152,8 +152,8 @@ class TUNASync(object): "btrfs", "service_dir") self.btrfs_working_dir_tmpl = self._settings.get( "btrfs", "working_dir") - self.btrfs_tmp_dir_tmpl = self._settings.get( - "btrfs", "tmp_dir") + self.btrfs_gc_dir_tmpl = self._settings.get( + "btrfs", "gc_dir") def hooks(self): return [] diff --git a/tunasync_snapshot_gc.py b/tunasync_snapshot_gc.py new file mode 100644 index 0000000..3b80f7e --- /dev/null +++ b/tunasync_snapshot_gc.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python2 +# -*- coding:utf-8 -*- +import re +import sh +import os +import argparse + +if __name__ == "__main__": + parser = argparse.ArgumentParser(prog="tunasync_snapshot_gc") + parser.add_argument("--max-level", type=int, default=2, help="max walk level to find garbage snapshots") + parser.add_argument("--pattern", default=r"^_gc_\d+", help="pattern to match garbage snapshots") + parser.add_argument("mirror_root", help="tunasync mirror root") + + args = parser.parse_args() + + pattern = re.compile(args.pattern) + + def walk(_dir, level=1): + if level > 2: + return + + for fname in os.listdir(_dir): + abs_fname = os.path.join(_dir, fname) + if os.path.isdir(abs_fname): + if pattern.match(fname): + print("GC: {}".format(abs_fname)) + try: + ret = sh.btrfs("subvolume", "delete", abs_fname) + except sh.ErrorReturnCode: + print("Error: {}".format(ret.stderr)) + else: + walk(abs_fname, level+1) + + walk(args.mirror_root) + +# vim: ts=4 sw=4 sts=4 expandtab