3 # Copyright (c) 2017 All rights reserved
4 # This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
11 # - Peering OpenDaylight with Quagga:
12 # - Set up a Quagga instance in the functest container
13 # - Start a BGP router with OpenDaylight
14 # - Add the functest Quagga as a neighbor
15 # - Verify that the OpenDaylight and gateway Quagga peer
22 from sdnvpn.lib import quagga
23 from sdnvpn.lib import openstack_utils as os_utils
24 from sdnvpn.lib import utils as test_utils
25 from sdnvpn.lib import config as sdnvpn_config
26 from sdnvpn.lib.results import Results
29 logger = logging.getLogger(__name__)
31 COMMON_CONFIG = sdnvpn_config.CommonConfig()
32 TESTCASE_CONFIG = sdnvpn_config.TestcaseConfig(
33 "sdnvpn.test.functest.testcase_3")
37 conn = os_utils.get_os_connection()
38 results = Results(COMMON_CONFIG.line_length, conn)
39 results.add_to_summary(0, "=")
40 results.add_to_summary(2, "STATUS", "SUBTEST")
41 results.add_to_summary(0, "=")
43 openstack_nodes = test_utils.get_nodes()
44 installer_type = str(os.environ['INSTALLER_TYPE'].lower())
46 # node.is_odl() doesn't work in Apex
47 # https://jira.opnfv.org/browse/RELENG-192
48 fuel_cmd = "sudo systemctl status opendaylight"
49 apex_cmd = "sudo docker exec opendaylight_api " \
50 "/opt/opendaylight/bin/status"
51 health_cmd = "sudo docker ps -f name=opendaylight_api -f " \
53 if installer_type in ["fuel"]:
54 odl_nodes = [node for node in openstack_nodes
55 if "running" in node.run_cmd(fuel_cmd)]
56 elif installer_type in ["apex"]:
57 odl_nodes = [node for node in openstack_nodes
58 if node.run_cmd(health_cmd)
59 if "Running" in node.run_cmd(apex_cmd)]
61 logger.error("Incompatible installer type")
63 computes = [node for node in openstack_nodes if node.is_compute()]
65 msg = ("Verify that OpenDaylight can start/communicate with zrpcd/Quagga")
66 results.record_action(msg)
67 results.add_to_summary(0, "-")
69 msg = ("ODL node list is empty. Skipping rest of tests.")
71 results.add_failure(msg)
72 return results.compile_summary()
74 msg = ("ODL node list is ready")
76 results.add_success(msg)
78 logger.info("Checking if zrpcd is "
79 "running on the opendaylight nodes")
81 for odl_node in odl_nodes:
82 output_zrpcd = odl_node.run_cmd("ps --no-headers -C "
84 states = output_zrpcd.split()
85 running = any([s != 'Z' for s in states])
86 msg = ("zrpcd is running in {name}".format(name=odl_node.name))
89 logger.info("zrpcd is not running on the opendaylight node {name}"
90 .format(name=odl_node.name))
91 results.add_failure(msg)
93 logger.info("zrpcd is running on the opendaylight node {name}"
94 .format(name=odl_node.name))
95 results.add_success(msg)
97 results.add_to_summary(0, "-")
99 # Find the BGP entity owner in ODL because of this bug:
100 # https://jira.opendaylight.org/browse/NETVIRT-1308
101 msg = ("Found BGP entity owner")
102 odl_node = test_utils.get_odl_bgp_entity_owner(odl_nodes)
104 logger.error("Failed to find the BGP entity owner")
105 results.add_failure(msg)
107 logger.info('BGP entity owner is {name}'
108 .format(name=odl_node.name))
109 results.add_success(msg)
110 results.add_to_summary(0, "-")
112 installer_type = str(os.environ['INSTALLER_TYPE'].lower())
113 if installer_type in ['apex']:
114 odl_interface = 'br-ex'
115 elif installer_type in ['fuel']:
116 odl_interface = 'br-ext'
118 logger.error("Incompatible installer type")
119 odl_ip, odl_netmask = test_utils.get_node_ip_and_netmask(
120 odl_node, odl_interface)
122 logger.info("Starting bgp speaker of opendaylight node at IP %s "
125 # Ensure that ZRPCD ip & port are well configured within ODL
126 add_client_conn_to_bgp = "bgp-connect -p 7644 -h 127.0.0.1 add"
127 test_utils.run_odl_cmd(odl_node, add_client_conn_to_bgp)
130 start_quagga = "odl:configure-bgp -op start-bgp-server " \
131 "--as-num 100 --router-id {0}".format(odl_ip)
132 test_utils.run_odl_cmd(odl_node, start_quagga)
134 # we need to wait a bit until the bgpd is up
137 logger.info("Checking if bgpd is running on the opendaylight node")
139 # Check if there is a non-zombie bgpd process
140 output_bgpd = odl_node.run_cmd("ps --no-headers -C "
142 states = output_bgpd.split()
143 running = any([s != 'Z' for s in states])
145 msg = ("bgpd is running")
147 logger.info("bgpd is not running on the opendaylight node")
148 results.add_failure(msg)
150 logger.info("bgpd is running on the opendaylight node")
151 results.add_success(msg)
153 results.add_to_summary(0, "-")
155 # We should be able to restart the speaker
156 # but the test is disabled because of buggy upstream
157 # https://github.com/6WIND/zrpcd/issues/15
158 # stop_quagga = 'odl:configure-bgp -op stop-bgp-server'
159 # test_utils.run_odl_cmd(odl_node, stop_quagga)
161 # logger.info("Checking if bgpd is still running"
162 # " on the opendaylight node")
164 # output_bgpd = odl_node.run_cmd("ps --no-headers -C " \
166 # states = output_bgpd.split()
167 # running = any([s != 'Z' for s in states])
169 # msg = ("bgpd is stopped")
171 # logger.info("bgpd is not running on the opendaylight node")
172 # results.add_success(msg)
174 # logger.info("bgpd is still running on the opendaylight node")
175 # results.add_failure(msg)
177 # Taken from the sfc tests
178 if not os.path.isfile(COMMON_CONFIG.ubuntu_image_path):
179 logger.info("Downloading image")
180 image_dest_path = '/'.join(
181 COMMON_CONFIG.ubuntu_image_path.split('/')[:-1])
182 os_utils.download_url(
183 "http://artifacts.opnfv.org/sdnvpn/"
184 "ubuntu-16.04-server-cloudimg-amd64-disk1.img",
187 logger.info("Using old image")
189 neutron_client = os_utils.get_neutron_client()
191 (floatingip_ids, instance_ids, router_ids, network_ids, image_ids,
192 subnet_ids, interfaces, bgpvpn_ids, flavor_ids) = ([] for i in range(9))
197 _, flavor_id = test_utils.create_custom_flavor()
198 flavor_ids.append(flavor_id)
200 sg_id = os_utils.create_security_group_full(
201 conn, TESTCASE_CONFIG.secgroup_name,
202 TESTCASE_CONFIG.secgroup_descr)
203 test_utils.open_icmp(conn, sg_id)
204 test_utils.open_http_port(conn, sg_id)
206 test_utils.open_bgp_port(conn, sg_id)
208 image_id = os_utils.create_glance_image(
209 conn, TESTCASE_CONFIG.image_name,
210 COMMON_CONFIG.image_path, disk=COMMON_CONFIG.image_format,
211 container="bare", public='public')
212 image_ids.append(image_id)
214 net_1_id, subnet_1_id, router_1_id = test_utils.create_network(
216 TESTCASE_CONFIG.net_1_name,
217 TESTCASE_CONFIG.subnet_1_name,
218 TESTCASE_CONFIG.subnet_1_cidr,
219 TESTCASE_CONFIG.router_1_name)
221 quagga_net_id, subnet_quagga_id, \
222 router_quagga_id = test_utils.create_network(
224 TESTCASE_CONFIG.quagga_net_name,
225 TESTCASE_CONFIG.quagga_subnet_name,
226 TESTCASE_CONFIG.quagga_subnet_cidr,
227 TESTCASE_CONFIG.quagga_router_name)
229 interfaces.append(tuple((router_1_id, subnet_1_id)))
230 interfaces.append(tuple((router_quagga_id, subnet_quagga_id)))
231 network_ids.extend([net_1_id, quagga_net_id])
232 router_ids.extend([router_1_id, router_quagga_id])
233 subnet_ids.extend([subnet_1_id, subnet_quagga_id])
235 installer_type = str(os.environ['INSTALLER_TYPE'].lower())
236 if installer_type == "fuel":
238 elif installer_type == "apex":
241 logger.error("Incompatible installer type")
243 ubuntu_image_id = os_utils.create_glance_image(
245 COMMON_CONFIG.ubuntu_image_name,
246 COMMON_CONFIG.ubuntu_image_path,
251 image_ids.append(ubuntu_image_id)
253 # NOTE(rski) The order of this seems a bit weird but
254 # there is a reason for this, namely
255 # https://jira.opnfv.org/projects/SDNVPN/issues/SDNVPN-99
256 # so we create the quagga instance using cloud-init
257 # and immediately give it a floating IP.
258 # The cloud-init script should contain a small sleep for
260 # We also create the FIP first because it is used in the
262 # fake_fip is needed to bypass NAT
263 # see below for the reason why.
264 fake_fip = os_utils.create_floating_ip(conn)
265 # pin quagga to some compute
266 floatingip_ids.append(fake_fip['fip_id'])
267 compute_node = conn.compute.hypervisors().next()
268 compute_node = conn.compute.get_hypervisor(compute_node)
269 quagga_compute_node = "nova:" + compute_node.name
270 # Map the hypervisor used above to a compute handle
271 # returned by releng's manager
272 for comp in computes:
273 if compute_node.host_ip in comp.run_cmd("sudo ip a"):
276 quagga_bootstrap_script = quagga.gen_quagga_setup_script(
278 fake_fip['fip_addr'],
280 TESTCASE_CONFIG.external_network_ip_prefix,
281 TESTCASE_CONFIG.route_distinguishers,
282 TESTCASE_CONFIG.import_targets,
283 TESTCASE_CONFIG.export_targets)
285 quagga_vm = test_utils.create_instance(
287 TESTCASE_CONFIG.quagga_instance_name,
291 fixed_ip=TESTCASE_CONFIG.quagga_instance_ip,
292 flavor=COMMON_CONFIG.custom_flavor_name,
293 userdata=quagga_bootstrap_script,
294 compute_node=quagga_compute_node)
296 instance_ids.append(quagga_vm.id)
298 quagga_vm_port = test_utils.get_port(conn,
300 fip_added = os_utils.attach_floating_ip(conn,
303 msg = ("Assign a Floating IP to %s " %
304 TESTCASE_CONFIG.quagga_instance_name)
306 results.add_success(msg)
307 floatingip_ids.append(fip_added.id)
309 results.add_failure(msg)
311 test_utils.attach_instance_to_ext_br(quagga_vm, compute)
313 testcase = "Bootstrap quagga inside an OpenStack instance"
314 cloud_init_success = test_utils.wait_for_cloud_init(conn, quagga_vm)
315 if cloud_init_success:
316 results.add_success(testcase)
318 results.add_failure(testcase)
319 results.add_to_summary(0, "=")
321 results.add_to_summary(0, '-')
322 results.add_to_summary(1, "Peer Quagga with OpenDaylight")
323 results.add_to_summary(0, '-')
325 neighbor = quagga.odl_add_neighbor(fake_fip['fip_addr'],
328 peer = quagga.check_for_peering(odl_node)
330 if neighbor and peer:
331 results.add_success("Peering with quagga")
333 results.add_failure("Peering with quagga")
335 test_utils.add_quagga_external_gre_end_point(odl_nodes,
336 fake_fip['fip_addr'])
337 test_utils.wait_before_subtest()
339 msg = ("Create VPN to define a VRF")
340 results.record_action(msg)
341 vpn_name = vpn_name = "sdnvpn-3"
343 "import_targets": TESTCASE_CONFIG.import_targets,
344 "export_targets": TESTCASE_CONFIG.export_targets,
345 "route_targets": TESTCASE_CONFIG.route_targets,
346 "route_distinguishers": TESTCASE_CONFIG.route_distinguishers,
349 bgpvpn = test_utils.create_bgpvpn(neutron_client, **kwargs)
350 bgpvpn_id = bgpvpn['bgpvpn']['id']
351 logger.debug("VPN1 created details: %s" % bgpvpn)
352 bgpvpn_ids.append(bgpvpn_id)
354 msg = ("Associate network '%s' to the VPN." %
355 TESTCASE_CONFIG.net_1_name)
356 results.record_action(msg)
357 results.add_to_summary(0, "-")
359 # create a vm and connect it with network1,
360 # which is going to be bgpvpn associated
361 userdata_common = test_utils.generate_ping_userdata(
362 [TESTCASE_CONFIG.external_network_ip])
364 compute_node = conn.compute.hypervisors().next()
365 av_zone_1 = "nova:" + compute_node.name
366 vm_bgpvpn = test_utils.create_instance(
368 TESTCASE_CONFIG.instance_1_name,
372 fixed_ip=TESTCASE_CONFIG.instance_1_ip,
373 secgroup_name=TESTCASE_CONFIG.secgroup_name,
374 compute_node=av_zone_1,
375 userdata=userdata_common)
376 instance_ids.append(vm_bgpvpn.id)
378 # wait for VM to get IP
379 instance_up = test_utils.wait_for_instances_get_dhcp(vm_bgpvpn)
381 logger.error("One or more instances are down")
383 test_utils.create_network_association(
384 neutron_client, bgpvpn_id, net_1_id)
386 test_utils.wait_before_subtest()
388 msg = ("External IP prefix %s is exchanged with ODL"
389 % TESTCASE_CONFIG.external_network_ip_prefix)
390 fib_added = test_utils.is_fib_entry_present_on_odl(
392 TESTCASE_CONFIG.external_network_ip_prefix,
393 TESTCASE_CONFIG.route_distinguishers)
395 results.add_success(msg)
397 results.add_failure(msg)
399 # TODO: uncomment the following once OVS is installed with > 2.8.3 and
400 # underlay connectivity is established between vxlan overlay and
402 # results.get_ping_status_target_ip(
404 # TESTCASE_CONFIG.external_network_name,
405 # TESTCASE_CONFIG.external_network_ip,
409 results.add_to_summary(0, "=")
410 logger.info("\n%s" % results.summary)
412 except Exception as e:
413 logger.error("exception occurred while executing testcase_3: %s", e)
416 if quagga_vm is not None:
417 test_utils.detach_instance_from_ext_br(quagga_vm, compute)
418 test_utils.cleanup_nova(conn, instance_ids, flavor_ids)
419 test_utils.cleanup_glance(conn, image_ids)
420 test_utils.cleanup_neutron(conn, neutron_client, floatingip_ids,
421 bgpvpn_ids, interfaces, subnet_ids,
422 router_ids, network_ids)
423 if fake_fip is not None:
424 bgp_nbr_disconnect_cmd = ("bgp-nbr -i %s -a 200 del"
425 % fake_fip['fip_addr'])
426 test_utils.run_odl_cmd(odl_node, bgp_nbr_disconnect_cmd)
427 bgp_server_stop_cmd = ("bgp-rtr -r %s -a 100 del"
429 odl_zrpc_disconnect_cmd = "bgp-connect -p 7644 -h 127.0.0.1 del"
430 test_utils.run_odl_cmd(odl_node, bgp_server_stop_cmd)
431 test_utils.run_odl_cmd(odl_node, odl_zrpc_disconnect_cmd)
433 return results.compile_summary()
436 if __name__ == '__main__':