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