Fix reference-before-assignment errors when getting exceptions
[sdnvpn.git] / sdnvpn / test / functest / testcase_3.py
1 #!/usr/bin/env python
2 #
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
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Tests performed:
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
16
17 import logging
18 import os
19 import sys
20 import time
21
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
27
28
29 logger = logging.getLogger(__name__)
30
31 COMMON_CONFIG = sdnvpn_config.CommonConfig()
32 TESTCASE_CONFIG = sdnvpn_config.TestcaseConfig(
33     "sdnvpn.test.functest.testcase_3")
34
35
36 def main():
37     results = Results(COMMON_CONFIG.line_length)
38     results.add_to_summary(0, "=")
39     results.add_to_summary(2, "STATUS", "SUBTEST")
40     results.add_to_summary(0, "=")
41
42     openstack_nodes = test_utils.get_nodes()
43
44     # node.is_odl() doesn't work in Apex
45     # https://jira.opnfv.org/browse/RELENG-192
46     controllers = [node for node in openstack_nodes
47                    if "running" in
48                    node.run_cmd("sudo systemctl status opendaylight")]
49     computes = [node for node in openstack_nodes if node.is_compute()]
50
51     msg = ("Verify that OpenDaylight can start/communicate with zrpcd/Quagga")
52     results.record_action(msg)
53     results.add_to_summary(0, "-")
54     if not controllers:
55         msg = ("Controller (ODL) list is empty. Skipping rest of tests.")
56         logger.info(msg)
57         results.add_failure(msg)
58         return results.compile_summary()
59     else:
60         msg = ("Controller (ODL) list is ready")
61         logger.info(msg)
62         results.add_success(msg)
63
64     logger.info("Checking if zrpcd is "
65                 "running on the controller nodes")
66
67     for controller in controllers:
68         output_zrpcd = controller.run_cmd("ps --no-headers -C "
69                                           "zrpcd -o state")
70         states = output_zrpcd.split()
71         running = any([s != 'Z' for s in states])
72         msg = ("zrpcd is running in {name}".format(name=controller.name))
73
74         if not running:
75             logger.info("zrpcd is not running on the controller node {name}"
76                         .format(name=controller.name))
77             results.add_failure(msg)
78         else:
79             logger.info("zrpcd is running on the controller node {name}"
80                         .format(name=controller.name))
81             results.add_success(msg)
82
83         results.add_to_summary(0, "-")
84
85     # Find the BGP entity owner in ODL because of this bug:
86     # https://jira.opendaylight.org/browse/NETVIRT-1308
87     msg = ("Found BGP entity owner")
88     controller = test_utils.get_odl_bgp_entity_owner(controllers)
89     if controller is None:
90         logger.error("Failed to find the BGP entity owner")
91         results.add_failure(msg)
92     else:
93         logger.info('BGP entity owner is {name}'
94                     .format(name=controller.name))
95         results.add_success(msg)
96     results.add_to_summary(0, "-")
97
98     get_ext_ip_cmd = "sudo ip a | grep br-ex | grep inet | awk '{print $2}'"
99     ext_net_cidr = controller.run_cmd(get_ext_ip_cmd).strip().split('\n')
100     ext_net_mask = ext_net_cidr[0].split('/')[1]
101     controller_ext_ip = ext_net_cidr[0].split('/')[0]
102
103     logger.info("Starting bgp speaker of controller at IP %s "
104                 % controller_ext_ip)
105
106     # Ensure that ZRPCD ip & port are well configured within ODL
107     add_client_conn_to_bgp = "bgp-connect -p 7644 -h 127.0.0.1 add"
108     test_utils.run_odl_cmd(controller, add_client_conn_to_bgp)
109
110     # Start bgp daemon
111     start_quagga = "odl:configure-bgp -op start-bgp-server " \
112                    "--as-num 100 --router-id {0}".format(controller_ext_ip)
113     test_utils.run_odl_cmd(controller, start_quagga)
114
115     # we need to wait a bit until the bgpd is up
116     time.sleep(5)
117
118     logger.info("Checking if bgpd is running"
119                 " on the controller node")
120
121     # Check if there is a non-zombie bgpd process
122     output_bgpd = controller.run_cmd("ps --no-headers -C "
123                                      "bgpd -o state")
124     states = output_bgpd.split()
125     running = any([s != 'Z' for s in states])
126
127     msg = ("bgpd is running")
128     if not running:
129         logger.info("bgpd is not running on the controller node")
130         results.add_failure(msg)
131     else:
132         logger.info("bgpd is running on the controller node")
133         results.add_success(msg)
134
135     results.add_to_summary(0, "-")
136
137     # We should be able to restart the speaker
138     # but the test is disabled because of buggy upstream
139     # https://github.com/6WIND/zrpcd/issues/15
140     # stop_quagga = 'odl:configure-bgp -op stop-bgp-server'
141     # test_utils.run_odl_cmd(controller, stop_quagga)
142
143     # logger.info("Checking if bgpd is still running"
144     #             " on the controller node")
145
146     # output_bgpd = controller.run_cmd("ps --no-headers -C " \
147     #                                  "bgpd -o state")
148     # states = output_bgpd.split()
149     # running = any([s != 'Z' for s in states])
150
151     # msg = ("bgpd is stopped")
152     # if not running:
153     #     logger.info("bgpd is not running on the controller node")
154     #     results.add_success(msg)
155     # else:
156     #     logger.info("bgpd is still running on the controller node")
157     #     results.add_failure(msg)
158
159     # Taken from the sfc tests
160     if not os.path.isfile(COMMON_CONFIG.ubuntu_image_path):
161         logger.info("Downloading image")
162         image_dest_path = '/'.join(
163                 COMMON_CONFIG.ubuntu_image_path.split('/')[:-1])
164         os_utils.download_url(
165             "http://artifacts.opnfv.org/sdnvpn/"
166             "ubuntu-16.04-server-cloudimg-amd64-disk1.img",
167             image_dest_path)
168     else:
169         logger.info("Using old image")
170
171     glance_client = os_utils.get_glance_client()
172     nova_client = os_utils.get_nova_client()
173     neutron_client = os_utils.get_neutron_client()
174
175     (floatingip_ids, instance_ids, router_ids, network_ids, image_ids,
176      subnet_ids, interfaces, bgpvpn_ids, flavor_ids) = ([] for i in range(9))
177     quagga_vm = None
178     fake_fip = None
179
180     try:
181         _, flavor_id = test_utils.create_custom_flavor()
182         flavor_ids.append(flavor_id)
183
184         sg_id = os_utils.create_security_group_full(
185             neutron_client, TESTCASE_CONFIG.secgroup_name,
186             TESTCASE_CONFIG.secgroup_descr)
187         test_utils.open_icmp(neutron_client, sg_id)
188         test_utils.open_http_port(neutron_client, sg_id)
189
190         test_utils.open_bgp_port(neutron_client, sg_id)
191
192         image_id = os_utils.create_glance_image(
193             glance_client, TESTCASE_CONFIG.image_name,
194             COMMON_CONFIG.image_path, disk=COMMON_CONFIG.image_format,
195             container="bare", public='public')
196         image_ids.append(image_id)
197
198         net_1_id, subnet_1_id, router_1_id = test_utils.create_network(
199             neutron_client,
200             TESTCASE_CONFIG.net_1_name,
201             TESTCASE_CONFIG.subnet_1_name,
202             TESTCASE_CONFIG.subnet_1_cidr,
203             TESTCASE_CONFIG.router_1_name)
204
205         quagga_net_id, subnet_quagga_id, \
206             router_quagga_id = test_utils.create_network(
207                 neutron_client,
208                 TESTCASE_CONFIG.quagga_net_name,
209                 TESTCASE_CONFIG.quagga_subnet_name,
210                 TESTCASE_CONFIG.quagga_subnet_cidr,
211                 TESTCASE_CONFIG.quagga_router_name)
212
213         interfaces.append(tuple((router_1_id, subnet_1_id)))
214         interfaces.append(tuple((router_quagga_id, subnet_quagga_id)))
215         network_ids.extend([net_1_id, quagga_net_id])
216         router_ids.extend([router_1_id, router_quagga_id])
217         subnet_ids.extend([subnet_1_id, subnet_quagga_id])
218
219         installer_type = str(os.environ['INSTALLER_TYPE'].lower())
220         if installer_type == "fuel":
221             disk = 'raw'
222         elif installer_type == "apex":
223             disk = 'qcow2'
224         else:
225             logger.error("Incompatible installer type")
226
227         ubuntu_image_id = os_utils.create_glance_image(
228             glance_client,
229             COMMON_CONFIG.ubuntu_image_name,
230             COMMON_CONFIG.ubuntu_image_path,
231             disk,
232             container="bare",
233             public="public")
234
235         image_ids.append(ubuntu_image_id)
236
237         # NOTE(rski) The order of this seems a bit weird but
238         # there is a reason for this, namely
239         # https://jira.opnfv.org/projects/SDNVPN/issues/SDNVPN-99
240         # so we create the quagga instance using cloud-init
241         # and immediately give it a floating IP.
242         # The cloud-init script should contain a small sleep for
243         # this to work.
244         # We also create the FIP first because it is used in the
245         # cloud-init script.
246         # fake_fip is needed to bypass NAT
247         # see below for the reason why.
248         fake_fip = os_utils.create_floating_ip(neutron_client)
249         # pin quagga to some compute
250         floatingip_ids.append(fake_fip['fip_id'])
251         compute_node = nova_client.hypervisors.list()[0]
252         quagga_compute_node = "nova:" + compute_node.hypervisor_hostname
253         # Map the hypervisor used above to a compute handle
254         # returned by releng's manager
255         for comp in computes:
256             if compute_node.host_ip in comp.run_cmd("sudo ip a"):
257                 compute = comp
258                 break
259         quagga_bootstrap_script = quagga.gen_quagga_setup_script(
260             controller_ext_ip,
261             fake_fip['fip_addr'],
262             ext_net_mask,
263             TESTCASE_CONFIG.external_network_ip_prefix,
264             TESTCASE_CONFIG.route_distinguishers,
265             TESTCASE_CONFIG.import_targets,
266             TESTCASE_CONFIG.export_targets)
267
268         quagga_vm = test_utils.create_instance(
269             nova_client,
270             TESTCASE_CONFIG.quagga_instance_name,
271             ubuntu_image_id,
272             quagga_net_id,
273             sg_id,
274             fixed_ip=TESTCASE_CONFIG.quagga_instance_ip,
275             flavor=COMMON_CONFIG.custom_flavor_name,
276             userdata=quagga_bootstrap_script,
277             compute_node=quagga_compute_node)
278
279         instance_ids.append(quagga_vm)
280
281         quagga_vm_port = test_utils.get_port(neutron_client,
282                                              quagga_vm.id)
283         fip_added = os_utils.attach_floating_ip(neutron_client,
284                                                 quagga_vm_port['id'])
285
286         msg = ("Assign a Floating IP to %s " %
287                TESTCASE_CONFIG.quagga_instance_name)
288         if fip_added:
289             results.add_success(msg)
290             floatingip_ids.append(fip_added['floatingip']['id'])
291         else:
292             results.add_failure(msg)
293
294         test_utils.attach_instance_to_ext_br(quagga_vm, compute)
295
296         testcase = "Bootstrap quagga inside an OpenStack instance"
297         cloud_init_success = test_utils.wait_for_cloud_init(quagga_vm)
298         if cloud_init_success:
299             results.add_success(testcase)
300         else:
301             results.add_failure(testcase)
302         results.add_to_summary(0, "=")
303
304         results.add_to_summary(0, '-')
305         results.add_to_summary(1, "Peer Quagga with OpenDaylight")
306         results.add_to_summary(0, '-')
307
308         neighbor = quagga.odl_add_neighbor(fake_fip['fip_addr'],
309                                            controller_ext_ip,
310                                            controller)
311         peer = quagga.check_for_peering(controller)
312
313         if neighbor and peer:
314             results.add_success("Peering with quagga")
315         else:
316             results.add_failure("Peering with quagga")
317
318         test_utils.add_quagga_external_gre_end_point(controllers,
319                                                      fake_fip['fip_addr'])
320         test_utils.wait_before_subtest()
321
322         msg = ("Create VPN to define a VRF")
323         results.record_action(msg)
324         vpn_name = vpn_name = "sdnvpn-3"
325         kwargs = {
326             "import_targets": TESTCASE_CONFIG.import_targets,
327             "export_targets": TESTCASE_CONFIG.export_targets,
328             "route_targets": TESTCASE_CONFIG.route_targets,
329             "route_distinguishers": TESTCASE_CONFIG.route_distinguishers,
330             "name": vpn_name
331         }
332         bgpvpn = test_utils.create_bgpvpn(neutron_client, **kwargs)
333         bgpvpn_id = bgpvpn['bgpvpn']['id']
334         logger.debug("VPN1 created details: %s" % bgpvpn)
335         bgpvpn_ids.append(bgpvpn_id)
336
337         msg = ("Associate network '%s' to the VPN." %
338                TESTCASE_CONFIG.net_1_name)
339         results.record_action(msg)
340         results.add_to_summary(0, "-")
341
342         # create a vm and connect it with network1,
343         # which is going to be bgpvpn associated
344         userdata_common = test_utils.generate_ping_userdata(
345             [TESTCASE_CONFIG.external_network_ip])
346
347         compute_node = nova_client.hypervisors.list()[0]
348         av_zone_1 = "nova:" + compute_node.hypervisor_hostname
349         vm_bgpvpn = test_utils.create_instance(
350             nova_client,
351             TESTCASE_CONFIG.instance_1_name,
352             image_id,
353             net_1_id,
354             sg_id,
355             fixed_ip=TESTCASE_CONFIG.instance_1_ip,
356             secgroup_name=TESTCASE_CONFIG.secgroup_name,
357             compute_node=av_zone_1,
358             userdata=userdata_common)
359         instance_ids.append(vm_bgpvpn)
360
361         # wait for VM to get IP
362         instance_up = test_utils.wait_for_instances_up(vm_bgpvpn)
363         if not instance_up:
364             logger.error("One or more instances are down")
365
366         test_utils.create_network_association(
367             neutron_client, bgpvpn_id, net_1_id)
368
369         test_utils.wait_before_subtest()
370
371         msg = ("External IP prefix %s is exchanged with ODL"
372                % TESTCASE_CONFIG.external_network_ip_prefix)
373         fib_added = test_utils.is_fib_entry_present_on_odl(
374             controllers,
375             TESTCASE_CONFIG.external_network_ip_prefix,
376             TESTCASE_CONFIG.route_distinguishers)
377         if fib_added:
378             results.add_success(msg)
379         else:
380             results.add_failure(msg)
381
382         # TODO: uncomment the following once OVS is installed with > 2.8.3 and
383         # underlay connectivity is established between vxlan overlay and
384         # external network.
385         # results.get_ping_status_target_ip(
386         #    vm_bgpvpn,
387         #    TESTCASE_CONFIG.external_network_name,
388         #    TESTCASE_CONFIG.external_network_ip,
389         #    expected="PASS",
390         #    timeout=300)
391
392         results.add_to_summary(0, "=")
393         logger.info("\n%s" % results.summary)
394
395     except Exception as e:
396         logger.error("exception occurred while executing testcase_3: %s", e)
397         raise
398     finally:
399         if quagga_vm is not None:
400             test_utils.detach_instance_from_ext_br(quagga_vm, compute)
401         test_utils.cleanup_nova(nova_client, instance_ids, flavor_ids)
402         test_utils.cleanup_glance(glance_client, image_ids)
403         test_utils.cleanup_neutron(neutron_client, floatingip_ids,
404                                    bgpvpn_ids, interfaces, subnet_ids,
405                                    router_ids, network_ids)
406         if fake_fip is not None:
407             bgp_nbr_disconnect_cmd = ("bgp-nbr -i %s -a 200 del"
408                                       % fake_fip['fip_addr'])
409             test_utils.run_odl_cmd(controller, bgp_nbr_disconnect_cmd)
410         bgp_server_stop_cmd = ("bgp-rtr -r %s -a 100 del"
411                                % controller_ext_ip)
412         odl_zrpc_disconnect_cmd = "bgp-connect -p 7644 -h 127.0.0.1 del"
413         test_utils.run_odl_cmd(controller, bgp_server_stop_cmd)
414         test_utils.run_odl_cmd(controller, odl_zrpc_disconnect_cmd)
415
416     return results.compile_summary()
417
418
419 if __name__ == '__main__':
420     sys.exit(main())