Source code for insights.parsers.df

"""
Disk free space commands
========================

Module for the processing of output from the ``df`` command.  The base class
``DiskFree`` provides all of the functionality for all classes.
Data is avaliable as rows of the output contained
in one ``Record`` object for each line of output.

Sample input data for the ``df -li`` command looks like::

    Filesystem        Inodes  IUsed     IFree IUse% Mounted on
    /dev/mapper/vg_lxcrhel6sat56-lv_root
                     6275072 124955   6150117    2% /
    devtmpfs         1497120    532   1496588    1% /dev
    tmpfs            1499684    331   1499353    1% /dev/shm
    tmpfs            1499684    728   1498956    1% /run
    tmpfs            1499684     16   1499668    1% /sys/fs/cgroup
    tmpfs            1499684     54   1499630    1% /tmp
    /dev/sda2      106954752 298662 106656090    1% /home
    /dev/sda1         128016    429    127587    1% /boot
    tmpfs            1499684      6   1499678    1% /V M T o o l s
    tmpfs            1499684     15   1499669    1% /VM Tools

This module provides two parsers:

DiskFree_LI - command ``df -li``
--------------------------------

DiskFree_ALP - command ``df -alP``
----------------------------------

DiskFree_AL - command ``df -al``
--------------------------------

This example demonstrates the ``DiskFree_LI`` class but all classes will provide
the same functionality.

Examples:
    >>> df_info = shared[DiskFree_LI]
    >>> df_info.filesystem_names
    ['tmpfs', '/dev/mapper/vg_lxcrhel6sat56-lv_root', 'devtmpfs', '/dev/sda2', '/dev/sda1']
    >>> df_info.get_filesystem('/dev/sda2')
    [Record(filesystem='/dev/sda2', total='106954752', used='298662', available='106656090', capacity='1%', mounted_on='/home')]
    >>> df_info.mount_names
    ['/tmp', '/home', '/dev', '/boot', '/VM Tools', '/sys/fs/cgroup', '/', '/run', '/V M T o o l s', '/dev/shm']
    >>> df_info.get_mount('/boot')
    Record(filesystem='/dev/sda1', total='128016', used='429', available='127587', capacity='1%', mounted_on='/boot')
    >>> len(df_info)
    10
    >>> [d.mounted_on for d in df_info if 'sda' in d.filesystem]
    ['/home', '/boot']
    >>> df_info.data[0].filesystem
    '/dev/mapper/vg_lxcrhel6sat56-lv_root'
    >>> df_info.data[0]
    Record(filesystem='/dev/mapper/vg_lxcrhel6sat56-lv_root', total='6275072', used='124955', available='6150117', capacity='2%', mounted_on='/')
"""
from .. import parser, CommandParser
from collections import namedtuple, defaultdict

from insights.parsers import ParseException
from insights.specs import Specs

Record = namedtuple("Record", ['filesystem', 'total', 'used', 'available', 'capacity', 'mounted_on'])
"""namedtuple: Represents the information parsed from ``df`` command output."""


