Rename current_snapshot attribute, return it on creation, use pickle

This commit is contained in:
Eric Torres 2019-03-28 12:12:35 -07:00
parent e2ed27cbe4
commit 2c16fe793a

View File

@ -5,6 +5,7 @@
""" """
import logging import logging
import datetime import datetime
import pickle
from rbackup.hierarchy.hierarchy import Hierarchy from rbackup.hierarchy.hierarchy import Hierarchy
from rbackup.hierarchy.snapshot import Snapshot from rbackup.hierarchy.snapshot import Snapshot
@ -18,9 +19,16 @@ syslog = logging.getLogger(__name__)
class Repository(Hierarchy): class Repository(Hierarchy):
"""A class for interacting with a backup repository. """A class for interacting with a backup repository.
Repository is a mutable, stateful class for representing a
directory that contains backup data sequestered into snapshots
and a symlink to the most recently created snapshot.
* Each snapshot in a repository is unaware of one another,
this is the job of the repository to organize
* The only way snapshots are linked together is in files
that are hardlinked together
At the time of creation, the following is true about the class: At the time of creation, the following is true about the class:
=============================================================== ===============================================================
The current snapshot points to: The current snapshot points to:
------------------------------- -------------------------------
* None if the repository is empty * None if the repository is empty
@ -31,14 +39,15 @@ class Repository(Hierarchy):
---------- ----------
* path (inherited from Hierarchy) * path (inherited from Hierarchy)
* name (inherited from Hierarchy) * name (inherited from Hierarchy)
* curr_snapshot - return either the most recent snapshot * current_snapshot - either the most recent snapshot
before running create_snapshot() or the new snapshot before running create_snapshot() or the new snapshot
created after running create_snapshot() created after running create_snapshot()
* snapshots - return a list of snapshots stored in this repository * snapshots - a list of snapshots stored in this repository
* snapshot_dir - the snapshot storage location of this repository
Methods Methods
------- -------
* create_snapshot() - create a new snapshot, then update curr_snapshot * create_snapshot() - create a new snapshot, then update current_snapshot
Directory Structure Directory Structure
------------------- -------------------
@ -60,15 +69,17 @@ class Repository(Hierarchy):
super().__init__(dest) super().__init__(dest)
self._snapshot_index = 0 self._snapshot_index = 0
self._snapshots = [
Snapshot(s) for s in self.snapshot_dir.glob("*") if s.is_dir() if self._metadata_path.exists():
] with self._metadata_path.open(mode="rb") as metadata_file:
self._snapshots.sort() self._snapshots = pickle.load(metadata_file)
else:
self._snapshots = []
try: try:
self._curr_snapshot = self._snapshots[-1] self._current_snapshot = self.snapshots[-1]
except IndexError: except IndexError:
self._curr_snapshot = None self._current_snapshot = None
def __len__(self): def __len__(self):
"""Return the number of snapshots in this Repository.""" """Return the number of snapshots in this Repository."""
@ -78,9 +89,17 @@ class Repository(Hierarchy):
"""Retrieve a Snapshot at a certain index.""" """Retrieve a Snapshot at a certain index."""
return self.snapshots[position] return self.snapshots[position]
def __delitem__(self, s):
"""Delete a Snapshot in this Repository."""
raise NotImplementedError
def __iter__(self): def __iter__(self):
return self return self
def __contains__(self, snapshot):
"""Check whether a Snapshot is in this Repository."""
raise NotImplementedError
def __next__(self): def __next__(self):
"""Return the next Snapshot in this Repository.""" """Return the next Snapshot in this Repository."""
try: try:
@ -114,7 +133,8 @@ class Repository(Hierarchy):
>>> repo = Repository('/tmp') >>> repo = Repository('/tmp')
>>> repo.snapshots >>> repo.snapshots
[] []
>>> repo.create_snapshot() >>> repo.create_snapshot() # doctest: +ELLIPSIS
<...Snapshot ... at 0x...>
>>> repo.snapshots # doctest: +ELLIPSIS >>> repo.snapshots # doctest: +ELLIPSIS
[<...Snapshot ... at 0x...>] [<...Snapshot ... at 0x...>]
@ -133,7 +153,8 @@ class Repository(Hierarchy):
>>> repo = Repository('/tmp') >>> repo = Repository('/tmp')
>>> repo.empty >>> repo.empty
True True
>>> repo.create_snapshot() >>> repo.create_snapshot() # doctest: +ELLIPSIS
<...Snapshot ... at 0x...>
>>> repo.empty >>> repo.empty
False False
@ -142,24 +163,12 @@ class Repository(Hierarchy):
return self.snapshots == [] return self.snapshots == []
@property @property
def curr_snapshot(self): def current_snapshot(self):
"""Return this Repository's current snapshot. """Return this Repository's current snapshot.
Example
-------
>>> repo = Repository('/tmp')
>>> repo.curr_snapshot
>>> repo.snapshots
[]
>>> repo.create_snapshot()
>>> repo.snapshots # doctest: +ELLIPSIS
[<...Snapshot ... at 0x...>]
>>> repo.curr_snapshot # doctest: +ELLIPSIS
<...Snapshot ... at 0x...>
:rtype: Snapshot object :rtype: Snapshot object
""" """
return self._curr_snapshot return self._current_snapshot
def create_snapshot( def create_snapshot(
self, name=datetime.datetime.utcnow().isoformat().replace(":", "-") self, name=datetime.datetime.utcnow().isoformat().replace(":", "-")
@ -169,26 +178,27 @@ class Repository(Hierarchy):
This method is non-intrusive in that it will not This method is non-intrusive in that it will not
make any changes in the filesystem when called. make any changes in the filesystem when called.
Example
-------
>>> repo = Repository('/tmp')
>>> repo.snapshots
[]
>>> repo.curr_snapshot
>>> repo.create_snapshot()
>>> repo.curr_snapshot # doctest: +ELLIPSIS
<...Snapshot ... at 0x...>
:return: a new Snapshot object :return: a new Snapshot object
""" """
syslog.debug("Creating snapshot") syslog.debug("Creating snapshot")
path = self.snapshot_dir / f"snapshot-{name}"
self._curr_snapshot = Snapshot(path) if not isinstance(name, str):
self.snapshots.append(self._curr_snapshot) raise ValueError(f"{name} is not a valid type for a snapshot name")
snapshot_name = (
name
if name is not None
else datetime.datetime.utcnow().isoformat().replace(":", "-")
)
path = self.snapshot_dir / f"snapshot-{snapshot_name}"
self._current_snapshot = Snapshot(path)
self.snapshots.append(self._current_snapshot)
syslog.debug("Snapshot created") syslog.debug("Snapshot created")
syslog.debug(f"Snapshot name: {self.curr_snapshot.name}") syslog.debug(f"Snapshot name: {self.current_snapshot.name}")
return self._current_snapshot
# ========== Functions ========== # ========== Functions ==========