Source code for insights.combiners.nfs_exports

"""
Combined NFS exports
====================

The NFS exports files are normally available to rules from both a single
NFSExports object and zero or more NFSExportsD objects.  This combiner turns
those into one set of data.

Examples:
    >>> type(all_nfs)
    <class 'insights.combiners.nfs_exports.AllNFSExports'>
    >>> all_nfs.files  # List of files exporting NFS shares
    ['/etc/exports', '/etc/exports.d/mnt.exports']
    >>> '/home/example' in all_nfs.exports  # All exports stored by path
    True
    >>> sorted(all_nfs.exports['/home/example'].keys())  # Each path is a dictionary of host specs and flags.
    ['@group', 'ins1.example.com', 'ins2.example.com']
    >>> all_nfs.exports['/home/example']['ins2.example.com']  # Each host contains a list of flags.
    ['rw', 'sync', 'no_root_squash']
    >>> '/home/example' in all_nfs.ignored_exports  # Ignored exports are remembered within one file
    True
    >>> list(all_nfs.ignored_exports['/home/example'].keys())  # Each ignored export is then stored by source file...
    ['/etc/exports']
    >>> list(all_nfs.ignored_exports['/home/example']['/etc/exports'].keys())  # ... and then by host spec...
    ['ins2.example.com']
    >>> all_nfs.ignored_exports['/home/example']['/etc/exports']['ins2.example.com']  # ... holding the values that were duplicated
    ['rw', 'sync', 'no_root_squash']
    >>> '/home/insights/shared/rw'  in all_nfs.ignored_exports  # Ignored exports are remembered across files
    True

"""
from insights.core.plugins import combiner
from insights.parsers.nfs_exports import NFSExports, NFSExportsD

try:
    from six.moves import collections_abc
except ImportError:
    import collections as collections_abc


[docs] @combiner(NFSExports, optional=[NFSExportsD]) class AllNFSExports(object): """ Combiner for accessing all the NFS export configuration files. Exports are allowed to be listed multiple times, with all duplicate host after the first causing ``exportfs`` to emit a warning and ignore the host. So we combine the raw lines and ignored exports into structures listing the source file for each Attributes: files (list): the list of source files that contained NFS export definitions. exports (dict of dicts): the NFS exports stored by export path, with each path storing a dictionary of host flag lists. ignored_exports (dict): A dictionary of exported paths that have host definitions that conflicted with a previous definition, stored by export path and then path of the file that defined it. raw_lines (dict of dicts): A dictionary of raw lines that define each exported path, with the lines stored by defining file. """ def __init__(self, nfsexports, nfsexportsd): self.files = [] self.exports = {} self.ignored_exports = {} self.raw_lines = {} sources = [nfsexports] # Make sure exports are stored in the order they're parsed - # alphabetically by file name. Ignore it if nfsexportsd isn't valid. if isinstance(nfsexportsd, collections_abc.Iterable): sources.extend(sorted(nfsexportsd, key=lambda f: f.file_path)) def add_paths_to_dict(src_path, src_dict, dest_dict): # Because ignored_exports and raw_lines are stored by path, we # keep that and add the paths in them piecewise, stored by the # path of this source file. for path, value in src_dict.items(): if path not in dest_dict: dest_dict[path] = {src_path: value} else: dest_dict[path][src_path] = value for source in sources: self.files.append(source.file_path) # Add all raw lines from the source to raw lines. add_paths_to_dict(source.file_path, source.raw_lines, self.raw_lines) # Likewise all the ignored exports from this file. add_paths_to_dict(source.file_path, source.ignored_exports, self.ignored_exports) # For exports though we have to preserve existing host definitions # and ignore repeated host specs. for path, hosts in source.data.items(): if path in self.exports: # Check whether the host specification has been listed # more than once across different files: for host, flags in hosts.items(): if host in self.exports[path]: # Add this to the ignored_exports list. But # because we know that this path-host tuple isn't # duplicated in this file (it's already in # ignored_exports if it does), then we can assume # that the file isn't in the common ignored_exports # already. if path in self.ignored_exports: self.ignored_exports[path][host] = flags else: self.ignored_exports[path] = {host: flags} else: # A new host spec - add as is. self.exports[path][host] = flags else: # No exports for this path so far, add them all self.exports[path] = hosts super(AllNFSExports, self).__init__()