Split hierarchy module into its own package
This commit is contained in:
parent
c7d465404f
commit
16adea1a58
@ -1,221 +0,0 @@
|
|||||||
"""
|
|
||||||
.. author:: Eric Torres
|
|
||||||
.. module:: rbackup.hierarchy
|
|
||||||
:synopsis: Classes for creating the backup hierarchy.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os.path
|
|
||||||
import datetime
|
|
||||||
import glob
|
|
||||||
|
|
||||||
|
|
||||||
class Hierarchy:
|
|
||||||
"""A class for organizing the backup root hierarchy.
|
|
||||||
See README.rst for more details."""
|
|
||||||
|
|
||||||
def __init__(self, dest):
|
|
||||||
"""Default constructor for the Hierarchy class.
|
|
||||||
|
|
||||||
:param dest: the root directory of the backup hierarchy
|
|
||||||
:type dest: str, bytes
|
|
||||||
:raises: NotADirectoryError if dest is not a directory
|
|
||||||
"""
|
|
||||||
if not os.path.isdir(dest):
|
|
||||||
raise NotADirectoryError(f"{dest}: no such directory")
|
|
||||||
|
|
||||||
self.dest = dest
|
|
||||||
|
|
||||||
@property
|
|
||||||
def base_path(self):
|
|
||||||
"""Return the base directory of this hierarchy.
|
|
||||||
|
|
||||||
>>> h = Hierarchy('backup')
|
|
||||||
>>> h.base_path
|
|
||||||
>>> 'backup'
|
|
||||||
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
return self.dest
|
|
||||||
|
|
||||||
|
|
||||||
class Repository(Hierarchy):
|
|
||||||
"""A class for interacting with a backup repository.
|
|
||||||
|
|
||||||
Attributes
|
|
||||||
----------
|
|
||||||
* "data" directory for storing snapshots
|
|
||||||
* Each snapshot is its own directory with its own sub-hierarchy
|
|
||||||
* Each snapshot has an "old" directory for storing deleted data
|
|
||||||
* rsync hardlinks unchanged files between snapshots
|
|
||||||
* A symlink in the root of the repository symlinking to the
|
|
||||||
most recent snapshot
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, dest):
|
|
||||||
"""Default constructor for the Repository class."""
|
|
||||||
super().__init__(dest)
|
|
||||||
|
|
||||||
self._snapshot_dir = os.path.join(self.base_path, "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 snapshots stored in this Repository.
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
* Assuming that backup/data has the snapshot dirs:
|
|
||||||
* snapshot-one
|
|
||||||
* snapshot-two
|
|
||||||
>>> repo = Repository('backup')
|
|
||||||
>>> repo.snapshots
|
|
||||||
>>> ['backup/data/snapshot-one', 'backup/data/snapshot-two']
|
|
||||||
|
|
||||||
:returns: the names of all snapshots in this repository sorted by
|
|
||||||
date
|
|
||||||
:rtype: list
|
|
||||||
:rtype: None if there are no snapshots in this Repository
|
|
||||||
"""
|
|
||||||
if self._snapshots == []:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return self._snapshots
|
|
||||||
|
|
||||||
@property
|
|
||||||
def prev_snapshot(self):
|
|
||||||
"""Return the instance of the previous Snapshot.
|
|
||||||
|
|
||||||
:rtype: Snapshot object
|
|
||||||
"""
|
|
||||||
if self._snapshots == []:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return self.snapshots[-1]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def curr_snapshot(self):
|
|
||||||
"""Create a new Snapshot object and return its handle.
|
|
||||||
|
|
||||||
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
|
|
||||||
>>> '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, path):
|
|
||||||
"""Default constructor for the Snapshot class."""
|
|
||||||
super().__init__(path)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def path(self):
|
|
||||||
"""Return the canonical path of this snapshot.
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
>>> s = Snapshot('backup/data/snapshot-{utcprev}')
|
|
||||||
>>> s.name
|
|
||||||
>>> 'backup/data/snapshot-{utcprev}'
|
|
||||||
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
return self.base_path
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""Return the name of this snapshot.
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
>>> s = Snapshot('backup/data/snapshot-{utcprev}')
|
|
||||||
>>> s.name
|
|
||||||
>>> 'snapshot-{utcprev}'
|
|
||||||
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
return os.path.basename(self.base_path)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def boot_dir(self):
|
|
||||||
"""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
|
|
||||||
"""
|
|
||||||
return os.path.join(self.base_path, "boot")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def etc_dir(self):
|
|
||||||
"""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
|
|
||||||
"""
|
|
||||||
return os.path.join(self.base_path, "etc")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def home_dir(self):
|
|
||||||
"""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
|
|
||||||
"""
|
|
||||||
return os.path.join(self.base_path, "home")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def root_home_dir(self):
|
|
||||||
"""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
|
|
||||||
"""
|
|
||||||
return os.path.join(self.base_path, "root")
|
|
0
rbackup/hierarchy/__init__.py
Normal file
0
rbackup/hierarchy/__init__.py
Normal file
38
rbackup/hierarchy/hierarchy.py
Normal file
38
rbackup/hierarchy/hierarchy.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
"""
|
||||||
|
.. author:: Eric Torres
|
||||||
|
.. module:: rbackup.hierarchy
|
||||||
|
:synopsis: Classes for creating the backup hierarchy.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import datetime
|
||||||
|
import glob
|
||||||
|
|
||||||
|
|
||||||
|
class Hierarchy:
|
||||||
|
"""A class for organizing the backup root hierarchy.
|
||||||
|
See README.rst for more details."""
|
||||||
|
|
||||||
|
def __init__(self, dest):
|
||||||
|
"""Default constructor for the Hierarchy class.
|
||||||
|
|
||||||
|
:param dest: the root directory of the backup hierarchy
|
||||||
|
:type dest: str, bytes
|
||||||
|
:raises: NotADirectoryError if dest is not a directory
|
||||||
|
"""
|
||||||
|
if not os.path.isdir(dest):
|
||||||
|
raise NotADirectoryError(f"{dest}: no such directory")
|
||||||
|
|
||||||
|
self.dest = dest
|
||||||
|
|
||||||
|
@property
|
||||||
|
def base_path(self):
|
||||||
|
"""Return the base directory of this hierarchy.
|
||||||
|
|
||||||
|
>>> h = Hierarchy('backup')
|
||||||
|
>>> h.base_path
|
||||||
|
>>> 'backup'
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self.dest
|
124
rbackup/hierarchy/repository.py
Normal file
124
rbackup/hierarchy/repository.py
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
"""
|
||||||
|
.. author:: Eric Torres
|
||||||
|
.. module:: rbackup.hierarchy.repository
|
||||||
|
:synopsis: Class for structuring a backup repository.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import datetime
|
||||||
|
import glob
|
||||||
|
|
||||||
|
|
||||||
|
class Repository(Hierarchy):
|
||||||
|
"""A class for interacting with a backup repository.
|
||||||
|
|
||||||
|
At the time of creation, the following is true about the class:
|
||||||
|
The current snapshot points to:
|
||||||
|
-------------------------------
|
||||||
|
* None if the repository is empty
|
||||||
|
* The most recent snapshot before running create_snapshot()
|
||||||
|
* A new, empty snapshot after running create_snapshot()
|
||||||
|
|
||||||
|
The previous snapshot points to:
|
||||||
|
--------------------------------
|
||||||
|
* None if the repository is empty
|
||||||
|
* The snapshot before the current snapshot if there is more than one
|
||||||
|
* The
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
* curr_snapshot - return either the most recent snapshot or
|
||||||
|
the new snapshot created after running create_snapshot()
|
||||||
|
* prev_snapshot - return the Snapshot of the previous backup
|
||||||
|
* snapshots - return a list of snapshots stored in this repository
|
||||||
|
|
||||||
|
Methods
|
||||||
|
-------
|
||||||
|
* create_snapshot() - create a new snapshot, then update curr_snapshot
|
||||||
|
* update_snapshots() - update the list of snapshots this repository
|
||||||
|
contains
|
||||||
|
|
||||||
|
Directory Structure
|
||||||
|
-------------------
|
||||||
|
* "data" directory for storing snapshots
|
||||||
|
* Each snapshot is its own directory with its own sub-hierarchy
|
||||||
|
* Each snapshot has an "old" directory for storing deleted data
|
||||||
|
* rsync hardlinks unchanged files between snapshots
|
||||||
|
* A symlink in the root of the repository symlinking to the
|
||||||
|
most recent snapshot
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, dest):
|
||||||
|
"""Default constructor for the Repository class."""
|
||||||
|
super().__init__(dest)
|
||||||
|
|
||||||
|
self._snapshot_dir = os.path.join(self.base_path, "data")
|
||||||
|
self.update_snapshots()
|
||||||
|
self._prev_snapshot = None
|
||||||
|
self._curr_snapshot = self._prev_snapshot
|
||||||
|
|
||||||
|
@property
|
||||||
|
def snapshots(self):
|
||||||
|
"""Return a list of snapshots stored in this Repository.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
* Assuming that backup/data has the snapshot dirs:
|
||||||
|
* snapshot-one
|
||||||
|
* snapshot-two
|
||||||
|
>>> repo = Repository('backup')
|
||||||
|
>>> repo.snapshots
|
||||||
|
>>> ['backup/data/snapshot-one', 'backup/data/snapshot-two']
|
||||||
|
|
||||||
|
:returns: the names of all snapshots in this repository sorted by
|
||||||
|
date
|
||||||
|
:rtype: list
|
||||||
|
:rtype: None if there are no snapshots in this Repository
|
||||||
|
"""
|
||||||
|
if self._snapshots == []:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return self._snapshots
|
||||||
|
|
||||||
|
def create_snapshot(self, name=datetime.datetime.utcnow().isoformat().replace(":", "-")):
|
||||||
|
"""
|
||||||
|
:return: a new Snapshot object
|
||||||
|
:rtype: Snapshot object
|
||||||
|
"""
|
||||||
|
path = os.path.join(self._snapshot_dir, f"snapshot-{name}")
|
||||||
|
|
||||||
|
self._curr_snapshot = Snapshot(path)
|
||||||
|
self._snapshots.append(_curr_snapshot)
|
||||||
|
|
||||||
|
def update_snapshots(self):
|
||||||
|
"""Update the list of snapshots in this repository."""
|
||||||
|
self._snapshots = [
|
||||||
|
Snapshot(s)
|
||||||
|
for s in glob.glob(f"{self._snapshot_dir}/*")
|
||||||
|
if os.path.isdir(s)
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prev_snapshot(self):
|
||||||
|
"""Return the instance of the previous Snapshot.
|
||||||
|
|
||||||
|
:rtype: Snapshot object
|
||||||
|
"""
|
||||||
|
if self.snapshots is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return self.snapshots[-1]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def curr_snapshot(self):
|
||||||
|
"""Return this Repository's current snapshot.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
>>> repo = Repository('backup')
|
||||||
|
>>> repo.curr_snapshot
|
||||||
|
>>> Snapshot('backup/data/snapshot-{utcnow}')
|
||||||
|
|
||||||
|
:rtype: Snapshot object
|
||||||
|
"""
|
||||||
|
return self._curr_snapshot
|
114
rbackup/hierarchy/snapshot.py
Normal file
114
rbackup/hierarchy/snapshot.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
"""
|
||||||
|
.. author:: Eric Torres
|
||||||
|
.. module:: rbackup.hierarchy
|
||||||
|
:synopsis: Classes for creating the backup hierarchy.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
|
class Snapshot(Hierarchy):
|
||||||
|
"""Hierarchy for a single snapshot.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
>>> repo = Repository('backup')
|
||||||
|
>>> snapshots = repo.snapshots
|
||||||
|
>>> prev = snapshots[-1]
|
||||||
|
>>> prev.name
|
||||||
|
>>> '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, path):
|
||||||
|
"""Default constructor for the Snapshot class."""
|
||||||
|
super().__init__(path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self):
|
||||||
|
"""Return the canonical path of this snapshot.
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
>>> s = Snapshot('backup/data/snapshot-{utcprev}')
|
||||||
|
>>> s.name
|
||||||
|
>>> 'backup/data/snapshot-{utcprev}'
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self.base_path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of this snapshot.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
>>> s = Snapshot('backup/data/snapshot-{utcprev}')
|
||||||
|
>>> s.name
|
||||||
|
>>> 'snapshot-{utcprev}'
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return os.path.basename(self.base_path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def boot_dir(self):
|
||||||
|
"""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
|
||||||
|
"""
|
||||||
|
return os.path.join(self.base_path, "boot")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def etc_dir(self):
|
||||||
|
"""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
|
||||||
|
"""
|
||||||
|
return os.path.join(self.base_path, "etc")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def home_dir(self):
|
||||||
|
"""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
|
||||||
|
"""
|
||||||
|
return os.path.join(self.base_path, "home")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def root_home_dir(self):
|
||||||
|
"""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
|
||||||
|
"""
|
||||||
|
return os.path.join(self.base_path, "root")
|
Loading…
x
Reference in New Issue
Block a user