@@ -125,8 +125,8 @@ def _parseDiffLine(line):
125125 return path
126126
127127
128- def _snapshots (directory ):
129- """Retrieve a list of snapshots in a directory .
128+ def _snapshots (repository ):
129+ """Retrieve a list of snapshots in a repository .
130130
131131 Note:
132132 Because of a supposed bug in btrfs' handling of passed in
@@ -136,7 +136,8 @@ def _snapshots(directory):
136136 usage of this function is discouraged. Use the Repository's
137137 snapshots() method instead.
138138 """
139- output , _ = execute (* listSnapshots (directory ), read_out = True )
139+ cmd = repository .command (listSnapshots , repository .path ())
140+ output , _ = execute (* cmd , read_out = True )
140141 # We might retrieve an empty output if no snapshots were present. In
141142 # this case, just return early here.
142143 if not output :
@@ -147,9 +148,10 @@ def _snapshots(directory):
147148 return [_parseListLine (line ) for line in output ]
148149
149150
150- def _isRoot (directory ):
151+ def _isRoot (directory , repository ):
151152 """Check if a given directory represents the root of a btrfs file system."""
152- output , _ = execute (* show (directory ), read_out = True )
153+ cmd = repository .command (show , directory )
154+ output , _ = execute (* cmd , read_out = True )
153155 output = output .decode ("utf-8" )[:- 1 ].split ("\n " )
154156
155157 # The output of show() contains multiple lines in case the given
@@ -160,7 +162,7 @@ def _isRoot(directory):
160162 return len (output ) == 1 and output [0 ].endswith (_SHOW_IS_ROOT )
161163
162164
163- def _findRoot (directory ):
165+ def _findRoot (directory , repository ):
164166 """Find the root of the btrfs file system containing the given directory."""
165167 assert directory
166168 assert directory == abspath (directory )
@@ -169,7 +171,7 @@ def _findRoot(directory):
169171 # or later because of a dirname invocation. However, the show command
170172 # in _isRoot will fail for an empty directory (a case that will also
171173 # be hit if this function is run on a non-btrfs file system).
172- while not _isRoot (directory ):
174+ while not _isRoot (directory , repository ):
173175 new_directory = dirname (directory )
174176
175177 # Executing a dirname on the root directory ('/') just returns the
@@ -302,8 +304,9 @@ def _findCommonSnapshots(src_snaps, dst_snaps):
302304def _createSnapshot (subvolume , repository , snapshots ):
303305 """Create a snapshot of the given subvolume in the given repository."""
304306 name = _snapshotName (subvolume , snapshots )
307+ cmd = repository .command (mkSnapshot , subvolume , repository .path (name ))
305308
306- execute (* mkSnapshot ( subvolume , repository . path ( name )) )
309+ execute (* cmd )
307310 return name
308311
309312
@@ -314,7 +317,7 @@ def _findOrCreate(subvolume, repository, snapshots):
314317 # If we found no snapshot or if files are changed between the current
315318 # state of the subvolume and the most recent snapshot we just found
316319 # then create a new snapshot.
317- if not snapshot or _diff (snapshot , subvolume ):
320+ if not snapshot or _diff (snapshot , subvolume , repository ):
318321 old = snapshot ["path" ] if snapshot else None
319322 new = _createSnapshot (subvolume , repository , snapshots )
320323 return new , old
@@ -355,12 +358,12 @@ def _deploy(snapshot, parent, src, dst, src_snaps, subvolume):
355358
356359 # Be sure to have the snapshot persisted to disk before trying to
357360 # serialize it.
358- execute (* syncFs ( src .root ))
361+ execute (* src . command ( syncFs , src .root ))
359362 # Finally transfer the snapshot from the source repository to the
360363 # destination.
361364 pipeline ([
362- serialize ( src .path (snapshot ), parents ),
363- deserialize ( dst .path ())
365+ src . command ( serialize , src .path (snapshot ), parents ),
366+ dst . command ( deserialize , dst .path ()),
364367 ])
365368
366369
@@ -440,7 +443,8 @@ def _restore(subvolume, src, dst, snapshots, snapshots_only):
440443 # Now that we got the snapshot back on the destination repository,
441444 # we can restore the actual subvolume from it (if desired).
442445 if not snapshots_only :
443- execute (* mkSnapshot (dst .path (snapshot ), subvolume , writable = True ))
446+ cmd = dst .command (mkSnapshot , dst .path (snapshot ), subvolume , writable = True )
447+ execute (* cmd )
444448
445449
446450def restore (subvolumes , src , dst , snapshots_only = False ):
@@ -455,7 +459,7 @@ def restore(subvolumes, src, dst, snapshots_only=False):
455459 _restore (subvolume , src , dst , snapshots , snapshots_only )
456460
457461
458- def _diff (snapshot , subvolume ):
462+ def _diff (snapshot , subvolume , repository ):
459463 """Find the files that changed in a given subvolume with respect to a snapshot."""
460464 # Because of an apparent bug in btrfs(8) (or a misunderstanding on my
461465 # side), we cannot use the generation reported for a snapshot to
@@ -469,7 +473,8 @@ def _diff(snapshot, subvolume):
469473 # to clarify whether a new snapshot *always* also means a new
470474 # generation (I assume so, but it would be best to get
471475 # confirmation).
472- output , _ = execute (* diff (subvolume , generation ), read_out = True )
476+ cmd = repository .command (diff , subvolume , generation )
477+ output , _ = execute (* cmd , read_out = True )
473478 output = output .decode ("utf-8" )[:- 1 ].split ("\n " )
474479 # The diff output usually is ended by a line such as:
475480 # "transid marker was" followed by a generation ID. We should ignore
@@ -500,7 +505,8 @@ def _purge(subvolume, repository, duration, snapshots):
500505 # old enough so that the snapshot should be deleted.
501506 time = datetime .strptime (string , _TIME_FORMAT )
502507 if time + duration < now :
503- execute (* delete (repository .path (snapshot )))
508+ cmd = repository .command (delete , repository .path (snapshot ))
509+ execute (* cmd )
504510
505511
506512def _trail (path ):
@@ -510,18 +516,19 @@ def _trail(path):
510516
511517class Repository :
512518 """This class represents a repository for snapshots."""
513- def __init__ (self , directory ):
519+ def __init__ (self , directory , remote_cmd = None ):
514520 """Initialize the object and bind it to the given directory."""
515521 # We always work with absolute paths here.
516522 directory = abspath (directory )
517523
518- self ._root = _findRoot (directory )
524+ self ._remote_cmd = remote_cmd
525+ self ._root = _findRoot (directory , self )
519526 self ._directory = _trail (directory )
520527
521528
522529 def snapshots (self ):
523530 """Retrieve a list of snapshots in this repository."""
524- snapshots = _snapshots (self . _directory )
531+ snapshots = _snapshots (self )
525532
526533 # We need to work around the btrfs problem that not necessarily all
527534 # snapshots listed are located in our repository's directory.
@@ -584,14 +591,20 @@ def diff(self, snapshot, subvolume):
584591 if not found :
585592 raise FileNotFoundError ("Snapshot not found: \" %s\" " % snapshot )
586593
587- return _diff (found , subvolume )
594+ return _diff (found , subvolume , self )
588595
589596
590597 def path (self , * components ):
591598 """Form an absolute path by combining the given path components."""
592599 return join (self ._directory , * components )
593600
594601
602+ def command (self , function , * args , ** kwargs ):
603+ """Create a command."""
604+ command = function (* args , ** kwargs )
605+ return (self ._remote_cmd if self ._remote_cmd else []) + command
606+
607+
595608 @property
596609 def root (self ):
597610 """Retrieve the root directory of the btrfs file system the repository resides on."""
0 commit comments