-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathtclparser1.py
87 lines (68 loc) · 2.92 KB
/
tclparser1.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
"""Sanitizer output parsing"""
from pyparsing import ParserElement, Suppress, Optional, ZeroOrMore, OneOrMore
from pyparsing import Literal, Word, Forward, Group, White, QuotedString
from pyparsing import StringEnd, restOfLine, alphanums
def _make_grammar():
"""Make a grammar for parsing a sanitized F5 config
The syntax is Tcl, except for a 'Sanitized out =' string at the
top. We only parse enough to find commands and their arguments.
Return a ParseResult where 'prog' is a list of commands. Each
command has a name and some arguments. These arguments can be
further nested lists in case of '{ ... }' and '[ ... ]' blocks.
"""
ParserElement.setDefaultWhitespaceChars(' ')
white = Suppress(Optional(White()))
comment = white + '#' - restOfLine
lbrace, rbrace = Suppress('{'), Suppress('}')
lbracket, rbracket = Suppress('['), Suppress(']')
cmds = Forward()
braces = Group(lbrace - white - Optional(cmds) - white - rbrace)
brackets = Group(lbracket - white - Optional(cmds) - white - rbracket)
string = QuotedString(quoteChar='"', escChar='\\', multiline=True)
word = string | braces | brackets | Word(alphanums + '-:()_./<>%*$|!=&?')
cmd = Group(word('name') + ZeroOrMore(word)('args'))
cmd_sep = OneOrMore(Literal('\n') | ';')
cmds << (cmd + ZeroOrMore(Suppress(cmd_sep) + cmd))
prog_end = Suppress(Optional(cmd_sep)) + StringEnd()
prog = cmds + prog_end
sanitized_begin = Suppress(Optional(White()))
sanitized = sanitized_begin + Optional('Sanitized out =') + prog('prog')
sanitized.ignore(comment)
return sanitized
def extract_pools(config):
"""Extract pools from a sanitized F5 config.
All pools are returned in a mapping of pool name to the list of
pool members.
"""
grammar = _make_grammar()
# Normalize line endings.
config = config.replace('\r\n', '\n')
# Work around the sanitizer removing too much from the line when
# removing an IP address
config = config.replace('servers <REMOVED>\n', 'servers {\n')
pools = {}
result = grammar.parseString(config)
for cmd in result.prog:
if cmd.name == 'pool':
pool_name = cmd.args[0]
parts = cmd.args[1]
for p in parts:
if p.name == 'members':
if len(p) == 2:
# A pool with a list of members
pools[pool_name] = [m.name for m in p.args[0]]
else:
# A single unpacked member
pools[pool_name] = [p.args[0]]
return pools
if __name__ == '__main__':
import fileinput
import pprint
from pyparsing import ParseException, ParseSyntaxException
data = ''.join(fileinput.input())
try:
pprint.pprint(extract_pools(data))
except (ParseException, ParseSyntaxException), e:
print e.line
print " "*(e.column-1) + "^"
print e