Fix Flake8 Violations in the Functest scripts
[functest.git] / testcases / vPing / CI / libraries / vPing_ssh.py
1 #!/usr/bin/python
2 #
3 # Copyright (c) 2015 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 # 0.1: This script boots the VM1 and allocates IP address from Nova
11 # Later, the VM2 boots then execute cloud-init to ping VM1.
12 # After successful ping, both the VMs are deleted.
13 # 0.2: measure test duration and publish results under json format
14 #
15 #
16 import argparse
17 import datetime
18 import logging
19 import os
20 import paramiko
21 import pprint
22 import re
23 import sys
24 import time
25 import yaml
26 from scp import SCPClient
27 from novaclient import client as novaclient
28 from neutronclient.v2_0 import client as neutronclient
29 from keystoneclient.v2_0 import client as keystoneclient
30 from glanceclient import client as glanceclient
31
32 pp = pprint.PrettyPrinter(indent=4)
33
34 parser = argparse.ArgumentParser()
35 image_exists = False
36
37 parser.add_argument("-d", "--debug", help="Debug mode", action="store_true")
38 parser.add_argument("-r", "--report",
39                     help="Create json result file",
40                     action="store_true")
41 parser.add_argument("-n", "--noclean",
42                     help="Don't clean the created resources for this test.",
43                     action="store_true")
44
45 args = parser.parse_args()
46
47 """ logging configuration """
48
49 logger = logging.getLogger('vPing_ssh')
50 logger.setLevel(logging.DEBUG)
51
52 ch = logging.StreamHandler()
53
54 if args.debug:
55     ch.setLevel(logging.DEBUG)
56 else:
57     ch.setLevel(logging.INFO)
58
59 formatter = logging.Formatter('%(asctime)s - %(name)s'
60                               '- %(levelname)s - %(message)s')
61
62 ch.setFormatter(formatter)
63 logger.addHandler(ch)
64 paramiko.util.log_to_file("/var/log/paramiko.log")
65
66 REPO_PATH = os.environ['repos_dir'] + '/functest/'
67 if not os.path.exists(REPO_PATH):
68     logger.error("Functest repository directory not found '%s'" % REPO_PATH)
69     exit(-1)
70 sys.path.append(REPO_PATH + "testcases/")
71 import functest_utils
72 import openstack_utils
73
74 with open("/home/opnfv/functest/conf/config_functest.yaml") as f:
75     functest_yaml = yaml.safe_load(f)
76 f.close()
77
78 HOME = os.environ['HOME'] + "/"
79 # vPing parameters
80 VM_BOOT_TIMEOUT = 180
81 VM_DELETE_TIMEOUT = 100
82 PING_TIMEOUT = functest_yaml.get("vping").get("ping_timeout")
83 TEST_DB = functest_yaml.get("results").get("test_db_url")
84 NAME_VM_1 = functest_yaml.get("vping").get("vm_name_1")
85 NAME_VM_2 = functest_yaml.get("vping").get("vm_name_2")
86 GLANCE_IMAGE_NAME = functest_yaml.get("vping").get("image_name")
87 GLANCE_IMAGE_FILENAME = functest_yaml.get("general"). \
88     get("openstack").get("image_file_name")
89 GLANCE_IMAGE_FORMAT = functest_yaml.get("general"). \
90     get("openstack").get("image_disk_format")
91 GLANCE_IMAGE_PATH = functest_yaml.get("general"). \
92     get("directories").get("dir_functest_data") + "/" + GLANCE_IMAGE_FILENAME
93
94
95 FLAVOR = functest_yaml.get("vping").get("vm_flavor")
96
97 # NEUTRON Private Network parameters
98
99 NEUTRON_PRIVATE_NET_NAME = functest_yaml.get("vping"). \
100     get("vping_private_net_name")
101 NEUTRON_PRIVATE_SUBNET_NAME = functest_yaml.get("vping"). \
102     get("vping_private_subnet_name")
103 NEUTRON_PRIVATE_SUBNET_CIDR = functest_yaml.get("vping"). \
104     get("vping_private_subnet_cidr")
105 NEUTRON_ROUTER_NAME = functest_yaml.get("vping"). \
106     get("vping_router_name")
107
108 SECGROUP_NAME = functest_yaml.get("vping"). \
109     get("vping_sg_name")
110 SECGROUP_DESCR = functest_yaml.get("vping"). \
111     get("vping_sg_descr")
112
113
114 def pMsg(value):
115
116     """pretty printing"""
117     pp.pprint(value)
118
119
120 def waitVmActive(nova, vm):
121
122     # sleep and wait for VM status change
123     sleep_time = 3
124     count = VM_BOOT_TIMEOUT / sleep_time
125     while True:
126         status = openstack_utils.get_instance_status(nova, vm)
127         logger.debug("Status: %s" % status)
128         if status == "ACTIVE":
129             return True
130         if status == "ERROR" or status == "error":
131             return False
132         if count == 0:
133             logger.debug("Booting a VM timed out...")
134             return False
135         count -= 1
136         time.sleep(sleep_time)
137     return False
138
139
140 def waitVmDeleted(nova, vm):
141
142     # sleep and wait for VM status change
143     sleep_time = 3
144     count = VM_DELETE_TIMEOUT / sleep_time
145     while True:
146         status = openstack_utils.get_instance_status(nova, vm)
147         if not status:
148             return True
149         elif count == 0:
150             logger.debug("Timeout")
151             return False
152         else:
153             # return False
154             count -= 1
155         time.sleep(sleep_time)
156     return False
157
158
159 def create_private_neutron_net(neutron):
160
161     # Check if the network already exists
162     network_id = openstack_utils.get_network_id(neutron,
163                                                 NEUTRON_PRIVATE_NET_NAME)
164     subnet_id = openstack_utils.get_subnet_id(neutron,
165                                               NEUTRON_PRIVATE_SUBNET_NAME)
166     router_id = openstack_utils.get_router_id(neutron,
167                                               NEUTRON_ROUTER_NAME)
168
169     if network_id != '' and subnet_id != '' and router_id != '':
170         logger.info("Using existing network '%s'..."
171                     % NEUTRON_PRIVATE_NET_NAME)
172     else:
173         neutron.format = 'json'
174         logger.info('Creating neutron network %s...'
175                     % NEUTRON_PRIVATE_NET_NAME)
176         network_id = openstack_utils. \
177             create_neutron_net(neutron, NEUTRON_PRIVATE_NET_NAME)
178
179         if not network_id:
180             return False
181         logger.debug("Network '%s' created successfully" % network_id)
182         logger.debug('Creating Subnet....')
183         subnet_id = openstack_utils. \
184             create_neutron_subnet(neutron,
185                                   NEUTRON_PRIVATE_SUBNET_NAME,
186                                   NEUTRON_PRIVATE_SUBNET_CIDR,
187                                   network_id)
188         if not subnet_id:
189             return False
190         logger.debug("Subnet '%s' created successfully" % subnet_id)
191         logger.debug('Creating Router...')
192         router_id = openstack_utils. \
193             create_neutron_router(neutron, NEUTRON_ROUTER_NAME)
194
195         if not router_id:
196             return False
197
198         logger.debug("Router '%s' created successfully" % router_id)
199         logger.debug('Adding router to subnet...')
200
201         if not openstack_utils.add_interface_router(neutron,
202                                                     router_id,
203                                                     subnet_id):
204             return False
205         logger.debug("Interface added successfully.")
206
207         logger.debug('Adding gateway to router...')
208         if not openstack_utils.add_gateway_router(neutron, router_id):
209             return False
210         logger.debug("Gateway added successfully.")
211
212     network_dic = {'net_id': network_id,
213                    'subnet_id': subnet_id,
214                    'router_id': router_id}
215     return network_dic
216
217
218 def create_security_group(neutron_client):
219     sg_id = openstack_utils.get_security_group_id(neutron_client,
220                                                   SECGROUP_NAME)
221     if sg_id != '':
222         logger.info("Using existing security group '%s'..." % SECGROUP_NAME)
223     else:
224         logger.info("Creating security group  '%s'..." % SECGROUP_NAME)
225         SECGROUP = openstack_utils.create_security_group(neutron_client,
226                                                          SECGROUP_NAME,
227                                                          SECGROUP_DESCR)
228         if not SECGROUP:
229             logger.error("Failed to create the security group...")
230             return False
231
232         sg_id = SECGROUP['id']
233
234         logger.debug("Security group '%s' with ID=%s created successfully."
235                      % (SECGROUP['name'], sg_id))
236
237         logger.debug("Adding ICMP rules in security group '%s'..."
238                      % SECGROUP_NAME)
239         if not openstack_utils.create_secgroup_rule(neutron_client, sg_id,
240                                                     'ingress', 'icmp'):
241             logger.error("Failed to create the security group rule...")
242             return False
243
244         logger.debug("Adding SSH rules in security group '%s'..."
245                      % SECGROUP_NAME)
246         if not openstack_utils.\
247             create_secgroup_rule(neutron_client, sg_id,
248                                  'ingress', 'tcp', '22', '22'):
249             logger.error("Failed to create the security group rule...")
250             return False
251
252         if not openstack_utils.\
253             create_secgroup_rule(neutron_client, sg_id,
254                                  'egress', 'tcp', '22', '22'):
255             logger.error("Failed to create the security group rule...")
256             return False
257     return sg_id
258
259
260 def cleanup(nova, neutron, image_id, network_dic, sg_id, floatingip):
261     if args.noclean:
262         logger.debug("The OpenStack resources are not deleted.")
263         return True
264
265     # delete both VMs
266     logger.info("Cleaning up...")
267     if not image_exists:
268         logger.debug("Deleting image...")
269         if not openstack_utils.delete_glance_image(nova, image_id):
270             logger.error("Error deleting the glance image")
271
272     vm1 = openstack_utils.get_instance_by_name(nova, NAME_VM_1)
273     if vm1:
274         logger.debug("Deleting '%s'..." % NAME_VM_1)
275         nova.servers.delete(vm1)
276         # wait until VMs are deleted
277         if not waitVmDeleted(nova, vm1):
278             logger.error(
279                 "Instance '%s' with cannot be deleted. Status is '%s'" % (
280                     NAME_VM_1, openstack_utils.get_instance_status(nova, vm1)))
281         else:
282             logger.debug("Instance %s terminated." % NAME_VM_1)
283
284     vm2 = openstack_utils.get_instance_by_name(nova, NAME_VM_2)
285
286     if vm2:
287         logger.debug("Deleting '%s'..." % NAME_VM_2)
288         vm2 = nova.servers.find(name=NAME_VM_2)
289         nova.servers.delete(vm2)
290
291         if not waitVmDeleted(nova, vm2):
292             logger.error(
293                 "Instance '%s' with cannot be deleted. Status is '%s'" % (
294                     NAME_VM_2, openstack_utils.get_instance_status(nova, vm2)))
295         else:
296             logger.debug("Instance %s terminated." % NAME_VM_2)
297
298     # delete created network
299     logger.debug("Deleting network '%s'..." % NEUTRON_PRIVATE_NET_NAME)
300     net_id = network_dic["net_id"]
301     subnet_id = network_dic["subnet_id"]
302     router_id = network_dic["router_id"]
303
304     if not openstack_utils.remove_interface_router(neutron, router_id,
305                                                    subnet_id):
306         logger.error("Unable to remove subnet '%s' from router '%s'" % (
307             subnet_id, router_id))
308         return False
309
310     logger.debug("Interface removed successfully")
311     if not openstack_utils.delete_neutron_router(neutron, router_id):
312         logger.error("Unable to delete router '%s'" % router_id)
313         return False
314
315     logger.debug("Router deleted successfully")
316
317     if not openstack_utils.delete_neutron_subnet(neutron, subnet_id):
318         logger.error("Unable to delete subnet '%s'" % subnet_id)
319         return False
320
321     logger.debug(
322         "Subnet '%s' deleted successfully" % NEUTRON_PRIVATE_SUBNET_NAME)
323
324     if not openstack_utils.delete_neutron_net(neutron, net_id):
325         logger.error("Unable to delete network '%s'" % net_id)
326         return False
327
328     logger.debug(
329         "Network '%s' deleted successfully" % NEUTRON_PRIVATE_NET_NAME)
330
331     if not openstack_utils.delete_security_group(neutron, sg_id):
332         logger.error("Unable to delete security group '%s'" % sg_id)
333         return False
334     logger.debug(
335         "Security group '%s' deleted successfully" % sg_id)
336
337     logger.debug("Releasing floating ip '%s'..." % floatingip['fip_addr'])
338     if not openstack_utils.delete_floating_ip(nova, floatingip['fip_id']):
339         logger.error("Unable to delete floatingip '%s'"
340                      % floatingip['fip_addr'])
341         return False
342     logger.debug(
343         "Floating IP '%s' deleted successfully" % floatingip['fip_addr'])
344     return True
345
346
347 def push_results(start_time_ts, duration, test_status):
348     try:
349         logger.debug("Pushing result into DB...")
350         scenario = functest_utils.get_scenario(logger)
351         version = scenario
352         criteria = "failed"
353         if test_status == "OK":
354             criteria = "passed"
355         pod_name = functest_utils.get_pod_name(logger)
356         build_tag = functest_utils.get_build_tag(logger)
357         functest_utils.push_results_to_db(TEST_DB,
358                                           "functest",
359                                           "vPing",
360                                           logger, pod_name, version, scenario,
361                                           criteria, build_tag,
362                                           payload={'timestart': start_time_ts,
363                                                    'duration': duration,
364                                                    'status': test_status})
365     except:
366         logger.error("Error pushing results into Database '%s'"
367                      % sys.exc_info()[0])
368
369
370 def main():
371
372     creds_nova = openstack_utils.get_credentials("nova")
373     nova_client = novaclient.Client('2', **creds_nova)
374     creds_neutron = openstack_utils.get_credentials("neutron")
375     neutron_client = neutronclient.Client(**creds_neutron)
376     creds_keystone = openstack_utils.get_credentials("keystone")
377     keystone_client = keystoneclient.Client(**creds_keystone)
378     glance_endpoint = keystone_client.\
379         service_catalog.url_for(service_type='image',
380                                 endpoint_type='publicURL')
381     glance_client = glanceclient.Client(1, glance_endpoint,
382                                         token=keystone_client.auth_token)
383     EXIT_CODE = -1
384
385     image_id = None
386     flavor = None
387
388     # Check if the given image exists
389     image_id = openstack_utils.get_image_id(glance_client, GLANCE_IMAGE_NAME)
390     if image_id != '':
391         logger.info("Using existing image '%s'..." % GLANCE_IMAGE_NAME)
392         global image_exists
393         image_exists = True
394     else:
395         logger.info("Creating image '%s' from '%s'..." % (GLANCE_IMAGE_NAME,
396                                                           GLANCE_IMAGE_PATH))
397         image_id = openstack_utils.create_glance_image(glance_client,
398                                                        GLANCE_IMAGE_NAME,
399                                                        GLANCE_IMAGE_PATH)
400         if not image_id:
401             logger.error("Failed to create a Glance image...")
402             return(EXIT_CODE)
403         logger.debug("Image '%s' with ID=%s created successfully."
404                      % (GLANCE_IMAGE_NAME, image_id))
405
406     network_dic = create_private_neutron_net(neutron_client)
407     if not network_dic:
408         logger.error(
409             "There has been a problem when creating the neutron network")
410         return(EXIT_CODE)
411
412     network_id = network_dic["net_id"]
413
414     sg_id = create_security_group(neutron_client)
415
416     # Check if the given flavor exists
417     try:
418         flavor = nova_client.flavors.find(name=FLAVOR)
419         logger.info("Using existing Flavor '%s'..." % FLAVOR)
420     except:
421         logger.error("Flavor '%s' not found." % FLAVOR)
422         logger.info("Available flavors are: ")
423         pMsg(nova_client.flavor.list())
424         return(EXIT_CODE)
425
426     # Deleting instances if they exist
427     servers = nova_client.servers.list()
428     for server in servers:
429         if server.name == NAME_VM_1 or server.name == NAME_VM_2:
430             logger.info("Instance %s found. Deleting..." % server.name)
431             server.delete()
432
433     # boot VM 1
434     start_time_ts = time.time()
435     end_time_ts = start_time_ts
436     logger.info("vPing Start Time:'%s'" % (
437         datetime.datetime.fromtimestamp(start_time_ts).strftime(
438             '%Y-%m-%d %H:%M:%S')))
439
440     logger.info("Creating instance '%s'..." % NAME_VM_1)
441     logger.debug(
442         "Configuration:\n name=%s \n flavor=%s \n image=%s \n "
443         "network=%s \n" % (NAME_VM_1, flavor, image_id, network_id))
444     vm1 = nova_client.servers.create(
445         name=NAME_VM_1,
446         flavor=flavor,
447         image=image_id,
448         nics=[{"net-id": network_id}]
449     )
450
451     # wait until VM status is active
452     if not waitVmActive(nova_client, vm1):
453         logger.error("Instance '%s' cannot be booted. Status is '%s'" % (
454             NAME_VM_1, openstack_utils.get_instance_status(nova_client, vm1)))
455         cleanup(nova_client, neutron_client, image_id, network_dic, sg_id,
456                 floatingip)
457         return (EXIT_CODE)
458     else:
459         logger.info("Instance '%s' is ACTIVE." % NAME_VM_1)
460
461     # Retrieve IP of first VM
462     test_ip = vm1.networks.get(NEUTRON_PRIVATE_NET_NAME)[0]
463     logger.debug("Instance '%s' got private ip '%s'." % (NAME_VM_1, test_ip))
464
465     logger.info("Adding '%s' to security group '%s'..."
466                 % (NAME_VM_1, SECGROUP_NAME))
467     openstack_utils.add_secgroup_to_instance(nova_client, vm1.id, sg_id)
468
469     # boot VM 2
470     logger.info("Creating instance '%s'..." % NAME_VM_2)
471     logger.debug(
472         "Configuration:\n name=%s \n flavor=%s \n image=%s \n "
473         "network=%s \n" % (NAME_VM_2, flavor, image_id, network_id))
474     vm2 = nova_client.servers.create(
475         name=NAME_VM_2,
476         flavor=flavor,
477         image=image_id,
478         nics=[{"net-id": network_id}]
479     )
480
481     if not waitVmActive(nova_client, vm2):
482         logger.error("Instance '%s' cannot be booted. Status is '%s'" % (
483             NAME_VM_2, openstack_utils.get_instance_status(nova_client, vm2)))
484         cleanup(nova_client, neutron_client, image_id, network_dic, sg_id,
485                 floatip_dic)
486         return (EXIT_CODE)
487     else:
488         logger.info("Instance '%s' is ACTIVE." % NAME_VM_2)
489
490     logger.info("Adding '%s' to security group '%s'..." % (NAME_VM_2,
491                                                            SECGROUP_NAME))
492     openstack_utils.add_secgroup_to_instance(nova_client, vm2.id, sg_id)
493
494     logger.info("Creating floating IP for VM '%s'..." % NAME_VM_2)
495     floatip_dic = openstack_utils.create_floating_ip(neutron_client)
496     floatip = floatip_dic['fip_addr']
497     # floatip_id = floatip_dic['fip_id']
498
499     if floatip is None:
500         logger.error("Cannot create floating IP.")
501         cleanup(nova_client, neutron_client, image_id, network_dic, sg_id,
502                 floatip_dic)
503         return (EXIT_CODE)
504     logger.info("Floating IP created: '%s'" % floatip)
505
506     logger.info("Associating floating ip: '%s' to VM '%s' "
507                 % (floatip, NAME_VM_2))
508     if not openstack_utils.add_floating_ip(nova_client, vm2.id, floatip):
509         logger.error("Cannot associate floating IP to VM.")
510         cleanup(nova_client, neutron_client, image_id, network_dic, sg_id,
511                 floatip_dic)
512         return (EXIT_CODE)
513
514     logger.info("Trying to establish SSH connection to %s..." % floatip)
515     username = 'cirros'
516     password = 'cubswin:)'
517     ssh = paramiko.SSHClient()
518     ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
519
520     timeout = 50
521     nolease = False
522     got_ip = False
523     discover_count = 0
524     cidr_first_octet = NEUTRON_PRIVATE_SUBNET_CIDR.split('.')[0]
525     while timeout > 0:
526         try:
527             ssh.connect(floatip, username=username,
528                         password=password, timeout=2)
529             logger.debug("SSH connection established to %s." % floatip)
530             break
531         except:
532             logger.debug("Waiting for %s..." % floatip)
533             time.sleep(6)
534             timeout -= 1
535
536         console_log = vm2.get_console_output()
537
538         # print each "Sending discover" captured on the console log
539         if len(re.findall("Sending discover", console_log)) > discover_count \
540            and not got_ip:
541             discover_count += 1
542             logger.debug("Console-log '%s': Sending discover..."
543                          % NAME_VM_2)
544
545         # check if eth0 got an ip,the line looks like this:
546         # "inet addr:192.168."....
547         # if the dhcp agent fails to assing ip, this line will not appear
548         if "inet addr:" + cidr_first_octet in console_log and not got_ip:
549             got_ip = True
550             logger.debug("The instance '%s' succeeded to get the IP "
551                          "from the dhcp agent.")
552
553         # if dhcp doesnt work,it shows "No lease, failing".The test will fail
554         if "No lease, failing" in console_log and not nolease and not got_ip:
555                 nolease = True
556                 logger.debug("Console-log '%s': No lease, failing..."
557                              % NAME_VM_2)
558                 logger.info("The instance failed to get an IP from the "
559                             "DHCP agent. The test will probably timeout...")
560
561     if timeout == 0:  # 300 sec timeout (5 min)
562         logger.error("Cannot establish connection to IP '%s'. Aborting"
563                      % floatip)
564         cleanup(nova_client, neutron_client, image_id, network_dic, sg_id,
565                 floatip_dic)
566         return (EXIT_CODE)
567
568     scp = SCPClient(ssh.get_transport())
569
570     ping_script = REPO_PATH + "testcases/vPing/CI/libraries/ping.sh"
571     try:
572         scp.put(ping_script, "~/")
573     except:
574         logger.error("Cannot SCP the file '%s' to VM '%s'"
575                      % (ping_script, floatip))
576
577     cmd = 'chmod 755 ~/ping.sh'
578     (stdin, stdout, stderr) = ssh.exec_command(cmd)
579     for line in stdout.readlines():
580         print line
581
582     logger.info("Waiting for ping...")
583     sec = 0
584     duration = 0
585
586     cmd = '~/ping.sh ' + test_ip
587     flag = False
588     while True:
589         time.sleep(1)
590         (stdin, stdout, stderr) = ssh.exec_command(cmd)
591         output = stdout.readlines()
592
593         for line in output:
594             if "vPing OK" in line:
595                 logger.info("vPing detected!")
596
597                 # we consider start time at VM1 booting
598                 end_time_ts = time.time()
599                 duration = round(end_time_ts - start_time_ts, 1)
600                 logger.info("vPing duration:'%s' s." % duration)
601                 EXIT_CODE = 0
602                 flag = True
603                 break
604             elif sec == PING_TIMEOUT:
605                 logger.info("Timeout reached.")
606                 flag = True
607                 break
608         if flag:
609             break
610         logger.debug("Pinging %s. Waiting for response..." % test_ip)
611         sec += 1
612
613     cleanup(nova_client, neutron_client, image_id, network_dic, sg_id,
614             floatip_dic)
615
616     test_status = "NOK"
617     if EXIT_CODE == 0:
618         logger.info("vPing OK")
619         test_status = "OK"
620     else:
621         duration = 0
622         logger.error("vPing FAILED")
623
624     if args.report:
625         push_results(start_time_ts, duration, test_status)
626
627     exit(EXIT_CODE)
628
629 if __name__ == '__main__':
630     main()