Skip to content
This repository was archived by the owner on May 15, 2019. It is now read-only.

Commit 56f804c

Browse files
authored
Merge pull request #82 from napalm-automation/develop
Release 0.0.7
2 parents c9530af + 29d862e commit 56f804c

File tree

181 files changed

+232156
-841
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

181 files changed

+232156
-841
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,5 @@ test/unit/test_devices.py
6565
tags
6666

6767
report.json
68+
69+
prof/

CHANGELOG.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
0.0.2
2+
+++++
3+
4+
- Translators now accept ``continue_negating`` option
5+
- YAML files can now include other files via the ``!include relative/path/to/file.yaml`` directive
6+
- ``TextParser``, ``list - block`` supports manual keys via the ``key`` argument
7+
- ``TextParser``, ``list - block`` now supports flat list of commands (i.e. BGP neighbors and static routes) via the ``flat`` argument
8+
- ``TextParser``, ``list - block`` now supports composite keys via the ``composite_key`` argument
9+
- ``TextParser``, ``list - block`` now supports creating elements manually via the ``mandatory`` argument
10+
11+
- Move mandatory elements previously on the default action to a dedicated action
12+
- from is optional, by default it will always follow the parent
13+
- from is now a pointer, no need to keep serializing/deserializing
14+
- mode is optional. All parsers have a main "default" action now.
15+
- JSONParser added
16+
17+
0.0.1
18+
+++++
19+
20+
- Initial version

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ Documentation
2929
yang/translators/TextTranslator
3030
yang/api
3131
yang/jinja_filters
32+
yang/faq

docs/yang/faq.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
FAQ
2+
===
3+
4+
Some YAML files are insanely largely. Can I break them down into multiple files?
5+
________________________________________________________________________________
6+
7+
Yes, you can with the ``!include relative/path/to/file.yaml`` directive. For example::
8+
9+
# ./main.yaml
10+
my_key:
11+
blah: asdasdasd
12+
bleh: !include includes/bleh.yaml
13+
14+
# ./includes/bleh.yaml
15+
qwe: 1
16+
asd: 2
17+
18+
Will result in the final object::
19+
20+
my_key:
21+
blah: asdasdasd
22+
bleh:
23+
qwe: 1
24+
asd: 2

docs/yang/parsers.rst

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,52 @@ Parsers
33

44
Parsers are responsible for mapping native configuration/show_commands to a YANG model.
55

6+
Special actions
7+
===============
8+
9+
Most actions depend on the parser you are using, however, some are common to all of them:
10+
11+
unnecessary
12+
-----------
13+
14+
This makes the parser skip the field and continue processing the tree.
15+
16+
not_implemented
17+
---------------
18+
19+
This makes the parser stop processing the tree underneath this value. For example::
20+
21+
field_1:
22+
process: unnecessary
23+
field_2:
24+
process: not_implemented
25+
subfield_1:
26+
process: ...
27+
subfield_2:
28+
process: ...
29+
field_3:
30+
...
31+
32+
The ``not_implemented`` action will stop the parser from processing ``subfield_1`` and ``subfield_2``
33+
and move directly onto ``field_3``.
34+
35+
gate
36+
----
37+
38+
Works like ``not_implemented`` but accepts a condition. For example::
39+
40+
protocols:
41+
protocol:
42+
bgp:
43+
_process:
44+
- mode: gate
45+
when: "{{ protocol_key != 'bgp bgp' }}"
46+
global:
47+
...
48+
49+
The snippet above will only process the ``bgp`` subtree if the condition is **not** met.
50+
51+
652
Special fields
753
==============
854

@@ -17,7 +63,7 @@ mode
1763
* **Example**: Parse the description field with a simple regular expression::
1864

1965
_process:
20-
mode: search
66+
- mode: search
2167
regexp: "description (?P<value>.*)"
2268
from: "{{ bookmarks.interface[interface_key] }}"
2369

@@ -52,7 +98,7 @@ from
5298

5399
address:
54100
_process:
55-
mode: xpath
101+
- mode: xpath
56102
xpath: "family/inet/address"
57103
key: name
58104
from: "{{ bookmarks['parent'] }}"
@@ -112,14 +158,14 @@ Some actions let's you provide additional information for later use. Those will
112158

113159
address:
114160
_process:
115-
mode: block
161+
- mode: block
116162
regexp: "(?P<block>ip address (?P<key>(?P<ip>.*))\\/(?P<prefix>\\d+))(?P<secondary> secondary)*"
117163
from: "{{ bookmarks['parent'] }}"
118164
config:
119165
_process: unnecessary
120166
ip:
121167
_process:
122-
mode: value
168+
- mode: value
123169
value: "{{ extra_vars.ip }}"
124170

