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