Split snapshot metadata, add is_valid_snapshot_name(), and remove current_snapshot

This commit is contained in:
Eric Torres 2019-04-10 17:53:17 -07:00
parent 4415a82da0
commit 3bfeda1400

View File

@ -45,15 +45,14 @@ class Repository(Hierarchy):
* path (inherited from Hierarchy) * path (inherited from Hierarchy)
* name (inherited from Hierarchy) * name (inherited from Hierarchy)
* metadata_path (inherited from Hierarchy) * metadata_path (inherited from Hierarchy)
* current_snapshot - either the most recent snapshot
before running create_snapshot() or the new snapshot
created after running create_snapshot()
* snapshots - 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 * snapshot_dir - the snapshot storage location of this repository
Methods Methods
------- -------
* create_snapshot - create a new snapshot, then update current_snapshot * create_snapshot - create a new snapshot
* gen_metadata (inherited from Hierarchy)
* is_valid_snapshot_name - validate a potential name for a snapshot
* read_metadata (inherited from Hierarchy) * read_metadata (inherited from Hierarchy)
* write_metadata (inherited from Hierarchy) * write_metadata (inherited from Hierarchy)
@ -72,42 +71,61 @@ class Repository(Hierarchy):
can be iterated through. can be iterated through.
""" """
"""Snapshots are serialized as their names relative to the repository
data directory, but have their full paths during runtime.
Private Attributes
------------------
* _snapshots - list of Snapshot objects created and accessed at runtime
* _snapshot_metadata - list of Snapshot names serialized and deserialized
when this Repository is first created
"""
def __init__(self, dest): def __init__(self, dest):
"""Default constructor for the Repository class.""" """Default constructor for the Repository class."""
super().__init__(dest) super().__init__(dest)
self._snapshot_index = 0
if not self.metadata_path.exists(): if not self.metadata_path.exists():
self._data = {"snapshots": [], "current_snapshot": None} self._snapshots = []
self._init_new_repository() self._snapshot_metadata = []
else:
self._data = self.read_metadata()
def _init_new_repository(self):
"""Perform the setup steps for a new repository."""
self.metadata_path.parent.mkdir(mode=DIRMODE, exist_ok=True) self.metadata_path.parent.mkdir(mode=DIRMODE, exist_ok=True)
self.metadata_path.touch(mode=FILEMODE) self.metadata_path.touch(mode=FILEMODE)
self.write_metadata(self._snapshots)
else:
self._snapshot_metadata = self.read_metadata()
self._snapshots = [
Snapshot(self.snapshot_dir / s) for s in self._snapshot_metadata
]
self.write_metadata() def __len__(self):
"""Return the number of snapshots in this Repository."""
return len(self._snapshots)
def _is_duplicate_name(self, name): def __getitem__(self, position):
"""Check whether or not a snapshot of a given name is already """Retrieve a Snapshot at a certain index."""
on this Repository. return self._snapshots[position]
If the repository is empty, then this method always returns False. def __delitem__(self, s):
"""Delete a Snapshot in this Repository."""
raise NotImplementedError
def __iter__(self):
return self
def __contains__(self, name):
"""Check whether a Snapshot is in this Repository by name.
:param name: the name to check for
:returns: True if this name is already in a snapshot.
:type name: str :type name: str
:rtype: bool :rtype: bool
""" """
for s in self._data["snapshots"]: return name in self._snapshot_metadata
if name == s.name:
return True
return False
def _is_valid_name(self, name): def __next__(self):
"""Return the next Snapshot in this Repository."""
return next(self._snapshots)
@staticmethod
def is_valid_snapshot_name(name):
"""Check if the given name is a valid name. If it is a duplicate, """Check if the given name is a valid name. If it is a duplicate,
log a warning. If it is invalid, raise a ValueError. log a warning. If it is invalid, raise a ValueError.
@ -126,34 +144,6 @@ class Repository(Hierarchy):
else: else:
return True return True
def __len__(self):
"""Return the number of snapshots in this Repository."""
return len(self._data["snapshots"])
def __getitem__(self, position):
"""Retrieve a Snapshot at a certain index."""
return self._data["snapshots"][position]
def __delitem__(self, s):
"""Delete a Snapshot in this Repository."""
raise NotImplementedError
def __iter__(self):
return self
def __contains__(self, snapshot):
"""Check whether a Snapshot is in this Repository."""
raise NotImplementedError
def __next__(self):
"""Return the next Snapshot in this Repository."""
try:
result = self._data["snapshots"][self._snapshot_index]
self._snapshot_index += 1
return result
except IndexError:
raise StopIteration
@property @property
def snapshot_dir(self): def snapshot_dir(self):
"""Return the directory in this Repository in which snapshots """Return the directory in this Repository in which snapshots
@ -171,7 +161,7 @@ class Repository(Hierarchy):
date date
:rtype: list of Snapshot objects :rtype: list of Snapshot objects
""" """
return self._data["snapshots"] return self._snapshots
@property @property
def empty(self): def empty(self):
@ -179,18 +169,8 @@ class Repository(Hierarchy):
:rtype: bool :rtype: bool
""" """
return self._data["snapshots"] == [] return not self.snapshots
@property
def current_snapshot(self):
"""Return this Repository's current snapshot.
:rtype: Snapshot object
"""
return self._data["current_snapshot"]
# TODO search for the name of snapshots
# add ability to use 'in' operator
def create_snapshot(self, name=None): def create_snapshot(self, name=None):
"""Create a new snapshot in this repository. """Create a new snapshot in this repository.
@ -202,7 +182,7 @@ class Repository(Hierarchy):
:param name: the name of the snapshot :param name: the name of the snapshot
:type name: str :type name: str
:return: a new Snapshot object :return: Snapshot object
:raises: ValueError if name is an invalid value :raises: ValueError if name is an invalid value
""" """
syslog.debug("Creating snapshot") syslog.debug("Creating snapshot")
@ -213,28 +193,20 @@ class Repository(Hierarchy):
else datetime.datetime.utcnow().isoformat().replace(":", "_") else datetime.datetime.utcnow().isoformat().replace(":", "_")
) )
if not self._is_valid_name(snapshot_name): if not self.is_valid_snapshot_name(snapshot_name):
raise ValueError(f"{name} is an invalid name") raise ValueError(f"{name} is an invalid name")
elif self._is_duplicate_name(snapshot_name): elif snapshot_name in self:
syslog.warning("Snapshot already exists, data will be overwritten.") syslog.warning("Snapshot already exists, data will be overwritten.")
else: else:
self._data["current_snapshot"] = Snapshot(self.snapshot_dir / snapshot_name) new_snapshot = Snapshot(self.snapshot_dir / snapshot_name)
self._data["snapshots"].append(self._data["current_snapshot"]) self._snapshot_metadata.append(snapshot_name)
self._snapshots.append(new_snapshot)
self.write_metadata() new_snapshot.path.mkdir(mode=DIRMODE, parents=True, exist_ok=True)
self._data["current_snapshot"].path.mkdir( self.write_metadata(self._snapshot_metadata)
mode=DIRMODE, parents=True, exist_ok=True
)
syslog.debug("Snapshot created") syslog.debug("Snapshot created")
syslog.debug(f"Snapshot name: {self.current_snapshot.name}") syslog.debug(f"Snapshot name: {new_snapshot.name}")
return self._data["current_snapshot"] return new_snapshot
# ========== Functions ==========
if __name__ == "__main__":
import doctest
doctest.testmod()