Remove cleanup from vPing scripts, and use the existing cleanup script
[functest.git] / testcases / vPing / CI / libraries / vPing_userdata.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
17 import os
18 import time
19 import argparse
20 import pprint
21 import sys
22 import logging
23 import yaml
24 import datetime
25 from novaclient import client as novaclient
26 from neutronclient.v2_0 import client as neutronclient
27 from keystoneclient.v2_0 import client as keystoneclient
28 from glanceclient import client as glanceclient
29
30 pp = pprint.PrettyPrinter(indent=4)
31
32 parser = argparse.ArgumentParser()
33 image_exists = False
34
35 parser.add_argument("-d", "--debug", help="Debug mode", action="store_true")
36 parser.add_argument("-r", "--report",
37                     help="Create json result file",
38                     action="store_true")
39
40 args = parser.parse_args()
41
42 """ logging configuration """
43
44 logger = logging.getLogger('vPing_userdata')
45 logger.setLevel(logging.DEBUG)
46
47 ch = logging.StreamHandler()
48
49 if args.debug:
50     ch.setLevel(logging.DEBUG)
51 else:
52     ch.setLevel(logging.INFO)
53
54 formatter = logging.Formatter('%(asctime)s - %(name)s'
55                               '- %(levelname)s - %(message)s')
56
57 ch.setFormatter(formatter)
58 logger.addHandler(ch)
59
60 REPO_PATH = os.environ['repos_dir'] + '/functest/'
61 if not os.path.exists(REPO_PATH):
62     logger.error("Functest repository directory not found '%s'" % REPO_PATH)
63     exit(-1)
64 sys.path.append(REPO_PATH + "testcases/")
65 import functest_utils
66 import openstack_utils
67
68 with open("/home/opnfv/functest/conf/config_functest.yaml") as f:
69     functest_yaml = yaml.safe_load(f)
70 f.close()
71
72 HOME = os.environ['HOME'] + "/"
73 # vPing parameters
74 VM_BOOT_TIMEOUT = 180
75 VM_DELETE_TIMEOUT = 100
76 PING_TIMEOUT = functest_yaml.get("vping").get("ping_timeout")
77 TEST_DB = functest_yaml.get("results").get("test_db_url")
78 NAME_VM_1 = functest_yaml.get("vping").get("vm_name_1")
79 NAME_VM_2 = functest_yaml.get("vping").get("vm_name_2")
80 GLANCE_IMAGE_NAME = functest_yaml.get("vping").get("image_name")
81 GLANCE_IMAGE_FILENAME = functest_yaml.get("general"). \
82     get("openstack").get("image_file_name")
83 GLANCE_IMAGE_FORMAT = functest_yaml.get("general"). \
84     get("openstack").get("image_disk_format")
85 GLANCE_IMAGE_PATH = functest_yaml.get("general"). \
86     get("directories").get("dir_functest_data") + "/" + GLANCE_IMAGE_FILENAME
87
88
89 FLAVOR = functest_yaml.get("vping").get("vm_flavor")
90
91 # NEUTRON Private Network parameters
92
93 NEUTRON_PRIVATE_NET_NAME = functest_yaml.get("vping"). \
94     get("vping_private_net_name")
95 NEUTRON_PRIVATE_SUBNET_NAME = functest_yaml.get("vping"). \
96     get("vping_private_subnet_name")
97 NEUTRON_PRIVATE_SUBNET_CIDR = functest_yaml.get("vping"). \
98     get("vping_private_subnet_cidr")
99 NEUTRON_ROUTER_NAME = functest_yaml.get("vping"). \
100     get("vping_router_name")
101
102 SECGROUP_NAME = functest_yaml.get("vping"). \
103     get("vping_sg_name")
104 SECGROUP_DESCR = functest_yaml.get("vping"). \
105     get("vping_sg_descr")
106
107
108 def pMsg(value):
109
110     """pretty printing"""
111     pp.pprint(value)
112
113
114 def waitVmActive(nova, vm):
115
116     # sleep and wait for VM status change
117     sleep_time = 3
118     count = VM_BOOT_TIMEOUT / sleep_time
119     while True:
120         status = openstack_utils.get_instance_status(nova, vm)
121         logger.debug("Status: %s" % status)
122         if status == "ACTIVE":
123             return True
124         if status == "ERROR" or status == "error":
125             return False
126         if count == 0:
127             logger.debug("Booting a VM timed out...")
128             return False
129         count -= 1
130         time.sleep(sleep_time)
131     return False
132
133
134 def waitVmDeleted(nova, vm):
135
136     # sleep and wait for VM status change
137     sleep_time = 3
138     count = VM_DELETE_TIMEOUT / sleep_time
139     while True:
140         status = openstack_utils.get_instance_status(nova, vm)
141         if not status:
142             return True
143         elif count == 0:
144             logger.debug("Timeout")
145             return False
146         else:
147             # return False
148             count -= 1
149         time.sleep(sleep_time)
150     return False
151
152
153 def create_private_neutron_net(neutron):
154
155     # Check if the network already exists
156     network_id = openstack_utils.get_network_id(neutron,
157                                                 NEUTRON_PRIVATE_NET_NAME)
158     subnet_id = openstack_utils.get_subnet_id(neutron,
159                                               NEUTRON_PRIVATE_SUBNET_NAME)
160     router_id = openstack_utils.get_router_id(neutron,
161                                               NEUTRON_ROUTER_NAME)
162
163     if network_id != '' and subnet_id != '' and router_id != '':
164         logger.info("Using existing network '%s'.." % NEUTRON_PRIVATE_NET_NAME)
165     else:
166         neutron.format = 'json'
167         logger.info('Creating neutron network %s..' % NEUTRON_PRIVATE_NET_NAME)
168         network_id = openstack_utils. \
169             create_neutron_net(neutron, NEUTRON_PRIVATE_NET_NAME)
170
171         if not network_id:
172             return False
173         logger.debug("Network '%s' created successfully" % network_id)
174         logger.debug('Creating Subnet....')
175         subnet_id = openstack_utils. \
176             create_neutron_subnet(neutron,
177                                   NEUTRON_PRIVATE_SUBNET_NAME,
178                                   NEUTRON_PRIVATE_SUBNET_CIDR,
179                                   network_id)
180         if not subnet_id:
181             return False
182         logger.debug("Subnet '%s' created successfully" % subnet_id)
183         logger.debug('Creating Router...')
184         router_id = openstack_utils. \
185             create_neutron_router(neutron, NEUTRON_ROUTER_NAME)
186
187         if not router_id:
188             return False
189
190         logger.debug("Router '%s' created successfully" % router_id)
191         logger.debug('Adding router to subnet...')
192
193         if not openstack_utils.add_interface_router(neutron, router_id,
194                                                     subnet_id):
195             return False
196         logger.debug("Interface added successfully.")
197
198         logger.debug('Adding gateway to router...')
199         if not openstack_utils.add_gateway_router(neutron, router_id):
200             return False
201         logger.debug("Gateway added successfully.")
202
203     network_dic = {'net_id': network_id,
204                    'subnet_id': subnet_id,
205                    'router_id': router_id}
206     return network_dic
207
208
209 def create_security_group(neutron_client):
210     sg_id = openstack_utils.get_security_group_id(neutron_client,
211                                                   SECGROUP_NAME)
212     if sg_id != '':
213         logger.info("Using existing security group '%s'..." % SECGROUP_NAME)
214     else:
215         logger.info("Creating security group  '%s'..." % SECGROUP_NAME)
216         SECGROUP = openstack_utils.create_security_group(neutron_client,
217                                                          SECGROUP_NAME,
218                                                          SECGROUP_DESCR)
219         if not SECGROUP:
220             logger.error("Failed to create the security group...")
221             return False
222
223         sg_id = SECGROUP['id']
224
225         logger.debug("Security group '%s' with ID=%s created successfully."
226                      % (SECGROUP['name'], sg_id))
227
228         logger.debug("Adding ICMP rules in security group '%s'..."
229                      % SECGROUP_NAME)
230         if not openstack_utils.create_secgroup_rule(neutron_client, sg_id,
231                                                     'ingress', 'icmp'):
232             logger.error("Failed to create the security group rule...")
233             return False
234
235         logger.debug("Adding SSH rules in security group '%s'..."
236                      % SECGROUP_NAME)
237         if not openstack_utils.create_secgroup_rule(neutron_client, sg_id,
238                                                     'ingress', 'tcp',
239                                                     '22', '22'):
240             logger.error("Failed to create the security group rule...")
241             return False
242
243         if not openstack_utils.create_secgroup_rule(neutron_client, sg_id,
244                                                     'egress', 'tcp',
245                                                     '22', '22'):
246             logger.error("Failed to create the security group rule...")
247             return False
248     return sg_id
249
250
251 def push_results(start_time_ts, duration, test_status):
252     try:
253         logger.debug("Pushing result into DB...")
254         scenario = functest_utils.get_scenario(logger)
255         version = scenario
256         criteria = "failed"
257         if test_status == "OK":
258             criteria = "passed"
259         pod_name = functest_utils.get_pod_name(logger)
260         build_tag = functest_utils.get_build_tag(logger)
261         functest_utils.push_results_to_db(TEST_DB,
262                                           "functest",
263                                           "vPing_userdata",
264                                           logger, pod_name, version, scenario,
265                                           criteria, build_tag,
266                                           payload={'timestart': start_time_ts,
267                                                    'duration': duration,
268                                                    'status': test_status})
269     except:
270         logger.error("Error pushing results into Database '%s'"
271                      % sys.exc_info()[0])
272
273
274 def main():
275
276     creds_nova = openstack_utils.get_credentials("nova")
277     nova_client = novaclient.Client('2', **creds_nova)
278     creds_neutron = openstack_utils.get_credentials("neutron")
279     neutron_client = neutronclient.Client(**creds_neutron)
280     creds_keystone = openstack_utils.get_credentials("keystone")
281     keystone_client = keystoneclient.Client(**creds_keystone)
282     glance_endpoint = keystone_client.\
283         service_catalog.url_for(service_type='image',
284                                 endpoint_type='publicURL')
285     glance_client = glanceclient.Client(1, glance_endpoint,
286                                         token=keystone_client.auth_token)
287     EXIT_CODE = -1
288
289     image_id = None
290     flavor = None
291
292     # Check if the given image exists
293     image_id = openstack_utils.get_image_id(glance_client, GLANCE_IMAGE_NAME)
294     if image_id != '':
295         logger.info("Using existing image '%s'..." % GLANCE_IMAGE_NAME)
296         global image_exists
297         image_exists = True
298     else:
299         logger.info("Creating image '%s' from '%s'..." % (GLANCE_IMAGE_NAME,
300                                                           GLANCE_IMAGE_PATH))
301         image_id = openstack_utils.create_glance_image(glance_client,
302                                                        GLANCE_IMAGE_NAME,
303                                                        GLANCE_IMAGE_PATH)
304         if not image_id:
305             logger.error("Failed to create a Glance image...")
306             return(EXIT_CODE)
307         logger.debug("Image '%s' with ID=%s created successfully."
308                      % (GLANCE_IMAGE_NAME, image_id))
309
310     network_dic = create_private_neutron_net(neutron_client)
311     if not network_dic:
312         logger.error(
313             "There has been a problem when creating the neutron network")
314         return(EXIT_CODE)
315     network_id = network_dic["net_id"]
316
317     create_security_group(neutron_client)
318
319     # Check if the given flavor exists
320     try:
321         flavor = nova_client.flavors.find(name=FLAVOR)
322         logger.info("Flavor found '%s'" % FLAVOR)
323     except:
324         logger.error("Flavor '%s' not found." % FLAVOR)
325         logger.info("Available flavors are: ")
326         pMsg(nova_client.flavor.list())
327         exit(-1)
328
329     # Deleting instances if they exist
330     servers = nova_client.servers.list()
331     for server in servers:
332         if server.name == NAME_VM_1 or server.name == NAME_VM_2:
333             logger.info("Instance %s found. Deleting..." % server.name)
334             server.delete()
335
336     # boot VM 1
337     # basic boot
338     # tune (e.g. flavor, images, network) to your specific
339     # openstack configuration here
340     # we consider start time at VM1 booting
341     start_time_ts = time.time()
342     end_time_ts = start_time_ts
343     logger.info("vPing Start Time:'%s'" % (
344         datetime.datetime.fromtimestamp(start_time_ts).strftime(
345             '%Y-%m-%d %H:%M:%S')))
346
347     # create VM
348     logger.info("Creating instance '%s'..." % NAME_VM_1)
349     logger.debug(
350         "Configuration:\n name=%s \n flavor=%s \n image=%s \n "
351         "network=%s \n" % (NAME_VM_1, flavor, image_id, network_id))
352     vm1 = nova_client.servers.create(
353         name=NAME_VM_1,
354         flavor=flavor,
355         image=image_id,
356         config_drive=True,
357         nics=[{"net-id": network_id}]
358     )
359
360     # wait until VM status is active
361     if not waitVmActive(nova_client, vm1):
362
363         logger.error("Instance '%s' cannot be booted. Status is '%s'" % (
364             NAME_VM_1, openstack_utils.get_instance_status(nova_client, vm1)))
365         return (EXIT_CODE)
366     else:
367         logger.info("Instance '%s' is ACTIVE." % NAME_VM_1)
368
369     # Retrieve IP of first VM
370     test_ip = vm1.networks.get(NEUTRON_PRIVATE_NET_NAME)[0]
371     logger.debug("Instance '%s' got %s" % (NAME_VM_1, test_ip))
372
373     # boot VM 2
374     # we will boot then execute a ping script with cloud-init
375     # the long chain corresponds to the ping procedure converted with base 64
376     # tune (e.g. flavor, images, network) to your specific openstack
377     #  configuration here
378     u = "#!/bin/sh\n\nwhile true; do\n ping -c 1 %s 2>&1 >/dev/null\n " \
379         "RES=$?\n if [ \"Z$RES\" = \"Z0\" ] ; then\n  echo 'vPing OK'\n " \
380         "break\n else\n  echo 'vPing KO'\n fi\n sleep 1\ndone\n" % test_ip
381
382     # create VM
383     logger.info("Creating instance '%s'..." % NAME_VM_2)
384     logger.debug(
385         "Configuration:\n name=%s \n flavor=%s \n image=%s \n network=%s "
386         "\n userdata= \n%s" % (
387             NAME_VM_2, flavor, image_id, network_id, u))
388     vm2 = nova_client.servers.create(
389         name=NAME_VM_2,
390         flavor=flavor,
391         image=image_id,
392         nics=[{"net-id": network_id}],
393         config_drive=True,
394         userdata=u
395     )
396
397     if not waitVmActive(nova_client, vm2):
398         logger.error("Instance '%s' cannot be booted. Status is '%s'" % (
399             NAME_VM_2, openstack_utils.get_instance_status(nova_client, vm2)))
400         return (EXIT_CODE)
401     else:
402         logger.info("Instance '%s' is ACTIVE." % NAME_VM_2)
403
404     logger.info("Waiting for ping...")
405     sec = 0
406     metadata_tries = 0
407     console_log = vm2.get_console_output()
408     duration = 0
409
410     while True:
411         time.sleep(1)
412         console_log = vm2.get_console_output()
413         # print "--"+console_log
414         # report if the test is failed
415         if "vPing OK" in console_log:
416             logger.info("vPing detected!")
417
418             # we consider start time at VM1 booting
419             end_time_ts = time.time()
420             duration = round(end_time_ts - start_time_ts, 1)
421             logger.info("vPing duration:'%s'" % duration)
422             EXIT_CODE = 0
423             break
424         elif "failed to read iid from metadata" in console_log or \
425                 metadata_tries > 5:
426             EXIT_CODE = -2
427             break
428         elif sec == PING_TIMEOUT:
429             logger.info("Timeout reached.")
430             break
431         elif sec % 10 == 0:
432             if "request failed" in console_log:
433                 logger.debug("It seems userdata is not supported in "
434                              "nova boot. Waiting a bit...")
435                 metadata_tries += 1
436             else:
437                 logger.debug("Pinging %s. Waiting for response..." % test_ip)
438         sec += 1
439
440     test_status = "NOK"
441     if EXIT_CODE == 0:
442         logger.info("vPing OK")
443         test_status = "OK"
444     elif EXIT_CODE == -2:
445         duration = 0
446         logger.info("Userdata is not supported in nova boot. Aborting test...")
447     else:
448         duration = 0
449         logger.error("vPing FAILED")
450
451     if args.report:
452         push_results(start_time_ts, duration, test_status)
453
454     exit(EXIT_CODE)
455
456 if __name__ == '__main__':
457     main()