125171
The first regexp captures a bunch of vars that later can be used by just reading them from
@@ -137,13 +183,15 @@ the device. For example::
137183
parser: XMLParser
138184
execute:
139185
- method: _rpc
140-
args:
186+
args: []
187+
kwargs:
141188
get: "<get-configuration/>"
142189

143190
* **execute** is a list of calls to do to from the device to extract the data.
144191

145192
* **method** is the method from the device to call.
146-
* **args** are arguments that will be passed to the method.
193+
* **args** are the numbered/ordered arguments for the method
194+
* **kwargs** are the keyword arguments for the method
147195

148196
In addition, some methods like ``parse_config`` and ``parse_state`` may have mechanisms to pass the
149197
information needed to the parser instead of relying on a live device to obtain it. For parsers, you

docs/yang/parsers/TextParser.rst

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,21 @@ Arguments:
3030
* **regexp** (mandatory) - Regular expression to apply. Note that it must capture two things at least;
3131
``block``, which will be the entire block of configuration relevant for the interface and
3232
``key``, which will be the key of the element.
33-
33+
* **mandatory** (optional) will force the creation of one or more elements by specifying them manually
34+
in a dict the ``key``, ``block`` (can be empty string) and any potential ``extra_vars`` you may want to specify.
35+
* **composite_key** (optional) is a list of attributes captured in the regexp to be used as the key for the element.
36+
* **flat** (optional) if set to ``true`` (default is ``false``) the parser will understand the configuration for the
37+
element consists of flat commands instead of nested (for example BGP neighbors or static routes)
38+
* **key** (optional) set key manually
39+
* **post_process_filter** (optional) - Modify the key with this Jinja expression. ``key`` and ``extra_vars``
40+
variables are available.
3441

3542
Example 1
3643

3744
Capture the interfaces::
3845

3946
_process:
40-
mode: block
47+
- mode: block
4148
regexp: "(?P<block>interface (?P<key>(\\w|-)*\\d+)\n(?:.|\n)*?^!$)"
4249
from: "{{ bookmarks.interfaces }}"
4350

@@ -66,22 +73,94 @@ Example 2
6673

6774
subinterface:
6875
_process:
69-
mode: block
76+
- mode: block
7077
regexp: "(?P<block>interface {{interface_key}}\\.(?P<key>\\d+)\\n(?:.|\\n)*?^!$)"
7178
from: "{{ bookmarks.interfaces }}"
7279

7380

74-
Example 3.
81+
Example 3
7582

7683
Sometimes we can get easily more information in one go than just the ``key`` and the ``block``. For
7784
those cases we can capture more groups and they will be stored in the ``extra_vars`` dictionary::
7885

7986
address:
8087
_process:
81-
mode: block
88+
- mode: block
8289
regexp: "(?P<block>ip address (?P<key>(?P<ip>.*))\\/(?P<prefix>\\d+))(?P<secondary> secondary)*"
8390
from: "{{ bookmarks['parent'] }}"
8491

92+
Example 4
93+
94+
In some cases native configuration might be "flat" but nested in a YANG model. This is the case of the `global`
95+
or `default` VRF, in those cases, it is hard you may want to ensure that `global` VRF is always created::
96+
97+
_process:
98+
- mode: block
99+
regexp: "(?P<block>vrf definition (?P<key>(.*))\n(?:.|\n)*?^!$)"
100+
from: "{{ bookmarks['network-instances'][0] }}"
101+
mandatory:
102+
- key: "global"
103+
block: ""
104+
extra_vars: {}
105+
106+
Example 5
107+
108+
Some list elements have composite keys, if that's the case, use the composite key to tell the parser how to map
109+
captured elements to the composite key::
110+
111+
protocols:
112+
_process: unnecessary
113+
protocol:
114+
_process:
115+
- mode: block
116+
regexp: "(?P<block>router (?P<protocol_name>(bgp))\\s*(?P<process_id>\\d+)*\n(?:.|\n)*?)^(!| vrf \\w+)$"
117+
from: "{{ bookmarks['network-instances'][0] }}"
118+
composite_key: [protocol_name, protocol_name]
119+
when: "{{ network_instance_key == 'global' }}"
120+
121+
Example 6
122+
123+
Some list elements (like static routes or BGP neighbors) are configured as a flat list of commands instead of
124+
nested. By default, if you would try to parse each command individually the parser would try to create
125+
a new element with each line and fail as multiple lines belong to the same element but they are treated independently.
126+
By setting ``flat: true`` this behavior is changed and subsequent commands will update an already created object::
127+
128+
bgp:
129+
neighbors:
130+
neighbor:
131+
_process:
132+
- mode: block
133+
regexp: "(?P<block>neighbor (?P<key>\\d+.\\d+.\\d+.\\d+).*)"
134+
from: "{{ bookmarks['protocol'][protocol_key] }}"
135+
flat: true
136+
137+
Example 7
138+
139+
In some rare cases you might not be able to extract the key directly from the configuration. For example,
140+
the ``static`` protocol consists of ``ip route`` commands. In that case you can set the key yourself::
141+
142+
protocols:
143+
protocol:
144+
_process:
145+
- mode: block
146+
regexp: "(?P<block>ip route .*\n(?:.|\n)*?^!$)"
147+
from: "{{ bookmarks['network-instances'][0] }}"
148+
key: "static static"
149+
150+
Example 8
151+
152+
Sometimes you need to transform the key value. For example, static routes require the prefix in CIDR format,
153+
but Cisco IOS outputs routes in ``<network> <mask>`` format. In that case you can use ``post_process_filter`` to
154+
apply additional filters::
155+
156+
static:
157+
_process:
158+
- mode: block
159+
regexp: "(?P<block>ip route (?P<key>\\d+\\S+ \\d+\\S+).*)"
160+
from: "{{ bookmarks['network-instances'][0] }}"
161+
post_process_filter: "{{ key|addrmask_to_cidr }}"
162+
163+
85164
Leaf - search
86165
-------------
87166

