import re import datetime class Snapshot(object): """ Snapshot class we use to convert zfs list string to proper object we can use to analyze replicas. """ def __init__(self, snapshot_string): """ We compile some regexes at init so we can use them quickly later. We keep tuples of data returned by zfs list command. We do this because it returns Tab-Seperated Data :param snapshot_string: string to parse into Snapshot() class object """ 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() self.disk_name = self.get_snapshot_name().split("@")[0] def __repr__(self): return self.get_snapshot_name() def get_snapshot_string(self): return self.snapshot_string def parse_string_to_table(self): """ We split Tab-Seperaed data returned by zfs list to tuples so we can use it without hassle. :return: Tuple containing name, ctime, used_size, ref_size of snapshot """ array = re.split(self.re_split_tabs, self.snapshot_string) return tuple(array) def get_snapshot_name(self): """ We grab the name only from tuple :return: Whole name of the snapshot as str() """ name_str = self.snapshot_tuple[0] name = next(self.re_name.finditer(name_str)) return name.group() def get_snapshot_zvol(self): """ We grab the zvol only from tuple :return: Zvol where replica is being kept - as str() """ name_str = self.snapshot_tuple[0] zvol = next(self.re_zvol.finditer(name_str)) return zvol.group() def get_snapshot_creation_time(self): """ We grab the epoch time from the data in the tuple :return: UTC Human-Readable Date of creation of the snapshot """ time_epoch = int(self.snapshot_tuple[1]) return str(datetime.datetime.utcfromtimestamp(time_epoch)) def get_snapshot_creation_time_epoch(self): """ We will probably also make use of the epoch time :return: Epoch time straight from tuple """ return int(self.snapshot_tuple[1]) def get_snapshot_used_size(self, human_readable = True): """ We grab used size of the snapshot :param human_readable: For backend we want it in Bytes. But we want the user to quickly see how big snapshot is. :return: Used Size of the snapshot Human-Readable or not """ used_str = self.snapshot_tuple[2] if human_readable: return self.sizeof_fmt(int(used_str)) else: return int(used_str) def get_snapshot_referenced_size(self, human_readable = True): """ We grab referenced size of the snapshot :param human_readable: For backend we want it in Bytes. But we want the user to quickly see how big snapshot is. :return: Referenced Size of the snapshot Human-Readable or not """ referenced_str = self.snapshot_tuple[3] if human_readable: return self.sizeof_fmt(int(referenced_str)) else: return int(referenced_str) def get_disk_name(self): return self.disk_name @staticmethod def sizeof_fmt(num, suffix='B'): """ Function taken from stackexchange. Used to make size in Bytes Human-readable :param num: Bytes value we want to convert :param suffix: suffix we want to add to SI Prefix like Ki, Gi etc. E.g. B for GiB :return: Human-Readable size string """ ## FIXME WOW taken from stack but it's too fucking slow. Probably because of division. Have to profile for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']: if abs(num) < 1024.0: return "%3.1f%s%s" % (num, unit, suffix) num /= 1024.0 return "%.1f%s%s" % (num, 'Yi', suffix)