]> git.proxmox.com Git - mirror_frr.git/blame - tests/topotests/lib/topojson.py
tests: cleanup - remove unused imports
[mirror_frr.git] / tests / topotests / lib / topojson.py
CommitLineData
2ad3d000
AP
1#
2# Modified work Copyright (c) 2019 by VMware, Inc. ("VMware")
3# Original work Copyright (c) 2018 by Network Device Education
4# Foundation, Inc. ("NetDEF")
5#
6# Permission to use, copy, modify, and/or distribute this software
7# for any purpose with or without fee is hereby granted, provided
8# that the above copyright notice and this permission notice appear
9# in all copies.
10#
11# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
12# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
14# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
15# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
16# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
17# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
18# OF THIS SOFTWARE.
19#
20
49581587 21import json
8b547a6d 22import ipaddress
49581587
CH
23import os
24from collections import OrderedDict
4256a209 25from copy import deepcopy
49581587 26from re import search as re_search
4256a209 27
49581587 28import pytest
2ad3d000 29
7fa2079a 30from lib.bgp import create_router_bgp
49581587
CH
31from lib.common_config import (create_bgp_community_lists,
32 create_interfaces_cfg, create_prefix_lists,
33 create_route_maps, create_static_routes,
34 create_vrf_cfg, load_config_to_routers,
35 start_topology,
36 topo_daemons,
37 number_to_column)
12011c40 38from lib.ospf import create_router_ospf, create_router_ospf6
49581587
CH
39from lib.pim import create_igmp_config, create_pim_config
40from lib.topolog import logger
701a0192 41
bca79837
AP
42ROUTER_LIST = []
43
44
fe50239b 45def build_topo_from_json(tgen, topo=None):
2ad3d000
AP
46 """
47 Reads configuration from JSON file. Adds routers, creates interface
48 names dynamically and link routers as defined in JSON to create
49 topology. Assigns IPs dynamically to all interfaces of each router.
2ad3d000 50 * `tgen`: Topogen object
fe50239b 51 * `topo`: json file data, or use tgen.json_topo if None
2ad3d000 52 """
fe50239b
CH
53 if topo is None:
54 topo = tgen.json_topo
2ad3d000 55
787e7624 56 ROUTER_LIST = sorted(
49581587 57 topo["routers"].keys(), key=lambda x: int(re_search(r"\d+", x).group(0))
787e7624 58 )
bca79837 59
4256a209 60 SWITCH_LIST = []
61 if "switches" in topo:
62 SWITCH_LIST = sorted(
49581587 63 topo["switches"].keys(), key=lambda x: int(re_search(r"\d+", x).group(0))
4256a209 64 )
65
e58fdb55 66 listRouters = sorted(ROUTER_LIST[:])
67 listSwitches = sorted(SWITCH_LIST[:])
4256a209 68 listAllRouters = deepcopy(listRouters)
69 dictSwitches = {}
70
bca79837 71 for routerN in ROUTER_LIST:
787e7624 72 logger.info("Topo: Add router {}".format(routerN))
bca79837 73 tgen.add_router(routerN)
2ad3d000 74
4256a209 75 for switchN in SWITCH_LIST:
76 logger.info("Topo: Add switch {}".format(switchN))
77 dictSwitches[switchN] = tgen.add_switch(switchN)
4256a209 78
787e7624 79 if "ipv4base" in topo:
8b547a6d 80 ipv4Next = ipaddress.IPv4Address(topo["link_ip_start"]["ipv4"])
787e7624 81 ipv4Step = 2 ** (32 - topo["link_ip_start"]["v4mask"])
82 if topo["link_ip_start"]["v4mask"] < 32:
2ad3d000 83 ipv4Next += 1
787e7624 84 if "ipv6base" in topo:
8b547a6d 85 ipv6Next = ipaddress.IPv6Address(topo["link_ip_start"]["ipv6"])
787e7624 86 ipv6Step = 2 ** (128 - topo["link_ip_start"]["v6mask"])
87 if topo["link_ip_start"]["v6mask"] < 127:
2ad3d000
AP
88 ipv6Next += 1
89 for router in listRouters:
787e7624 90 topo["routers"][router]["nextIfname"] = 0
2ad3d000 91
0705f312 92 router_count = 0
2ad3d000
AP
93 while listRouters != []:
94 curRouter = listRouters.pop(0)
95 # Physical Interfaces
787e7624 96 if "links" in topo["routers"][curRouter]:
787e7624 97 for destRouterLink, data in sorted(
c8e5983d 98 topo["routers"][curRouter]["links"].items()
787e7624 99 ):
100 currRouter_lo_json = topo["routers"][curRouter]["links"][destRouterLink]
2ad3d000 101 # Loopback interfaces
787e7624 102 if "type" in data and data["type"] == "loopback":
0705f312 103 router_count += 1
787e7624 104 if (
105 "ipv4" in currRouter_lo_json
106 and currRouter_lo_json["ipv4"] == "auto"
107 ):
108 currRouter_lo_json["ipv4"] = "{}{}.{}/{}".format(
109 topo["lo_prefix"]["ipv4"],
0705f312 110 router_count,
787e7624 111 number_to_column(curRouter),
112 topo["lo_prefix"]["v4mask"],
113 )
114 if (
115 "ipv6" in currRouter_lo_json
116 and currRouter_lo_json["ipv6"] == "auto"
117 ):
118 currRouter_lo_json["ipv6"] = "{}{}:{}/{}".format(
119 topo["lo_prefix"]["ipv6"],
0705f312 120 router_count,
787e7624 121 number_to_column(curRouter),
122 topo["lo_prefix"]["v6mask"],
123 )
2ad3d000
AP
124
125 if "-" in destRouterLink:
126 # Spliting and storing destRouterLink data in tempList
127 tempList = destRouterLink.split("-")
128
129 # destRouter
130 destRouter = tempList.pop(0)
131
132 # Current Router Link
133 tempList.insert(0, curRouter)
134 curRouterLink = "-".join(tempList)
135 else:
136 destRouter = destRouterLink
137 curRouterLink = curRouter
138
139 if destRouter in listRouters:
787e7624 140 currRouter_link_json = topo["routers"][curRouter]["links"][
141 destRouterLink
142 ]
143 destRouter_link_json = topo["routers"][destRouter]["links"][
144 curRouterLink
145 ]
2ad3d000
AP
146
147 # Assigning name to interfaces
787e7624 148 currRouter_link_json["interface"] = "{}-{}-eth{}".format(
149 curRouter, destRouter, topo["routers"][curRouter]["nextIfname"]
150 )
151 destRouter_link_json["interface"] = "{}-{}-eth{}".format(
152 destRouter, curRouter, topo["routers"][destRouter]["nextIfname"]
153 )
2ad3d000 154
0705f312 155 # add link interface
156 destRouter_link_json["peer-interface"] = "{}-{}-eth{}".format(
157 curRouter, destRouter, topo["routers"][curRouter]["nextIfname"]
158 )
159 currRouter_link_json["peer-interface"] = "{}-{}-eth{}".format(
160 destRouter, curRouter, topo["routers"][destRouter]["nextIfname"]
161 )
162
787e7624 163 topo["routers"][curRouter]["nextIfname"] += 1
164 topo["routers"][destRouter]["nextIfname"] += 1
2ad3d000
AP
165
166 # Linking routers to each other as defined in JSON file
787e7624 167 tgen.gears[curRouter].add_link(
168 tgen.gears[destRouter],
169 topo["routers"][curRouter]["links"][destRouterLink][
170 "interface"
171 ],
172 topo["routers"][destRouter]["links"][curRouterLink][
173 "interface"
174 ],
175 )
2ad3d000
AP
176
177 # IPv4
787e7624 178 if "ipv4" in currRouter_link_json:
179 if currRouter_link_json["ipv4"] == "auto":
180 currRouter_link_json["ipv4"] = "{}/{}".format(
181 ipv4Next, topo["link_ip_start"]["v4mask"]
182 )
183 destRouter_link_json["ipv4"] = "{}/{}".format(
184 ipv4Next + 1, topo["link_ip_start"]["v4mask"]
185 )
2ad3d000
AP
186 ipv4Next += ipv4Step
187 # IPv6
787e7624 188 if "ipv6" in currRouter_link_json:
189 if currRouter_link_json["ipv6"] == "auto":
190 currRouter_link_json["ipv6"] = "{}/{}".format(
191 ipv6Next, topo["link_ip_start"]["v6mask"]
192 )
193 destRouter_link_json["ipv6"] = "{}/{}".format(
194 ipv6Next + 1, topo["link_ip_start"]["v6mask"]
195 )
8b547a6d 196 ipv6Next = ipaddress.IPv6Address(int(ipv6Next) + ipv6Step)
2ad3d000 197
787e7624 198 logger.debug(
199 "Generated link data for router: %s\n%s",
200 curRouter,
49581587 201 json.dumps(
787e7624 202 topo["routers"][curRouter]["links"], indent=4, sort_keys=True
203 ),
204 )
7fa2079a 205
4256a209 206 switch_count = 0
207 add_switch_to_topo = []
208 while listSwitches != []:
209 curSwitch = listSwitches.pop(0)
210 # Physical Interfaces
701a0192 211 if "links" in topo["switches"][curSwitch]:
4256a209 212 for destRouterLink, data in sorted(
701a0192 213 topo["switches"][curSwitch]["links"].items()
214 ):
4256a209 215
216 # Loopback interfaces
217 if "dst_node" in data:
701a0192 218 destRouter = data["dst_node"]
4256a209 219
220 elif "-" in destRouterLink:
221 # Spliting and storing destRouterLink data in tempList
222 tempList = destRouterLink.split("-")
223 # destRouter
224 destRouter = tempList.pop(0)
225 else:
226 destRouter = destRouterLink
227
228 if destRouter in listAllRouters:
229
701a0192 230 topo["routers"][destRouter]["links"][curSwitch] = deepcopy(
231 topo["switches"][curSwitch]["links"][destRouterLink]
232 )
4256a209 233
234 # Assigning name to interfaces
701a0192 235 topo["routers"][destRouter]["links"][curSwitch][
236 "interface"
237 ] = "{}-{}-eth{}".format(
238 destRouter, curSwitch, topo["routers"][destRouter]["nextIfname"]
239 )
4256a209 240
701a0192 241 topo["switches"][curSwitch]["links"][destRouter][
242 "interface"
243 ] = "{}-{}-eth{}".format(
244 curSwitch, destRouter, topo["routers"][destRouter]["nextIfname"]
245 )
4256a209 246
701a0192 247 topo["routers"][destRouter]["nextIfname"] += 1
4256a209 248
249 # Add links
701a0192 250 dictSwitches[curSwitch].add_link(
251 tgen.gears[destRouter],
252 topo["switches"][curSwitch]["links"][destRouter]["interface"],
253 topo["routers"][destRouter]["links"][curSwitch]["interface"],
254 )
4256a209 255
256 # IPv4
701a0192 257 if "ipv4" in topo["routers"][destRouter]["links"][curSwitch]:
258 if (
259 topo["routers"][destRouter]["links"][curSwitch]["ipv4"]
260 == "auto"
261 ):
262 topo["routers"][destRouter]["links"][curSwitch][
263 "ipv4"
264 ] = "{}/{}".format(
265 ipv4Next, topo["link_ip_start"]["v4mask"]
266 )
4256a209 267 ipv4Next += 1
268 # IPv6
701a0192 269 if "ipv6" in topo["routers"][destRouter]["links"][curSwitch]:
270 if (
271 topo["routers"][destRouter]["links"][curSwitch]["ipv6"]
272 == "auto"
273 ):
274 topo["routers"][destRouter]["links"][curSwitch][
275 "ipv6"
276 ] = "{}/{}".format(
277 ipv6Next, topo["link_ip_start"]["v6mask"]
278 )
c8e5983d 279 ipv6Next = ipaddress.IPv6Address(int(ipv6Next) + ipv6Step)
4256a209 280
281 logger.debug(
282 "Generated link data for router: %s\n%s",
283 curRouter,
49581587 284 json.dumps(
4256a209 285 topo["routers"][curRouter]["links"], indent=4, sort_keys=True
286 ),
287 )
288
7659b8d4 289
fe50239b 290def linux_intf_config_from_json(tgen, topo=None):
845b234a 291 """Configure interfaces from linux based on topo."""
fe50239b
CH
292 if topo is None:
293 topo = tgen.json_topo
294
845b234a
CH
295 routers = topo["routers"]
296 for rname in routers:
49581587 297 router = tgen.net[rname]
845b234a
CH
298 links = routers[rname]["links"]
299 for rrname in links:
300 link = links[rrname]
301 if rrname == "lo":
302 lname = "lo"
303 else:
304 lname = link["interface"]
305 if "ipv4" in link:
49581587 306 router.cmd_raises("ip addr add {} dev {}".format(link["ipv4"], lname))
845b234a 307 if "ipv6" in link:
49581587 308 router.cmd_raises("ip -6 addr add {} dev {}".format(link["ipv6"], lname))
845b234a
CH
309
310
fe50239b 311def build_config_from_json(tgen, topo=None, save_bkup=True):
7659b8d4
AP
312 """
313 Reads initial configuraiton from JSON for each router, builds
314 configuration and loads its to router.
315
316 * `tgen`: Topogen object
fe50239b 317 * `topo`: json file data, or use tgen.json_topo if None
7659b8d4
AP
318 """
319
787e7624 320 func_dict = OrderedDict(
321 [
f8d572e8 322 ("vrfs", create_vrf_cfg),
787e7624 323 ("links", create_interfaces_cfg),
324 ("static_routes", create_static_routes),
325 ("prefix_lists", create_prefix_lists),
326 ("bgp_community_list", create_bgp_community_lists),
327 ("route_maps", create_route_maps),
e58fdb55 328 ("pim", create_pim_config),
329 ("igmp", create_igmp_config),
787e7624 330 ("bgp", create_router_bgp),
701a0192 331 ("ospf", create_router_ospf),
12011c40 332 ("ospf6", create_router_ospf6),
787e7624 333 ]
334 )
7659b8d4 335
fe50239b
CH
336 if topo is None:
337 topo = tgen.json_topo
338
7659b8d4
AP
339 data = topo["routers"]
340 for func_type in func_dict.keys():
787e7624 341 logger.info("Checking for {} configuration in input data".format(func_type))
7659b8d4
AP
342
343 func_dict.get(func_type)(tgen, data, build=True)
344
4f99894d
CH
345 routers = sorted(topo["routers"].keys())
346 result = load_config_to_routers(tgen, routers, save_bkup)
347 if not result:
348 logger.info("build_config_from_json: failed to configure topology")
349 pytest.exit(1)
49581587
CH
350
351
352def create_tgen_from_json(testfile, json_file=None):
353 """Create a topogen object given a testfile.
354
355 - `testfile` : The path to the testfile.
356 - `json_file` : The path to the json config file. If None the pathname is derived
357 from the `testfile` first by trying to replace `.py` by `.json` and if that isn't
358 present then by removing `test_` prefix as well.
359 """
360 from lib.topogen import Topogen # Topogen imports this module too
361 thisdir = os.path.dirname(os.path.realpath(testfile))
362 basename = os.path.basename(testfile)
363 logger.debug("starting standard JSON based module setup for %s", basename)
364
365 assert basename.startswith("test_")
366 assert basename.endswith(".py")
367 json_file = os.path.join(thisdir, basename[:-3] + ".json")
368 if not os.path.exists(json_file):
369 json_file = os.path.join(thisdir, basename[5:-3] + ".json")
370 assert os.path.exists(json_file)
371 with open(json_file, "r") as topof:
372 topo = json.load(topof)
373
374 # Create topology
375 tgen = Topogen(lambda tgen: build_topo_from_json(tgen, topo), basename[:-3])
376 tgen.json_topo = topo
377 return tgen
378
379
380def setup_module_from_json(testfile, json_file=None):
381 """Do the standard module setup for JSON based test.
382
383 * `testfile` : The path to the testfile. The name is used to derive the json config
384 file name as well (removing `test_` prefix and replacing `.py` suffix with `.json`
385 """
386 # Create topology object
387 tgen = create_tgen_from_json(testfile, json_file)
388
389 # Start routers (and their daemons)
390 start_topology(tgen, topo_daemons(tgen))
391
392 # Configure routers
393 build_config_from_json(tgen)
394 assert not tgen.routers_have_failure()
395
396 return tgen