Adapt functest testcase to APi refactoring
[functest.git] / testcases / OpenStack / vPing / 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 # 0.3: adapt push 2 DB after Test API refacroting
15 #
16 #
17
18 import argparse
19 import datetime
20 import os
21 import pprint
22 import sys
23 import time
24 import yaml
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_userdata").getLogger()
49
50 REPO_PATH = os.environ['repos_dir'] + '/functest/'
51 if not os.path.exists(REPO_PATH):
52     logger.error("Functest repository directory not found '%s'" % REPO_PATH)
53     exit(-1)
54
55 with open(os.environ["CONFIG_FUNCTEST_YAML"]) as f:
56     functest_yaml = yaml.safe_load(f)
57 f.close()
58
59 HOME = os.environ['HOME'] + "/"
60 # vPing parameters
61 VM_BOOT_TIMEOUT = 180
62 VM_DELETE_TIMEOUT = 100
63 PING_TIMEOUT = functest_yaml.get("vping").get("ping_timeout")
64 TEST_DB = functest_yaml.get("results").get("test_db_url")
65 NAME_VM_1 = functest_yaml.get("vping").get("vm_name_1")
66 NAME_VM_2 = functest_yaml.get("vping").get("vm_name_2")
67 GLANCE_IMAGE_NAME = functest_yaml.get("vping").get("image_name")
68 GLANCE_IMAGE_FILENAME = functest_yaml.get("general").get(
69     "openstack").get("image_file_name")
70 GLANCE_IMAGE_FORMAT = functest_yaml.get("general").get(
71     "openstack").get("image_disk_format")
72 GLANCE_IMAGE_PATH = functest_yaml.get("general").get("directories").get(
73     "dir_functest_data") + "/" + GLANCE_IMAGE_FILENAME
74
75
76 FLAVOR = functest_yaml.get("vping").get("vm_flavor")
77
78 # NEUTRON Private Network parameters
79
80 PRIVATE_NET_NAME = functest_yaml.get("vping").get(
81     "vping_private_net_name")
82 PRIVATE_SUBNET_NAME = functest_yaml.get("vping").get(
83     "vping_private_subnet_name")
84 PRIVATE_SUBNET_CIDR = functest_yaml.get("vping").get(
85     "vping_private_subnet_cidr")
86 ROUTER_NAME = functest_yaml.get("vping").get("vping_router_name")
87
88 SECGROUP_NAME = functest_yaml.get("vping").get("vping_sg_name")
89 SECGROUP_DESCR = functest_yaml.get("vping").get("vping_sg_descr")
90
91
92 def pMsg(value):
93
94     """pretty printing"""
95     pp.pprint(value)
96
97
98 def waitVmActive(nova, vm):
99
100     # sleep and wait for VM status change
101     sleep_time = 3
102     count = VM_BOOT_TIMEOUT / sleep_time
103     while True:
104         status = openstack_utils.get_instance_status(nova, vm)
105         logger.debug("Status: %s" % status)
106         if status == "ACTIVE":
107             return True
108         if status == "ERROR" or status == "error":
109             return False
110         if count == 0:
111             logger.debug("Booting a VM timed out...")
112             return False
113         count -= 1
114         time.sleep(sleep_time)
115     return False
116
117
118 def waitVmDeleted(nova, vm):
119
120     # sleep and wait for VM status change
121     sleep_time = 3
122     count = VM_DELETE_TIMEOUT / sleep_time
123     while True:
124         status = openstack_utils.get_instance_status(nova, vm)
125         if not status:
126             return True
127         elif count == 0:
128             logger.debug("Timeout")
129             return False
130         else:
131             # return False
132             count -= 1
133         time.sleep(sleep_time)
134     return False
135
136
137 def create_security_group(neutron_client):
138     sg_id = openstack_utils.get_security_group_id(neutron_client,
139                                                   SECGROUP_NAME)
140     if sg_id != '':
141         logger.info("Using existing security group '%s'..." % SECGROUP_NAME)
142     else:
143         logger.info("Creating security group  '%s'..." % SECGROUP_NAME)
144         SECGROUP = openstack_utils.create_security_group(neutron_client,
145                                                          SECGROUP_NAME,
146                                                          SECGROUP_DESCR)
147         if not SECGROUP:
148             logger.error("Failed to create the security group...")
149             return False
150
151         sg_id = SECGROUP['id']
152
153         logger.debug("Security group '%s' with ID=%s created successfully."
154                      % (SECGROUP['name'], sg_id))
155
156         logger.debug("Adding ICMP rules in security group '%s'..."
157                      % SECGROUP_NAME)
158         if not openstack_utils.create_secgroup_rule(neutron_client, sg_id,
159                                                     'ingress', 'icmp'):
160             logger.error("Failed to create the security group rule...")
161             return False
162
163         logger.debug("Adding SSH rules in security group '%s'..."
164                      % SECGROUP_NAME)
165         if not openstack_utils.create_secgroup_rule(neutron_client, sg_id,
166                                                     'ingress', 'tcp',
167                                                     '22', '22'):
168             logger.error("Failed to create the security group rule...")
169             return False
170
171         if not openstack_utils.create_secgroup_rule(neutron_client, sg_id,
172                                                     'egress', 'tcp',
173                                                     '22', '22'):
174             logger.error("Failed to create the security group rule...")
175             return False
176     return sg_id
177
178
179 def main():
180
181     creds_nova = openstack_utils.get_credentials("nova")
182     nova_client = novaclient.Client('2', **creds_nova)
183     creds_neutron = openstack_utils.get_credentials("neutron")
184     neutron_client = neutronclient.Client(**creds_neutron)
185     creds_keystone = openstack_utils.get_credentials("keystone")
186     keystone_client = keystoneclient.Client(**creds_keystone)
187     glance_endpoint = keystone_client.service_catalog.url_for(
188         service_type='image', endpoint_type='publicURL')
189     glance_client = glanceclient.Client(1, glance_endpoint,
190                                         token=keystone_client.auth_token)
191     EXIT_CODE = -1
192
193     image_id = None
194     flavor = None
195
196     # Check if the given image exists
197     image_id = openstack_utils.get_image_id(glance_client, GLANCE_IMAGE_NAME)
198     if image_id != '':
199         logger.info("Using existing image '%s'..." % GLANCE_IMAGE_NAME)
200         global image_exists
201         image_exists = True
202     else:
203         logger.info("Creating image '%s' from '%s'..." % (GLANCE_IMAGE_NAME,
204                                                           GLANCE_IMAGE_PATH))
205         image_id = openstack_utils.create_glance_image(glance_client,
206                                                        GLANCE_IMAGE_NAME,
207                                                        GLANCE_IMAGE_PATH)
208         if not image_id:
209             logger.error("Failed to create a Glance image...")
210             return(EXIT_CODE)
211         logger.debug("Image '%s' with ID=%s created successfully."
212                      % (GLANCE_IMAGE_NAME, image_id))
213
214     network_dic = openstack_utils.create_network_full(logger,
215                                                       neutron_client,
216                                                       PRIVATE_NET_NAME,
217                                                       PRIVATE_SUBNET_NAME,
218                                                       ROUTER_NAME,
219                                                       PRIVATE_SUBNET_CIDR)
220     if not network_dic:
221         logger.error(
222             "There has been a problem when creating the neutron network")
223         return(EXIT_CODE)
224     network_id = network_dic["net_id"]
225
226     create_security_group(neutron_client)
227
228     # Check if the given flavor exists
229     try:
230         flavor = nova_client.flavors.find(name=FLAVOR)
231         logger.info("Flavor found '%s'" % FLAVOR)
232     except:
233         logger.error("Flavor '%s' not found." % FLAVOR)
234         logger.info("Available flavors are: ")
235         pMsg(nova_client.flavor.list())
236         exit(-1)
237
238     # Deleting instances if they exist
239     servers = nova_client.servers.list()
240     for server in servers:
241         if server.name == NAME_VM_1 or server.name == NAME_VM_2:
242             logger.info("Instance %s found. Deleting..." % server.name)
243             server.delete()
244
245     # boot VM 1
246     # basic boot
247     # tune (e.g. flavor, images, network) to your specific
248     # openstack configuration here
249     # we consider start time at VM1 booting
250     start_time = time.time()
251     stop_time = start_time
252     logger.info("vPing Start Time:'%s'" % (
253         datetime.datetime.fromtimestamp(start_time).strftime(
254             '%Y-%m-%d %H:%M:%S')))
255
256     # create VM
257     logger.info("Creating instance '%s'..." % NAME_VM_1)
258     logger.debug(
259         "Configuration:\n name=%s \n flavor=%s \n image=%s \n "
260         "network=%s \n" % (NAME_VM_1, flavor, image_id, network_id))
261     vm1 = nova_client.servers.create(
262         name=NAME_VM_1,
263         flavor=flavor,
264         image=image_id,
265         config_drive=True,
266         nics=[{"net-id": network_id}]
267     )
268
269     # wait until VM status is active
270     if not waitVmActive(nova_client, vm1):
271
272         logger.error("Instance '%s' cannot be booted. Status is '%s'" % (
273             NAME_VM_1, openstack_utils.get_instance_status(nova_client, vm1)))
274         return (EXIT_CODE)
275     else:
276         logger.info("Instance '%s' is ACTIVE." % NAME_VM_1)
277
278     # Retrieve IP of first VM
279     test_ip = vm1.networks.get(PRIVATE_NET_NAME)[0]
280     logger.debug("Instance '%s' got %s" % (NAME_VM_1, test_ip))
281
282     # boot VM 2
283     # we will boot then execute a ping script with cloud-init
284     # the long chain corresponds to the ping procedure converted with base 64
285     # tune (e.g. flavor, images, network) to your specific openstack
286     #  configuration here
287     u = ("#!/bin/sh\n\nwhile true; do\n ping -c 1 %s 2>&1 >/dev/null\n "
288          "RES=$?\n if [ \"Z$RES\" = \"Z0\" ] ; then\n  echo 'vPing OK'\n "
289          "break\n else\n  echo 'vPing KO'\n fi\n sleep 1\ndone\n" % test_ip)
290
291     # create VM
292     logger.info("Creating instance '%s'..." % NAME_VM_2)
293     logger.debug(
294         "Configuration:\n name=%s \n flavor=%s \n image=%s \n network=%s "
295         "\n userdata= \n%s" % (
296             NAME_VM_2, flavor, image_id, network_id, u))
297     vm2 = nova_client.servers.create(
298         name=NAME_VM_2,
299         flavor=flavor,
300         image=image_id,
301         nics=[{"net-id": network_id}],
302         config_drive=True,
303         userdata=u
304     )
305
306     if not waitVmActive(nova_client, vm2):
307         logger.error("Instance '%s' cannot be booted. Status is '%s'" % (
308             NAME_VM_2, openstack_utils.get_instance_status(nova_client, vm2)))
309         return (EXIT_CODE)
310     else:
311         logger.info("Instance '%s' is ACTIVE." % NAME_VM_2)
312
313     logger.info("Waiting for ping...")
314     sec = 0
315     metadata_tries = 0
316     console_log = vm2.get_console_output()
317     duration = 0
318     stop_time = time.time()
319
320     while True:
321         time.sleep(1)
322         console_log = vm2.get_console_output()
323         # print "--"+console_log
324         # report if the test is failed
325         if "vPing OK" in console_log:
326             logger.info("vPing detected!")
327
328             # we consider start time at VM1 booting
329             stop_time = time.time()
330             duration = round(stop_time - start_time, 1)
331             logger.info("vPing duration:'%s'" % duration)
332             EXIT_CODE = 0
333             break
334         elif ("failed to read iid from metadata" in console_log or
335               metadata_tries > 5):
336             EXIT_CODE = -2
337             break
338         elif sec == PING_TIMEOUT:
339             logger.info("Timeout reached.")
340             break
341         elif sec % 10 == 0:
342             if "request failed" in console_log:
343                 logger.debug("It seems userdata is not supported in "
344                              "nova boot. Waiting a bit...")
345                 metadata_tries += 1
346             else:
347                 logger.debug("Pinging %s. Waiting for response..." % test_ip)
348         sec += 1
349
350     test_status = "NOK"
351     if EXIT_CODE == 0:
352         logger.info("vPing OK")
353         test_status = "OK"
354     elif EXIT_CODE == -2:
355         duration = 0
356         logger.info("Userdata is not supported in nova boot. Aborting test...")
357     else:
358         duration = 0
359         logger.error("vPing FAILED")
360
361     if args.report:
362         try:
363             logger.debug("Pushing vPing userdata results into DB...")
364             functest_utils.push_results_to_db("functest",
365                                               "vPing_userdata",
366                                               logger,
367                                               start_time,
368                                               stop_time,
369                                               test_status,
370                                               details={'timestart': start_time,
371                                                        'duration': duration,
372                                                        'status': test_status})
373         except:
374             logger.error("Error pushing results into Database '%s'"
375                          % sys.exc_info()[0])
376
377     exit(EXIT_CODE)
378
379 if __name__ == '__main__':
380     main()