Separate Hierarchy class into Repository and Snapshot subclasses

This commit is contained in:
Eric Torres 2019-03-13 14:26:37 -07:00
parent 5b27f5c3d6
commit 6a8dcf1ce8

View File

@ -2,20 +2,22 @@
import os.path import os.path
import datetime import datetime
import glob
class Hierarchy: class Hierarchy:
"""A class for generating the backup root hierarchy. """A class for organizing the backup root hierarchy.
""" See README.rst for more details."""
def __init__(self, dest): def __init__(self, dest):
"""Default constructor for the hierarchy class. """Default constructor for the Hierarchy class.
:param dest: the root directory of the backup hierarchy :param dest: the root directory of the backup hierarchy
:type dest: str, bytes, or path-like object :type dest: str, bytes
:raises: NotADirectoryError if dest is not a directory
""" """
if not os.path.isdir(dest): if not os.path.isdir(dest):
raise ValueError(f"{dest} is not a valid directory") raise NotADirectoryError(f"{dest}: no such directory")
self.dest = dest self.dest = dest
@ -23,53 +25,122 @@ class Hierarchy:
def base_dir(self): def base_dir(self):
"""Return the base directory of this hierarchy. """Return the base directory of this hierarchy.
>>> h = Hierarchy('backup')
>>> h.base_dir
>>> 'backup'
:rtype: str :rtype: str
""" """
return self.dest return self.dest
class Repository(Hierarchy):
def __init__(self, dest):
"""Default constructor for the Repository class."""
super().__init__(dest)
self._snapshot_dir = os.path.join(self.base_dir, "data")
self._snapshots = [
Snapshot(s)
for s in glob.glob(f"{self._snapshot_dir}/*")
if os.path.isdir(s)
]
@property
def snapshots(self):
"""Return a list of snapshot names stored in this Repository.
Example
-------
* Assuming that backup/data has the snapshot dirs:
* snapshot-one
* snapshot-two
>>> repo = Repository('backup')
>>> repo.snapshots
>>> ['snapshot-one', 'snapshot-two']
:returns: the names of all snapshots in this repository sorted by
date
:rtype: list
"""
return self._snapshots
@property @property
def prev_snapshot(self): def prev_snapshot(self):
"""Return the canonical path of the previous snapshot stored in """Return the canonical path of the previous snapshot stored in
this hierarchy. this hierarchy.
This method checks whether or not '/dest/prev' is a symlink to
a snapshot. If it is not, then a FileExistsError is raised.
:rtype: str
:raises: FileExistsError if 'dest/prev' is not a symlink
to a snapshot
"""
prevpath = os.path.join(self.base_dir, "prev")
if os.path.exists(prevpath) and not os.path.islink(prevpath):
raise FileExistsError(f"{prevpath} exists and is not a symlink")
elif not os.path.exists(prevpath):
raise FileNotFoundError(f"prevpath does not exist")
return os.path.realpath(prevpath)
@property
def prev_snapshot_link(self):
"""Return the symlink path of the previous snapshot stored in
this hierarchy.
:rtype: str :rtype: str
""" """
return os.path.join(self.base_dir, "prev") if self._snapshots == []:
return None
else:
return os.path.realpath(self.snapshot_dir, self.snapshots[-1])
@property @property
def curr_snapshot(self): def curr_snapshot(self):
"""Retrieve the current date and time and use that in the path """Create a new Snapshot object and return its handle.
for the current snapshot-to-be.
Example
-------
>>> repo = Repository('backup')
>>> repo.curr_snapshot
>>> Snapshot('backup/data/snapshot-{utcnow}')
:rtype: Snapshot object
"""
date = datetime.datetime.utcnow().isoformat().replace(":", "-")
path = os.path.join(self._snapshot_dir, f"snapshot-{date}")
return Snapshot(path)
class Snapshot(Hierarchy):
"""Hierarchy for a single snapshot.
Example
-------
>>> repo = Repository('backup')
>>> snapshots = repo.snapshots
>>> prev = snapshots[-1]
>>> prev.name
>>> 'backup/data/snapshot-{prevtime}'
>>> prev.home_dir
>>> 'backup/data/snapshot-{prevtime}/home'
>>> curr = repo.curr_snapshot
>>> curr.name
>>> 'snapshot-{utcnow}'
>>> curr.home_dir
>>> 'backup/data/snapshot-{utcnow}/home'
"""
def __init__(self, dest):
"""Default constructor for the Snapshot class."""
super().__init__(dest)
@property
def name(self):
"""Return the name of this snapshot.
Example
-------
>>> s = Snapshot('backup/data/snapshot-{utcprev}')
>>> s.name
>>> 'snapshot-{utcprev}'
:rtype: str :rtype: str
""" """
date = datetime.datetime.utcnow().isoformat() return os.path.basename(self.base_dir)
return os.path.join(self.base_dir, date.replace(":", "-"))
@property @property
def boot_dir(self): def boot_dir(self):
"""Retrieve the /boot backup directory of this hierarchy. """Retrieve the /boot backup directory of this snapshot.
Example
-------
>>> s = Snapshot('backup/data/snapshot-{utcnow}')
>>> s.boot_dir
>>> 'backup/data/snapshot-{utcnow}/boot'
:rtype: str :rtype: str
""" """
@ -77,7 +148,13 @@ class Hierarchy:
@property @property
def etc_dir(self): def etc_dir(self):
"""Retrieve the /etc backup directory of this hierarchy. """Retrieve the /etc backup directory of this snapshot.
Example
-------
>>> s = Snapshot('backup/data/snapshot-{utcnow}')
>>> s.etc_dir
>>> 'backup/data/snapshot-{utcnow}/etc'
:rtype: str :rtype: str
""" """
@ -85,7 +162,13 @@ class Hierarchy:
@property @property
def home_dir(self): def home_dir(self):
"""Retrieve the /home backup directory of this hierarchy. """Retrieve the /home backup directory of this snapshot.
Example
-------
>>> s = Snapshot('backup/data/snapshot-{utcnow}')
>>> s.home_dir
>>> 'backup/data/snapshot-{utcnow}/home'
:rtype: str :rtype: str
""" """
@ -93,7 +176,13 @@ class Hierarchy:
@property @property
def root_home_dir(self): def root_home_dir(self):
"""Retrieve root's home directory of this hierarchy. """Retrieve root's home directory of this snapshot.
Example
-------
>>> s = Snapshot('backup/data/snapshot-{utcnow}')
>>> s.root_home_dir
>>> 'backup/data/snapshot-{utcnow}/root'
:rtype: str :rtype: str
""" """