* * 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
"""
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
.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():
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]
'\s+(?P<freq>\d+)' \
'\s+(?P<passno>\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()
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