Fix path for vping.sh and exit if SCP fails
[functest.git] / testcases / OpenStack / vPing / 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 os
19 import paramiko
20 import pprint
21 import re
22 import time
23 import yaml
24 from scp import SCPClient
25
26 from novaclient import client as novaclient
27 from neutronclient.v2_0 import client as neutronclient
28 from keystoneclient.v2_0 import client as keystoneclient
29 from glanceclient import client as glanceclient
30
31 import functest.utils.functest_logger as ft_logger
32 import functest.utils.functest_utils as functest_utils
33 import functest.utils.openstack_utils as openstack_utils
34
35 pp = pprint.PrettyPrinter(indent=4)
36
37 parser = argparse.ArgumentParser()
38 image_exists = False
39
40 parser.add_argument("-d", "--debug", help="Debug mode", action="store_true")
41 parser.add_argument("-r", "--report",
42                     help="Create json result file",
43                     action="store_true")
44
45 args = parser.parse_args()
46
47 """ logging configuration """
48 logger = ft_logger.Logger("vping_ssh").getLogger()
49
50 paramiko.util.log_to_file("/var/log/paramiko.log")
51
52 REPO_PATH = os.environ['repos_dir'] + '/functest/'
53 if not os.path.exists(REPO_PATH):
54     logger.error("Functest repository directory not found '%s'" % REPO_PATH)
55     exit(-1)
56
57 with open(os.environ["CONFIG_FUNCTEST_YAML"]) as f:
58     functest_yaml = yaml.safe_load(f)
59 f.close()
60
61 HOME = os.environ['HOME'] + "/"
62 # vPing parameters
63 VM_BOOT_TIMEOUT = 180
64 VM_DELETE_TIMEOUT = 100
65 PING_TIMEOUT = functest_yaml.get("vping").get("ping_timeout")
66 TEST_DB = functest_yaml.get("results").get("test_db_url")
67 NAME_VM_1 = functest_yaml.get("vping").get("vm_name_1")
68 NAME_VM_2 = functest_yaml.get("vping").get("vm_name_2")
69 GLANCE_IMAGE_NAME = functest_yaml.get("vping").get("image_name")
70 GLANCE_IMAGE_FILENAME = functest_yaml.get("general").get("openstack").get(
71     "image_file_name")
72 GLANCE_IMAGE_FORMAT = functest_yaml.get("general").get("openstack").get(
73     "image_disk_format")
74 GLANCE_IMAGE_PATH = functest_yaml.get("general").get("directories").get(
75     "dir_functest_data") + "/" + GLANCE_IMAGE_FILENAME
76
77 FLAVOR = functest_yaml.get("vping").get("vm_flavor")
78
79 # NEUTRON Private Network parameters
80
81 PRIVATE_NET_NAME = functest_yaml.get("vping").get(
82     "vping_private_net_name")
83 PRIVATE_SUBNET_NAME = functest_yaml.get("vping").get(
84     "vping_private_subnet_name")
85 PRIVATE_SUBNET_CIDR = functest_yaml.get("vping").get(
86     "vping_private_subnet_cidr")
87 ROUTER_NAME = functest_yaml.get("vping").get(
88     "vping_router_name")
89
90 SECGROUP_NAME = functest_yaml.get("vping").get("vping_sg_name")
91 SECGROUP_DESCR = functest_yaml.get("vping").get("vping_sg_descr")
92
93
94 def pMsg(value):
95
96     """pretty printing"""
97     pp.pprint(value)
98
99
100 def waitVmActive(nova, vm):
101
102     # sleep and wait for VM status change
103     sleep_time = 3
104     count = VM_BOOT_TIMEOUT / sleep_time
105     while True:
106         status = openstack_utils.get_instance_status(nova, vm)
107         logger.debug("Status: %s" % status)
108         if status == "ACTIVE":
109             return True
110         if status == "ERROR" or status == "error":
111             return False
112         if count == 0:
113             logger.debug("Booting a VM timed out...")
114             return False
115         count -= 1
116         time.sleep(sleep_time)
117     return False
118
119
120 def waitVmDeleted(nova, vm):
121
122     # sleep and wait for VM status change
123     sleep_time = 3
124     count = VM_DELETE_TIMEOUT / sleep_time
125     while True:
126         status = openstack_utils.get_instance_status(nova, vm)
127         if not status:
128             return True
129         elif count == 0:
130             logger.debug("Timeout")
131             return False
132         else:
133             # return False
134             count -= 1
135         time.sleep(sleep_time)
136     return False
137
138
139 def create_security_group(neutron_client):
140     sg_id = openstack_utils.get_security_group_id(neutron_client,
141                                                   SECGROUP_NAME)
142     if sg_id != '':
143         logger.info("Using existing security group '%s'..." % SECGROUP_NAME)
144     else:
145         logger.info("Creating security group  '%s'..." % SECGROUP_NAME)
146         SECGROUP = openstack_utils.create_security_group(neutron_client,
147                                                          SECGROUP_NAME,
148                                                          SECGROUP_DESCR)
149         if not SECGROUP:
150             logger.error("Failed to create the security group...")
151             return False
152
153         sg_id = SECGROUP['id']
154
155         logger.debug("Security group '%s' with ID=%s created successfully."
156                      % (SECGROUP['name'], sg_id))
157
158         logger.debug("Adding ICMP rules in security group '%s'..."
159                      % SECGROUP_NAME)
160         if not openstack_utils.create_secgroup_rule(neutron_client, sg_id,
161                                                     'ingress', 'icmp'):
162             logger.error("Failed to create the security group rule...")
163             return False
164
165         logger.debug("Adding SSH rules in security group '%s'..."
166                      % SECGROUP_NAME)
167         if not openstack_utils.create_secgroup_rule(
168                 neutron_client, sg_id, 'ingress', 'tcp', '22', '22'):
169             logger.error("Failed to create the security group rule...")
170             return False
171
172         if not openstack_utils.create_secgroup_rule(
173                 neutron_client, sg_id, 'egress', 'tcp', '22', '22'):
174             logger.error("Failed to create the security group rule...")
175             return False
176     return sg_id
177
178
179 def push_results(start_time_ts, duration, test_status):
180     try:
181         logger.debug("Pushing result into DB...")
182         scenario = functest_utils.get_scenario(logger)
183         version = functest_utils.get_version(logger)
184         criteria = "failed"
185         if test_status == "OK":
186             criteria = "passed"
187         pod_name = functest_utils.get_pod_name(logger)
188         build_tag = functest_utils.get_build_tag(logger)
189         functest_utils.push_results_to_db(TEST_DB,
190                                           "functest",
191                                           "vPing",
192                                           logger, pod_name, version, scenario,
193                                           criteria, build_tag,
194                                           payload={'timestart': start_time_ts,
195                                                    'duration': duration,
196                                                    'status': test_status})
197     except:
198         logger.error("Error pushing results into Database '%s'"
199                      % sys.exc_info()[0])
200
201
202 def main():
203
204     creds_nova = openstack_utils.get_credentials("nova")
205     nova_client = novaclient.Client('2', **creds_nova)
206     creds_neutron = openstack_utils.get_credentials("neutron")
207     neutron_client = neutronclient.Client(**creds_neutron)
208     creds_keystone = openstack_utils.get_credentials("keystone")
209     keystone_client = keystoneclient.Client(**creds_keystone)
210     glance_endpoint = keystone_client.service_catalog.url_for(
211         service_type='image', endpoint_type='publicURL')
212     glance_client = glanceclient.Client(1, glance_endpoint,
213                                         token=keystone_client.auth_token)
214     EXIT_CODE = -1
215
216     image_id = None
217     flavor = None
218
219     # Check if the given image exists
220     image_id = openstack_utils.get_image_id(glance_client, GLANCE_IMAGE_NAME)
221     if image_id != '':
222         logger.info("Using existing image '%s'..." % GLANCE_IMAGE_NAME)
223         global image_exists
224         image_exists = True
225     else:
226         logger.info("Creating image '%s' from '%s'..." % (GLANCE_IMAGE_NAME,
227                                                           GLANCE_IMAGE_PATH))
228         image_id = openstack_utils.create_glance_image(glance_client,
229                                                        GLANCE_IMAGE_NAME,
230                                                        GLANCE_IMAGE_PATH)
231         if not image_id:
232             logger.error("Failed to create a Glance image...")
233             return(EXIT_CODE)
234         logger.debug("Image '%s' with ID=%s created successfully."
235                      % (GLANCE_IMAGE_NAME, image_id))
236
237     network_dic = openstack_utils.create_network_full(logger,
238                                                       neutron_client,
239                                                       PRIVATE_NET_NAME,
240                                                       PRIVATE_SUBNET_NAME,
241                                                       ROUTER_NAME,
242                                                       PRIVATE_SUBNET_CIDR)
243     if not network_dic:
244         logger.error(
245             "There has been a problem when creating the neutron network")
246         return(EXIT_CODE)
247
248     network_id = network_dic["net_id"]
249
250     sg_id = create_security_group(neutron_client)
251
252     # Check if the given flavor exists
253     try:
254         flavor = nova_client.flavors.find(name=FLAVOR)
255         logger.info("Using existing Flavor '%s'..." % FLAVOR)
256     except:
257         logger.error("Flavor '%s' not found." % FLAVOR)
258         logger.info("Available flavors are: ")
259         pMsg(nova_client.flavor.list())
260         return(EXIT_CODE)
261
262     # Deleting instances if they exist
263     servers = nova_client.servers.list()
264     for server in servers:
265         if server.name == NAME_VM_1 or server.name == NAME_VM_2:
266             logger.info("Instance %s found. Deleting..." % server.name)
267             server.delete()
268
269     # boot VM 1
270     start_time_ts = time.time()
271     end_time_ts = start_time_ts
272     logger.info("vPing Start Time:'%s'" % (
273         datetime.datetime.fromtimestamp(start_time_ts).strftime(
274             '%Y-%m-%d %H:%M:%S')))
275
276     logger.info("Creating instance '%s'..." % NAME_VM_1)
277     logger.debug(
278         "Configuration:\n name=%s \n flavor=%s \n image=%s \n "
279         "network=%s \n" % (NAME_VM_1, flavor, image_id, network_id))
280     vm1 = nova_client.servers.create(
281         name=NAME_VM_1,
282         flavor=flavor,
283         image=image_id,
284         nics=[{"net-id": network_id}]
285     )
286
287     # wait until VM status is active
288     if not waitVmActive(nova_client, vm1):
289         logger.error("Instance '%s' cannot be booted. Status is '%s'" % (
290             NAME_VM_1, openstack_utils.get_instance_status(nova_client, vm1)))
291         return (EXIT_CODE)
292     else:
293         logger.info("Instance '%s' is ACTIVE." % NAME_VM_1)
294
295     # Retrieve IP of first VM
296     test_ip = vm1.networks.get(PRIVATE_NET_NAME)[0]
297     logger.debug("Instance '%s' got private ip '%s'." % (NAME_VM_1, test_ip))
298
299     logger.info("Adding '%s' to security group '%s'..."
300                 % (NAME_VM_1, SECGROUP_NAME))
301     openstack_utils.add_secgroup_to_instance(nova_client, vm1.id, sg_id)
302
303     # boot VM 2
304     logger.info("Creating instance '%s'..." % NAME_VM_2)
305     logger.debug(
306         "Configuration:\n name=%s \n flavor=%s \n image=%s \n "
307         "network=%s \n" % (NAME_VM_2, flavor, image_id, network_id))
308     vm2 = nova_client.servers.create(
309         name=NAME_VM_2,
310         flavor=flavor,
311         image=image_id,
312         nics=[{"net-id": network_id}]
313     )
314
315     if not waitVmActive(nova_client, vm2):
316         logger.error("Instance '%s' cannot be booted. Status is '%s'" % (
317             NAME_VM_2, openstack_utils.get_instance_status(nova_client, vm2)))
318         return (EXIT_CODE)
319     else:
320         logger.info("Instance '%s' is ACTIVE." % NAME_VM_2)
321
322     logger.info("Adding '%s' to security group '%s'..." % (NAME_VM_2,
323                                                            SECGROUP_NAME))
324     openstack_utils.add_secgroup_to_instance(nova_client, vm2.id, sg_id)
325
326     logger.info("Creating floating IP for VM '%s'..." % NAME_VM_2)
327     floatip_dic = openstack_utils.create_floating_ip(neutron_client)
328     floatip = floatip_dic['fip_addr']
329     # floatip_id = floatip_dic['fip_id']
330
331     if floatip is None:
332         logger.error("Cannot create floating IP.")
333         return (EXIT_CODE)
334     logger.info("Floating IP created: '%s'" % floatip)
335
336     logger.info("Associating floating ip: '%s' to VM '%s' "
337                 % (floatip, NAME_VM_2))
338     if not openstack_utils.add_floating_ip(nova_client, vm2.id, floatip):
339         logger.error("Cannot associate floating IP to VM.")
340         return (EXIT_CODE)
341
342     logger.info("Trying to establish SSH connection to %s..." % floatip)
343     username = 'cirros'
344     password = 'cubswin:)'
345     ssh = paramiko.SSHClient()
346     ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
347
348     timeout = 50
349     nolease = False
350     got_ip = False
351     discover_count = 0
352     cidr_first_octet = PRIVATE_SUBNET_CIDR.split('.')[0]
353     while timeout > 0:
354         try:
355             ssh.connect(floatip, username=username,
356                         password=password, timeout=2)
357             logger.debug("SSH connection established to %s." % floatip)
358             break
359         except:
360             logger.debug("Waiting for %s..." % floatip)
361             time.sleep(6)
362             timeout -= 1
363
364         console_log = vm2.get_console_output()
365
366         # print each "Sending discover" captured on the console log
367         if (len(re.findall("Sending discover", console_log)) >
368                 discover_count and not got_ip):
369             discover_count += 1
370             logger.debug("Console-log '%s': Sending discover..."
371                          % NAME_VM_2)
372
373         # check if eth0 got an ip,the line looks like this:
374         # "inet addr:192.168."....
375         # if the dhcp agent fails to assing ip, this line will not appear
376         if "inet addr:" + cidr_first_octet in console_log and not got_ip:
377             got_ip = True
378             logger.debug("The instance '%s' succeeded to get the IP "
379                          "from the dhcp agent.")
380
381         # if dhcp doesnt work,it shows "No lease, failing".The test will fail
382         if "No lease, failing" in console_log and not nolease and not got_ip:
383                 nolease = True
384                 logger.debug("Console-log '%s': No lease, failing..."
385                              % NAME_VM_2)
386                 logger.info("The instance failed to get an IP from the "
387                             "DHCP agent. The test will probably timeout...")
388
389     if timeout == 0:  # 300 sec timeout (5 min)
390         logger.error("Cannot establish connection to IP '%s'. Aborting"
391                      % floatip)
392         return (EXIT_CODE)
393
394     scp = SCPClient(ssh.get_transport())
395
396     ping_script = REPO_PATH + "testcases/OpenStack/vPing/ping.sh"
397     try:
398         scp.put(ping_script, "~/")
399     except:
400         logger.error("Cannot SCP the file '%s' to VM '%s'"
401                      % (ping_script, floatip))
402         return (EXIT_CODE)
403
404     cmd = 'chmod 755 ~/ping.sh'
405     (stdin, stdout, stderr) = ssh.exec_command(cmd)
406     for line in stdout.readlines():
407         print line
408
409     logger.info("Waiting for ping...")
410     sec = 0
411     duration = 0
412
413     cmd = '~/ping.sh ' + test_ip
414     flag = False
415     while True:
416         time.sleep(1)
417         (stdin, stdout, stderr) = ssh.exec_command(cmd)
418         output = stdout.readlines()
419
420         for line in output:
421             if "vPing OK" in line:
422                 logger.info("vPing detected!")
423
424                 # we consider start time at VM1 booting
425                 end_time_ts = time.time()
426                 duration = round(end_time_ts - start_time_ts, 1)
427                 logger.info("vPing duration:'%s' s." % duration)
428                 EXIT_CODE = 0
429                 flag = True
430                 break
431
432             elif sec == PING_TIMEOUT:
433                 logger.info("Timeout reached.")
434                 flag = True
435                 break
436         if flag:
437             break
438         logger.debug("Pinging %s. Waiting for response..." % test_ip)
439         sec += 1
440
441     test_status = "NOK"
442     if EXIT_CODE == 0:
443         logger.info("vPing OK")
444         test_status = "OK"
445     else:
446         duration = 0
447         logger.error("vPing FAILED")
448
449     if args.report:
450         push_results(start_time_ts, duration, test_status)
451
452     exit(EXIT_CODE)
453
454 if __name__ == '__main__':
455     main()