[docs]def parse_df_lines(df_content): """Parse contents of each line in ``df`` output. Parse each line of ``df`` output ensuring that wrapped lines are reassembled prior to parsing, and that mount names containing spaces are maintained. Parameters: df_content (list): Lines of df output to be parsed. Returns: list: A list of ``Record`` ``namedtuple``'s. One for each line of the ``df`` output with columns as the key values. The fields of ``Record`` provide information about the file system attributes as determined by the arguments to the ``df`` command. So, for example, if ``df`` is given the ``-alP``, the values are in terms of 1024 blocks. If ``-li`` is given, then the values are in terms of inodes:: - filesystem: Name of the filesystem - total: total number of resources on the filesystem - used: number of the resources used on the filesystem - available: number of the resource available on the filesystem - capacity: percentage of the resource used on the filesystem - mounted_on: mount point of the filesystem """ df_ls = {} df_out = [] is_sep = False columns = Record._fields for line in df_content[1:]: # [1:] -> Skip the header # Stop at 5 splits to avoid splitting spaces in path line_splits = line.rstrip().split(None, 5) if len(line_splits) >= 6: for i, name in enumerate(columns): df_ls[name] = line_splits[i] is_sep = False elif len(line_splits) == 1: # First line of the separated line df_ls[columns[0]] = line_splits[0] is_sep = True elif is_sep and len(line_splits) >= 5: # Re-split to avoid this kind of "Mounted on": "VMware Tools" line_splits = line.split(None, 4) # Last line of the separated line for i, name in enumerate(columns[1:]): df_ls[name] = line_splits[i] is_sep = False elif not line_splits: # Skip empty lines (might in sosreport) continue else: raise ParseException("Could not parse line '{l}'".format(l=line)) # Only add this record if we've got a line and it's not separated if df_ls and not is_sep: rec = Record(**df_ls) df_out.append(rec) df_ls = {} return df_out
[docs]class DiskFree(CommandParser): """Class to provide methods used by all ``df`` command classes. Attributes: data (list of Record): List of ``Record`` objects for each line of command output. filesystems (dict of list): Dictionary with each entry being a list of ``Record`` objects, for all lines in the command output. The dictionary is keyed by the ``filesystem`` value of the Record. mounts (dict): Dictionary with each entry being a ``Record`` object corresponding to the ``mounted_on`` key. """ def __init__(self, context): super(DiskFree, self).__init__(context) filesystems = defaultdict(list) self.mounts = {} for datum in self.data: filesystems[datum.filesystem].append(datum) self.mounts[datum.mounted_on] = datum self.filesystems = dict(filesystems) def __len__(self): return len(self.data) def __iter__(self): for row in self.data: yield row
[docs] def parse_content(self, content): bad_lines = ["no such file or directory"] content = [l for l in content if bad_lines[0] not in l.lower()] self.data = parse_df_lines(content)
@property def filesystem_names(self): """list: Returns list of unique filesystem names.""" return self.filesystems.keys()
[docs] def get_filesystem(self, name): """str: Returns list of Record objects for filesystem ``name``.""" return self.filesystems.get(name, [])
@property def mount_names(self): """list: Returns list of unique mount point names.""" return self.mounts.keys()
[docs] def get_mount(self, name): """Record: Returns Record obj for mount point ``name``.""" return self.mounts.get(name)
[docs] def get_dir(self, path): """ Record: returns the Record object that contains the given path. This finds the most specific mount path that contains the given path. """ try: longest = max(m for m in self.mounts if path.startswith(m)) return self.mounts[longest] except ValueError: return None
[docs]@parser(Specs.df__li) class DiskFree_LI(DiskFree): """Parse lines from the output of the ``df -li`` command. Typical content of the ``df -li`` command output looks like:: Filesystem Inodes IUsed IFree IUse% Mounted on /dev/mapper/vg_lxcrhel6sat56-lv_root 6275072 124955 6150117 2% / devtmpfs 1497120 532 1496588 1% /dev tmpfs 1499684 331 1499353 1% /dev/shm tmpfs 1499684 728 1498956 1% /tmp /dev/sda2 106954752 298662 106656090 1% /home /dev/sda1 128016 429 127587 1% /boot tmpfs 1499684 6 1499678 1% /run/user/988 tmpfs 1499684 15 1499669 1% /run/user/100 Attributes: data (list): A list of the ``df`` information with one ``Record`` object for each line of command output. Mapping of input columns to output fields is:: Input column Output Field ------------ ------------ Filesystem filesystem Inodes total IUsed used IFree available IUse% capacity Mounted on mounted_on """ pass
[docs]@parser(Specs.df__alP) class DiskFree_ALP(DiskFree): """Parse lines from the output of the ``df -alP`` command. Typical content of the ``df -alP`` command looks like:: Filesystem 1024-blocks Used Available Capacity Mounted on /dev/mapper/vg_lxcrhel6sat56-lv_root 98571884 4244032 89313940 5% / sysfs 0 0 0 - /sys proc 0 0 0 - /proc devtmpfs 5988480 0 5988480 0% /dev securityfs 0 0 0 - /sys/kernel/security tmpfs 5998736 491660 5507076 9% /dev/shm devpts 0 0 0 - /dev/pts tmpfs 5998736 1380 5997356 1% /run tmpfs 5998736 0 5998736 0% /sys/fs/cgroup cgroup 0 0 0 - /sys/fs/cgroup/systemd Attributes: data (list): A list of the ``df`` information with one ``Record`` object for each line of command output. Mapping of input columns to output fields is:: Input column Output Field ------------ ------------ Filesystem filesystem 1024-blocks total Used used Available available Capacity capacity Mounted on mounted_on """ pass
[docs]@parser(Specs.df__al) class DiskFree_AL(DiskFree): """Parse lines from the output of the ``df -al`` command. Typical content of the ``df -al`` command looks like:: Filesystem 1K-blocks Used Available Use% Mounted on /dev/mapper/vg_lxcrhel6sat56-lv_root 98571884 4244032 89313940 5% / sysfs 0 0 0 - /sys proc 0 0 0 - /proc devtmpfs 5988480 0 5988480 0% /dev securityfs 0 0 0 - /sys/kernel/security tmpfs 5998736 491660 5507076 9% /dev/shm devpts 0 0 0 - /dev/pts tmpfs 5998736 1380 5997356 1% /run tmpfs 5998736 0 5998736 0% /sys/fs/cgroup cgroup 0 0 0 - /sys/fs/cgroup/systemd Attributes: data (list): A list of the ``df`` information with one ``Record`` object for each line of command output. Mapping of input columns to output fields is:: Input column Output Field ------------ ------------ Filesystem filesystem 1K-blocks total Used used Available available Use% capacity Mounted on mounted_on """ pass