summaryrefslogtreecommitdiffstats
path: root/doc/developer/topotests-snippets.rst
blob: fb3c928a77d57dae678b952a6d210fdbfcd651e6 (plain)
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
.. _topotests-snippets:

Snippets
--------

This document will describe common snippets of code that are frequently needed
to perform some test checks.

Checking for router / test failures
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The following check uses the topogen API to check for software failure (e.g.
zebra died) and/or for errors manually set by ``Topogen.set_error()``.

.. code:: py

   # Get the topology reference
   tgen = get_topogen()

   # Check for errors in the topology
   if tgen.routers_have_failure():
       # Skip the test with the topology errors as reason
       pytest.skip(tgen.errors)

Checking FRR routers version
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This code snippet is usually run after the topology setup to make sure all
routers instantiated in the topology have the correct software version.

.. code:: py

   # Get the topology reference
   tgen = get_topogen()

   # Get the router list
   router_list = tgen.routers()

   # Run the check for all routers
   for router in router_list.values():
       if router.has_version('<', '3'):
           # Set topology error, so the next tests are skipped
           tgen.set_error('unsupported version')

A sample of this snippet in a test can be found `here
<ldp-vpls-topo1/test_ldp_vpls_topo1.py>`__.

Interacting with equipment
^^^^^^^^^^^^^^^^^^^^^^^^^^

You might want to interact with the topology equipment during the tests and
there are different ways to do so.

Notes:

1. When using the Topogen API, all the equipment code derives from ``Topogear``
   (`lib/topogen.py <lib/topogen.py>`__). If you feel brave you can look by
   yourself how the abstractions that will be mentioned here work.

2. When not using the ``Topogen`` API there is only one way to interact with
   the equipment, which is by calling the ``mininet`` API functions directly
   to spawn commands.

Interacting with the Linux sandbox
""""""""""""""""""""""""""""""""""

Without ``Topogen``:

.. code:: py

   global net
   output = net['r1'].cmd('echo "foobar"')
   print 'output is: {}'.format(output)

With ``Topogen``:

.. code:: py

   tgen = get_topogen()
   output = tgen.gears['r1'].run('echo "foobar"')
   print 'output is: {}'.format(output)

Interacting with VTYSH
""""""""""""""""""""""

Without ``Topogen``:

.. code:: py

   global net
   output = net['r1'].cmd('vtysh "show ip route" 2>/dev/null')
   print 'output is: {}'.format(output)

With ``Topogen``:

.. code:: py

   tgen = get_topogen()
   output = tgen.gears['r1'].vtysh_cmd("show ip route")
   print 'output is: {}'.format(output)

``Topogen`` also supports sending multiple lines of command:

.. code:: py

   tgen = get_topogen()
   output = tgen.gears['r1'].vtysh_cmd("""
   configure terminal
   router bgp 10
     bgp router-id 10.0.255.1
     neighbor 1.2.3.4 remote-as 10
     !
   router bgp 11
     bgp router-id 10.0.255.2
     !
   """)
   print 'output is: {}'.format(output)

You might also want to run multiple commands and get only the commands that
failed:

.. code:: py

   tgen = get_topogen()
   output = tgen.gears['r1'].vtysh_multicmd("""
   configure terminal
   router bgp 10
     bgp router-id 10.0.255.1
     neighbor 1.2.3.4 remote-as 10
     !
   router bgp 11
     bgp router-id 10.0.255.2
     !
   """, pretty_output=false)
   print 'output is: {}'.format(output)

Translating vtysh JSON output into Python structures:

.. code:: py

   tgen = get_topogen()
   json_output = tgen.gears['r1'].vtysh_cmd("show ip route json", isjson=True)
   output = json.dumps(json_output, indent=4)
   print 'output is: {}'.format(output)

   # You can also access the data structure as normal. For example:
   # protocol = json_output['1.1.1.1/32']['protocol']
   # assert protocol == "ospf", "wrong protocol"

.. note::

   ``vtysh_(multi)cmd`` is only available for router types of equipment.

Invoking mininet CLI
^^^^^^^^^^^^^^^^^^^^

Without ``Topogen``:

.. code:: py

   CLI(net)

With ``Topogen``:

.. code:: py

   tgen = get_topogen()
   tgen.mininet_cli()

Reading files
^^^^^^^^^^^^^

Loading a normal text file content in the current directory:

.. code:: py

   # If you are using Topogen
   # CURDIR = CWD
   #
   # Otherwise find the directory manually:
   CURDIR = os.path.dirname(os.path.realpath(__file__))

   file_name = '{}/r1/show_ip_route.txt'.format(CURDIR)
   file_content = open(file_name).read()

Loading JSON from a file:

.. code:: py

   import json

   file_name = '{}/r1/show_ip_route.json'.format(CURDIR)
   file_content = json.loads(open(file_name).read())

Comparing JSON output
^^^^^^^^^^^^^^^^^^^^^

After obtaining JSON output formatted with Python data structures, you may use
it to assert a minimalist schema:

.. code:: py

   tgen = get_topogen()
   json_output = tgen.gears['r1'].vtysh_cmd("show ip route json", isjson=True)

   expect = {
     '1.1.1.1/32': {
       'protocol': 'ospf'
     }
   }

   assertmsg = "route 1.1.1.1/32 was not learned through OSPF"
   assert json_cmp(json_output, expect) is None, assertmsg

``json_cmp`` function description (it might be outdated, you can find the
latest description in the source code at
:file:`tests/topotests/lib/topotest.py`

.. code:: text

   JSON compare function. Receives two parameters:
   * `d1`: json value
   * `d2`: json subset which we expect

   Returns `None` when all keys that `d1` has matches `d2`,
   otherwise a string containing what failed.

   Note: key absence can be tested by adding a key with value `None`.

Pausing execution
^^^^^^^^^^^^^^^^^

Preferably, choose the ``sleep`` function that ``topotest`` provides, as it
prints a notice during the test execution to help debug topology test execution
time.

.. code:: py

    # Using the topotest sleep
    from lib import topotest

    topotest.sleep(10, 'waiting 10 seconds for bla')
    # or just tell it the time:
    # topotest.sleep(10)
    # It will print 'Sleeping for 10 seconds'.

    # Or you can also use the Python sleep, but it won't show anything
    from time import sleep
    sleep(5)

iproute2 Linux commands as JSON
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

``topotest`` has two helpers implemented that parses the output of ``ip route``
commands to JSON. It might simplify your comparison needs by only needing to
provide a Python dictionary.

.. code:: py

   from lib import topotest

   tgen = get_topogen()
   routes = topotest.ip4_route(tgen.gears['r1'])
   expected = {
     '10.0.1.0/24': {},
     '10.0.2.0/24': {
       'dev': 'r1-eth0'
     }
   }

   assertmsg = "failed to find 10.0.1.0/24 and/or 10.0.2.0/24"
   assert json_cmp(routes, expected) is None, assertmsg