config_functest.py download functest.yaml if not present
[functest.git] / testcases / config_functest.py
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2015 Ericsson
4 # jose.lausuch@ericsson.com
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10
11 import re, json, os, urllib2, argparse, logging, shutil, subprocess, yaml
12 from git import Repo
13
14 actions = ['start', 'check', 'clean']
15
16
17
18 parser = argparse.ArgumentParser()
19 parser.add_argument("action", help="Possible actions are: '{d[0]}|{d[1]}|{d[2]}' ".format(d=actions))
20 parser.add_argument("-d", "--debug", help="Debug mode",  action="store_true")
21 args = parser.parse_args()
22
23
24 """ logging configuration """
25 logger = logging.getLogger('config_functest')
26 logger.setLevel(logging.DEBUG)
27
28 ch = logging.StreamHandler()
29 if args.debug:
30     ch.setLevel(logging.DEBUG)
31 else:
32     ch.setLevel(logging.INFO)
33
34 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
35 ch.setFormatter(formatter)
36 logger.addHandler(ch)
37
38
39
40 yaml_url = 'https://git.opnfv.org/cgit/functest/plain/testcases/functest.yaml'
41 name = yaml_url.rsplit('/')[-1]
42 dest = "./" + name
43 if not os.path.exists(dest):
44     logger.info("Downloading functest.yaml...")
45     try:
46         response = urllib2.urlopen(yaml_url)
47     except (urllib2.HTTPError, urllib2.URLError):
48         logger.error("Error in fetching %s" %yaml_url)
49         exit(-1)
50     with open(dest, 'wb') as f:
51         f.write(response.read())
52     logger.info("functest.yaml stored in %s" % dest)
53 else:
54     logger.info("functest.yaml found in %s" % dest)
55     
56 with open('functest.yaml') as f:
57     functest_yaml = yaml.safe_load(f)
58 f.close()
59
60
61 """ global variables """
62 HOME = os.environ['HOME']+"/"
63 FUNCTEST_BASE_DIR = HOME + functest_yaml.get("general").get("directories").get("dir_functest")
64 RALLY_REPO_DIR = HOME + functest_yaml.get("general").get("directories").get("dir_rally_repo")
65 RALLY_TEST_DIR = HOME + functest_yaml.get("general").get("directories").get("dir_rally")
66 RALLY_INSTALLATION_DIR = HOME + functest_yaml.get("general").get("directories").get("dir_rally_inst")
67 BENCH_TESTS_DIR = HOME + functest_yaml.get("general").get("directories").get("dir_rally_scn")
68 VPING_DIR = HOME + functest_yaml.get("general").get("directories").get("dir_vping")
69 ODL_DIR = HOME + functest_yaml.get("general").get("directories").get("dir_odl")
70 IMAGE_URL = functest_yaml.get("general").get("openstack").get("image_url")
71 IMAGE_DISK_FORMAT = functest_yaml.get("general").get("openstack").get("image_disk_format")
72 IMAGE_NAME = functest_yaml.get("general").get("openstack").get("image_name")
73 IMAGE_FILE_NAME = IMAGE_URL.rsplit('/')[-1]
74 IMAGE_DOWNLOAD_PATH = FUNCTEST_BASE_DIR + IMAGE_FILE_NAME
75
76
77
78
79 def config_functest_start():
80     """
81     Start the functest environment installation
82     """
83     if config_functest_check():
84         logger.info("Functest environment already installed in %s. Nothing to do." %FUNCTEST_BASE_DIR)
85         exit(0)
86     elif not check_internet_connectivity():
87         logger.error("There is no Internet connectivity. Please check the network configuration.")
88         exit(-1)
89     elif not check_credentials():
90         logger.error("Please source the openrc credentials and run the script again.")
91         #TODO: source the credentials in this script
92         exit(-1)
93     else:
94         config_functest_clean()
95
96         logger.info("Starting installationg of functest environment in %s" %FUNCTEST_BASE_DIR)
97         os.makedirs(FUNCTEST_BASE_DIR)
98         if not os.path.exists(FUNCTEST_BASE_DIR):
99             logger.error("There has been a problem while creating the environment directory.")
100             exit(-1)
101
102         logger.info("Donwloading test scripts and scenarios...")
103         if not download_tests():
104             logger.error("There has been a problem while downloading the test scripts and scenarios.")
105             config_functest_clean()
106             exit(-1)
107
108         logger.info("Installing Rally...")
109         if not install_rally():
110             logger.error("There has been a problem while installing Rally.")
111             config_functest_clean()
112             exit(-1)
113
114         logger.info("Installing ODL environment...")
115         if not install_odl():
116             logger.error("There has been a problem while installing Robot.")
117             config_functest_clean()
118             exit(-1)
119
120         logger.info("Donwloading image...")
121         if not download_url_with_progress(IMAGE_URL, FUNCTEST_BASE_DIR):
122             logger.error("There has been a problem while downloading the image.")
123             config_functest_clean()
124             exit(-1)
125
126         logger.info("Creating Glance image: %s ..." %IMAGE_NAME)
127         if not create_glance_image(IMAGE_DOWNLOAD_PATH,IMAGE_NAME,IMAGE_DISK_FORMAT):
128             logger.error("There has been a problem while creating the Glance image.")
129             config_functest_clean()
130             exit(-1)
131
132         exit(0)
133
134
135
136 def config_functest_check():
137     """
138     Check if the functest environment is properly installed
139     """
140     logger.info("Checking current functest configuration...")
141
142     logger.debug("Checking directories...")
143     dirs = [FUNCTEST_BASE_DIR, RALLY_INSTALLATION_DIR, RALLY_REPO_DIR, RALLY_TEST_DIR, BENCH_TESTS_DIR, VPING_DIR, ODL_DIR]
144     for dir in dirs:
145         if not os.path.exists(dir):
146             logger.debug("The directory %s does not exist." %dir)
147             return False
148         logger.debug("   %s found" % dir)
149
150     logger.debug("...OK")
151     logger.debug("Checking Rally deployment...")
152     if not check_rally():
153         logger.debug("Rally deployment not found.")
154         return False
155     logger.debug("...OK")
156
157     logger.debug("Checking Image...")
158     if not os.path.isfile(IMAGE_DOWNLOAD_PATH):
159         return False
160     logger.debug("   Image file found in %s" %IMAGE_DOWNLOAD_PATH)
161
162     cmd="glance image-list | grep " + IMAGE_NAME
163     FNULL = open(os.devnull, 'w');
164     logger.debug('   Executing command : {}'.format(cmd))
165     p=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=FNULL);
166     #if the command does not exist or there is no glance image
167     line = p.stdout.readline()
168     if line == "":
169         logger.debug("   Glance image not found")
170         return False
171     logger.debug("   Glance image found")
172     logger.debug("...OK")
173
174     #TODO: check OLD environment setup
175
176     return True
177
178
179
180
181 def config_functest_clean():
182     """
183     Clean the existing functest environment
184     """
185     logger.info("Removing current functest environment...")
186     if os.path.exists(RALLY_INSTALLATION_DIR):
187         logger.debug("Removing rally installation directory %s" % RALLY_INSTALLATION_DIR)
188         shutil.rmtree(RALLY_INSTALLATION_DIR,ignore_errors=True)
189
190     if os.path.exists(FUNCTEST_BASE_DIR):
191         logger.debug("Removing functest directory %s" % FUNCTEST_BASE_DIR)
192         cmd = "sudo rm -rf " + FUNCTEST_BASE_DIR #need to be sudo, not possible with rmtree
193         execute_command(cmd)
194
195     logger.debug("Deleting glance images")
196     cmd = "glance image-list | grep "+IMAGE_NAME+" | cut -c3-38"
197     p = os.popen(cmd,"r")
198
199     #while image_id = p.readline()
200     for image_id in p.readlines():
201         cmd = "glance image-delete " + image_id
202         execute_command(cmd)
203
204     return True
205
206
207 def install_rally():
208     if check_rally():
209         logger.info("Rally is already installed.")
210     else:
211         logger.debug("Cloning repository...")
212         url = "https://git.openstack.org/openstack/rally"
213         Repo.clone_from(url, RALLY_REPO_DIR)
214
215         logger.debug("Executing %s./install_rally.sh..." %RALLY_REPO_DIR)
216         install_script = RALLY_REPO_DIR + "install_rally.sh"
217         cmd = 'sudo ' + install_script
218         execute_command(cmd)
219         #subprocess.call(['sudo', install_script])
220
221         logger.debug("Creating Rally environment...")
222         cmd = "rally deployment create --fromenv --name=opnfv-arno-rally"
223         execute_command(cmd)
224
225         logger.debug("Installing tempest...")
226         cmd = "rally-manage tempest install"
227         execute_command(cmd)
228
229         cmd = "rally deployment check"
230         execute_command(cmd)
231         #TODO: check that everything is 'Available' and warn if not
232
233         cmd = "rally show images"
234         execute_command(cmd)
235
236         cmd = "rally show flavors"
237         execute_command(cmd)
238
239     return True
240
241
242
243 def check_rally():
244     """
245     Check if Rally is installed and properly configured
246     """
247     if os.path.exists(RALLY_INSTALLATION_DIR):
248         logger.debug("   Rally installation directory found in %s" % RALLY_INSTALLATION_DIR)
249         FNULL = open(os.devnull, 'w');
250         cmd="rally deployment list | grep opnfv";
251         logger.debug('   Executing command : {}'.format(cmd))
252         p=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=FNULL);
253         #if the command does not exist or there is no deployment
254         line = p.stdout.readline()
255         if line == "":
256             logger.debug("   Rally deployment not found")
257             return False
258         logger.debug("   Rally deployment found")
259         return True
260     else:
261         logger.debug("   Rally installation directory not found")
262         return False
263
264
265 def install_odl():
266     cmd = "chmod +x " + ODL_DIR + "create_venv.sh"
267     execute_command(cmd)
268     cmd = ODL_DIR + "create_venv.sh"
269     execute_command(cmd)
270     return True
271
272
273 def check_credentials():
274     """
275     Check if the OpenStack credentials (openrc) are sourced
276     """
277     #TODO: there must be a short way to do this, doing if os.environ["something"] == "" throws an error
278     try:
279        os.environ['OS_AUTH_URL']
280     except KeyError:
281         return False
282     try:
283        os.environ['OS_USERNAME']
284     except KeyError:
285         return False
286     try:
287        os.environ['OS_PASSWORD']
288     except KeyError:
289         return False
290     try:
291        os.environ['OS_TENANT_NAME']
292     except KeyError:
293         return False
294     try:
295        os.environ['OS_REGION_NAME']
296     except KeyError:
297         return False
298     return True
299
300
301 def download_tests():
302     os.makedirs(VPING_DIR)
303     os.makedirs(ODL_DIR)
304     os.makedirs(BENCH_TESTS_DIR)
305
306     logger.info("Downloading functest.yaml...")
307     yaml_url = 'https://git.opnfv.org/cgit/functest/plain/testcases/functest.yaml'
308     if not download_url(yaml_url,FUNCTEST_BASE_DIR):
309         logger.error("Unable to download the configuration file functest.yaml")
310         return False
311
312     logger.info("Downloading vPing test...")
313     vPing_url = 'https://git.opnfv.org/cgit/functest/plain/testcases/vPing/CI/libraries/vPing.py'
314     if not download_url(vPing_url,VPING_DIR):
315         return False
316
317
318     logger.info("Downloading Rally bench tests...")
319     run_rally_url = 'https://git.opnfv.org/cgit/functest/plain/testcases/VIM/OpenStack/CI/libraries/run_rally.py'
320     if not download_url(run_rally_url,RALLY_TEST_DIR):
321         return False
322
323     rally_bench_base_url = 'https://git.opnfv.org/cgit/functest/plain/testcases/VIM/OpenStack/CI/suites/'
324     bench_tests = ['authenticate', 'cinder', 'glance', 'heat', 'keystone', 'neutron', 'nova', 'quotas', 'requests', 'tempest', 'vm']
325     for i in bench_tests:
326         rally_bench_url = rally_bench_base_url + "opnfv-" + i + ".json"
327         logger.debug("Downloading %s" %rally_bench_url)
328         if not download_url(rally_bench_url,BENCH_TESTS_DIR):
329             return False
330
331     logger.info("Downloading OLD tests...")
332     odl_base_url = 'https://git.opnfv.org/cgit/functest/plain/testcases/Controllers/ODL/CI/'
333     odl_tests = ['create_venv.sh', 'requirements.pip', 'start_tests.sh', 'test_list.txt']
334     for i in odl_tests:
335         odl_url = odl_base_url + i
336         logger.debug("Downloading %s" %odl_url)
337         if not download_url(odl_url,ODL_DIR):
338             return False
339
340     return True
341
342
343
344 def create_glance_image(path,name,disk_format):
345     """
346     Create a glance image given the absolute path of the image, its name and the disk format
347     """
348     cmd = "glance image-create --name "+name+" --is-public true --disk-format "+disk_format+" --container-format bare --file "+path
349     execute_command(cmd)
350     return True
351
352
353 def download_url(url, dest_path):
354     """
355     Download a file to a destination path given a URL
356     """
357     name = url.rsplit('/')[-1]
358     dest = dest_path + name
359     try:
360         response = urllib2.urlopen(url)
361     except (urllib2.HTTPError, urllib2.URLError):
362         logger.error("Error in fetching %s" %url)
363         return False
364
365     with open(dest, 'wb') as f:
366         f.write(response.read())
367     return True
368
369
370 def download_url_with_progress(url, dest_path):
371     """
372     Download a file to a destination path given a URL showing the progress
373     """
374     name = url.rsplit('/')[-1]
375     dest = dest_path + name
376     try:
377         response = urllib2.urlopen(url)
378     except (urllib2.HTTPError, urllib2.URLError):
379         logger.error("Error in fetching %s" %url)
380         return False
381
382     f = open(dest, 'wb')
383     meta = response.info()
384     file_size = int(meta.getheaders("Content-Length")[0])
385     logger.info("Downloading: %s Bytes: %s" %(dest, file_size))
386
387     file_size_dl = 0
388     block_sz = 8192
389     while True:
390         buffer = response.read(block_sz)
391         if not buffer:
392             break
393
394         file_size_dl += len(buffer)
395         f.write(buffer)
396         status = r"%10d  [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
397         status = status + chr(8)*(len(status)+1)
398         print status,
399
400     f.close()
401     print("\n")
402     return True
403
404
405 def check_internet_connectivity(url='http://www.google.com/'):
406     """
407     Check if there is access to the internet
408     """
409     try:
410         urllib2.urlopen(url, timeout=5)
411         return True
412     except urllib.request.URLError:
413         return False
414
415 def execute_command(cmd):
416     """
417     Execute Linux command
418     """
419     logger.debug('Executing command : {}'.format(cmd))
420     #p = os.popen(cmd,"r")
421     #logger.debug(p.read())
422     output_file = "/tmp/output.txt"
423     f = open(output_file, 'w+')
424     p = subprocess.call(cmd,shell=True, stdout=f, stderr=subprocess.STDOUT)
425     f.close()
426     f = open(output_file, 'r')
427     logger.debug(f.read())
428     #p = subprocess.call(cmd,shell=True);
429     if p == 0 :
430         return True
431     else:
432         logger.error("Error when executing command %s" %cmd)
433         exit(-1)
434
435
436
437
438 def main():
439     if not (args.action in actions):
440         logger.error('argument not valid')
441         exit(-1)
442
443     if args.action == "start":
444         config_functest_start()
445
446     if args.action == "check":
447         if config_functest_check():
448             logger.info("Functest environment correctly installed")
449         else:
450             logger.info("Functest environment not found or faulty")
451
452     if args.action == "clean":
453         while True:
454             print("Are you sure? [y|n]")
455             answer = raw_input("")
456             if answer == "y":
457                 config_functest_clean()
458                 break
459             elif answer == "n":
460                 break
461             else:
462                 print("Invalid option.")
463     exit(0)
464
465
466 if __name__ == '__main__':
467     main()
468