From: Christian Herdtweck Date: Fri, 5 Feb 2016 10:14:09 +0000 (+0100) Subject: cleaner way to sort out unwanted file system info: bool flag X-Git-Tag: v1.2~41 X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=0bf8036da11d36c7d489b7d34bd5f35176bb51fe;p=pyi2ncommon cleaner way to sort out unwanted file system info: bool flag --- diff --git a/src/file_helpers.py b/src/file_helpers.py index cafeaea..79eb2b3 100644 --- a/src/file_helpers.py +++ b/src/file_helpers.py @@ -29,6 +29,18 @@ Featuring:: * * a wrapper around "df" * * a wrapper around statvfs (default since faster without forking) +What I found out on the way about filesystems: +* usually size != used + available +* there are several sources that list "all" filesystems, each omiting different + things: +* * /etc/fstab --> maintained by admin and tools +* * /etc/mtab --> usually a link to /proc/mounts +* * /proc/mounts --> usually the longest +* * df --> usually the shortest of all +* with all those virtual filesystems in memory and bind-mounts, things get + complicated! +* forking for df takes time! + .. codeauthor:: Intra2net """ @@ -71,6 +83,12 @@ FS_FILL_METHOD_STATVFS = 'statvfs' IGNORE_MOUNT_TYPES = 'cgroup', 'pstore' +#: filesystems that usually do not correspond to something on disc +#: (except maybe swap) +NOT_REAL_FILESYSTEMS = 'none', 'shmfs', 'procfs', 'tmpfs', 'ramfs', 'proc', \ + 'rootfs', 'sysfs', 'devpts', 'sunrpc', 'nfsd' + + class FilesystemFillState: """ representation of 1 line of the 'df' command @@ -94,21 +112,18 @@ class FilesystemFillState: .format(self.name, self.mount_point, int(round(self.capacity))) -def get_filesystem_fill_states(method=FS_FILL_METHOD_STATVFS, *args, **kwargs): +def get_filesystem_fill_states(method=FS_FILL_METHOD_STATVFS): """ get fill state on all filesystems :param method: FS_FILL_METHOD_DF (use df_wrapper, forks) or - FS_FILL_METHOD_STATVFS (uses get_all_statvfs) + FS_FILL_METHOD_STATVFS (uses get_all_mounts(False), default) all other args and kwargs are forwarded """ if method == FS_FILL_METHOD_DF: - return df_wrapper(*args, **kwargs) + return df_wrapper() else: - # could do this more equal to df-output by - # - removing file systems with size 0 - # - detecting multiple entries for same filesystem - return get_all_statvfs_fills(*args, **kwargs) + return get_all_statvfs_fills(include_duplicates=False) def df_wrapper(): @@ -202,12 +217,11 @@ def df_wrapper(): return result -def get_all_statvfs_fills(*mounts): +def get_all_statvfs_fills(mounts=None, include_duplicates=True): """ run get_fill_from_statvfs on given MountPoints or get_all_mounts """ - if not mounts: - mounts = get_all_mounts() - + if mounts is None: + mounts = get_all_mounts(include_duplicates) return [get_fill_from_statvfs(mount) for mount in mounts] @@ -297,14 +311,21 @@ MOUNT_REGEXP = r'^\s*(?P.+)' \ '\s+(?P\d+)' \ '\s+(?P\d+)\s*$' -def get_all_mounts(): +def get_all_mounts(include_duplicates=True): """ parse /proc/mounts does not return those with type in :py:data:IGNORE_MOUNT_TYPES + :param bool include_duplicates: if False, try to list every "real" + filesystem only once, e.g. ignore bind + mounts, return rootfs only if no other fs + is mounted in same file; default: True :results: list of :class:`MountPoint` """ result = [] + rootfs = [] + files = [] + specs = [] with open('/proc/mounts', 'rt') as file_handle: for line in file_handle: parts = line.split() @@ -317,8 +338,34 @@ def get_all_mounts(): setattr(new_mount, field_name, value) if new_mount.vfstype in IGNORE_MOUNT_TYPES: continue + + if not include_duplicates: + if new_mount.spec == 'rootfs': + rootfs.append(new_mount) + continue # deal with rootfs in the end + if new_mount.file in files: + warn('multiple non-rootfs mounts in same file {0}!' + .format(new_mount.file)) + if new_mount.spec in specs \ + and new_mount.spec not in NOT_REAL_FILESYSTEMS: + continue # e.g. bind mounts; ignore this mount + + # if we reach this, this is no duplicate; remember it + files.append(new_mount.file) + specs.append(new_mount.spec) result.append(new_mount) + # if not include_duplicates: + # add rootfs mounts only if no other mount is in same file + for root_mount in rootfs: + have_mount = False + for mount in result: + if mount.file == root_mount.file: + have_mount = True # some other mount in same place + break + if not have_mount: + result.append(root_mount) + return result diff --git a/src/test_helpers.py b/src/test_helpers.py index eedcd20..f1bbcf2 100644 --- a/src/test_helpers.py +++ b/src/test_helpers.py @@ -45,15 +45,11 @@ except ImportError: from buffers import LogarithmicBuffer from file_helpers import get_filesystem_fill_states, FilesystemFillState, \ - get_mount_info, get_fill_from_statvfs + get_mount_info, get_fill_from_statvfs, \ + NOT_REAL_FILESYSTEMS from iter_helpers import pairwise -#: filesystems shown by df that usually do not correspond to something on disc -#: (except maybe swap) -NOT_REAL_FILESYSTEMS = 'none', 'shmfs', 'procfs', 'tmpfs', 'ramfs' - - class DiscFullPreventionError(Exception): """ Exception raised when disc space gets critical