]>
Commit | Line | Data |
---|---|---|
370c8e07 QY |
1 | .. _topotests-snippets: |
2 | ||
3 | Snippets | |
4 | -------- | |
5 | ||
6 | This document will describe common snippets of code that are frequently needed | |
7 | to perform some test checks. | |
8 | ||
9 | Checking for router / test failures | |
10 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
11 | ||
12 | The following check uses the topogen API to check for software failure (e.g. | |
13 | zebra died) and/or for errors manually set by ``Topogen.set_error()``. | |
14 | ||
15 | .. code:: py | |
16 | ||
17 | # Get the topology reference | |
18 | tgen = get_topogen() | |
19 | ||
20 | # Check for errors in the topology | |
21 | if tgen.routers_have_failure(): | |
22 | # Skip the test with the topology errors as reason | |
23 | pytest.skip(tgen.errors) | |
24 | ||
25 | Checking FRR routers version | |
26 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
27 | ||
28 | This code snippet is usually run after the topology setup to make sure all | |
29 | routers instantiated in the topology have the correct software version. | |
30 | ||
31 | .. code:: py | |
32 | ||
33 | # Get the topology reference | |
34 | tgen = get_topogen() | |
35 | ||
36 | # Get the router list | |
37 | router_list = tgen.routers() | |
38 | ||
39 | # Run the check for all routers | |
40 | for router in router_list.values(): | |
41 | if router.has_version('<', '3'): | |
42 | # Set topology error, so the next tests are skipped | |
43 | tgen.set_error('unsupported version') | |
44 | ||
45 | A sample of this snippet in a test can be found `here | |
46 | <ldp-vpls-topo1/test_ldp_vpls_topo1.py>`__. | |
47 | ||
48 | Interacting with equipment | |
49 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
50 | ||
56f0bea7 | 51 | You might want to interact with the topology equipment during the tests and |
370c8e07 QY |
52 | there are different ways to do so. |
53 | ||
54 | Notes: | |
55 | ||
56f0bea7 | 56 | 1. When using the Topogen API, all the equipment code derives from ``Topogear`` |
370c8e07 | 57 | (`lib/topogen.py <lib/topogen.py>`__). If you feel brave you can look by |
56f0bea7 | 58 | yourself how the abstractions that will be mentioned here work. |
370c8e07 QY |
59 | |
60 | 2. When not using the ``Topogen`` API there is only one way to interact with | |
56f0bea7 | 61 | the equipment, which is by calling the ``mininet`` API functions directly |
370c8e07 QY |
62 | to spawn commands. |
63 | ||
64 | Interacting with the Linux sandbox | |
65 | """""""""""""""""""""""""""""""""" | |
66 | ||
67 | Without ``Topogen``: | |
68 | ||
69 | .. code:: py | |
70 | ||
71 | global net | |
72 | output = net['r1'].cmd('echo "foobar"') | |
73 | print 'output is: {}'.format(output) | |
74 | ||
75 | With ``Topogen``: | |
76 | ||
77 | .. code:: py | |
78 | ||
79 | tgen = get_topogen() | |
80 | output = tgen.gears['r1'].run('echo "foobar"') | |
81 | print 'output is: {}'.format(output) | |
82 | ||
83 | Interacting with VTYSH | |
84 | """""""""""""""""""""" | |
85 | ||
86 | Without ``Topogen``: | |
87 | ||
88 | .. code:: py | |
89 | ||
90 | global net | |
91 | output = net['r1'].cmd('vtysh "show ip route" 2>/dev/null') | |
92 | print 'output is: {}'.format(output) | |
93 | ||
94 | With ``Topogen``: | |
95 | ||
96 | .. code:: py | |
97 | ||
98 | tgen = get_topogen() | |
99 | output = tgen.gears['r1'].vtysh_cmd("show ip route") | |
100 | print 'output is: {}'.format(output) | |
101 | ||
102 | ``Topogen`` also supports sending multiple lines of command: | |
103 | ||
104 | .. code:: py | |
105 | ||
106 | tgen = get_topogen() | |
107 | output = tgen.gears['r1'].vtysh_cmd(""" | |
108 | configure terminal | |
109 | router bgp 10 | |
110 | bgp router-id 10.0.255.1 | |
111 | neighbor 1.2.3.4 remote-as 10 | |
112 | ! | |
113 | router bgp 11 | |
114 | bgp router-id 10.0.255.2 | |
115 | ! | |
116 | """) | |
117 | print 'output is: {}'.format(output) | |
118 | ||
119 | You might also want to run multiple commands and get only the commands that | |
120 | failed: | |
121 | ||
122 | .. code:: py | |
123 | ||
124 | tgen = get_topogen() | |
125 | output = tgen.gears['r1'].vtysh_multicmd(""" | |
126 | configure terminal | |
127 | router bgp 10 | |
128 | bgp router-id 10.0.255.1 | |
129 | neighbor 1.2.3.4 remote-as 10 | |
130 | ! | |
131 | router bgp 11 | |
132 | bgp router-id 10.0.255.2 | |
133 | ! | |
134 | """, pretty_output=false) | |
135 | print 'output is: {}'.format(output) | |
136 | ||
137 | Translating vtysh JSON output into Python structures: | |
138 | ||
139 | .. code:: py | |
140 | ||
141 | tgen = get_topogen() | |
142 | json_output = tgen.gears['r1'].vtysh_cmd("show ip route json", isjson=True) | |
143 | output = json.dumps(json_output, indent=4) | |
144 | print 'output is: {}'.format(output) | |
145 | ||
146 | # You can also access the data structure as normal. For example: | |
147 | # protocol = json_output['1.1.1.1/32']['protocol'] | |
148 | # assert protocol == "ospf", "wrong protocol" | |
149 | ||
150 | .. note:: | |
151 | ||
56f0bea7 | 152 | ``vtysh_(multi)cmd`` is only available for router types of equipment. |
370c8e07 QY |
153 | |
154 | Invoking mininet CLI | |
155 | ^^^^^^^^^^^^^^^^^^^^ | |
156 | ||
157 | Without ``Topogen``: | |
158 | ||
159 | .. code:: py | |
160 | ||
161 | CLI(net) | |
162 | ||
163 | With ``Topogen``: | |
164 | ||
165 | .. code:: py | |
166 | ||
167 | tgen = get_topogen() | |
168 | tgen.mininet_cli() | |
169 | ||
170 | Reading files | |
171 | ^^^^^^^^^^^^^ | |
172 | ||
173 | Loading a normal text file content in the current directory: | |
174 | ||
175 | .. code:: py | |
176 | ||
177 | # If you are using Topogen | |
178 | # CURDIR = CWD | |
179 | # | |
180 | # Otherwise find the directory manually: | |
181 | CURDIR = os.path.dirname(os.path.realpath(__file__)) | |
182 | ||
183 | file_name = '{}/r1/show_ip_route.txt'.format(CURDIR) | |
184 | file_content = open(file_name).read() | |
185 | ||
186 | Loading JSON from a file: | |
187 | ||
188 | .. code:: py | |
189 | ||
190 | import json | |
191 | ||
192 | file_name = '{}/r1/show_ip_route.json'.format(CURDIR) | |
193 | file_content = json.loads(open(file_name).read()) | |
194 | ||
195 | Comparing JSON output | |
196 | ^^^^^^^^^^^^^^^^^^^^^ | |
197 | ||
56f0bea7 | 198 | After obtaining JSON output formatted with Python data structures, you may use |
370c8e07 QY |
199 | it to assert a minimalist schema: |
200 | ||
201 | .. code:: py | |
202 | ||
203 | tgen = get_topogen() | |
204 | json_output = tgen.gears['r1'].vtysh_cmd("show ip route json", isjson=True) | |
205 | ||
206 | expect = { | |
207 | '1.1.1.1/32': { | |
208 | 'protocol': 'ospf' | |
209 | } | |
210 | } | |
211 | ||
212 | assertmsg = "route 1.1.1.1/32 was not learned through OSPF" | |
213 | assert json_cmp(json_output, expect) is None, assertmsg | |
214 | ||
215 | ``json_cmp`` function description (it might be outdated, you can find the | |
216 | latest description in the source code at | |
217 | :file:`tests/topotests/lib/topotest.py` | |
218 | ||
219 | .. code:: text | |
220 | ||
221 | JSON compare function. Receives two parameters: | |
222 | * `d1`: json value | |
223 | * `d2`: json subset which we expect | |
224 | ||
225 | Returns `None` when all keys that `d1` has matches `d2`, | |
226 | otherwise a string containing what failed. | |
227 | ||
228 | Note: key absence can be tested by adding a key with value `None`. | |
229 | ||
230 | Pausing execution | |
231 | ^^^^^^^^^^^^^^^^^ | |
232 | ||
233 | Preferably, choose the ``sleep`` function that ``topotest`` provides, as it | |
234 | prints a notice during the test execution to help debug topology test execution | |
235 | time. | |
236 | ||
237 | .. code:: py | |
238 | ||
239 | # Using the topotest sleep | |
240 | from lib import topotest | |
241 | ||
242 | topotest.sleep(10, 'waiting 10 seconds for bla') | |
243 | # or just tell it the time: | |
244 | # topotest.sleep(10) | |
245 | # It will print 'Sleeping for 10 seconds'. | |
246 | ||
247 | # Or you can also use the Python sleep, but it won't show anything | |
248 | from time import sleep | |
249 | sleep(5) | |
250 | ||
251 | iproute2 Linux commands as JSON | |
252 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
253 | ||
254 | ``topotest`` has two helpers implemented that parses the output of ``ip route`` | |
255 | commands to JSON. It might simplify your comparison needs by only needing to | |
256 | provide a Python dictionary. | |
257 | ||
258 | .. code:: py | |
259 | ||
260 | from lib import topotest | |
261 | ||
262 | tgen = get_topogen() | |
263 | routes = topotest.ip4_route(tgen.gears['r1']) | |
264 | expected = { | |
265 | '10.0.1.0/24': {}, | |
266 | '10.0.2.0/24': { | |
267 | 'dev': 'r1-eth0' | |
268 | } | |
269 | } | |
270 | ||
271 | assertmsg = "failed to find 10.0.1.0/24 and/or 10.0.2.0/24" | |
272 | assert json_cmp(routes, expected) is None, assertmsg |