1.. _topotests-snippets:
2
3Snippets
4--------
5
6This document will describe common snippets of code that are frequently needed
7to perform some test checks.
8
9Checking for router / test failures
10^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11
12The following check uses the topogen API to check for software failure (e.g.
13zebra 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
25Checking FRR routers version
26^^^^^^^^^^^^^^^^^^^^^^^^^^^^
27
28This code snippet is usually run after the topology setup to make sure all
29routers 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
45A sample of this snippet in a test can be found `here
46<ldp-vpls-topo1/test_ldp_vpls_topo1.py>`__.
47
48Interacting with equipment
49^^^^^^^^^^^^^^^^^^^^^^^^^^
50
51You might want to interact with the topology equipment during the tests and
52there are different ways to do so.
53
54Notes:
55
561. When using the Topogen API, all the equipment code derives from ``Topogear``
57   (`lib/topogen.py <lib/topogen.py>`__). If you feel brave you can look by
58   yourself how the abstractions that will be mentioned here work.
59
602. When not using the ``Topogen`` API there is only one way to interact with
61   the equipment, which is by calling the ``mininet`` API functions directly
62   to spawn commands.
63
64Interacting with the Linux sandbox
65""""""""""""""""""""""""""""""""""
66
67Without ``Topogen``:
68
69.. code:: py
70
71   global net
72   output = net['r1'].cmd('echo "foobar"')
73   print 'output is: {}'.format(output)
74
75With ``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
83Interacting with VTYSH
84""""""""""""""""""""""
85
86Without ``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
94With ``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
119You might also want to run multiple commands and get only the commands that
120failed:
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
137Translating 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
152   ``vtysh_(multi)cmd`` is only available for router types of equipment.
153
154Invoking mininet CLI
155^^^^^^^^^^^^^^^^^^^^
156
157Without ``Topogen``:
158
159.. code:: py
160
161   CLI(net)
162
163With ``Topogen``:
164
165.. code:: py
166
167   tgen = get_topogen()
168   tgen.mininet_cli()
169
170Reading files
171^^^^^^^^^^^^^
172
173Loading 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
186Loading 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
195Comparing JSON output
196^^^^^^^^^^^^^^^^^^^^^
197
198After obtaining JSON output formatted with Python data structures, you may use
199it 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
216latest 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
230Pausing execution
231^^^^^^^^^^^^^^^^^
232
233Preferably, choose the ``sleep`` function that ``topotest`` provides, as it
234prints a notice during the test execution to help debug topology test execution
235time.
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
251iproute2 Linux commands as JSON
252^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
253
254``topotest`` has two helpers implemented that parses the output of ``ip route``
255commands to JSON. It might simplify your comparison needs by only needing to
256provide 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
273