"""
multipath.conf file content
===========================
The base class is the MultipathConfParser class, which reads the multipath
daemon's ``/etc/multipath.conf`` configuration file.
This is in a pseudo-JSON format.
MultipathConf - file ``/etc/multipath.conf``
--------------------------------------------
MultipathConfInitramfs - command ``lsinitrd -f /etc/multipath.conf``
--------------------------------------------------------------------
"""
import string
from insights.contrib import pyparsing as p
from insights import parser, Parser, LegacyItemAccess
from insights.core import ConfigParser
from insights.parsers import SkipException
from insights.parsr import (EOF, Forward, LeftCurly, Lift, Literal, LineEnd,
RightCurly, Many, Number, OneLineComment, PosMarker, skip_none, String,
QuotedString, WS, WSChar)
from insights.parsr.query import Entry
from insights.specs import Specs
[docs]class MultipathConfParser(Parser, LegacyItemAccess):
"""
Shared parser for the file ``/etc/multipath.conf`` and output of
``lsinitrd -f /etc/multipath.conf`` applied to
/boot/initramfs-<kernel-version>.img.
Return a dict where the keys are the name of sections in multipath
configuraion file. If there are subsections, the value is a list of
dictionaries with parameters as key and value. Otherwise the value is
just a single dictionary.
Configuration File Example::
defaults {
path_selector "round-robin 0"
user_friendly_names yes
}
multipaths {
multipath {
alias yellow
path_grouping_policy multibus
}
multipath {
wwid 1DEC_____321816758474
alias red
}
}
devices {
device {
path_selector "round-robin 0"
no_path_retry queue
}
device {
vendor 1DEC_____321816758474
path_grouping_policy red
}
}
blacklist {
wwid 26353900f02796769
devnode "^hd[a-z]"
}
Parse Result::
data = {
"blacklist": {
"devnode": "^hd[a-z]",
"wwid": "26353900f02796769"
},
"devices": [
{
"path_selector": "round-robin 0",
"no_path_retry": "queue"
},
{
"path_grouping_policy": "red",
"vendor": "1DEC_____321816758474"
}
],
"defaults": {
"path_selector": "round-robin 0",
"user_friendly_names": "yes"
},
"multipaths": [
{
"alias": "yellow",
"path_grouping_policy": "multibus"
},
{
"alias": "red",
"wwid": "1DEC_____321816758474"
}
]
}
"""
@classmethod
def _create_parser(cls):
"""
Need to check the specific symbol "/" in attr_value part as well.
I checked some multipath configuraion files from the sosreport and got
although there are some more specific symbols like "-%", it is enclosed
in double quotes and will be accepted. Furthermore, I also checked the
source code of "device-mapper-multipath" and got if the attr_value in
"multipath.conf" include a "whitespace", it must be enclosed in double
quotation marks. So, we could just add one more specific symbol "/" to
check.
----------------------------------------------------------
udev_dir /dev
getuid_callout "/sbin/scsi_id -g -u -s /block/%n"
----------------------------------------------------------
"""
section_name = p.Word(p.alphas + "_")
attr_name = attr_value = p.Word(p.alphanums + "_/")
LBRACE, RBRACE = map(p.Suppress, "{}")
attr = p.Group(attr_name + (attr_value | p.quotedString.setParseAction(p.removeQuotes)))
attr_list = p.Dict(p.ZeroOrMore(attr))
simple_section = p.Group(section_name + LBRACE + attr_list + RBRACE)
complex_section = p.Group(section_name + LBRACE + p.OneOrMore(simple_section) + RBRACE)
simple_or_complex = p.Dict(simple_section | complex_section)
my_conf = p.Group(p.ZeroOrMore(simple_or_complex))
my_conf.ignore("#" + p.restOfLine)
return my_conf
[docs] def parse_content(self, content):
if not content:
raise SkipException("Empty content.")
self.data = MultipathConfParser._create_parser().parseString("\n".join(content))[0].asDict()
[docs]@parser(Specs.multipath_conf)
class MultipathConf(MultipathConfParser):
"""
Parser for the file ``/etc/multipath.conf``.
Examples:
>>> conf = shared[MultipathConf]
>>> conf.data['blacklist']['devnode'] # Access via data property
'^hd[a-z]'
>>> conf['defaults']['user_friendly_names'] # Pseudo-dict access
'yes'
>>> len(conf['multipaths'])
2
>>> conf['multipaths'][0]['alias']
'yellow'
"""
pass
[docs]@parser(Specs.multipath_conf_initramfs)
class MultipathConfInitramfs(MultipathConfParser):
"""
Parser for the output of ``lsinitrd -f /etc/multipath.conf`` applied to
/boot/initramfs-<kernel-version>.img.
Examples:
>>> conf = shared[MultipathConfInitramfs]
>>> conf.data['blacklist']['devnode'] # Access via data property
'^hd[a-z]'
>>> conf['defaults']['user_friendly_names'] # Pseudo-dict access
'yes'
>>> len(conf['multipaths'])
2
>>> conf['multipaths'][0]['alias']
'yellow'
"""
pass
def parse_doc(content, ctx):
def to_entry(name, rest):
if isinstance(rest, list):
return Entry(name=name.value, children=rest, lineno=name.lineno, src=ctx)
return Entry(name=name.value, attrs=[rest], lineno=name.lineno, src=ctx)
Stmt = Forward()
Num = Number & (WSChar | LineEnd)
NULL = Literal("none", value=None)
Comment = (WS >> OneLineComment("#").map(lambda x: None))
BeginBlock = (WS >> LeftCurly << WS)
EndBlock = (WS >> RightCurly << WS)
Bare = String(set(string.printable) - (set(string.whitespace) | set("#{}'\"")))
Name = WS >> PosMarker(String(string.ascii_letters + "_")) << WS
Value = WS >> (Num | NULL | QuotedString | Bare) << WS
Block = BeginBlock >> Many(Stmt).map(skip_none) << EndBlock
Stanza = (Lift(to_entry) * Name * (Block | Value)) | Comment
Stmt <= WS >> Stanza << WS
Doc = Many(Stmt).map(skip_none)
Top = Doc + EOF
return Entry(children=Top(content)[0])
[docs]@parser(Specs.multipath_conf)
class MultipathConfTree(ConfigParser):
"""
Exposes multipath configuration through the parsr query interface.
See the :py:class:`insights.core.ConfigComponent` class for example usage.
"""
def parse_doc(self, content):
return parse_doc("\n".join(content), ctx=self)
[docs]def get_tree(root=None):
"""
This is a helper function to get a multipath configuration component for
your local machine or an archive. It's for use in interactive sessions.
"""
from insights import run
return run(MultipathConfTree, root=root).get(MultipathConfTree)
[docs]@parser(Specs.multipath_conf_initramfs)
class MultipathConfTreeInitramfs(ConfigParser):
"""
Exposes the multipath configuration from initramfs image through the
parsr query interface.
See the :py:class:`insights.core.ConfigComponent` class for example usage.
"""
def parse_doc(self, content):
return parse_doc("\n".join(content), ctx=self)
[docs]def get_tree_from_initramfs(root=None):
"""
This is a helper function to get a multipath configuration(from initramfs
image) component for your local machine or an archive. It's for use in
interactive sessions.
"""
from insights import run
return run(MultipathConfTreeInitramfs, root=root).get(MultipathConfTreeInitramfs)