contentmonster/classes/vessel.py

192 lines
6.7 KiB
Python
Raw Normal View History

2021-11-20 14:40:07 +00:00
from classes.connection import Connection
2021-11-22 10:14:38 +00:00
from classes.database import Database
from classes.file import File
2021-11-20 14:40:07 +00:00
from paramiko.ssh_exception import SSHException
2021-11-25 15:31:49 +00:00
from configparser import SectionProxy
from typing import Optional, Union
2021-11-22 10:14:38 +00:00
import pathlib
2021-11-25 15:31:49 +00:00
2021-11-20 14:40:07 +00:00
class Vessel:
2021-11-25 15:31:49 +00:00
"""Class describing a Vessel (= a replication destination)
"""
2021-11-20 14:40:07 +00:00
@classmethod
2021-11-25 15:31:49 +00:00
def fromConfig(cls, config: SectionProxy):
"""Create Vessel object from a Vessel section in the Config file
Args:
config (configparser.SectionProxy): Vessel section defining a
Vessel
Raises:
ValueError: Raised if section does not contain Address parameter
Returns:
classes.vessel.Vessel: Vessel object for the vessel specified in
the config section
"""
tempdir = None
2021-11-26 06:03:13 +00:00
username = None
password = None
passphrase = None
port = 22
timeout = None
ignoredirs = []
2021-11-25 15:31:49 +00:00
2021-11-22 10:14:38 +00:00
if "TempDir" in config.keys():
tempdir = config["TempDir"]
2021-11-25 15:31:49 +00:00
2021-11-26 06:03:13 +00:00
if "Username" in config.keys():
username = config["Username"]
if "Password" in config.keys():
password = config["Password"]
if "Passphrase" in config.keys():
passphrase = config["Passphrase"]
if "Port" in config.keys():
port = config["Port"]
if "Timeout" in config.keys():
timeout = config["Timeout"]
if "IgnoreDirs" in config.keys():
ignoredirs = [d.strip() for d in config["IgnoreDirs"].split(",")]
2021-11-20 14:40:07 +00:00
if "Address" in config.keys():
2021-11-26 06:52:16 +00:00
return cls(config.name.split()[1], config["Address"], username,
password, passphrase, port, timeout, tempdir, ignoredirs)
2021-11-20 14:40:07 +00:00
else:
2021-11-25 15:31:49 +00:00
raise ValueError("Definition for Vessel " +
config.name.split()[1] + " does not contain Address!")
2021-11-20 14:40:07 +00:00
2021-11-26 06:03:13 +00:00
def __init__(self, name: str, address: str, username: Optional[str] = None,
password: Optional[str] = None, passphrase: Optional[str] = None,
port: Optional[int] = None, timeout: Optional[int] = None,
tempdir: Optional[Union[str, pathlib.Path]] = None,
ignoredirs: list[Optional[str]] = []) -> None:
2021-11-25 15:31:49 +00:00
"""Initialize new Vessel object
Args:
name (str): Name of the Vessel
address (str): Address (IP or resolvable hostname) of the Vessel
tempdir (pathlib.Path, optional): Temporary upload location on the
Vessel, to store Chunks in
"""
2021-11-20 14:40:07 +00:00
self.name = name
self.address = address
2021-11-25 15:31:49 +00:00
self.tempdir = pathlib.Path(tempdir or "/tmp/.ContentMonster/")
2021-11-26 06:03:13 +00:00
self.username = username
self.password = password
self.passphrase = passphrase
self.port = port or 22
self.timeout = timeout or 10
2021-11-20 14:40:07 +00:00
self._connection = None
2021-11-25 18:03:58 +00:00
self._uploaded = self.getUploadedFromDB() # Files already uploaded
self._ignoredirs = ignoredirs # Directories not replicated to this vessel
2021-11-20 14:40:07 +00:00
@property
2021-11-25 15:31:49 +00:00
def connection(self) -> Connection:
"""Get a Connection to the Vessel
Returns:
classes.connection.Connection: SSH/SFTP connection to the Vessel
"""
# If a connection exists
2021-11-20 14:40:07 +00:00
if self._connection:
try:
2021-11-25 15:31:49 +00:00
# ... check if it is up
self._connection._listdir(".")
except (SSHException, OSError):
2021-11-25 15:31:49 +00:00
# ... and throw it away if it isn't
2021-11-20 14:40:07 +00:00
self._connection = None
2021-11-25 15:31:49 +00:00
# If no connection exists (anymore), set up a new one
2021-11-22 10:14:38 +00:00
self._connection = self._connection or Connection(self)
return self._connection
2021-11-25 15:31:49 +00:00
def getUploadedFromDB(self) -> list[str]:
"""Get a list of files that have previously been uploaded to the Vessel
Returns:
list: List of UUIDs of Files that have been successfully uploaded
"""
2021-11-22 10:14:38 +00:00
db = Database()
return db.getCompletionForVessel(self)
def currentUpload(self) -> Optional[tuple[str, str, str]]:
2021-11-25 15:31:49 +00:00
"""Get the File that is currently being uploaded to this Vessel
Returns:
tuple: A tuple consisting of (directory, name, checksum), where
"directory" is the name of the Directory object the File is
located in, "name" is the filename (basename) of the File and
checksum is the SHA256 hash of the file at the time of insertion
into the database. None is returned if no such record is found.
2021-11-25 15:31:49 +00:00
"""
self.assertTempDirectory() # After a reboot, the tempdir may be gone
2021-11-22 10:14:38 +00:00
db = Database()
output = db.getFileByUUID(self.connection.getCurrentUploadUUID())
del db
return output
2021-11-20 14:40:07 +00:00
2021-11-25 15:31:49 +00:00
def clearTempDir(self) -> None:
"""Clean up the temporary directory on the Vessel
"""
self.connection.clearTempDir()
2021-11-25 18:03:58 +00:00
def pushChunk(self, chunk, path: Optional[Union[str, pathlib.Path]] = None) -> None:
"""Push the content of a Chunk object to the Vessel
Args:
chunk (classes.chunk.Chunk): Chunk object containing the data to
push to the Vessel
path (str, pathlib.Path, optional): Path at which to store the
Chunk on the Vessel. If None, use default location provided by
Vessel configuration and name provided by Chunk object. Defaults
to None.
"""
self.connection.pushChunk(chunk, str(path) if path else None)
2021-11-25 18:03:58 +00:00
def compileComplete(self, remotefile) -> None:
"""Build a complete File from uploaded Chunks.
Args:
remotefile (classes.remotefile.RemoteFile): RemoteFile object
describing the uploaded File
"""
2021-11-26 06:03:13 +00:00
self.connection.compileComplete(remotefile)
def assertDirectories(self, directory) -> None:
"""Make sure that destination and temp directories exist on the Vessel
Args:
directory (classes.directory.Directory): Directory object
representing the directory to check
Raises:
ValueError: Raised if a path is already in use on the vessel but
not a directory.
IOError: Raised if a directory that does not exist cannot be
created.
"""
self.connection.assertDirectories(directory)
def assertTempDirectory(self) -> None:
"""Make sure that the temp directory exists on the Vessel
Raises:
ValueError: Raised if the path is already in use on the vessel but
is not a directory.
IOError: Raised if the directory does not exist but cannot be
created.
"""
self.connection.assertTempDirectory()