From bf68db3101305bcd4fbd59d631df086a0990955c Mon Sep 17 00:00:00 2001 From: Christian Herdtweck Date: Tue, 12 Jan 2016 13:27:01 +0100 Subject: [PATCH] created a wrapper around "df" command in file_helpers --- file_helpers.py | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 107 insertions(+), 0 deletions(-) diff --git a/file_helpers.py b/file_helpers.py index 20c7f9c..06566d7 100644 --- a/file_helpers.py +++ b/file_helpers.py @@ -4,6 +4,7 @@ Featuring:: * the cd context manager pwd(); with cd(other_dir): pwd(); pwd(); will print current working dir, then other_dir, then first dir again +* a wrapper around "df" to determine size and usage of file systems .. todo:: create unittest @@ -12,6 +13,10 @@ Featuring:: import contextlib import os +from call_helpers import call_and_capture +from warnings import warn +from math import floor, ceil + @contextlib.contextmanager @@ -29,3 +34,105 @@ def cd(path): yield finally: os.chdir(prev_cwd) + + +DF_CMD = ['/usr/bin/df', '--no-sync', '--portability'] + +class FilesystemStats: + """ representation of 1 line of the 'df' command + + has fields filesystem, size, used, available, capacity, mount_point + + Note that only apprixomately capacity == used/size + and that only approximately used + available == size + """ + + def __init__(self): + name = None + size = None + used = None + available = None + capacity = None + mount_point = None + + +def get_disc_stats(): + """ get stats on all filesystems + + parses the output of cmd 'df', returns list of FilesystemStats + """ + + # call + code, out, err = call_and_capture(DF_CMD) + + # warn if unexpected outcome + if code != 0: + warn('df returned non-zero exit code {0}!'.format(code)) + if err: + for line in err: + warn('df produced output to stderr: "{0}"'.format(line)) + + # find columns in output that are just spaces + min_len = min(len(line) for line in out) + separator_cols = [idx for idx in range(min_len) if \ + all(line[idx] == ' ' for line in out)] + + # check columns and their header + if len(separator_cols) != 5: + raise ValueError('unepexpected number of separator columns: {0}' + .format(separator_cols)) # must eliminate neighbours? + + title_line = out[0] + title = title_line[ : separator_cols[0]].strip() + if title != 'Filesystem': + warn('Unexpected column title: "{0}" != "Filesystem"!' + .format(title)) + title = title_line[separator_cols[0] : separator_cols[1]].strip() + if title != '1024-blocks': + warn('Unexpected column title: "{0}" != "1024-blocks"!' + .format(title)) + title = title_line[separator_cols[1] : separator_cols[2]].strip() + if title != 'Used': + warn('Unexpected column title: "{0}" != "Used"!' + .format(title)) + title = title_line[separator_cols[2] : separator_cols[3]].strip() + if title != 'Available': + warn('Unexpected column title: "{0}" != "Available"!' + .format(title)) + title = title_line[separator_cols[3] : separator_cols[4]].strip() + if title != 'Capacity': + warn('Unexpected column title: "{0}" != "Capacity"!' + .format(title)) + title = title_line[separator_cols[4] : ].strip() + if title != 'Mounted on': + warn('Unexpected column title: "{0}" != "Mounted on"!' + .format(title)) + + # create result + result = [] + for line in out[1:]: + stats = FilesystemStats() + stats.name = line[ : separator_cols[0]].strip() + stats.size = int(line[separator_cols[0] : separator_cols[1]].strip()) + stats.used = int(line[separator_cols[1] : separator_cols[2]].strip()) + stats.available = int(line[separator_cols[2] : separator_cols[3]]\ + .strip()) + stats.capacity = int(line[separator_cols[3] : separator_cols[4]]\ + .strip()[:-1]) + stats.mount_point = line[separator_cols[4] : ].strip() + + # more checks: does match capacity + capacity = 100. * stats.used / stats.size + if abs(capacity - stats.capacity) > 5.: + warn('capacities for {0} deviate more than 5%: ' + '{1} != {2:.2f}(={3}/{4})'.format( + stats.name, stats.capacity, capacity, stats.used, stats.size)) + + size = stats.used + stats.available + if float(abs(stats.size - size)) / float(max(stats.size, size)) > 0.1: + warn('size for {0} differs by more than 10% from used+available!' + .format(stats.name)) + + result.append(stats) + + return result -- 1.7.1