Conform to interface change of ODL node in Fuel Deployment
[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     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, "=")
42
43     openstack_nodes = test_utils.get_nodes()
44     installer_type = str(os.environ['INSTALLER_TYPE'].lower())
45
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 " \
52                  "health=healthy -q"
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)]
60     else:
61         logger.error("Incompatible installer type")
62
63     computes = [node for node in openstack_nodes if node.is_compute()]
64
65     msg = ("Verify that OpenDaylight can start/communicate with zrpcd/Quagga")
66     results.record_action(msg)
67     results.add_to_summary(0, "-")
68     if not odl_nodes:
69         msg = ("ODL node list is empty. Skipping rest of tests.")
70         logger.info(msg)
71         results.add_failure(msg)
72         return results.compile_summary()
73     else:
74         msg = ("ODL node list is ready")
75         logger.info(msg)
76         results.add_success(msg)
77
78     logger.info("Checking if zrpcd is "
79                 "running on the opendaylight nodes")
80
81     for odl_node in odl_nodes:
82         output_zrpcd = odl_node.run_cmd("ps --no-headers -C "
83                                         "zrpcd -o state")
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))
87
88         if not running:
89             logger.info("zrpcd is not running on the opendaylight node {name}"
90                         .format(name=odl_node.name))
91             results.add_failure(msg)
92         else:
93             logger.info("zrpcd is running on the opendaylight node {name}"
94                         .format(name=odl_node.name))
95             results.add_success(msg)
96
97         results.add_to_summary(0, "-")
98
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)
103     if odl_node is None:
104         logger.error("Failed to find the BGP entity owner")
105         results.add_failure(msg)
106     else:
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, "-")
111
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'
117     else:
118         logger.error("Incompatible installer type")
119     odl_ip, odl_netmask = test_utils.get_node_ip_and_netmask(
120         odl_node, odl_interface)
121
122     logger.info("Starting bgp speaker of opendaylight node at IP %s "
123                 % odl_ip)
124
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)
128
129     # Start bgp daemon
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)
133
134     # we need to wait a bit until the bgpd is up
135     time.sleep(5)
136
137     logger.info("Checking if bgpd is running on the opendaylight node")
138
139     # Check if there is a non-zombie bgpd process
140     output_bgpd = odl_node.run_cmd("ps --no-headers -C "
141                                    "bgpd -o state")
142     states = output_bgpd.split()
143     running = any([s != 'Z' for s in states])
144
145     msg = ("bgpd is running")
146     if not running:
147         logger.info("bgpd is not running on the opendaylight node")
148         results.add_failure(msg)
149     else:
150         logger.info("bgpd is running on the opendaylight node")
151         results.add_success(msg)
152
153     results.add_to_summary(0, "-")
154
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)
160
161     # logger.info("Checking if bgpd is still running"
162     #             " on the opendaylight node")
163
164     # output_bgpd = odl_node.run_cmd("ps --no-headers -C " \
165     #                                "bgpd -o state")
166     # states = output_bgpd.split()
167     # running = any([s != 'Z' for s in states])
168
169     # msg = ("bgpd is stopped")
170     # if not running:
171     #     logger.info("bgpd is not running on the opendaylight node")
172     #     results.add_success(msg)
173     # else:
174     #     logger.info("bgpd is still running on the opendaylight node")
175     #     results.add_failure(msg)
176
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",
185             image_dest_path)
186     else:
187         logger.info("Using old image")
188
189     neutron_client = os_utils.get_neutron_client()
190
191     (floatingip_ids, instance_ids, router_ids, network_ids, image_ids,
192      subnet_ids, interfaces, bgpvpn_ids, flavor_ids) = ([] for i in range(9))
193     quagga_vm = None
194     fake_fip = None
195
196     try:
197         _, flavor_id = test_utils.create_custom_flavor()
198         flavor_ids.append(flavor_id)
199
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)
205
206         test_utils.open_bgp_port(conn, sg_id)
207
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)
213
214         net_1_id, subnet_1_id, router_1_id = test_utils.create_network(
215             conn,
216             TESTCASE_CONFIG.net_1_name,
217             TESTCASE_CONFIG.subnet_1_name,
218             TESTCASE_CONFIG.subnet_1_cidr,
219             TESTCASE_CONFIG.router_1_name)
220
221         quagga_net_id, subnet_quagga_id, \
222             router_quagga_id = test_utils.create_network(
223                 conn,
224                 TESTCASE_CONFIG.quagga_net_name,
225                 TESTCASE_CONFIG.quagga_subnet_name,
226                 TESTCASE_CONFIG.quagga_subnet_cidr,
227                 TESTCASE_CONFIG.quagga_router_name)
228
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])
234
235         installer_type = str(os.environ['INSTALLER_TYPE'].lower())
236         if installer_type == "fuel":
237             disk = 'raw'
238         elif installer_type == "apex":
239             disk = 'qcow2'
240         else:
241             logger.error("Incompatible installer type")
242
243         ubuntu_image_id = os_utils.create_glance_image(
244             conn,
245             COMMON_CONFIG.ubuntu_image_name,
246             COMMON_CONFIG.ubuntu_image_path,
247             disk,
248             container="bare",
249             public="public")
250
251         image_ids.append(ubuntu_image_id)
252
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
259         # this to work.
260         # We also create the FIP first because it is used in the
261         # cloud-init script.
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"):
274                 compute = comp
275                 break
276         quagga_bootstrap_script = quagga.gen_quagga_setup_script(
277             odl_ip,
278             fake_fip['fip_addr'],
279             odl_netmask,
280             TESTCASE_CONFIG.external_network_ip_prefix,
281             TESTCASE_CONFIG.route_distinguishers,
282             TESTCASE_CONFIG.import_targets,
283             TESTCASE_CONFIG.export_targets)
284
285         quagga_vm = test_utils.create_instance(
286             conn,
287             TESTCASE_CONFIG.quagga_instance_name,
288             ubuntu_image_id,
289             quagga_net_id,
290             sg_id,
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)
295
296         instance_ids.append(quagga_vm.id)
297
298         quagga_vm_port = test_utils.get_port(conn,
299                                              quagga_vm.id)
300         fip_added = os_utils.attach_floating_ip(conn,
301                                                 quagga_vm_port.id)
302
303         msg = ("Assign a Floating IP to %s " %
304                TESTCASE_CONFIG.quagga_instance_name)
305         if fip_added:
306             results.add_success(msg)
307             floatingip_ids.append(fip_added.id)
308         else:
309             results.add_failure(msg)
310
311         test_utils.attach_instance_to_ext_br(quagga_vm, compute)
312
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)
317         else:
318             results.add_failure(testcase)
319         results.add_to_summary(0, "=")
320
321         results.add_to_summary(0, '-')
322         results.add_to_summary(1, "Peer Quagga with OpenDaylight")
323         results.add_to_summary(0, '-')
324
325         neighbor = quagga.odl_add_neighbor(fake_fip['fip_addr'],
326                                            odl_ip,
327                                            odl_node)
328         peer = quagga.check_for_peering(odl_node)
329
330         if neighbor and peer:
331             results.add_success("Peering with quagga")
332         else:
333             results.add_failure("Peering with quagga")
334
335         test_utils.add_quagga_external_gre_end_point(odl_nodes,
336                                                      fake_fip['fip_addr'])
337         test_utils.wait_before_subtest()
338
339         msg = ("Create VPN to define a VRF")
340         results.record_action(msg)
341         vpn_name = vpn_name = "sdnvpn-3"
342         kwargs = {
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,
347             "name": vpn_name
348         }
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)
353
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, "-")
358
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])
363
364         compute_node = conn.compute.hypervisors().next()
365         av_zone_1 = "nova:" + compute_node.name
366         vm_bgpvpn = test_utils.create_instance(
367             conn,
368             TESTCASE_CONFIG.instance_1_name,
369             image_id,
370             net_1_id,
371             sg_id,
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)
377
378         # wait for VM to get IP
379         instance_up = test_utils.wait_for_instances_get_dhcp(vm_bgpvpn)
380         if not instance_up:
381             logger.error("One or more instances are down")
382
383         test_utils.create_network_association(
384             neutron_client, bgpvpn_id, net_1_id)
385
386         test_utils.wait_before_subtest()
387
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(
391             odl_nodes,
392             TESTCASE_CONFIG.external_network_ip_prefix,
393             TESTCASE_CONFIG.route_distinguishers)
394         if fib_added:
395             results.add_success(msg)
396         else:
397             results.add_failure(msg)
398
399         # TODO: uncomment the following once OVS is installed with > 2.8.3 and
400         # underlay connectivity is established between vxlan overlay and
401         # external network.
402         # results.get_ping_status_target_ip(
403         #    vm_bgpvpn,
404         #    TESTCASE_CONFIG.external_network_name,
405         #    TESTCASE_CONFIG.external_network_ip,
406         #    expected="PASS",
407         #    timeout=300)
408
409         results.add_to_summary(0, "=")
410         logger.info("\n%s" % results.summary)
411
412     except Exception as e:
413         logger.error("exception occurred while executing testcase_3: %s", e)
414         raise
415     finally:
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"
428                                % odl_ip)
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)
432
433     return results.compile_summary()
434
435
436 if __name__ == '__main__':
437     sys.exit(main())