Multi-compute support and python refactoring
[functest.git] / testcases / features / sfc / sfc_colorado1.py
1 import os
2 import subprocess
3 import sys
4 import time
5 import argparse
6 import paramiko
7
8 import functest.utils.functest_logger as ft_logger
9 import functest.utils.functest_utils as ft_utils
10 import functest.utils.openstack_utils as os_utils
11
12 parser = argparse.ArgumentParser()
13
14 parser.add_argument("-r", "--report",
15                     help="Create json result file",
16                     action="store_true")
17
18 args = parser.parse_args()
19
20 """ logging configuration """
21 logger = ft_logger.Logger("ODL_SFC").getLogger()
22
23 FUNCTEST_REPO = ft_utils.FUNCTEST_REPO
24
25 HOME = os.environ['HOME'] + "/"
26
27 VM_BOOT_TIMEOUT = 180
28 INSTANCE_NAME = "client"
29 FLAVOR = "custom"
30 IMAGE_NAME = "sf_nsh_colorado"
31 IMAGE_FILENAME = "sf_nsh_colorado.qcow2"
32 IMAGE_FORMAT = "qcow2"
33 IMAGE_PATH = "/home/opnfv/functest/data" + "/" + IMAGE_FILENAME
34
35 # NEUTRON Private Network parameters
36
37 NET_NAME = "example-net"
38 SUBNET_NAME = "example-subnet"
39 SUBNET_CIDR = "11.0.0.0/24"
40 ROUTER_NAME = "example-router"
41
42 SECGROUP_NAME = "example-sg"
43 SECGROUP_DESCR = "Example Security group"
44
45 INSTANCE_NAME_2 = "server"
46
47 # TEST_DB = ft_utils.get_parameter_from_yaml("results.test_db_url")
48
49 PRE_SETUP_SCRIPT = 'sfc_pre_setup.bash'
50 TACKER_SCRIPT = 'sfc_tacker.bash'
51 TEARDOWN_SCRIPT = "sfc_teardown.bash"
52 TACKER_CHANGECLASSI = "sfc_change_classi.bash"
53
54 ssh_options = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
55
56
57 def check_ssh(ip):
58     cmd = "sshpass -p opnfv ssh " + ssh_options + " -q " + ip + " exit"
59     success = subprocess.call(cmd, shell=True) == 0
60     if not success:
61         logger.debug("Wating for SSH connectivity in SF with IP: %s" % ip)
62     return success
63
64
65 def main():
66
67     # Allow any port so that tacker commands reaches the server.
68     # This will be deleted when tacker is included in OPNFV installation
69
70     status = "PASS"
71     failures = 0
72     start_time = time.time()
73     json_results = {}
74
75     contr_cmd = ("sshpass -p r00tme ssh " + ssh_options + " root@10.20.0.2"
76                  " 'fuel node'|grep controller|awk '{print $10}'")
77     logger.info("Executing script to get ip_server: '%s'" % contr_cmd)
78     process = subprocess.Popen(contr_cmd,
79                                shell=True,
80                                stdout=subprocess.PIPE,
81                                stderr=subprocess.PIPE)
82     ip_server = process.stdout.readline().rstrip()
83
84     contr_cmd2 = ("sshpass -p r00tme ssh " + ssh_options + " root@10.20.0.2"
85                   " 'fuel node'|grep compute|awk '{print $10}'")
86     logger.info("Executing script to get ip_compute: '%s'" % contr_cmd2)
87     process = subprocess.Popen(contr_cmd2,
88                                shell=True,
89                                stdout=subprocess.PIPE,
90                                stderr=subprocess.PIPE)
91     ip_compute = process.stdout.readline().rstrip()
92
93     iptable_cmd1 = ("sshpass -p r00tme ssh " + ssh_options + " root@10.20.0.2"
94                     " ssh " + ip_server + " iptables -P INPUT ACCEPT ")
95     iptable_cmd2 = ("sshpass -p r00tme ssh " + ssh_options + " root@10.20.0.2"
96                     " ssh " + ip_server + " iptables -t nat -P INPUT ACCEPT ")
97     iptable_cmd3 = ("sshpass -p r00tme ssh " + ssh_options + " root@10.20.0.2"
98                     " ssh " + ssh_options + " " + ip_server +
99                     " iptables -A INPUT -m state"
100                     " --state NEW,ESTABLISHED,RELATED -j ACCEPT")
101
102     logger.info("Changing firewall policy in controller: '%s'" % iptable_cmd1)
103     subprocess.call(iptable_cmd1, shell=True, stderr=subprocess.PIPE)
104
105     logger.info("Changing firewall policy in controller: '%s'" % iptable_cmd2)
106     subprocess.call(iptable_cmd2, shell=True, stderr=subprocess.PIPE)
107
108     logger.info("Changing firewall policy in controller: '%s'" % iptable_cmd3)
109     subprocess.call(iptable_cmd2, shell=True, stderr=subprocess.PIPE)
110
111 # Getting the different clients
112
113     nova_client = os_utils.get_nova_client()
114     neutron_client = os_utils.get_neutron_client()
115     glance_client = os_utils.get_glance_client()
116
117 # Download the image
118
119     if not os.path.isfile(IMAGE_PATH):
120         logger.info("Downloading image")
121         ft_utils.download_url(
122             "http://artifacts.opnfv.org/sfc/demo/sf_nsh_colorado.qcow2",
123             "/home/opnfv/functest/data/")
124     else:
125         logger.info("Using old image")
126
127 # Create glance image and the neutron network
128
129     image_id = os_utils.create_glance_image(glance_client,
130                                             IMAGE_NAME,
131                                             IMAGE_PATH,
132                                             disk=IMAGE_FORMAT,
133                                             container="bare",
134                                             public=True)
135
136     network_dic = os_utils.create_network_full(neutron_client,
137                                                NET_NAME,
138                                                SUBNET_NAME,
139                                                ROUTER_NAME,
140                                                SUBNET_CIDR)
141     if not network_dic:
142         logger.error(
143             "There has been a problem when creating the neutron network")
144         sys.exit(-1)
145
146     network_id = network_dic["net_id"]
147
148     sg_id = os_utils.create_security_group_full(neutron_client,
149                                                 SECGROUP_NAME, SECGROUP_DESCR)
150
151     secgroups = os_utils.get_security_groups(neutron_client)
152
153     for sg in secgroups:
154         os_utils.create_secgroup_rule(neutron_client, sg['id'],
155                                       'ingress', 'tcp',
156                                       port_range_min=22,
157                                       port_range_max=22)
158         os_utils.create_secgroup_rule(neutron_client, sg['id'],
159                                       'egress', 'tcp',
160                                       port_range_min=22,
161                                       port_range_max=22)
162
163     _, custom_flv_id = os_utils.get_or_create_flavor(
164         'custom', 1500, 10, 1, public=True)
165     if not custom_flv_id:
166         logger.error("Failed to create custom flavor")
167         sys.exit(1)
168
169     iterator = 0
170     while(iterator < 6):
171         # boot INSTANCE
172         logger.info("Creating instance '%s'..." % INSTANCE_NAME)
173         logger.debug(
174             "Configuration:\n name=%s \n flavor=%s \n image=%s \n "
175             "network=%s \n" % (INSTANCE_NAME, FLAVOR, image_id, network_id))
176         instance = os_utils.create_instance_and_wait_for_active(FLAVOR,
177                                                                 image_id,
178                                                                 network_id,
179                                                                 INSTANCE_NAME)
180
181         if instance is None:
182             logger.error("Error while booting instance.")
183             iterator += 1
184             continue
185         # Retrieve IP of INSTANCE
186         instance_ip = instance.networks.get(NET_NAME)[0]
187         logger.debug("Instance '%s' got private ip '%s'." %
188                      (INSTANCE_NAME, instance_ip))
189
190         logger.info("Adding '%s' to security group '%s'..."
191                     % (INSTANCE_NAME, SECGROUP_NAME))
192         os_utils.add_secgroup_to_instance(nova_client, instance.id, sg_id)
193
194         logger.info("Creating floating IP for VM '%s'..." % INSTANCE_NAME)
195         floatip_dic = os_utils.create_floating_ip(neutron_client)
196         floatip_client = floatip_dic['fip_addr']
197         # floatip_id = floatip_dic['fip_id']
198
199         if floatip_client is None:
200             logger.error("Cannot create floating IP.")
201             iterator += 1
202             continue
203         logger.info("Floating IP created: '%s'" % floatip_client)
204
205         logger.info("Associating floating ip: '%s' to VM '%s' "
206                     % (floatip_client, INSTANCE_NAME))
207         if not os_utils.add_floating_ip(nova_client,
208                                         instance.id,
209                                         floatip_client):
210             logger.error("Cannot associate floating IP to VM.")
211             iterator += 1
212             continue
213
214     # STARTING SECOND VM (server) ###
215
216         # boot INTANCE
217         logger.info("Creating instance '%s'..." % INSTANCE_NAME)
218         logger.debug(
219             "Configuration:\n name=%s \n flavor=%s \n image=%s \n "
220             "network=%s \n" % (INSTANCE_NAME, FLAVOR, image_id, network_id))
221         instance_2 = os_utils.create_instance_and_wait_for_active(
222             FLAVOR,
223             image_id,
224             network_id,
225             INSTANCE_NAME_2)
226
227         if instance_2 is None:
228             logger.error("Error while booting instance.")
229             iterator += 1
230             continue
231         # Retrieve IP of INSTANCE
232         instance_ip_2 = instance_2.networks.get(NET_NAME)[0]
233         logger.debug("Instance '%s' got private ip '%s'." %
234                      (INSTANCE_NAME_2, instance_ip_2))
235
236         logger.info("Adding '%s' to security group '%s'..."
237                     % (INSTANCE_NAME_2, SECGROUP_NAME))
238         os_utils.add_secgroup_to_instance(nova_client, instance_2.id, sg_id)
239
240         logger.info("Creating floating IP for VM '%s'..." % INSTANCE_NAME_2)
241         floatip_dic = os_utils.create_floating_ip(neutron_client)
242         floatip_server = floatip_dic['fip_addr']
243         # floatip_id = floatip_dic['fip_id']
244
245         if floatip_server is None:
246             logger.error("Cannot create floating IP.")
247             iterator += 1
248             continue
249         logger.info("Floating IP created: '%s'" % floatip_server)
250
251         logger.info("Associating floating ip: '%s' to VM '%s' "
252                     % (floatip_server, INSTANCE_NAME_2))
253
254         if not os_utils.add_floating_ip(nova_client,
255                                         instance_2.id,
256                                         floatip_server):
257             logger.error("Cannot associate floating IP to VM.")
258             iterator += 1
259             continue
260
261         # CREATION OF THE 2 SF ####
262
263         tacker_script = "%s/testcases/features/sfc/%s" % \
264                         (FUNCTEST_REPO, TACKER_SCRIPT)
265         logger.info("Executing tacker script: '%s'" % tacker_script)
266         subprocess.call(tacker_script, shell=True)
267
268         # SSH CALL TO START HTTP SERVER
269         ssh = paramiko.SSHClient()
270         ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
271
272         try:
273             ssh.connect(floatip_server, username="root",
274                         password="opnfv", timeout=2)
275             command = "python -m SimpleHTTPServer 80 > /dev/null 2>&1 &"
276             logger.info("Starting HTTP server")
277             (stdin, stdout, stderr) = ssh.exec_command(command)
278         except:
279             logger.debug("Waiting for %s..." % floatip_server)
280             time.sleep(6)
281             # timeout -= 1
282
283         instances = nova_client.servers.list(search_opts={'all_tenants': 1})
284         ips = []
285         try:
286             for instance in instances:
287                 if "server" not in instance.name:
288                     if "client" not in instance.name:
289                         logger.debug(
290                             "This is the instance name: %s " % instance.name)
291                         floatip_dic = os_utils.create_floating_ip(
292                             neutron_client)
293                         floatip = floatip_dic['fip_addr']
294                         ips.append(floatip)
295                         instance.add_floating_ip(floatip)
296         except:
297             logger.debug("Problems assigning floating IP to SFs")
298
299         # If no IPs were obtained, then we cant continue
300         if not ips:
301             logger.error('Failed to obtain IPs, cant continue, exiting')
302             return
303
304         logger.debug("Floating IPs for SFs: %s..." % ips)
305
306         # Check SSH connectivity to VNFs
307         r = 0
308         retries = 100
309         check = [False, False]
310
311         logger.info("Checking SSH connectivity to the SFs with ips {0}"
312                     .format(str(ips)))
313         while r < retries and not all(check):
314             try:
315                 check = [check_ssh(ips[0]), check_ssh(ips[1])]
316             except Exception:
317                 logger.exception("SSH check failed")
318                 check = [False, False]
319             time.sleep(3)
320             r += 1
321
322         if not all(check):
323             logger.error("Cannot establish SSH connection to the SFs")
324             iterator += 1
325             continue
326
327         logger.info("SSH connectivity to the SFs established")
328
329         # SSH TO START THE VXLAN_TOOL ON SF1
330         logger.info("Configuring the SFs")
331         try:
332             ssh.connect(ips[0], username="root",
333                         password="opnfv", timeout=2)
334             command = ("nohup python vxlan_tool.py -i eth0 "
335                        "-d forward -v off -b 80 > /dev/null 2>&1 &")
336             (stdin, stdout, stderr) = ssh.exec_command(command)
337         except:
338             logger.debug("Waiting for %s..." % ips[0])
339             time.sleep(6)
340             # timeout -= 1
341
342         try:
343             n = 0
344             while 1:
345                 (stdin, stdout, stderr) = ssh.exec_command(
346                     "ps aux | grep \"vxlan_tool.py\" | grep -v grep")
347                 if len(stdout.readlines()) > 0:
348                     logger.debug("HTTP firewall started")
349                     break
350                 else:
351                     n += 1
352                     if (n > 7):
353                         break
354                     logger.debug("HTTP firewall not started")
355                     time.sleep(3)
356         except Exception:
357             logger.exception("vxlan_tool not started in SF1")
358
359         # SSH TO START THE VXLAN_TOOL ON SF2
360         try:
361             ssh.connect(ips[1], username="root",
362                         password="opnfv", timeout=2)
363             command = ("nohup python vxlan_tool.py -i eth0 "
364                        "-d forward -v off -b 22 > /dev/null 2>&1 &")
365             (stdin, stdout, stderr) = ssh.exec_command(command)
366         except:
367             logger.debug("Waiting for %s..." % ips[1])
368             time.sleep(6)
369             # timeout -= 1
370
371         try:
372             n = 0
373             while 1:
374                 (stdin, stdout, stderr) = ssh.exec_command(
375                     "ps aux | grep \"vxlan_tool.py\" | grep -v grep")
376                 if len(stdout.readlines()) > 0:
377                     logger.debug("SSH firewall started")
378                     break
379                 else:
380                     n += 1
381                     if (n > 7):
382                         break
383                     logger.debug("SSH firewall not started")
384                     time.sleep(3)
385         except Exception:
386             logger.exception("vxlan_tool not started in SF2")
387
388         # SSH to modify the classification flows in compute
389
390         contr_cmd3 = ("sshpass -p r00tme ssh " + ssh_options + " "
391                       "root@10.20.0.2 'ssh " + ip_compute + " "
392                       "'bash correct_classifier.bash''")
393         logger.info("Executing script to modify the classi: '%s'" % contr_cmd3)
394         process = subprocess.Popen(contr_cmd3,
395                                    shell=True,
396                                    stdout=subprocess.PIPE)
397
398         i = 0
399
400         # SSH TO EXECUTE cmd_client
401         logger.info("TEST STARTED")
402         try:
403             ssh.connect(floatip_client, username="root",
404                         password="opnfv", timeout=2)
405             command = "nc -w 5 -zv " + instance_ip_2 + " 22 2>&1"
406             (stdin, stdout, stderr) = ssh.exec_command(command)
407
408             # WRITE THE CORRECT WAY TO DO LOGGING
409             if "timed out" in stdout.readlines()[0]:
410                 logger.info('\033[92m' + "TEST 1 [PASSED] "
411                             "==> SSH BLOCKED" + '\033[0m')
412                 i = i + 1
413                 json_results.update({"Test 1: SSH Blocked": "Passed"})
414             else:
415                 logger.error('\033[91m' + "TEST 1 [FAILED] "
416                              "==> SSH NOT BLOCKED" + '\033[0m')
417                 status = "FAIL"
418                 json_results.update({"Test 1: SSH Blocked": "Failed"})
419                 failures += 1
420         except:
421             logger.debug("Waiting for %s..." % floatip_client)
422             time.sleep(6)
423             # timeout -= 1
424
425         # SSH TO EXECUTE cmd_client
426         try:
427             ssh.connect(floatip_client, username="root",
428                         password="opnfv", timeout=2)
429             command = "nc -w 5 -zv " + instance_ip_2 + " 80 2>&1"
430             (stdin, stdout, stderr) = ssh.exec_command(command)
431
432             if "succeeded" in stdout.readlines()[0]:
433                 logger.info('\033[92m' + "TEST 2 [PASSED] "
434                             "==> HTTP WORKS" + '\033[0m')
435                 i = i + 1
436                 json_results.update({"Test 2: HTTP works": "Passed"})
437             else:
438                 logger.error('\033[91m' + "TEST 2 [FAILED] "
439                              "==> HTTP BLOCKED" + '\033[0m')
440                 status = "FAIL"
441                 json_results.update({"Test 2: HTTP works": "Failed"})
442                 failures += 1
443         except:
444             logger.debug("Waiting for %s..." % floatip_client)
445             time.sleep(6)
446             # timeout -= 1
447
448         # CHANGE OF CLASSIFICATION #
449         logger.info("Changing the classification")
450         tacker_classi = "%s/testcases/features/sfc/%s" % \
451                         (FUNCTEST_REPO, TACKER_CHANGECLASSI)
452         subprocess.call(tacker_classi, shell=True)
453
454         logger.info("Wait for ODL to update the classification rules in OVS")
455         time.sleep(10)
456
457         # SSH to modify the classification flows in compute
458
459         contr_cmd4 = ("sshpass -p r00tme ssh " + ssh_options + " "
460                       "root@10.20.0.2 'ssh " + ip_compute + " "
461                       "'bash correct_classifier.bash''")
462         logger.info("Executing script to modify the classi: '%s'" % contr_cmd4)
463         process = subprocess.Popen(contr_cmd4,
464                                    shell=True,
465                                    stdout=subprocess.PIPE)
466
467         # SSH TO EXECUTE cmd_client
468
469         try:
470             ssh.connect(floatip_client, username="root",
471                         password="opnfv", timeout=2)
472             command = "nc -w 5 -zv " + instance_ip_2 + " 80 2>&1"
473             (stdin, stdout, stderr) = ssh.exec_command(command)
474
475             if "timed out" in stdout.readlines()[0]:
476                 logger.info('\033[92m' + "TEST 3 [PASSED] "
477                             "==> HTTP BLOCKED" + '\033[0m')
478                 i = i + 1
479                 json_results.update({"Test 3: HTTP Blocked": "Passed"})
480             else:
481                 logger.error('\033[91m' + "TEST 3 [FAILED] "
482                              "==> HTTP NOT BLOCKED" + '\033[0m')
483                 status = "FAIL"
484                 json_results.update({"Test 3: HTTP Blocked": "Failed"})
485                 failures += 1
486         except:
487             logger.debug("Waiting for %s..." % floatip_client)
488             time.sleep(6)
489             # timeout -= 1
490
491         # SSH TO EXECUTE cmd_client
492         try:
493             ssh.connect(floatip_client, username="root",
494                         password="opnfv", timeout=2)
495             command = "nc -w 5 -zv " + instance_ip_2 + " 22 2>&1"
496             (stdin, stdout, stderr) = ssh.exec_command(command)
497
498             if "succeeded" in stdout.readlines()[0]:
499                 logger.info('\033[92m' + "TEST 4 [PASSED] "
500                             "==> SSH WORKS" + '\033[0m')
501                 i = i + 1
502                 json_results.update({"Test 4: SSH works": "Passed"})
503             else:
504                 logger.error('\033[91m' + "TEST 4 [FAILED] "
505                              "==> SSH BLOCKED" + '\033[0m')
506                 status = "FAIL"
507                 json_results.update({"Test 4: SSH works": "Failed"})
508                 failures += 1
509         except:
510             logger.debug("Waiting for %s..." % floatip_client)
511             time.sleep(6)
512             # timeout -= 1
513
514         iterator += 1
515         if i == 4:
516             for x in range(0, 5):
517                 logger.info('\033[92m' + "SFC TEST WORKED"
518                             " :) \n" + '\033[0m')
519             break
520         else:
521             logger.debug("Iterating again!")
522             delete = ("bash delete.sh")
523             subprocess.call(delete, shell=True, stderr=subprocess.PIPE)
524             time.sleep(10)
525
526     if args.report:
527         stop_time = time.time()
528         json_results.update({"tests": "4", "failures": int(failures)})
529         logger.debug("Promise Results json: " + str(json_results))
530         ft_utils.push_results_to_db("sfc",
531                                     "functest-odl-sfc",
532                                     start_time,
533                                     stop_time,
534                                     status,
535                                     json_results)
536     if status == "PASS":
537         sys.exit(0)
538     else:
539         sys.exit(1)
540
541 if __name__ == '__main__':
542     main()