]>
Commit | Line | Data |
---|---|---|
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 | ||
7659b8d4 | 21 | from collections import OrderedDict |
2ad3d000 | 22 | from json import dumps as json_dumps |
bca79837 | 23 | from re import search as re_search |
8b547a6d | 24 | import ipaddress |
7659b8d4 | 25 | import pytest |
4256a209 | 26 | import ipaddr |
27 | from copy import deepcopy | |
28 | ||
2ad3d000 AP |
29 | |
30 | # Import topogen and topotest helpers | |
31 | from lib.topolog import logger | |
32 | ||
33 | # Required to instantiate the topology builder class. | |
7659b8d4 | 34 | from lib.common_config import ( |
787e7624 | 35 | number_to_row, |
36 | number_to_column, | |
7659b8d4 | 37 | load_config_to_router, |
e4663527 | 38 | create_interfaces_cfg, |
00db2a89 AP |
39 | create_static_routes, |
40 | create_prefix_lists, | |
e6db2bb1 | 41 | create_route_maps, |
787e7624 | 42 | create_bgp_community_lists, |
f8d572e8 | 43 | create_vrf_cfg, |
7659b8d4 | 44 | ) |
2ad3d000 | 45 | |
7fa2079a | 46 | from lib.bgp import create_router_bgp |
4256a209 | 47 | from lib.ospf import create_router_ospf |
bca79837 AP |
48 | ROUTER_LIST = [] |
49 | ||
50 | ||
2ad3d000 AP |
51 | def build_topo_from_json(tgen, topo): |
52 | """ | |
53 | Reads configuration from JSON file. Adds routers, creates interface | |
54 | names dynamically and link routers as defined in JSON to create | |
55 | topology. Assigns IPs dynamically to all interfaces of each router. | |
2ad3d000 AP |
56 | * `tgen`: Topogen object |
57 | * `topo`: json file data | |
58 | """ | |
59 | ||
787e7624 | 60 | ROUTER_LIST = sorted( |
61 | topo["routers"].keys(), key=lambda x: int(re_search("\d+", x).group(0)) | |
62 | ) | |
bca79837 | 63 | |
4256a209 | 64 | SWITCH_LIST = [] |
65 | if "switches" in topo: | |
66 | SWITCH_LIST = sorted( | |
67 | topo["switches"].keys(), key=lambda x: int(re_search("\d+", x).group(0)) | |
68 | ) | |
69 | ||
bca79837 | 70 | listRouters = ROUTER_LIST[:] |
4256a209 | 71 | listSwitches = SWITCH_LIST[:] |
72 | listAllRouters = deepcopy(listRouters) | |
73 | dictSwitches = {} | |
74 | ||
bca79837 | 75 | for routerN in ROUTER_LIST: |
787e7624 | 76 | logger.info("Topo: Add router {}".format(routerN)) |
bca79837 AP |
77 | tgen.add_router(routerN) |
78 | listRouters.append(routerN) | |
2ad3d000 | 79 | |
4256a209 | 80 | for switchN in SWITCH_LIST: |
81 | logger.info("Topo: Add switch {}".format(switchN)) | |
82 | dictSwitches[switchN] = tgen.add_switch(switchN) | |
83 | listSwitches.append(switchN) | |
84 | ||
787e7624 | 85 | if "ipv4base" in topo: |
8b547a6d | 86 | ipv4Next = ipaddress.IPv4Address(topo["link_ip_start"]["ipv4"]) |
787e7624 | 87 | ipv4Step = 2 ** (32 - topo["link_ip_start"]["v4mask"]) |
88 | if topo["link_ip_start"]["v4mask"] < 32: | |
2ad3d000 | 89 | ipv4Next += 1 |
787e7624 | 90 | if "ipv6base" in topo: |
8b547a6d | 91 | ipv6Next = ipaddress.IPv6Address(topo["link_ip_start"]["ipv6"]) |
787e7624 | 92 | ipv6Step = 2 ** (128 - topo["link_ip_start"]["v6mask"]) |
93 | if topo["link_ip_start"]["v6mask"] < 127: | |
2ad3d000 AP |
94 | ipv6Next += 1 |
95 | for router in listRouters: | |
787e7624 | 96 | topo["routers"][router]["nextIfname"] = 0 |
2ad3d000 AP |
97 | |
98 | while listRouters != []: | |
99 | curRouter = listRouters.pop(0) | |
100 | # Physical Interfaces | |
787e7624 | 101 | if "links" in topo["routers"][curRouter]: |
102 | ||
2ad3d000 | 103 | def link_sort(x): |
787e7624 | 104 | if x == "lo": |
2ad3d000 | 105 | return 0 |
787e7624 | 106 | elif "link" in x: |
107 | return int(x.split("-link")[1]) | |
2ad3d000 | 108 | else: |
787e7624 | 109 | return int(re_search("\d+", x).group(0)) |
110 | ||
111 | for destRouterLink, data in sorted( | |
e5f0ed14 | 112 | topo["routers"][curRouter]["links"].items(), |
787e7624 | 113 | key=lambda x: link_sort(x[0]), |
114 | ): | |
115 | currRouter_lo_json = topo["routers"][curRouter]["links"][destRouterLink] | |
2ad3d000 | 116 | # Loopback interfaces |
787e7624 | 117 | if "type" in data and data["type"] == "loopback": |
118 | if ( | |
119 | "ipv4" in currRouter_lo_json | |
120 | and currRouter_lo_json["ipv4"] == "auto" | |
121 | ): | |
122 | currRouter_lo_json["ipv4"] = "{}{}.{}/{}".format( | |
123 | topo["lo_prefix"]["ipv4"], | |
124 | number_to_row(curRouter), | |
125 | number_to_column(curRouter), | |
126 | topo["lo_prefix"]["v4mask"], | |
127 | ) | |
128 | if ( | |
129 | "ipv6" in currRouter_lo_json | |
130 | and currRouter_lo_json["ipv6"] == "auto" | |
131 | ): | |
132 | currRouter_lo_json["ipv6"] = "{}{}:{}/{}".format( | |
133 | topo["lo_prefix"]["ipv6"], | |
134 | number_to_row(curRouter), | |
135 | number_to_column(curRouter), | |
136 | topo["lo_prefix"]["v6mask"], | |
137 | ) | |
2ad3d000 AP |
138 | |
139 | if "-" in destRouterLink: | |
140 | # Spliting and storing destRouterLink data in tempList | |
141 | tempList = destRouterLink.split("-") | |
142 | ||
143 | # destRouter | |
144 | destRouter = tempList.pop(0) | |
145 | ||
146 | # Current Router Link | |
147 | tempList.insert(0, curRouter) | |
148 | curRouterLink = "-".join(tempList) | |
149 | else: | |
150 | destRouter = destRouterLink | |
151 | curRouterLink = curRouter | |
152 | ||
153 | if destRouter in listRouters: | |
787e7624 | 154 | currRouter_link_json = topo["routers"][curRouter]["links"][ |
155 | destRouterLink | |
156 | ] | |
157 | destRouter_link_json = topo["routers"][destRouter]["links"][ | |
158 | curRouterLink | |
159 | ] | |
2ad3d000 AP |
160 | |
161 | # Assigning name to interfaces | |
787e7624 | 162 | currRouter_link_json["interface"] = "{}-{}-eth{}".format( |
163 | curRouter, destRouter, topo["routers"][curRouter]["nextIfname"] | |
164 | ) | |
165 | destRouter_link_json["interface"] = "{}-{}-eth{}".format( | |
166 | destRouter, curRouter, topo["routers"][destRouter]["nextIfname"] | |
167 | ) | |
2ad3d000 | 168 | |
787e7624 | 169 | topo["routers"][curRouter]["nextIfname"] += 1 |
170 | topo["routers"][destRouter]["nextIfname"] += 1 | |
2ad3d000 AP |
171 | |
172 | # Linking routers to each other as defined in JSON file | |
787e7624 | 173 | tgen.gears[curRouter].add_link( |
174 | tgen.gears[destRouter], | |
175 | topo["routers"][curRouter]["links"][destRouterLink][ | |
176 | "interface" | |
177 | ], | |
178 | topo["routers"][destRouter]["links"][curRouterLink][ | |
179 | "interface" | |
180 | ], | |
181 | ) | |
2ad3d000 AP |
182 | |
183 | # IPv4 | |
787e7624 | 184 | if "ipv4" in currRouter_link_json: |
185 | if currRouter_link_json["ipv4"] == "auto": | |
186 | currRouter_link_json["ipv4"] = "{}/{}".format( | |
187 | ipv4Next, topo["link_ip_start"]["v4mask"] | |
188 | ) | |
189 | destRouter_link_json["ipv4"] = "{}/{}".format( | |
190 | ipv4Next + 1, topo["link_ip_start"]["v4mask"] | |
191 | ) | |
2ad3d000 AP |
192 | ipv4Next += ipv4Step |
193 | # IPv6 | |
787e7624 | 194 | if "ipv6" in currRouter_link_json: |
195 | if currRouter_link_json["ipv6"] == "auto": | |
196 | currRouter_link_json["ipv6"] = "{}/{}".format( | |
197 | ipv6Next, topo["link_ip_start"]["v6mask"] | |
198 | ) | |
199 | destRouter_link_json["ipv6"] = "{}/{}".format( | |
200 | ipv6Next + 1, topo["link_ip_start"]["v6mask"] | |
201 | ) | |
8b547a6d | 202 | ipv6Next = ipaddress.IPv6Address(int(ipv6Next) + ipv6Step) |
2ad3d000 | 203 | |
787e7624 | 204 | logger.debug( |
205 | "Generated link data for router: %s\n%s", | |
206 | curRouter, | |
207 | json_dumps( | |
208 | topo["routers"][curRouter]["links"], indent=4, sort_keys=True | |
209 | ), | |
210 | ) | |
7fa2079a | 211 | |
4256a209 | 212 | switch_count = 0 |
213 | add_switch_to_topo = [] | |
214 | while listSwitches != []: | |
215 | curSwitch = listSwitches.pop(0) | |
216 | # Physical Interfaces | |
217 | if "links" in topo['switches'][curSwitch]: | |
218 | for destRouterLink, data in sorted( | |
11761ab0 | 219 | topo['switches'][curSwitch]['links'].items()): |
4256a209 | 220 | |
221 | # Loopback interfaces | |
222 | if "dst_node" in data: | |
223 | destRouter = data['dst_node'] | |
224 | ||
225 | elif "-" in destRouterLink: | |
226 | # Spliting and storing destRouterLink data in tempList | |
227 | tempList = destRouterLink.split("-") | |
228 | # destRouter | |
229 | destRouter = tempList.pop(0) | |
230 | else: | |
231 | destRouter = destRouterLink | |
232 | ||
233 | if destRouter in listAllRouters: | |
234 | ||
235 | topo['routers'][destRouter]['links'][curSwitch] = \ | |
236 | deepcopy(topo['switches'][curSwitch]['links'][destRouterLink]) | |
237 | ||
238 | # Assigning name to interfaces | |
239 | topo['routers'][destRouter]['links'][curSwitch]['interface'] = \ | |
240 | '{}-{}-eth{}'.format(destRouter, curSwitch, topo['routers'] \ | |
241 | [destRouter]['nextIfname']) | |
242 | ||
243 | topo['switches'][curSwitch]['links'][destRouter]['interface'] = \ | |
244 | '{}-{}-eth{}'.format(curSwitch, destRouter, topo['routers'] \ | |
245 | [destRouter]['nextIfname']) | |
246 | ||
247 | topo['routers'][destRouter]['nextIfname'] += 1 | |
248 | ||
249 | # Add links | |
250 | dictSwitches[curSwitch].add_link(tgen.gears[destRouter], \ | |
251 | topo['switches'][curSwitch]['links'][destRouter]['interface'], | |
252 | topo['routers'][destRouter]['links'][curSwitch]['interface'], | |
253 | ) | |
254 | ||
255 | # IPv4 | |
256 | if 'ipv4' in topo['routers'][destRouter]['links'][curSwitch]: | |
257 | if topo['routers'][destRouter]['links'][curSwitch]['ipv4'] == 'auto': | |
258 | topo['routers'][destRouter]['links'][curSwitch]['ipv4'] = \ | |
259 | '{}/{}'.format(ipv4Next, topo['link_ip_start'][ \ | |
260 | 'v4mask']) | |
261 | ipv4Next += 1 | |
262 | # IPv6 | |
263 | if 'ipv6' in topo['routers'][destRouter]['links'][curSwitch]: | |
264 | if topo['routers'][destRouter]['links'][curSwitch]['ipv6'] == 'auto': | |
265 | topo['routers'][destRouter]['links'][curSwitch]['ipv6'] = \ | |
266 | '{}/{}'.format(ipv6Next, topo['link_ip_start'][ \ | |
267 | 'v6mask']) | |
268 | ipv6Next = ipaddr.IPv6Address(int(ipv6Next) + ipv6Step) | |
269 | ||
270 | logger.debug( | |
271 | "Generated link data for router: %s\n%s", | |
272 | curRouter, | |
273 | json_dumps( | |
274 | topo["routers"][curRouter]["links"], indent=4, sort_keys=True | |
275 | ), | |
276 | ) | |
277 | ||
7659b8d4 AP |
278 | |
279 | def build_config_from_json(tgen, topo, save_bkup=True): | |
280 | """ | |
281 | Reads initial configuraiton from JSON for each router, builds | |
282 | configuration and loads its to router. | |
283 | ||
284 | * `tgen`: Topogen object | |
285 | * `topo`: json file data | |
286 | """ | |
287 | ||
787e7624 | 288 | func_dict = OrderedDict( |
289 | [ | |
f8d572e8 | 290 | ("vrfs", create_vrf_cfg), |
787e7624 | 291 | ("links", create_interfaces_cfg), |
292 | ("static_routes", create_static_routes), | |
293 | ("prefix_lists", create_prefix_lists), | |
294 | ("bgp_community_list", create_bgp_community_lists), | |
295 | ("route_maps", create_route_maps), | |
296 | ("bgp", create_router_bgp), | |
4256a209 | 297 | ("ospf", create_router_ospf) |
787e7624 | 298 | ] |
299 | ) | |
7659b8d4 AP |
300 | |
301 | data = topo["routers"] | |
302 | for func_type in func_dict.keys(): | |
787e7624 | 303 | logger.info("Checking for {} configuration in input data".format(func_type)) |
7659b8d4 AP |
304 | |
305 | func_dict.get(func_type)(tgen, data, build=True) | |
306 | ||
787e7624 | 307 | for router in sorted(topo["routers"].keys()): |
308 | logger.debug("Configuring router {}...".format(router)) | |
7659b8d4 AP |
309 | |
310 | result = load_config_to_router(tgen, router, save_bkup) | |
311 | if not result: | |
312 | logger.info("Failed while configuring {}".format(router)) | |
313 | pytest.exit(1) |