@@ -99,7 +178,7 @@ Example.
99178

100179
description:
101180
_process:
102-
mode: search
181+
- mode: search
103182
regexp: "description (?P<value>.*)"
104183
from: "{{ bookmarks.interface[interface_key] }}"
105184

@@ -118,7 +197,7 @@ Example.
118197

119198
secondary:
120199
_process:
121-
mode: value
200+
- mode: value
122201
value: "{{ extra_vars.secondary != None }}"
123202

124203
Leaf - is_absent
@@ -136,7 +215,7 @@ Example.
136215
_process: unnecessary
137216
enabled:
138217
_process:
139-
mode: is_absent
218+
- mode: is_absent
140219
regexp: "(?P<value>^\\W*switchport$)"
141220
from: "{{ bookmarks['parent'] }}"
142221

@@ -151,7 +230,7 @@ Example.
151230

152231
enabled:
153232
_process:
154-
mode: is_present
233+
- mode: is_present
155234
regexp: "(?P<value>no shutdown)"
156235
from: "{{ bookmarks.interface[interface_key] }}"
157236

@@ -172,7 +251,7 @@ Example.
172251
Check type of interface by extracting the name and doing a lookup::
173252

174253
_process:
175-
mode: map
254+
- mode: map
176255
regexp: "(?P<value>(\\w|-)*)\\d+"
177256
from: "{{ interface_key }}"
178257
map:
@@ -181,4 +260,3 @@ Example.
181260
Loopback: softwareLoopback
182261
Port-Channel: ieee8023adLag
183262
Vlan: l3ipvlan
184-

docs/yang/parsers/XMLParser.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Arguments:
2727

2828
* **xpath** (mandatory): elements to traverse
2929
* **key** (mandatory): which element is the key of the list
30+
* **post_process_filter** (optional): modify the key with this Jinja2 expression
3031

3132
Example:
3233

@@ -35,7 +36,7 @@ Example:
3536

3637
interface:
3738
_process:
38-
mode: xpath
39+
- mode: xpath
3940
xpath: "interfaces/interface"
4041
key: name
4142
from: "{{ bookmarks.interfaces }}"
@@ -73,7 +74,7 @@ Example:
7374

7475
description:
7576
_process:
76-
mode: xpath
77+
- mode: xpath
7778
xpath: description
7879
from: "{{ bookmarks['parent'] }}"
7980

@@ -92,7 +93,7 @@ Example:
9293

9394
name:
9495
_process:
95-
mode: value
96+
- mode: value
9697
value: "{{ interface_key }}"
9798

9899
Leaf - map
@@ -114,7 +115,7 @@ Example:
114115

115116
type:
116117
_process:
117-
mode: map
118+
- mode: map
118119
xpath: name
119120
regexp: "(?P<value>[a-z]+).*"
120121
from: "{{ bookmarks['parent'] }}"
@@ -137,7 +138,7 @@ Example:
137138

138139
enabled:
139140
_process:
140-
mode: is_absent
141+
- mode: is_absent
141142
xpath: "disable"
142143
from: "{{ bookmarks['parent'] }}"
143144

@@ -148,4 +149,3 @@ Leaf - is_present
148149
-----------------
149150

150151
Works exactly like ``xpath`` but if the evaluation is ``None``, it will return ``False``.
151-

0 commit comments

Comments
 (0)