Add module for parsing BIRD's OSPF dumps
commit
9edd3dea02
@ -0,0 +1,90 @@
|
||||
"""
|
||||
A simple implementation of parsing and creating BIRD's OSPF state descriptions
|
||||
|
||||
The code-facing data structure is list of directives, which are tuples of string
|
||||
(directive) and list of sub-directives. For example, the following input:
|
||||
|
||||
```
|
||||
BIRD 2.0.9 ready.
|
||||
|
||||
area 0.0.0.0
|
||||
|
||||
router 172.23.100.1
|
||||
distance 30
|
||||
network [172.23.100.4-3] metric 10
|
||||
network [172.23.100.10-2] metric 10
|
||||
|
||||
```
|
||||
|
||||
would be parsed into:
|
||||
|
||||
```python3
|
||||
[('BIRD 2.0.9 ready.', []),
|
||||
('area 0.0.0.0', [
|
||||
('router 172.23.100.1', [
|
||||
('distance 30', []),
|
||||
('network [172.23.100.4-3] metric 10', []),
|
||||
('network [172.23.100.10-2] metric 10', []),
|
||||
]),
|
||||
]), # end of area directive
|
||||
] # end of the whole representation
|
||||
```
|
||||
|
||||
We expect that users will read the representation sequentially, so we do not
|
||||
provide any means of accessing individual sub-directives directly.
|
||||
|
||||
Here we only deal with syntax. What the directives mean and how to act on them
|
||||
is up to modules using this. Therefore, we only handle strings and do not try
|
||||
to understand them here. We only strip comments and whitespace. (We are
|
||||
guessing here that the '#' symbol will not appear in real BIRD output.)
|
||||
|
||||
We use lists instead of tuples just for convenience. When encoding, we SHOULD
|
||||
support other sequences as well. Our users likewise SHOULD not depend on this
|
||||
being a list. Supplying a string instead of a tuple with an empty sequence is
|
||||
supported and equivalent to specifying the tuple, both representing a directive
|
||||
without children. Using empty list is currently considered canonical.
|
||||
|
||||
We provide functions dump, dumps, load and loads, similar to the API of json,
|
||||
marshal, pickle and possibly others. Note however that unlike those, we cannot
|
||||
represent arbitrary data, just the tuple-list trees described above.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
def _tree_lines(tree: list, depth):
|
||||
result = []
|
||||
for child in tree:
|
||||
if isinstance(child, str):
|
||||
node = child
|
||||
subtree = []
|
||||
else:
|
||||
node, subtree = child
|
||||
result.append('\t'*depth + node)
|
||||
result.extend(_tree_lines(subtree, depth+1))
|
||||
return result
|
||||
|
||||
def dumps(tree):
|
||||
return '\n'.join(_tree_lines(tree, 0))
|
||||
|
||||
def dump(tree, fp):
|
||||
fp.write(dumps(tree))
|
||||
|
||||
def loads(s):
|
||||
line_regex = re.compile(r'(\t*)([^#]*)(#.*)?')
|
||||
result = []
|
||||
open_directives = [result]
|
||||
for line in s.splitlines():
|
||||
indent, directive, comment = line_regex.match(line).groups()
|
||||
directive = directive.strip()
|
||||
indent = len(indent)
|
||||
if directive == '': continue
|
||||
new_node = (directive, [])
|
||||
open_directives = open_directives[:indent+1]
|
||||
open_directives[indent].append(new_node)
|
||||
open_directives.append(new_node[1])
|
||||
return result
|
||||
|
||||
def load(fp):
|
||||
return loads(fp.read())
|
||||
|
||||
|
Loading…
Reference in New Issue