Source code for insights.parsers.kdump

"""
Kernel dump configuration files
===============================

This module contains the following parsers:

KDumpConf - file ``/etc/kdump.conf``
------------------------------------

KexecCrashLoaded - file ``/sys/kernel/kexec_crash_loaded``
----------------------------------------------------------

KexecCrashSize - file ``/sys/kernel/kexec_crash_size``
------------------------------------------------------

"""
import re

from insights.core import LegacyItemAccess, Parser
from insights.core.exceptions import ParseException
from insights.core.plugins import parser
from insights.specs import Specs

try:
    from urlparse import urlparse
except ImportError:
    from urllib.parse import urlparse


[docs] @parser(Specs.kdump_conf) class KDumpConf(Parser, LegacyItemAccess): """ A dictionary like object for the values of the ``/etc/kdump.conf`` file. Attributes: lines (list): raw lines from the file, in order data (dict): a dictionary of options set in the data comments(list): fully commented lines inline_comments(list): lines containing inline comments target(tuple): target line parsed as a (x, y) tuple if set, else None The ``data`` property has two special behaviours: * If an option - e.g. ``blacklist`` - is repeated, its values are collected together in a list. Options that only appear once have their values stored as is. * The ``options`` option is special - it appears in the form ``option module value``. The ``options`` key in the data dictionary is therefore stored as a dictionary, keyed on the ``module`` name. The ``target`` property has following possibilities: * If target-line starts with any keyword in ['raw', 'ssh', 'net', 'nfs', 'nfs4'], return tuple (keyword, value). * If target-line is set with '<fs_type> <partation>', return tuple (<fs_type>, <partation>). * If target-line is not set, the target is default which is depending on what's mounted in the current system, return None instead of tuple here. Main helper functions: * ``options`` - the ``options`` value in the data(see above). Sample ``/etc/kdump.conf`` file:: path /var/crash core_collector makedumpfile -c --message-level 1 -d 24 default shell Examples: >>> kd.is_local_disk True >>> kd.is_ssh() False >>> 'path' in kd True """ NET_COMMANDS = set(['nfs', 'net', 'ssh']) SUPPORTED_FS_TYPES = ['ext2', 'ext3', 'ext4', 'btrfs', 'xfs']
[docs] def parse_content(self, content): lines = list(content) opt_kw = 'options' items = {opt_kw: {}} # Paul Wayper - 2017-03-27 - why do we care about comments? comments = [] inline_comments = [] for _line in content: line = _line.strip() if not line: continue # Ignore lines that are entirely comments if line.startswith('#'): comments.append(_line) continue # Remove comments if '#' in line: comment_start = line.index('#') inline_comments.append(_line) line = line[0:comment_start] # Settings of the form 'option value' where value is the rest of # the line. No equals is expected here. lineparts = [s.strip() for s in line.split(None, 1)] # All options must have a value if len(lineparts) < 2: continue opt, value = (lineparts) if opt != opt_kw: # Some items can be repeated - if they are, create a list of # their values if opt in items: # Append to the list, creating if necessary if not isinstance(items[opt], list): items[opt] = [items[opt]] items[opt].append(value) else: items[opt] = value else: # 'options' is special - it becomes a dictionary mod, rest = value.split(None, 1) items[opt_kw][mod] = rest.strip() self.lines = lines self.data = items self.comments = comments self.inline_comments = inline_comments self.target = self._parse_target()
[docs] def options(self, module): """ Returns the options for this module in the settings. Arguments: module(str): The module name Returns: (str) The module's options, or '' if either ``options`` or ``module`` is not found. """ return self.get('options', {}).get(module, '')
def _network_lines(self, net_commands=NET_COMMANDS): """ A list of all the options in the given list of commands, defaulting to the list of network destinations for kernel dumps (i.e. 'ssh', 'nfs', 'nfs4' and 'net'). """ return filter(None, [self.get(n) for n in net_commands])
[docs] def get_ip(self, net_commands=NET_COMMANDS): """ Find the first IP address in the given list of commands. Uses ``_network_lines`` above to find the list of commands. The first line that lists an IP address is returned, otherwise None is returned. """ ip_re = re.compile(r'(\d{1,3}\.){3}\d{1,3}') for l in self._network_lines(net_commands): matched_ip = ip_re.search(l) if matched_ip: return matched_ip.group()
[docs] def is_ssh(self): """ Is the destination of the kernel dump an ssh connection? """ return 'ssh' in self or ('net' in self and '@' in self['net'])
[docs] def is_nfs(self): """ Is the destination of the kernel dump a NFS or NFSv4 connection? """ return ( ('nfs' in self or 'nfs4' in self) or ('net' in self and '@' not in self['net']) )
[docs] def get_hostname(self, net_commands=NET_COMMANDS): """ Find the first host name in the given list of commands. Uses ``_network_lines`` above to find the list of commands. The first line that matches ``urlparse``'s definition of a host name is returned, or None is returned. """ for l in self._network_lines(net_commands): # required for urlparse to interpret as host instead of # relative path if '//' not in l: l = '//' + l netloc = urlparse(l).netloc # strip user:pass@ i = netloc.find('@') if i != -1: netloc = netloc[i + 1:] # strip port return netloc.rsplit(':', 1)[0]
@property def ip(self): """ Uses get_ip() above to give the first IP address found in the list of crash dump destinations. """ return self.get_ip() @property def hostname(self): """ Uses get_hostname() above to give the first host name found in the list of crash dump destinations. """ return self.get_hostname() @property def using_local_disk(self): """ Is kdump configured to only use local disk? Several target types: * If 'raw' is given, then the dump is local. * If 'ssh', 'net', 'nfs', or 'nfs4' is given, then the dump is NOT local. * If '<fs type> <partition>' is given, then the dump is local. * Otherwise, the dump is local. Since only one target could be set, the logic used here is checking if remote target is used, return True for not. """ return not ('ssh' in self.data or 'net' in self.data or 'nfs' in self.data or 'nfs4' in self.data) def _parse_target(self): """ More than one dump targets will lead to kudmp service start failure. Raise an exception here if more than one target is set here. https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/kernel_administration_guide/kernel_crash_dump_guide#sect-supported-kdump-targets """ target = None keys = ['ssh', 'net', 'nfs', 'nfs4', 'raw'] + self.SUPPORTED_FS_TYPES for k in keys: if k in self.data: v = self.data[k] if isinstance(v, list): raise ParseException("More than one %s type targets are\ configured." % k) if target: raise ParseException("More than one target is configured.") else: target = (k, v) return target
[docs] @parser(Specs.kexec_crash_loaded) class KexecCrashLoaded(Parser): """ A simple parser to determine if a crash kernel (i.e. a second kernel capable of capturing the machine state should the main kernel crash) is present. This simply returns a set of whether the ``/sys/kernel/kexec_crash_loaded`` file has the value ``1``. """
[docs] def parse_content(self, content): if len(content) == 0: self.is_loaded = False return line = list(content)[0].strip() self.is_loaded = line == '1'
[docs] @parser(Specs.kexec_crash_size) class KexecCrashSize(Parser): """ Parses the `/sys/kernel/kexec_crash_size` file which tells the reserved memory size for the crash kernel. Attributes: size (int): reserved memory size for the crash kernel, or 0 if not found. """
[docs] def parse_content(self, content): self.size = 0 if len(content) == 0: return size = list(content)[0].strip() if size.isdigit(): self.size = int(size)