"""
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.core import ConfigParser, LegacyItemAccess, Parser
from insights.core.exceptions import SkipComponent
from insights.core.plugins import parser
from insights.parsr import (EOF, Forward, LeftCurly, Lift, LineEnd, Literal,
Many, Number, OneLineComment, PosMarker, QuotedString,
RightCurly, String, WS, WSChar, skip_none)
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 SkipComponent("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)