diff --git a/Snapshot.py b/Snapshot.py index c2dd35b..418d1a8 100644 --- a/Snapshot.py +++ b/Snapshot.py @@ -4,13 +4,10 @@ import datetime class Snapshot(object): def __init__(self, snapshot_string): self.snapshot_string = snapshot_string + self.re_split_tabs = re.compile(r'\t+') + self.re_name = re.compile(r"[^/]+$") + self.re_zvol = re.compile(r"^.*[\\/]") self.snapshot_tuple = self.parse_string_to_table() - ## TODO Dunno if it's useful yet. Probably not - #self.snapshot_name = self.get_snapshot_name() - #self.snapshot_zvol = self.get_snapshot_zvol() - #self.snapshot_creation_time = self.get_snapshot_creation_time() - #self.snaphot_used_size = self.get_snapshot_used_size() - #self.snapshot_referenced_size = self.get_snapshot_referenced_size() def __repr__(self): return self.get_snapshot_name() @@ -19,19 +16,18 @@ class Snapshot(object): return self.snapshot_string def parse_string_to_table(self): - array = re.split(r'\t+', self.snapshot_string) + array = re.split(self.re_split_tabs, self.snapshot_string) return tuple(array) - ## FIXME Have to precompile regexes for speed. Or maybe use another re method; findall might be slow def get_snapshot_name(self): name_str = self.snapshot_tuple[0] - name = re.findall(r"[^/]+$", name_str)[0] - return name + name = next(self.re_name.finditer(name_str)) + return name.group() def get_snapshot_zvol(self): name_str = self.snapshot_tuple[0] - zvol = re.findall(r"^.*[\\/]", name_str)[0] - return zvol + zvol = next(self.re_zvol.finditer(name_str)) + return zvol.group() def get_snapshot_creation_time(self): time_epoch = int(self.snapshot_tuple[1]) diff --git a/zfssm_client.py b/zfssm_client.py index 812a740..45b99d6 100644 --- a/zfssm_client.py +++ b/zfssm_client.py @@ -2,6 +2,8 @@ import Pyro4 # from argparse import ArgumentParser ## TODO Arguments from Snapshot import Snapshot +import time +from zfssmd_worker import DEFAULT_REFRESH_INTERVAL PYRO_URI = "PYRO:058b7dde9ec53de9235cfc57a07ce17a9eabfce3@./u:/run/zfssmd.sock" @@ -11,9 +13,18 @@ class ZFSSMDClient(object): self.pyro_conn = Pyro4.Proxy(self.uri) ## FIXME Check for old timestamp + def check_old_timestamp(self, timestamp): + target_interval = DEFAULT_REFRESH_INTERVAL * 2 + if time.time() - timestamp <= target_interval: + return False + else: + return True def get_snapshot_list(self): response = self.pyro_conn.get_current_list() + timestamp = next(iter(response)) + if (self.check_old_timestamp(timestamp)): + print("WARNING! Data is older than supposed to be. Check daemon worker") slist = next(iter(response.values())) return slist @@ -26,4 +37,4 @@ class ZFSSMDClient(object): objects = (ZFSSMDClient().make_snapshot_objects()) for o in objects: - print(o.get_snapshot_zvol()+o.get_snapshot_name()+" "+o.get_snapshot_creation_time() + " " + o.get_snapshot_used_size() + " " + o.get_snapshot_referenced_size()) \ No newline at end of file + print = (o.get_snapshot_zvol()+o.get_snapshot_name()+" "+o.get_snapshot_creation_time() )#+ " " + o.get_snapshot_used_size() + " " + o.get_snapshot_referenced_size()) \ No newline at end of file diff --git a/zfssmd.py b/zfssmd.py index 49ad846..82de665 100644 --- a/zfssmd.py +++ b/zfssmd.py @@ -5,6 +5,7 @@ from pathlib import Path import logging as log import signal import time +from os import getpid from zfssmd_worker import ZFSSMDaemonRefresh ENCODING = 'utf-8' @@ -22,12 +23,11 @@ class ZfsSnapshotManagerDaemon(object): @Pyro4.expose def call_zfs_list_snapshots(self): command = 'zfs list -Hp -t snapshot -o name,creation,used,referenced -s name' - ## TODO try except this shit - ## TODO log exec time of this command - p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + try: + p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except Exception as e: + log.error("Couldn't run ZFS list command. " + e) snapshot_list = [snapshot.decode(ENCODING) for snapshot in p.stdout] - # noinspection PyUnusedLocal - retval = p.wait() ## TODO Implement retval actions last_refreshed = time.time() self.snapshots = snapshot_list self.last_refreshed = last_refreshed @@ -37,12 +37,25 @@ class ZfsSnapshotManagerDaemon(object): def get_current_list(self): return {self.last_refreshed:self.snapshots} -# TODO socket start and delete handling + SIGINT handling def check_start_conditions(): - ## TODO test if already running PID - ## FIXME For now we're just forcing start with deleting sock file first + if UNIX_PID.is_file(): + log.warning("Daemon already running or didn't exit gracefully. Trying to clean up.") + try: + UNIX_PID.unlink() + except Exception as e: + log.error("Couldn't delete PID file" + str(UNIX_PID)) + log.error(e) + raise SystemExit(1) + try: + UNIX_PID.touch() + except Exception as e: + log.error("Couldn't create PID file. " + UNIX_PID + " Check Permissions") + log.error(e) + raise SystemExit(1) + if UNIX_SOCK.is_socket() or UNIX_SOCK.is_file(): + log.warning("Daemon already running or didn't exit gracefully. Trying to clean up.") try: UNIX_SOCK.unlink() except Exception as e: @@ -55,6 +68,7 @@ def check_start_conditions(): def sigterm_handler(_signo, _stack_frame): log.warning("Received Termination signal. Cleaning up.") UNIX_SOCK.unlink() + UNIX_PID.unlink() raise SystemExit(0) def start_daemon(): diff --git a/zfssmd_worker.py b/zfssmd_worker.py index a14c490..5e6bb96 100644 --- a/zfssmd_worker.py +++ b/zfssmd_worker.py @@ -13,14 +13,12 @@ class ZFSSMDaemonRefresh(object): thread = threading.Thread(target=self.run, args=()) thread.daemon = True thread.start() - ## TODO make it exit gracefully with daemon def run(self): while True: - ## FIXME Rework it as root process cursor if it's even possible to avoid RPC + ## FIXME Rework it as root process cursor if it's even possible to avoid RPC. For now it's not bad. uri = "PYRO:058b7dde9ec53de9235cfc57a07ce17a9eabfce3@./u:/run/zfssmd.sock" zfssmd_connection = Pyro4.Proxy(uri) last_ref = zfssmd_connection.call_zfs_list_snapshots() log.debug("Refreshed list " + str(last_ref)) - ## TODO Check if this doesn't cause CPU locks time.sleep(self.interval) \ No newline at end of file