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