def get_snapshots(self, root=DEFAULT_POOL): snapshots = [] datasets = self.get_datasets() for dataset in datasets: # BTRFS in kernel has a weird bug where listing will fail # immediately after a filesystem update. Loop on list until # it is successful try_again = True failure = False while try_again: try: subvol_reader = reader(StringIO(btrfs_cmd.subvolume.list("--sort=path", "-s", dataset)), delimiter=" ") if failure: log.debug("btrfs listing finished") try_again = False except ErrorReturnCode_19: if not failure: failure = True log.debug("btrfs listing failed") for row in subvol_reader: if 'btrfs-auto-snap' in row[-1:][0] and row[-1:][0].startswith('.btrfs-auto-snap'): _, interval, label = row[-1:][0].split("/") snapshots.append(Snapshot(self, dataset, interval, label, size=1)) return snapshots
def auto_snapshot(self, interval, keep): """create auto-snapshot set with interval, purging old snapshots""" fs_config = self.get_config() now = datetime.now() date = "%s-%s-%s-%sh%s" % (str(now.year), str(now.month).zfill(2), str(now.day).zfill(2), str(now.hour).zfill(2), str(now.minute).zfill(2)) key = "auto-snapshot:%s" % interval to_snapshot = [] exclude = [] for dataset in sorted(fs_config.keys()): if fs_config[dataset]['auto-snapshot']: if key in fs_config[dataset].keys(): if fs_config[dataset][key]: to_snapshot.append(dataset) else: exclude.append(dataset) else: to_snapshot.append(dataset) else: exclude.append(dataset) log.debug("Snapshotting the following: %s" % ' '.join(to_snapshot)) log.debug("Excluding the following: %s" % ' '.join(exclude)) for dataset in to_snapshot: self.commit_snapshot(dataset, interval, date)
def destroy_snapshot(self, name): """callout to zfs command to destroy snapshot""" # name = "%s@%s" % (filesystem, interval) if env.DRY_RUN: log.info("dry-run: would destroy %s" % name) else: log.debug("destroy_snapshot(%s)" % (name)) if "@" in name: zfs_cmd.destroy(name) else: log.error("should not be destroying a dataset! name: %s" % name)
def destroy_snapshot(self, name): """callout to btrfs command to destroy snapshot""" if env.DRY_RUN: log.info("dry-run: would destroy %s" % name) else: log.debug("destroy_snapshot(%s)" % (name)) if os.access(name, os.F_OK): if "/.btrfs-auto-snap/" in name: btrfs_cmd.subvolume.delete(name) else: log.error("should not be destroying a dataset! name: %s" % name) else: log.error("cannot find %s" % name)
def commit_snapshot(self, filesystem, interval, label): """call out to zfs command to create snapshot return a Snapshot object""" name = "%s@zfs-auto-snap_%s-%s" % (filesystem, interval, label) current_snapshots = [s.name for s in self.get_snapshots()] if name in current_snapshots: log.error("snapshot %s already exists, not overwriting!" % name) return if env.DRY_RUN: log.info("dry-run: would commit %s" % name) else: log.debug("commit_snapshot(%s, %s, %s)" % (filesystem, interval, label)) zfs_cmd.snapshot(name)
def is_enabled(cls): if os.geteuid() != 0: log.debug("btrfs is only enabled for the root user!") return False if not sys.platform.startswith('linux'): log.debug("btrfs is only enabled on linux!") return False elif BTRFS_AVAILABLE: if "v0.19" in btrfs_cmd("--version"): log.warning("'btrfs' command v0.19 is incompatible, please upgrade to at least v0.20 to enable btrfs") return False return True else: return False
def commit_snapshot(self, dataset, interval, label): """call out to btrfs command to create snapshot return a Snapshot object""" snap_dir = os.path.join(dataset, '.btrfs-auto-snap') interval_dir = os.path.join(snap_dir, interval) destination = os.path.join(interval_dir, label) if os.path.exists(destination): log.error("snapshot %s already exists, not overwriting!" % destination) return # if interval_dir isn't a directory, and also isn't a file with that name # create the snapshot label directory if not os.path.isdir(interval_dir) and not os.path.exists(interval_dir): s = os.stat(snap_dir) os.mkdir(interval_dir) os.chown(interval_dir, s[stat.ST_UID], s[stat.ST_GID]) if env.DRY_RUN: log.info("dry-run: would commit %s" % (destination)) else: log.debug("commit_snapshot(%s, %s, %s)" % (dataset, interval, label)) btrfs_cmd.subvolume.snapshot(dataset, destination)
import stat from csv import reader from StringIO import StringIO from tardis import log, env, Snapshot from tardis.fs import Filesystem import re try: from sh import btrfs as btrfs_cmd BTRFS_AVAILABLE = True except: BTRFS_AVAILABLE = False log.debug("unable to find btrfs command") from sh import ErrorReturnCode_19 def dir_is_writable(directory): s = os.stat(directory) mode = s[stat.ST_MODE] return bool( (mode & stat.S_IWUSR) or (mode & stat.S_IWGRP) or (mode & stat.S_IWOTH) ) class Btrfs(Filesystem):