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