92ababcbdd93fbafcfd08c8635c52e56b0b475f4
[functest-xtesting.git] / testcases / functest_utils.py
1 #!/usr/bin/env python
2 #
3 # jose.lausuch@ericsson.com
4 # valentin.boucher@orange.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 json
12 import os
13 import os.path
14 import re
15 import requests
16 import shutil
17 import socket
18 import subprocess
19 import sys
20 import urllib2
21 import yaml
22 from git import Repo
23
24
25 # ----------------------------------------------------------
26 #
27 #               OPENSTACK UTILS
28 #
29 # -----------------------------------------------------------
30
31
32 #*********************************************
33 #   CREDENTIALS
34 #*********************************************
35 def check_credentials():
36     """
37     Check if the OpenStack credentials (openrc) are sourced
38     """
39     env_vars = ['OS_AUTH_URL', 'OS_USERNAME', 'OS_PASSWORD', 'OS_TENANT_NAME']
40     return all(map(lambda v: v in os.environ and os.environ[v], env_vars))
41
42 def get_credentials(service):
43     """Returns a creds dictionary filled with the following keys:
44     * username
45     * password/api_key (depending on the service)
46     * tenant_name/project_id (depending on the service)
47     * auth_url
48     :param service: a string indicating the name of the service
49                     requesting the credentials.
50     """
51     creds = {}
52     # Unfortunately, each of the OpenStack client will request slightly
53     # different entries in their credentials dict.
54     if service.lower() in ("nova", "cinder"):
55         password = "api_key"
56         tenant = "project_id"
57     else:
58         password = "password"
59         tenant = "tenant_name"
60
61     # The most common way to pass these info to the script is to do it through
62     # environment variables.
63     creds.update({
64         "username": os.environ.get('OS_USERNAME', "admin"),
65         password: os.environ.get("OS_PASSWORD", 'admin'),
66         "auth_url": os.environ.get("OS_AUTH_URL",
67                                    "http://192.168.20.71:5000/v2.0"),
68         tenant: os.environ.get("OS_TENANT_NAME", "admin"),
69     })
70     return creds
71
72
73
74 #*********************************************
75 #   NOVA
76 #*********************************************
77 def get_instances(nova_client):
78     try:
79         instances = nova_client.servers.list(search_opts={'all_tenants': 1})
80         return instances
81     except Exception, e:
82         print "Error [get_instances(nova_client)]:", e
83         return None
84
85
86 def get_instance_status(nova_client, instance):
87     try:
88         instance = nova_client.servers.get(instance.id)
89         return instance.status
90     except Exception, e:
91         print "Error [get_instance_status(nova_client, instance)]:" % \
92                                                         str(instance), e
93         return None
94
95
96 def get_instance_by_name(nova_client, instance_name):
97     try:
98         instance = nova_client.servers.find(name=instance_name)
99         return instance
100     except Exception, e:
101         print "Error [get_instance_by_name(nova_client, '%s')]:" %instance_name, e
102         return None
103
104
105 def get_flavor_id(nova_client, flavor_name):
106     flavors = nova_client.flavors.list(detailed=True)
107     id = ''
108     for f in flavors:
109         if f.name == flavor_name:
110             id = f.id
111             break
112     return id
113
114
115 def get_flavor_id_by_ram_range(nova_client, min_ram, max_ram):
116     flavors = nova_client.flavors.list(detailed=True)
117     id = ''
118     for f in flavors:
119         if min_ram <= f.ram and f.ram <= max_ram:
120             id = f.id
121             break
122     return id
123
124
125 def get_floating_ips(nova_client):
126     try:
127         floating_ips = nova_client.floating_ips.list()
128         return floating_ips
129     except Exception, e:
130         print "Error [get_floating_ips(nova_client)]:", e
131         return None
132
133
134 def create_flavor(nova_client, flavor_name, ram, disk, vcpus):
135     try:
136         flavor = nova_client.flavors.create(flavor_name,ram,vcpus,disk)
137     except Exception, e:
138         print "Error [create_flavor(nova_client, '%s', '%s', '%s', "\
139             "'%s')]:" %(flavor_name,ram, disk, vcpus), e
140         return None
141     return flavor.id
142
143
144 def create_floating_ip(neutron_client):
145     extnet_id = get_external_net_id(neutron_client)
146     props = {'floating_network_id': extnet_id}
147     try:
148         ip_json = neutron_client.create_floatingip({'floatingip': props})
149         floating_ip = ip_json['floatingip']['floating_ip_address']
150     except Exception, e:
151         print "Error [create_floating_ip(neutron_client)]:", e
152         return None
153     return floating_ip
154
155
156 def add_floating_ip(nova_client, server_id, floatingip_id):
157     try:
158         nova_client.servers.add_floating_ip(server_id,floatingip_id)
159         return True
160     except Exception, e:
161         print "Error [add_floating_ip(nova_client, '%s', '%s')]:" % \
162             (server_id, floatingip_id), e
163         return False
164
165
166 def delete_instance(nova_client, instance_id):
167     try:
168         nova_client.servers.force_delete(instance_id)
169         return True
170     except Exception, e:
171         print "Error [delete_instance(nova_client, '%s')]:" % instance_id, e
172         return False
173
174
175 def delete_floating_ip(nova_client, floatingip_id):
176     try:
177         nova_client.floating_ips.delete(floatingip_id)
178         return True
179     except Exception, e:
180         print "Error [delete_floating_ip(nova_client, '%s')]:" % floatingip_id, e
181         return False
182
183
184
185
186 #*********************************************
187 #   NEUTRON
188 #*********************************************
189 def get_network_list(neutron_client):
190     network_list = neutron_client.list_networks()['networks']
191     if len(network_list) == 0:
192         return None
193     else:
194         return network_list
195
196
197 def get_router_list(neutron_client):
198     router_list = neutron_client.list_routers()['routers']
199     if len(router_list) == 0:
200         return None
201     else:
202         return router_list
203
204
205 def get_port_list(neutron_client):
206     port_list = neutron_client.list_ports()['ports']
207     if len(port_list) == 0:
208         return None
209     else:
210         return port_list
211
212
213 def get_network_id(neutron_client, network_name):
214     networks = neutron_client.list_networks()['networks']
215     id = ''
216     for n in networks:
217         if n['name'] == network_name:
218             id = n['id']
219             break
220     return id
221
222
223 def get_private_net(neutron_client):
224     # Checks if there is an existing shared private network
225     networks = neutron_client.list_networks()['networks']
226     if len(networks) == 0:
227         return None
228     for net in networks:
229         if (net['router:external'] is False) and (net['shared'] is True):
230             return net
231     return None
232
233
234 def get_external_net(neutron_client):
235     for network in neutron_client.list_networks()['networks']:
236         if network['router:external']:
237             return network['name']
238     return False
239
240
241 def get_external_net_id(neutron_client):
242     for network in neutron_client.list_networks()['networks']:
243         if network['router:external']:
244             return network['id']
245     return False
246
247
248 def check_neutron_net(neutron_client, net_name):
249     for network in neutron_client.list_networks()['networks']:
250         if network['name'] == net_name:
251             for subnet in network['subnets']:
252                 return True
253     return False
254
255
256 def create_neutron_net(neutron_client, name):
257     json_body = {'network': {'name': name,
258                              'admin_state_up': True}}
259     try:
260         network = neutron_client.create_network(body=json_body)
261         network_dict = network['network']
262         return network_dict['id']
263     except Exception, e:
264         print "Error [create_neutron_net(neutron_client, '%s')]:" % name, e
265         return False
266
267
268 def create_neutron_subnet(neutron_client, name, cidr, net_id):
269     json_body = {'subnets': [{'name': name, 'cidr': cidr,
270                               'ip_version': 4, 'network_id': net_id}]}
271     try:
272         subnet = neutron_client.create_subnet(body=json_body)
273         return subnet['subnets'][0]['id']
274     except Exception, e:
275         print "Error [create_neutron_subnet(neutron_client, '%s', '%s', "\
276             "'%s')]:" %(name,cidr, net_id), e
277         return False
278
279
280 def create_neutron_router(neutron_client, name):
281     json_body = {'router': {'name': name, 'admin_state_up': True}}
282     try:
283         router = neutron_client.create_router(json_body)
284         return router['router']['id']
285     except Exception, e:
286         print "Error [create_neutron_router(neutron_client, name)]:" % name, e
287         return False
288
289
290 def create_neutron_port(neutron_client, name, network_id, ip):
291     json_body = {'port': {
292                  'admin_state_up': True,
293                  'name': name,
294                  'network_id': network_id,
295                  'fixed_ips': [{"ip_address": ip}]
296                  }}
297     try:
298         port = neutron_client.create_port(body=json_body)
299         return port['port']['id']
300     except Exception, e:
301         print "Error [create_neutron_port(neutron_client, '%s', '%s', "\
302             "'%s')]:" %(name,network_id, ip), e
303         return False
304
305
306 def update_neutron_net(neutron_client, network_id, shared=False):
307     json_body = {'network': {'shared': shared}}
308     try:
309         neutron_client.update_network(network_id, body=json_body)
310         return True
311     except Exception, e:
312         print "Error [update_neutron_net(neutron_client, '%s', '%s')]:" % \
313             (network_id,str(shared)), e
314         return False
315
316
317 def update_neutron_port(neutron_client, port_id, device_owner):
318     json_body = {'port': {
319                  'device_owner': device_owner,
320                  }}
321     try:
322         port = neutron_client.update_port(port=port_id,
323                                           body=json_body)
324         return port['port']['id']
325     except Exception, e:
326         print "Error [update_neutron_port(neutron_client, '%s', '%s')]:" % \
327             (port_id,device_owner), e
328         return False
329
330
331 def add_interface_router(neutron_client, router_id, subnet_id):
332     json_body = {"subnet_id": subnet_id}
333     try:
334         neutron_client.add_interface_router(router=router_id, body=json_body)
335         return True
336     except Exception, e:
337         print "Error [add_interface_router(neutron_client, '%s', '%s')]:" % \
338             (router_id,subnet_id), e
339         return False
340
341 def add_gateway_router(neutron_client, router_id):
342     ext_net_id = get_external_net_id(neutron_client)
343     router_dict = {'network_id': ext_net_id}
344     try:
345         neutron_client.add_gateway_router(router_id,router_dict)
346         return True
347     except Exception, e:
348         print "Error [add_gateway_router(neutron_client, '%s')]:" % router_id, e
349         return False
350
351
352 def delete_neutron_net(neutron_client, network_id):
353     try:
354         neutron_client.delete_network(network_id)
355         return True
356     except Exception, e:
357         print "Error [delete_neutron_net(neutron_client, '%s')]:" % network_id, e
358         return False
359
360
361 def delete_neutron_subnet(neutron_client, subnet_id):
362     try:
363         neutron_client.delete_subnet(subnet_id)
364         return True
365     except Exception, e:
366         print "Error [delete_neutron_subnet(neutron_client, '%s')]:" % subnet_id, e
367         return False
368
369
370 def delete_neutron_router(neutron_client, router_id):
371     json_body = {'router': {'id': router_id}}
372     try:
373         neutron_client.delete_router(router=router_id)
374         return True
375     except Exception, e:
376         print "Error [delete_neutron_router(neutron_client, '%s')]:" % \
377             router_id, e
378         return False
379
380
381 def delete_neutron_port(neutron_client, port_id):
382     try:
383         neutron_client.delete_port(port_id)
384         return True
385     except Exception, e:
386         print "Error [delete_neutron_port(neutron_client, '%s')]:" % port_id, e
387         return False
388
389
390 def remove_interface_router(neutron_client, router_id, subnet_id):
391     json_body = {"subnet_id": subnet_id}
392     try:
393         neutron_client.remove_interface_router(router=router_id,
394                                                body=json_body)
395         return True
396     except Exception, e:
397         print "Error [remove_interface_router(neutron_client, '%s', '%s')]:" % \
398             (router_id,subnet_id), e
399         return False
400
401
402 def remove_gateway_router(neutron_client, router_id):
403     try:
404         neutron_client.remove_gateway_router(router_id)
405         return True
406     except Exception, e:
407         print "Error [remove_gateway_router(neutron_client, '%s')]:" % router_id, e
408         return False
409
410
411
412 #*********************************************
413 #   SEC GROUPS
414 #*********************************************
415 def get_security_groups(neutron_client):
416     try:
417         security_groups = neutron_client.list_security_groups()[
418             'security_groups']
419         return security_groups
420     except Exception, e:
421         print "Error [get_security_groups(neutron_client)]:", e
422         return None
423
424
425 def update_sg_quota(neutron_client, tenant_id, sg_quota, sg_rule_quota):
426     json_body = {"quota": {
427         "security_group": sg_quota,
428         "security_group_rule": sg_rule_quota
429     }}
430
431     try:
432         quota = neutron_client.update_quota(tenant_id=tenant_id,
433                                             body=json_body)
434         return True
435     except Exception, e:
436         print "Error [update_sg_quota(neutron_client, '%s', '%s', "\
437             "'%s')]:" %(tenant_id,sg_quota, sg_rule_quota), e
438         return False
439
440
441 def delete_security_group(neutron_client, secgroup_id):
442     try:
443         neutron_client.delete_security_group(secgroup_id)
444         return True
445     except Exception, e:
446         print "Error [delete_security_group(neutron_client, '%s')]:" % secgroup_id, e
447         return False
448
449
450
451 #*********************************************
452 #   GLANCE
453 #*********************************************
454 def get_images(nova_client):
455     try:
456         images = nova_client.images.list()
457         return images
458     except Exception, e:
459         print "Error [get_images]:", e
460         return None
461
462
463 def get_image_id(glance_client, image_name):
464     images = glance_client.images.list()
465     id = ''
466     for i in images:
467         if i.name == image_name:
468             id = i.id
469             break
470     return id
471
472
473 def create_glance_image(glance_client, image_name, file_path, public=True):
474     if not os.path.isfile(file_path):
475         print "Error: file " + file_path + " does not exist."
476         return False
477     try:
478         with open(file_path) as fimage:
479             image = glance_client.images.create(name=image_name,
480                                                 is_public=public,
481                                                 disk_format="qcow2",
482                                                 container_format="bare",
483                                                 data=fimage)
484         return image.id
485     except Exception, e:
486         print "Error [create_glance_image(glance_client, '%s', '%s', "\
487             "'%s')]:" %(image_name,file_path, str(public)), e
488         return False
489
490
491 def delete_glance_image(nova_client, image_id):
492     try:
493         nova_client.images.delete(image_id)
494         return True
495     except Exception, e:
496         print "Error [delete_glance_image(nova_client, image_id)]:" % image_id, e
497         return False
498
499
500
501 #*********************************************
502 #   CINDER
503 #*********************************************
504 def get_volumes(cinder_client):
505     try:
506         volumes = cinder_client.volumes.list(search_opts={'all_tenants': 1})
507         return volumes
508     except Exception, e:
509         print "Error [get_volumes(cinder_client)]:", e
510         return None
511
512
513 def update_cinder_quota(cinder_client, tenant_id, vols_quota,
514                         snapshots_quota, gigabytes_quota):
515     quotas_values = {"volumes": vols_quota,
516                      "snapshots": snapshots_quota,
517                      "gigabytes": gigabytes_quota}
518
519     try:
520         quotas_default = cinder_client.quotas.update(tenant_id,
521                                                       **quotas_values)
522         return True
523     except Exception, e:
524         print "Error [update_cinder_quota(cinder_client, '%s', '%s', '%s'" \
525             "'%s')]:" %(tenant_id, vols_quota, snapshots_quota, gigabytes_quota), e
526         return False
527
528
529 def delete_volume(cinder_client, volume_id, forced=False):
530     try:
531         if forced:
532             try:
533                 cinder_client.volumes.detach(volume_id)
534             except:
535                 print "Error:", sys.exc_info()[0]
536             cinder_client.volumes.force_delete(volume_id)
537         else:
538             cinder_client.volumes.delete(volume_id)
539         return True
540     except Exception, e:
541         print "Error [delete_volume(cinder_client, '%s', '%s')]:" % \
542             (volume_id, str(forced)), e
543         return False
544
545
546
547 #*********************************************
548 #   KEYSTONE
549 #*********************************************
550 def get_tenants(keystone_client):
551     try:
552         tenants = keystone_client.tenants.list()
553         return tenants
554     except Exception, e:
555         print "Error [get_tenants(keystone_client)]:", e
556         return None
557
558
559 def get_users(keystone_client):
560     try:
561         users = keystone_client.users.list()
562         return users
563     except Exception, e:
564         print "Error [get_users(keystone_client)]:", e
565         return None
566
567
568 def get_tenant_id(keystone_client, tenant_name):
569     tenants = keystone_client.tenants.list()
570     id = ''
571     for t in tenants:
572         if t.name == tenant_name:
573             id = t.id
574             break
575     return id
576
577
578 def get_user_id(keystone_client, user_name):
579     users = keystone_client.users.list()
580     id = ''
581     for u in users:
582         if u.name == user_name:
583             id = u.id
584             break
585     return id
586
587
588 def get_role_id(keystone_client, role_name):
589     roles = keystone_client.roles.list()
590     id = ''
591     for r in roles:
592         if r.name == role_name:
593             id = r.id
594             break
595     return id
596
597
598 def create_tenant(keystone_client, tenant_name, tenant_description):
599     try:
600         tenant = keystone_client.tenants.create(tenant_name,
601                                                 tenant_description,
602                                                 enabled=True)
603         return tenant.id
604     except Exception, e:
605         print "Error [create_tenant(cinder_client, '%s', '%s')]:" % \
606             (tenant_name, tenant_description), e
607         return False
608
609
610 def create_user(keystone_client, user_name, user_password,
611                 user_email, tenant_id):
612     try:
613         user = keystone_client.users.create(user_name, user_password,
614                                             user_email, tenant_id,
615                                             enabled=True)
616         return user.id
617     except Exception, e:
618         print "Error [create_user(keystone_client, '%s', '%s', '%s'" \
619             "'%s')]:" %(user_name, user_password, user_email, tenant_id), e
620         return False
621
622
623 def add_role_user(keystone_client, user_id, role_id, tenant_id):
624     try:
625         keystone_client.roles.add_user_role(user_id, role_id, tenant_id)
626         return True
627     except Exception, e:
628         print "Error [add_role_user(keystone_client, '%s', '%s'" \
629             "'%s')]:" %(user_id, role_id, tenant_id), e
630         return False
631
632
633 def delete_tenant(keystone_client, tenant_id):
634     try:
635         tenant = keystone_client.tenants.delete(tenant_id)
636         return True
637     except Exception, e:
638         print "Error [delete_tenant(keystone_client, '%s')]:" % tenant_id, e
639         return False
640
641
642 def delete_user(keystone_client, user_id):
643     try:
644         tenant = keystone_client.users.delete(user_id)
645         return True
646     except Exception, e:
647         print "Error [delete_user(keystone_client, '%s')]:" % user_id, e
648         return False
649
650
651
652
653
654
655
656 # ----------------------------------------------------------
657 #
658 #               INTERNET UTILS
659 #
660 # -----------------------------------------------------------
661 def check_internet_connectivity(url='http://www.opnfv.org/'):
662     """
663     Check if there is access to the internet
664     """
665     try:
666         urllib2.urlopen(url, timeout=5)
667         return True
668     except urllib2.URLError:
669         return False
670
671
672 def download_url(url, dest_path):
673     """
674     Download a file to a destination path given a URL
675     """
676     name = url.rsplit('/')[-1]
677     dest = dest_path + "/" + name
678     try:
679         response = urllib2.urlopen(url)
680     except (urllib2.HTTPError, urllib2.URLError):
681         return False
682
683     with open(dest, 'wb') as f:
684         shutil.copyfileobj(response, f)
685     return True
686
687
688
689
690 # ----------------------------------------------------------
691 #
692 #               CI UTILS
693 #
694 # -----------------------------------------------------------
695 def get_git_branch(repo_path):
696     """
697     Get git branch name
698     """
699     repo = Repo(repo_path)
700     branch = repo.active_branch
701     return branch.name
702
703
704 def get_installer_type(logger=None):
705     """
706     Get installer type (fuel, apex, joid, compass)
707     """
708     try:
709         installer = os.environ['INSTALLER_TYPE']
710     except KeyError:
711         if logger:
712             logger.error("Impossible to retrieve the installer type")
713         installer = "Unknown_installer"
714
715     return installer
716
717
718 def get_scenario(logger=None):
719     """
720     Get scenario
721     """
722     try:
723         scenario = os.environ['DEPLOY_SCENARIO']
724     except KeyError:
725         if logger:
726             logger.error("Impossible to retrieve the scenario")
727         scenario = "Unknown_scenario"
728
729     return scenario
730
731
732 def get_pod_name(logger=None):
733     """
734     Get PoD Name from env variable NODE_NAME
735     """
736     try:
737         return os.environ['NODE_NAME']
738     except KeyError:
739         if logger:
740             logger.error(
741                 "Unable to retrieve the POD name from environment.Using pod name 'unknown-pod'")
742         return "unknown-pod"
743
744
745 def push_results_to_db(db_url, case_name, logger, pod_name,
746                        version, payload):
747     """
748     POST results to the Result target DB
749     """
750     url = db_url + "/results"
751     installer = get_installer_type(logger)
752     params = {"project_name": "functest", "case_name": case_name,
753               "pod_name": pod_name, "installer": installer,
754               "version": version, "details": payload}
755
756     headers = {'Content-Type': 'application/json'}
757     try:
758         r = requests.post(url, data=json.dumps(params), headers=headers)
759         if logger:
760             logger.debug(r)
761         return True
762     except Exception, e:
763         print "Error []:", e
764         return False
765
766
767 def get_resolvconf_ns():
768     """
769     Get nameservers from current resolv.conf
770     """
771     nameservers = []
772     rconf = open("/etc/resolv.conf", "r")
773     line = rconf.readline()
774     while line:
775         ip = re.search(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", line)
776         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
777         if ip:
778             result = sock.connect_ex((ip.group(),53))
779             if result == 0:
780                 nameservers.append(ip.group())
781         line = rconf.readline()
782     return nameservers
783
784
785 def getTestEnv(test, functest_yaml):
786     """
787     Get the config of the testcase based on functest_config.yaml
788       2 options
789         - test = test project e.g; ovno
790         - test = testcase e.g. functest/odl
791        look for the / to see if it is a test project or a testcase
792     """
793     try:
794         TEST_ENV = functest_yaml.get("test-dependencies")
795
796         if test.find("/") < 0:
797             config_test = TEST_ENV[test]
798         else:
799             test_split = test.split("/")
800             testproject = test_split[0]
801             testcase = test_split[1]
802             config_test = TEST_ENV[testproject][testcase]
803     except KeyError:
804         # if not defined in dependencies => no dependencies
805         config_test = ""
806     except Exception, e:
807         print "Error []:", e
808
809     return config_test
810
811
812 def get_ci_envvars():
813     """
814     Get the CI env variables
815     """
816     ci_env_var = {
817         "installer": os.environ.get('INSTALLER_TYPE'),
818         "scenario": os.environ.get('DEPLOY_SCENARIO')}
819     return ci_env_var
820
821
822 def isTestRunnable(test, functest_yaml):
823     """
824     Return True if the test is runnable in the current scenario
825     """
826     # By default we assume that all the tests are always runnable...
827     is_runnable = True
828     # Retrieve CI environment
829     ci_env = get_ci_envvars()
830     # Retrieve test environement from config file
831     test_env = getTestEnv(test, functest_yaml)
832
833     # if test_env not empty => dependencies to be checked
834     if test_env is not None and len(test_env) > 0:
835         # possible criteria = ["installer", "scenario"]
836         # consider test criteria from config file
837         # compare towards CI env through CI en variable
838         for criteria in test_env:
839             if re.search(test_env[criteria], ci_env[criteria]) is None:
840                 # print "Test "+ test + " cannot be run on the environment"
841                 is_runnable = False
842     return is_runnable
843
844
845 def generateTestcaseList(functest_yaml):
846     """
847     Generate a test file with the runnable test according to
848     the current scenario
849     """
850     test_list = ""
851     # get testcases
852     testcase_list = functest_yaml.get("test-dependencies")
853     projects = testcase_list.keys()
854
855     for project in projects:
856         testcases = testcase_list[project]
857         # 1 or 2 levels for testcases project[/case]l
858         # if only project name without controller or scenario
859         # => shall be runnable on any controller/scenario
860         if testcases is None:
861             test_list += project + " "
862         else:
863             for testcase in testcases:
864                 if testcase == "installer" or testcase == "scenario":
865                     # project (1 level)
866                     if isTestRunnable(project, functest_yaml):
867                         test_list += project + " "
868                 else:
869                     # project/testcase (2 levels)
870                     thetest = project + "/" + testcase
871                     if isTestRunnable(thetest, functest_yaml):
872                         test_list += testcase + " "
873
874     # sort the list to execute the test in the right order
875     test_order_list = functest_yaml.get("test_exec_priority")
876     test_sorted_list = ""
877     for test in test_order_list:
878         if test_order_list[test] in test_list:
879             test_sorted_list += test_order_list[test] + " "
880
881     # create a file that could be consumed by run-test.sh
882     # this method is used only for CI
883     # so it can be run only in container
884     # reuse default conf directory to store the list of runnable tests
885     file = open("/home/opnfv/functest/conf/testcase-list.txt", 'w')
886     file.write(test_sorted_list)
887     file.close()
888
889     return test_sorted_list
890
891
892 def execute_command(cmd, logger=None, exit_on_error=True):
893     """
894     Execute Linux command
895         prints stdout to a file and depending on if there
896         is a logger defined, it will print it or not.
897     """
898     if logger:
899         logger.debug('Executing command : {}'.format(cmd))
900     output_file = "output.txt"
901     f = open(output_file, 'w+')
902     p = subprocess.call(cmd, shell=True, stdout=f, stderr=subprocess.STDOUT)
903     f.close()
904     f = open(output_file, 'r')
905     result = f.read()
906     if result != "" and logger:
907         logger.debug(result)
908     if p == 0:
909         return True
910     else:
911         if logger:
912             logger.error("Error when executing command %s" % cmd)
913         if exit_on_error:
914             exit(-1)
915         return False