Merge "Add reporting results for Danube 3.0"
[functest.git] / functest / utils / openstack_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 logging
12 import os.path
13 import re
14 import sys
15 import time
16
17 from keystoneauth1 import loading
18 from keystoneauth1 import session
19 from cinderclient import client as cinderclient
20 from glanceclient import client as glanceclient
21 from heatclient import client as heatclient
22 from novaclient import client as novaclient
23 from keystoneclient import client as keystoneclient
24 from neutronclient.neutron import client as neutronclient
25
26 from functest.utils.constants import CONST
27 import functest.utils.functest_utils as ft_utils
28
29 logger = logging.getLogger(__name__)
30
31 DEFAULT_API_VERSION = '2'
32 DEFAULT_HEAT_API_VERSION = '1'
33
34
35 # *********************************************
36 #   CREDENTIALS
37 # *********************************************
38 class MissingEnvVar(Exception):
39
40     def __init__(self, var):
41         self.var = var
42
43     def __str__(self):
44         return str.format("Please set the mandatory env var: {}", self.var)
45
46
47 def is_keystone_v3():
48     keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION')
49     if (keystone_api_version is None or
50             keystone_api_version == '2'):
51         return False
52     else:
53         return True
54
55
56 def get_rc_env_vars():
57     env_vars = ['OS_AUTH_URL', 'OS_USERNAME', 'OS_PASSWORD']
58     if is_keystone_v3():
59         env_vars.extend(['OS_PROJECT_NAME',
60                          'OS_USER_DOMAIN_NAME',
61                          'OS_PROJECT_DOMAIN_NAME'])
62     else:
63         env_vars.extend(['OS_TENANT_NAME'])
64     return env_vars
65
66
67 def check_credentials():
68     """
69     Check if the OpenStack credentials (openrc) are sourced
70     """
71     env_vars = get_rc_env_vars()
72     return all(map(lambda v: v in os.environ and os.environ[v], env_vars))
73
74
75 def get_env_cred_dict():
76     env_cred_dict = {
77         'OS_USERNAME': 'username',
78         'OS_PASSWORD': 'password',
79         'OS_AUTH_URL': 'auth_url',
80         'OS_TENANT_NAME': 'tenant_name',
81         'OS_USER_DOMAIN_NAME': 'user_domain_name',
82         'OS_PROJECT_DOMAIN_NAME': 'project_domain_name',
83         'OS_PROJECT_NAME': 'project_name',
84         'OS_ENDPOINT_TYPE': 'endpoint_type',
85         'OS_REGION_NAME': 'region_name',
86         'OS_CACERT': 'https_cacert',
87         'OS_INSECURE': 'https_insecure'
88     }
89     return env_cred_dict
90
91
92 def get_credentials(other_creds={}):
93     """Returns a creds dictionary filled with parsed from env
94     """
95     creds = {}
96     env_vars = get_rc_env_vars()
97     env_cred_dict = get_env_cred_dict()
98
99     for envvar in env_vars:
100         if os.getenv(envvar) is None:
101             raise MissingEnvVar(envvar)
102         else:
103             creds_key = env_cred_dict.get(envvar)
104             creds.update({creds_key: os.getenv(envvar)})
105
106     if 'tenant' in other_creds.keys():
107         if is_keystone_v3():
108             tenant = 'project_name'
109         else:
110             tenant = 'tenant_name'
111         other_creds[tenant] = other_creds.pop('tenant')
112
113     creds.update(other_creds)
114
115     return creds
116
117
118 def source_credentials(rc_file):
119     with open(rc_file, "r") as f:
120         for line in f:
121             var = (line.rstrip('"\n').replace('export ', '').split("=")
122                    if re.search(r'(.*)=(.*)', line) else None)
123             # The two next lines should be modified as soon as rc_file
124             # conforms with common rules. Be aware that it could induce
125             # issues if value starts with '
126             if var:
127                 key = re.sub(r'^["\' ]*|[ \'"]*$', '', var[0])
128                 value = re.sub(r'^["\' ]*|[ \'"]*$', '', "".join(var[1:]))
129                 os.environ[key] = value
130
131
132 def get_credentials_for_rally():
133     creds = get_credentials()
134     env_cred_dict = get_env_cred_dict()
135     rally_conf = {"type": "ExistingCloud", "admin": {}}
136     for key in creds:
137         if key == 'auth_url':
138             rally_conf[key] = creds[key]
139         else:
140             rally_conf['admin'][key] = creds[key]
141
142     endpoint_types = [('internalURL', 'internal'),
143                       ('publicURL', 'public'), ('adminURL', 'admin')]
144
145     endpoint_type = get_endpoint_type_from_env()
146     if endpoint_type is not None:
147         cred_key = env_cred_dict.get('OS_ENDPOINT_TYPE')
148         for k, v in endpoint_types:
149             if endpoint_type == v:
150                 rally_conf[cred_key] = v
151
152     region_name = os.getenv('OS_REGION_NAME')
153     if region_name is not None:
154         cred_key = env_cred_dict.get('OS_REGION_NAME')
155         rally_conf[cred_key] = region_name
156
157     cred_key = env_cred_dict.get('OS_CACERT')
158     rally_conf[cred_key] = os.getenv('OS_CACERT', '')
159
160     insecure_key = env_cred_dict.get('OS_INSECURE')
161     rally_conf[insecure_key] = os.getenv('OS_INSECURE', '').lower() == 'true'
162
163     return rally_conf
164
165
166 def get_endpoint_type_from_env():
167     endpoint_type = os.environ.get("OS_ENDPOINT_TYPE",
168                                    os.environ.get("OS_INTERFACE"))
169     if endpoint_type and "URL" in endpoint_type:
170         endpoint_type = endpoint_type.replace("URL", "")
171     return endpoint_type
172
173
174 def get_session_auth(other_creds={}):
175     loader = loading.get_plugin_loader('password')
176     creds = get_credentials(other_creds)
177     auth = loader.load_from_options(**creds)
178     return auth
179
180
181 def get_endpoint(service_type, interface='public'):
182     auth = get_session_auth()
183     return get_session().get_endpoint(auth=auth,
184                                       service_type=service_type,
185                                       interface=interface)
186
187
188 def get_session(other_creds={}):
189     auth = get_session_auth(other_creds)
190     https_cacert = os.getenv('OS_CACERT', '')
191     https_insecure = os.getenv('OS_INSECURE', '').lower() == 'true'
192     return session.Session(auth=auth,
193                            verify=(https_cacert or not https_insecure))
194
195
196 # *********************************************
197 #   CLIENTS
198 # *********************************************
199 def get_keystone_client_version():
200     api_version = os.getenv('OS_IDENTITY_API_VERSION')
201     if api_version is not None:
202         logger.info("OS_IDENTITY_API_VERSION is set in env as '%s'",
203                     api_version)
204         return api_version
205     return DEFAULT_API_VERSION
206
207
208 def get_keystone_client(other_creds={}):
209     sess = get_session(other_creds)
210     return keystoneclient.Client(get_keystone_client_version(),
211                                  session=sess,
212                                  interface=os.getenv('OS_INTERFACE', 'admin'))
213
214
215 def get_nova_client_version():
216     api_version = os.getenv('OS_COMPUTE_API_VERSION')
217     if api_version is not None:
218         logger.info("OS_COMPUTE_API_VERSION is set in env as '%s'",
219                     api_version)
220         return api_version
221     return DEFAULT_API_VERSION
222
223
224 def get_nova_client(other_creds={}):
225     sess = get_session(other_creds)
226     return novaclient.Client(get_nova_client_version(), session=sess)
227
228
229 def get_cinder_client_version():
230     api_version = os.getenv('OS_VOLUME_API_VERSION')
231     if api_version is not None:
232         logger.info("OS_VOLUME_API_VERSION is set in env as '%s'",
233                     api_version)
234         return api_version
235     return DEFAULT_API_VERSION
236
237
238 def get_cinder_client(other_creds={}):
239     sess = get_session(other_creds)
240     return cinderclient.Client(get_cinder_client_version(), session=sess)
241
242
243 def get_neutron_client_version():
244     api_version = os.getenv('OS_NETWORK_API_VERSION')
245     if api_version is not None:
246         logger.info("OS_NETWORK_API_VERSION is set in env as '%s'",
247                     api_version)
248         return api_version
249     return DEFAULT_API_VERSION
250
251
252 def get_neutron_client(other_creds={}):
253     sess = get_session(other_creds)
254     return neutronclient.Client(get_neutron_client_version(), session=sess)
255
256
257 def get_glance_client_version():
258     api_version = os.getenv('OS_IMAGE_API_VERSION')
259     if api_version is not None:
260         logger.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version)
261         return api_version
262     return DEFAULT_API_VERSION
263
264
265 def get_glance_client(other_creds={}):
266     sess = get_session(other_creds)
267     return glanceclient.Client(get_glance_client_version(), session=sess)
268
269
270 def get_heat_client_version():
271     api_version = os.getenv('OS_ORCHESTRATION_API_VERSION')
272     if api_version is not None:
273         logger.info("OS_ORCHESTRATION_API_VERSION is set in env as '%s'",
274                     api_version)
275         return api_version
276     return DEFAULT_HEAT_API_VERSION
277
278
279 def get_heat_client(other_creds={}):
280     sess = get_session(other_creds)
281     return heatclient.Client(get_heat_client_version(), session=sess)
282
283
284 def download_and_add_image_on_glance(glance, image_name, image_url, data_dir):
285     try:
286         dest_path = data_dir
287         if not os.path.exists(dest_path):
288             os.makedirs(dest_path)
289         file_name = image_url.rsplit('/')[-1]
290         if not ft_utils.download_url(image_url, dest_path):
291             return False
292     except Exception:
293         raise Exception("Impossible to download image from {}".format(
294                         image_url))
295
296     try:
297         image = create_glance_image(
298             glance, image_name, dest_path + file_name)
299         if not image:
300             return False
301         else:
302             return image
303     except Exception:
304         raise Exception("Impossible to put image {} in glance".format(
305                         image_name))
306
307
308 # *********************************************
309 #   NOVA
310 # *********************************************
311 def get_instances(nova_client):
312     try:
313         instances = nova_client.servers.list(search_opts={'all_tenants': 1})
314         return instances
315     except Exception as e:
316         logger.error("Error [get_instances(nova_client)]: %s" % e)
317         return None
318
319
320 def get_instance_status(nova_client, instance):
321     try:
322         instance = nova_client.servers.get(instance.id)
323         return instance.status
324     except Exception as e:
325         logger.error("Error [get_instance_status(nova_client)]: %s" % e)
326         return None
327
328
329 def get_instance_by_name(nova_client, instance_name):
330     try:
331         instance = nova_client.servers.find(name=instance_name)
332         return instance
333     except Exception as e:
334         logger.error("Error [get_instance_by_name(nova_client, '%s')]: %s"
335                      % (instance_name, e))
336         return None
337
338
339 def get_flavor_id(nova_client, flavor_name):
340     flavors = nova_client.flavors.list(detailed=True)
341     id = ''
342     for f in flavors:
343         if f.name == flavor_name:
344             id = f.id
345             break
346     return id
347
348
349 def get_flavor_id_by_ram_range(nova_client, min_ram, max_ram):
350     flavors = nova_client.flavors.list(detailed=True)
351     id = ''
352     for f in flavors:
353         if min_ram <= f.ram and f.ram <= max_ram:
354             id = f.id
355             break
356     return id
357
358
359 def get_aggregates(nova_client):
360     try:
361         aggregates = nova_client.aggregates.list()
362         return aggregates
363     except Exception as e:
364         logger.error("Error [get_aggregates(nova_client)]: %s" % e)
365         return None
366
367
368 def get_aggregate_id(nova_client, aggregate_name):
369     try:
370         aggregates = get_aggregates(nova_client)
371         _id = [ag.id for ag in aggregates if ag.name == aggregate_name][0]
372         return _id
373     except Exception as e:
374         logger.error("Error [get_aggregate_id(nova_client, %s)]:"
375                      " %s" % (aggregate_name, e))
376         return None
377
378
379 def get_availability_zones(nova_client):
380     try:
381         availability_zones = nova_client.availability_zones.list()
382         return availability_zones
383     except Exception as e:
384         logger.error("Error [get_availability_zones(nova_client)]: %s" % e)
385         return None
386
387
388 def get_availability_zone_names(nova_client):
389     try:
390         az_names = [az.zoneName for az in get_availability_zones(nova_client)]
391         return az_names
392     except Exception as e:
393         logger.error("Error [get_availability_zone_names(nova_client)]:"
394                      " %s" % e)
395         return None
396
397
398 def create_flavor(nova_client, flavor_name, ram, disk, vcpus, public=True):
399     try:
400         flavor = nova_client.flavors.create(
401             flavor_name, ram, vcpus, disk, is_public=public)
402         try:
403             extra_specs = ft_utils.get_functest_config(
404                 'general.flavor_extra_specs')
405             flavor.set_keys(extra_specs)
406         except ValueError:
407             # flavor extra specs are not configured, therefore skip the update
408             pass
409
410     except Exception as e:
411         logger.error("Error [create_flavor(nova_client, '%s', '%s', '%s', "
412                      "'%s')]: %s" % (flavor_name, ram, disk, vcpus, e))
413         return None
414     return flavor.id
415
416
417 def get_or_create_flavor(flavor_name, ram, disk, vcpus, public=True):
418     flavor_exists = False
419     nova_client = get_nova_client()
420
421     flavor_id = get_flavor_id(nova_client, flavor_name)
422     if flavor_id != '':
423         logger.info("Using existing flavor '%s'..." % flavor_name)
424         flavor_exists = True
425     else:
426         logger.info("Creating flavor '%s' with '%s' RAM, '%s' disk size, "
427                     "'%s' vcpus..." % (flavor_name, ram, disk, vcpus))
428         flavor_id = create_flavor(
429             nova_client, flavor_name, ram, disk, vcpus, public=public)
430         if not flavor_id:
431             raise Exception("Failed to create flavor '%s'..." % (flavor_name))
432         else:
433             logger.debug("Flavor '%s' with ID=%s created successfully."
434                          % (flavor_name, flavor_id))
435
436     return flavor_exists, flavor_id
437
438
439 def get_floating_ips(neutron_client):
440     try:
441         floating_ips = neutron_client.list_floatingips()
442         return floating_ips['floatingips']
443     except Exception as e:
444         logger.error("Error [get_floating_ips(neutron_client)]: %s" % e)
445         return None
446
447
448 def get_hypervisors(nova_client):
449     try:
450         nodes = []
451         hypervisors = nova_client.hypervisors.list()
452         for hypervisor in hypervisors:
453             if hypervisor.state == "up":
454                 nodes.append(hypervisor.hypervisor_hostname)
455         return nodes
456     except Exception as e:
457         logger.error("Error [get_hypervisors(nova_client)]: %s" % e)
458         return None
459
460
461 def create_aggregate(nova_client, aggregate_name, av_zone):
462     try:
463         nova_client.aggregates.create(aggregate_name, av_zone)
464         return True
465     except Exception as e:
466         logger.error("Error [create_aggregate(nova_client, %s, %s)]: %s"
467                      % (aggregate_name, av_zone, e))
468         return None
469
470
471 def add_host_to_aggregate(nova_client, aggregate_name, compute_host):
472     try:
473         aggregate_id = get_aggregate_id(nova_client, aggregate_name)
474         nova_client.aggregates.add_host(aggregate_id, compute_host)
475         return True
476     except Exception as e:
477         logger.error("Error [add_host_to_aggregate(nova_client, %s, %s)]: %s"
478                      % (aggregate_name, compute_host, e))
479         return None
480
481
482 def create_aggregate_with_host(
483         nova_client, aggregate_name, av_zone, compute_host):
484     try:
485         create_aggregate(nova_client, aggregate_name, av_zone)
486         add_host_to_aggregate(nova_client, aggregate_name, compute_host)
487         return True
488     except Exception as e:
489         logger.error("Error [create_aggregate_with_host("
490                      "nova_client, %s, %s, %s)]: %s"
491                      % (aggregate_name, av_zone, compute_host, e))
492         return None
493
494
495 def create_instance(flavor_name,
496                     image_id,
497                     network_id,
498                     instance_name="functest-vm",
499                     confdrive=True,
500                     userdata=None,
501                     av_zone='',
502                     fixed_ip=None,
503                     files=None):
504     nova_client = get_nova_client()
505     try:
506         flavor = nova_client.flavors.find(name=flavor_name)
507     except:
508         flavors = nova_client.flavors.list()
509         logger.error("Error: Flavor '%s' not found. Available flavors are: "
510                      "\n%s" % (flavor_name, flavors))
511         return None
512     if fixed_ip is not None:
513         nics = {"net-id": network_id, "v4-fixed-ip": fixed_ip}
514     else:
515         nics = {"net-id": network_id}
516     if userdata is None:
517         instance = nova_client.servers.create(
518             name=instance_name,
519             flavor=flavor,
520             image=image_id,
521             nics=[nics],
522             availability_zone=av_zone,
523             files=files
524         )
525     else:
526         instance = nova_client.servers.create(
527             name=instance_name,
528             flavor=flavor,
529             image=image_id,
530             nics=[nics],
531             config_drive=confdrive,
532             userdata=userdata,
533             availability_zone=av_zone,
534             files=files
535         )
536     return instance
537
538
539 def create_instance_and_wait_for_active(flavor_name,
540                                         image_id,
541                                         network_id,
542                                         instance_name="",
543                                         config_drive=False,
544                                         userdata="",
545                                         av_zone='',
546                                         fixed_ip=None,
547                                         files=None):
548     SLEEP = 3
549     VM_BOOT_TIMEOUT = 180
550     nova_client = get_nova_client()
551     instance = create_instance(flavor_name,
552                                image_id,
553                                network_id,
554                                instance_name,
555                                config_drive,
556                                userdata,
557                                av_zone=av_zone,
558                                fixed_ip=fixed_ip,
559                                files=files)
560     count = VM_BOOT_TIMEOUT / SLEEP
561     for n in range(count, -1, -1):
562         status = get_instance_status(nova_client, instance)
563         if status.lower() == "active":
564             return instance
565         elif status.lower() == "error":
566             logger.error("The instance %s went to ERROR status."
567                          % instance_name)
568             return None
569         time.sleep(SLEEP)
570     logger.error("Timeout booting the instance %s." % instance_name)
571     return None
572
573
574 def create_floating_ip(neutron_client):
575     extnet_id = get_external_net_id(neutron_client)
576     props = {'floating_network_id': extnet_id}
577     try:
578         ip_json = neutron_client.create_floatingip({'floatingip': props})
579         fip_addr = ip_json['floatingip']['floating_ip_address']
580         fip_id = ip_json['floatingip']['id']
581     except Exception as e:
582         logger.error("Error [create_floating_ip(neutron_client)]: %s" % e)
583         return None
584     return {'fip_addr': fip_addr, 'fip_id': fip_id}
585
586
587 def add_floating_ip(nova_client, server_id, floatingip_addr):
588     try:
589         nova_client.servers.add_floating_ip(server_id, floatingip_addr)
590         return True
591     except Exception as e:
592         logger.error("Error [add_floating_ip(nova_client, '%s', '%s')]: %s"
593                      % (server_id, floatingip_addr, e))
594         return False
595
596
597 def delete_instance(nova_client, instance_id):
598     try:
599         nova_client.servers.force_delete(instance_id)
600         return True
601     except Exception as e:
602         logger.error("Error [delete_instance(nova_client, '%s')]: %s"
603                      % (instance_id, e))
604         return False
605
606
607 def delete_floating_ip(neutron_client, floatingip_id):
608     try:
609         neutron_client.delete_floatingip(floatingip_id)
610         return True
611     except Exception as e:
612         logger.error("Error [delete_floating_ip(neutron_client, '%s')]: %s"
613                      % (floatingip_id, e))
614         return False
615
616
617 def remove_host_from_aggregate(nova_client, aggregate_name, compute_host):
618     try:
619         aggregate_id = get_aggregate_id(nova_client, aggregate_name)
620         nova_client.aggregates.remove_host(aggregate_id, compute_host)
621         return True
622     except Exception as e:
623         logger.error("Error [remove_host_from_aggregate(nova_client, %s, %s)]:"
624                      " %s" % (aggregate_name, compute_host, e))
625         return False
626
627
628 def remove_hosts_from_aggregate(nova_client, aggregate_name):
629     aggregate_id = get_aggregate_id(nova_client, aggregate_name)
630     hosts = nova_client.aggregates.get(aggregate_id).hosts
631     assert(
632         all(remove_host_from_aggregate(nova_client, aggregate_name, host)
633             for host in hosts))
634
635
636 def delete_aggregate(nova_client, aggregate_name):
637     try:
638         remove_hosts_from_aggregate(nova_client, aggregate_name)
639         nova_client.aggregates.delete(aggregate_name)
640         return True
641     except Exception as e:
642         logger.error("Error [delete_aggregate(nova_client, %s)]: %s"
643                      % (aggregate_name, e))
644         return False
645
646
647 # *********************************************
648 #   NEUTRON
649 # *********************************************
650 def get_network_list(neutron_client):
651     network_list = neutron_client.list_networks()['networks']
652     if len(network_list) == 0:
653         return None
654     else:
655         return network_list
656
657
658 def get_router_list(neutron_client):
659     router_list = neutron_client.list_routers()['routers']
660     if len(router_list) == 0:
661         return None
662     else:
663         return router_list
664
665
666 def get_port_list(neutron_client):
667     port_list = neutron_client.list_ports()['ports']
668     if len(port_list) == 0:
669         return None
670     else:
671         return port_list
672
673
674 def get_network_id(neutron_client, network_name):
675     networks = neutron_client.list_networks()['networks']
676     id = ''
677     for n in networks:
678         if n['name'] == network_name:
679             id = n['id']
680             break
681     return id
682
683
684 def get_subnet_id(neutron_client, subnet_name):
685     subnets = neutron_client.list_subnets()['subnets']
686     id = ''
687     for s in subnets:
688         if s['name'] == subnet_name:
689             id = s['id']
690             break
691     return id
692
693
694 def get_router_id(neutron_client, router_name):
695     routers = neutron_client.list_routers()['routers']
696     id = ''
697     for r in routers:
698         if r['name'] == router_name:
699             id = r['id']
700             break
701     return id
702
703
704 def get_private_net(neutron_client):
705     # Checks if there is an existing shared private network
706     networks = neutron_client.list_networks()['networks']
707     if len(networks) == 0:
708         return None
709     for net in networks:
710         if (net['router:external'] is False) and (net['shared'] is True):
711             return net
712     return None
713
714
715 def get_external_net(neutron_client):
716     if (hasattr(CONST, 'EXTERNAL_NETWORK')):
717         return CONST.__getattribute__('EXTERNAL_NETWORK')
718     for network in neutron_client.list_networks()['networks']:
719         if network['router:external']:
720             return network['name']
721     return None
722
723
724 def get_external_net_id(neutron_client):
725     if (hasattr(CONST, 'EXTERNAL_NETWORK')):
726         networks = neutron_client.list_networks(
727             name=CONST.__getattribute__('EXTERNAL_NETWORK'))
728         net_id = networks['networks'][0]['id']
729         return net_id
730     for network in neutron_client.list_networks()['networks']:
731         if network['router:external']:
732             return network['id']
733     return None
734
735
736 def check_neutron_net(neutron_client, net_name):
737     for network in neutron_client.list_networks()['networks']:
738         if network['name'] == net_name:
739             for subnet in network['subnets']:
740                 return True
741     return False
742
743
744 def create_neutron_net(neutron_client, name):
745     json_body = {'network': {'name': name,
746                              'admin_state_up': True}}
747     try:
748         network = neutron_client.create_network(body=json_body)
749         network_dict = network['network']
750         return network_dict['id']
751     except Exception as e:
752         logger.error("Error [create_neutron_net(neutron_client, '%s')]: %s"
753                      % (name, e))
754         return None
755
756
757 def create_neutron_subnet(neutron_client, name, cidr, net_id,
758                           dns=['8.8.8.8', '8.8.4.4']):
759     json_body = {'subnets': [{'name': name, 'cidr': cidr,
760                               'ip_version': 4, 'network_id': net_id,
761                               'dns_nameservers': dns}]}
762
763     try:
764         subnet = neutron_client.create_subnet(body=json_body)
765         return subnet['subnets'][0]['id']
766     except Exception as e:
767         logger.error("Error [create_neutron_subnet(neutron_client, '%s', "
768                      "'%s', '%s')]: %s" % (name, cidr, net_id, e))
769         return None
770
771
772 def create_neutron_router(neutron_client, name):
773     json_body = {'router': {'name': name, 'admin_state_up': True}}
774     try:
775         router = neutron_client.create_router(json_body)
776         return router['router']['id']
777     except Exception as e:
778         logger.error("Error [create_neutron_router(neutron_client, '%s')]: %s"
779                      % (name, e))
780         return None
781
782
783 def create_neutron_port(neutron_client, name, network_id, ip):
784     json_body = {'port': {
785                  'admin_state_up': True,
786                  'name': name,
787                  'network_id': network_id,
788                  'fixed_ips': [{"ip_address": ip}]
789                  }}
790     try:
791         port = neutron_client.create_port(body=json_body)
792         return port['port']['id']
793     except Exception as e:
794         logger.error("Error [create_neutron_port(neutron_client, '%s', '%s', "
795                      "'%s')]: %s" % (name, network_id, ip, e))
796         return None
797
798
799 def update_neutron_net(neutron_client, network_id, shared=False):
800     json_body = {'network': {'shared': shared}}
801     try:
802         neutron_client.update_network(network_id, body=json_body)
803         return True
804     except Exception as e:
805         logger.error("Error [update_neutron_net(neutron_client, '%s', '%s')]: "
806                      "%s" % (network_id, str(shared), e))
807         return False
808
809
810 def update_neutron_port(neutron_client, port_id, device_owner):
811     json_body = {'port': {
812                  'device_owner': device_owner,
813                  }}
814     try:
815         port = neutron_client.update_port(port=port_id,
816                                           body=json_body)
817         return port['port']['id']
818     except Exception as e:
819         logger.error("Error [update_neutron_port(neutron_client, '%s', '%s')]:"
820                      " %s" % (port_id, device_owner, e))
821         return None
822
823
824 def add_interface_router(neutron_client, router_id, subnet_id):
825     json_body = {"subnet_id": subnet_id}
826     try:
827         neutron_client.add_interface_router(router=router_id, body=json_body)
828         return True
829     except Exception as e:
830         logger.error("Error [add_interface_router(neutron_client, '%s', "
831                      "'%s')]: %s" % (router_id, subnet_id, e))
832         return False
833
834
835 def add_gateway_router(neutron_client, router_id):
836     ext_net_id = get_external_net_id(neutron_client)
837     router_dict = {'network_id': ext_net_id}
838     try:
839         neutron_client.add_gateway_router(router_id, router_dict)
840         return True
841     except Exception as e:
842         logger.error("Error [add_gateway_router(neutron_client, '%s')]: %s"
843                      % (router_id, e))
844         return False
845
846
847 def delete_neutron_net(neutron_client, network_id):
848     try:
849         neutron_client.delete_network(network_id)
850         return True
851     except Exception as e:
852         logger.error("Error [delete_neutron_net(neutron_client, '%s')]: %s"
853                      % (network_id, e))
854         return False
855
856
857 def delete_neutron_subnet(neutron_client, subnet_id):
858     try:
859         neutron_client.delete_subnet(subnet_id)
860         return True
861     except Exception as e:
862         logger.error("Error [delete_neutron_subnet(neutron_client, '%s')]: %s"
863                      % (subnet_id, e))
864         return False
865
866
867 def delete_neutron_router(neutron_client, router_id):
868     try:
869         neutron_client.delete_router(router=router_id)
870         return True
871     except Exception as e:
872         logger.error("Error [delete_neutron_router(neutron_client, '%s')]: %s"
873                      % (router_id, e))
874         return False
875
876
877 def delete_neutron_port(neutron_client, port_id):
878     try:
879         neutron_client.delete_port(port_id)
880         return True
881     except Exception as e:
882         logger.error("Error [delete_neutron_port(neutron_client, '%s')]: %s"
883                      % (port_id, e))
884         return False
885
886
887 def remove_interface_router(neutron_client, router_id, subnet_id):
888     json_body = {"subnet_id": subnet_id}
889     try:
890         neutron_client.remove_interface_router(router=router_id,
891                                                body=json_body)
892         return True
893     except Exception as e:
894         logger.error("Error [remove_interface_router(neutron_client, '%s', "
895                      "'%s')]: %s" % (router_id, subnet_id, e))
896         return False
897
898
899 def remove_gateway_router(neutron_client, router_id):
900     try:
901         neutron_client.remove_gateway_router(router_id)
902         return True
903     except Exception as e:
904         logger.error("Error [remove_gateway_router(neutron_client, '%s')]: %s"
905                      % (router_id, e))
906         return False
907
908
909 def create_network_full(neutron_client,
910                         net_name,
911                         subnet_name,
912                         router_name,
913                         cidr,
914                         dns=['8.8.8.8', '8.8.4.4']):
915
916     # Check if the network already exists
917     network_id = get_network_id(neutron_client, net_name)
918     subnet_id = get_subnet_id(neutron_client, subnet_name)
919     router_id = get_router_id(neutron_client, router_name)
920
921     if network_id != '' and subnet_id != '' and router_id != '':
922         logger.info("A network with name '%s' already exists..." % net_name)
923     else:
924         neutron_client.format = 'json'
925         logger.info('Creating neutron network %s...' % net_name)
926         network_id = create_neutron_net(neutron_client, net_name)
927
928         if not network_id:
929             return False
930
931         logger.debug("Network '%s' created successfully" % network_id)
932         logger.debug('Creating Subnet....')
933         subnet_id = create_neutron_subnet(neutron_client, subnet_name,
934                                           cidr, network_id, dns)
935         if not subnet_id:
936             return None
937
938         logger.debug("Subnet '%s' created successfully" % subnet_id)
939         logger.debug('Creating Router...')
940         router_id = create_neutron_router(neutron_client, router_name)
941
942         if not router_id:
943             return None
944
945         logger.debug("Router '%s' created successfully" % router_id)
946         logger.debug('Adding router to subnet...')
947
948         if not add_interface_router(neutron_client, router_id, subnet_id):
949             return None
950
951         logger.debug("Interface added successfully.")
952
953         logger.debug('Adding gateway to router...')
954         if not add_gateway_router(neutron_client, router_id):
955             return None
956
957         logger.debug("Gateway added successfully.")
958
959     network_dic = {'net_id': network_id,
960                    'subnet_id': subnet_id,
961                    'router_id': router_id}
962     return network_dic
963
964
965 def create_shared_network_full(net_name, subnt_name, router_name, subnet_cidr):
966     neutron_client = get_neutron_client()
967
968     network_dic = create_network_full(neutron_client,
969                                       net_name,
970                                       subnt_name,
971                                       router_name,
972                                       subnet_cidr)
973     if network_dic:
974         if not update_neutron_net(neutron_client,
975                                   network_dic['net_id'],
976                                   shared=True):
977             logger.error("Failed to update network %s..." % net_name)
978             return None
979         else:
980             logger.debug("Network '%s' is available..." % net_name)
981     else:
982         logger.error("Network %s creation failed" % net_name)
983         return None
984     return network_dic
985
986
987 # *********************************************
988 #   SEC GROUPS
989 # *********************************************
990
991
992 def get_security_groups(neutron_client):
993     try:
994         security_groups = neutron_client.list_security_groups()[
995             'security_groups']
996         return security_groups
997     except Exception as e:
998         logger.error("Error [get_security_groups(neutron_client)]: %s" % e)
999         return None
1000
1001
1002 def get_security_group_id(neutron_client, sg_name):
1003     security_groups = get_security_groups(neutron_client)
1004     id = ''
1005     for sg in security_groups:
1006         if sg['name'] == sg_name:
1007             id = sg['id']
1008             break
1009     return id
1010
1011
1012 def create_security_group(neutron_client, sg_name, sg_description):
1013     json_body = {'security_group': {'name': sg_name,
1014                                     'description': sg_description}}
1015     try:
1016         secgroup = neutron_client.create_security_group(json_body)
1017         return secgroup['security_group']
1018     except Exception as e:
1019         logger.error("Error [create_security_group(neutron_client, '%s', "
1020                      "'%s')]: %s" % (sg_name, sg_description, e))
1021         return None
1022
1023
1024 def create_secgroup_rule(neutron_client, sg_id, direction, protocol,
1025                          port_range_min=None, port_range_max=None):
1026     # We create a security group in 2 steps
1027     # 1 - we check the format and set the json body accordingly
1028     # 2 - we call neturon client to create the security group
1029
1030     # Format check
1031     json_body = {'security_group_rule': {'direction': direction,
1032                                          'security_group_id': sg_id,
1033                                          'protocol': protocol}}
1034     # parameters may be
1035     # - both None => we do nothing
1036     # - both Not None => we add them to the json description
1037     # but one cannot be None is the other is not None
1038     if (port_range_min is not None and port_range_max is not None):
1039         # add port_range in json description
1040         json_body['security_group_rule']['port_range_min'] = port_range_min
1041         json_body['security_group_rule']['port_range_max'] = port_range_max
1042         logger.debug("Security_group format set (port range included)")
1043     else:
1044         # either both port range are set to None => do nothing
1045         # or one is set but not the other => log it and return False
1046         if port_range_min is None and port_range_max is None:
1047             logger.debug("Security_group format set (no port range mentioned)")
1048         else:
1049             logger.error("Bad security group format."
1050                          "One of the port range is not properly set:"
1051                          "range min: {},"
1052                          "range max: {}".format(port_range_min,
1053                                                 port_range_max))
1054             return False
1055
1056     # Create security group using neutron client
1057     try:
1058         neutron_client.create_security_group_rule(json_body)
1059         return True
1060     except:
1061         logger.exception("Impossible to create_security_group_rule,"
1062                          "security group rule probably already exists")
1063         return False
1064
1065
1066 def get_security_group_rules(neutron_client, sg_id):
1067     try:
1068         security_rules = neutron_client.list_security_group_rules()[
1069             'security_group_rules']
1070         security_rules = [rule for rule in security_rules
1071                           if rule["security_group_id"] == sg_id]
1072         return security_rules
1073     except Exception as e:
1074         logger.error("Error [get_security_group_rules(neutron_client, sg_id)]:"
1075                      " %s" % e)
1076         return None
1077
1078
1079 def check_security_group_rules(neutron_client, sg_id, direction, protocol,
1080                                port_min=None, port_max=None):
1081     try:
1082         security_rules = get_security_group_rules(neutron_client, sg_id)
1083         security_rules = [rule for rule in security_rules
1084                           if (rule["direction"].lower() == direction and
1085                               rule["protocol"].lower() == protocol and
1086                               rule["port_range_min"] == port_min and
1087                               rule["port_range_max"] == port_max)]
1088         if len(security_rules) == 0:
1089             return True
1090         else:
1091             return False
1092     except Exception as e:
1093         logger.error("Error [check_security_group_rules("
1094                      " neutron_client, sg_id, direction,"
1095                      " protocol, port_min=None, port_max=None)]: "
1096                      "%s" % e)
1097         return None
1098
1099
1100 def create_security_group_full(neutron_client,
1101                                sg_name, sg_description):
1102     sg_id = get_security_group_id(neutron_client, sg_name)
1103     if sg_id != '':
1104         logger.info("Using existing security group '%s'..." % sg_name)
1105     else:
1106         logger.info("Creating security group  '%s'..." % sg_name)
1107         SECGROUP = create_security_group(neutron_client,
1108                                          sg_name,
1109                                          sg_description)
1110         if not SECGROUP:
1111             logger.error("Failed to create the security group...")
1112             return None
1113
1114         sg_id = SECGROUP['id']
1115
1116         logger.debug("Security group '%s' with ID=%s created successfully."
1117                      % (SECGROUP['name'], sg_id))
1118
1119         logger.debug("Adding ICMP rules in security group '%s'..."
1120                      % sg_name)
1121         if not create_secgroup_rule(neutron_client, sg_id,
1122                                     'ingress', 'icmp'):
1123             logger.error("Failed to create the security group rule...")
1124             return None
1125
1126         logger.debug("Adding SSH rules in security group '%s'..."
1127                      % sg_name)
1128         if not create_secgroup_rule(
1129                 neutron_client, sg_id, 'ingress', 'tcp', '22', '22'):
1130             logger.error("Failed to create the security group rule...")
1131             return None
1132
1133         if not create_secgroup_rule(
1134                 neutron_client, sg_id, 'egress', 'tcp', '22', '22'):
1135             logger.error("Failed to create the security group rule...")
1136             return None
1137     return sg_id
1138
1139
1140 def add_secgroup_to_instance(nova_client, instance_id, secgroup_id):
1141     try:
1142         nova_client.servers.add_security_group(instance_id, secgroup_id)
1143         return True
1144     except Exception as e:
1145         logger.error("Error [add_secgroup_to_instance(nova_client, '%s', "
1146                      "'%s')]: %s" % (instance_id, secgroup_id, e))
1147         return False
1148
1149
1150 def update_sg_quota(neutron_client, tenant_id, sg_quota, sg_rule_quota):
1151     json_body = {"quota": {
1152         "security_group": sg_quota,
1153         "security_group_rule": sg_rule_quota
1154     }}
1155
1156     try:
1157         neutron_client.update_quota(tenant_id=tenant_id,
1158                                     body=json_body)
1159         return True
1160     except Exception as e:
1161         logger.error("Error [update_sg_quota(neutron_client, '%s', '%s', "
1162                      "'%s')]: %s" % (tenant_id, sg_quota, sg_rule_quota, e))
1163         return False
1164
1165
1166 def delete_security_group(neutron_client, secgroup_id):
1167     try:
1168         neutron_client.delete_security_group(secgroup_id)
1169         return True
1170     except Exception as e:
1171         logger.error("Error [delete_security_group(neutron_client, '%s')]: %s"
1172                      % (secgroup_id, e))
1173         return False
1174
1175
1176 # *********************************************
1177 #   GLANCE
1178 # *********************************************
1179 def get_images(glance_client):
1180     try:
1181         images = glance_client.images.list()
1182         return images
1183     except Exception as e:
1184         logger.error("Error [get_images]: %s" % e)
1185         return None
1186
1187
1188 def get_image_id(glance_client, image_name):
1189     images = glance_client.images.list()
1190     id = ''
1191     for i in images:
1192         if i.name == image_name:
1193             id = i.id
1194             break
1195     return id
1196
1197
1198 def create_glance_image(glance_client, image_name, file_path, disk="qcow2",
1199                         container="bare", public="public"):
1200     if not os.path.isfile(file_path):
1201         logger.error("Error: file %s does not exist." % file_path)
1202         return None
1203     try:
1204         image_id = get_image_id(glance_client, image_name)
1205         if image_id != '':
1206             logger.info("Image %s already exists." % image_name)
1207         else:
1208             logger.info("Creating image '%s' from '%s'..." % (image_name,
1209                                                               file_path))
1210
1211             image = glance_client.images.create(name=image_name,
1212                                                 visibility=public,
1213                                                 disk_format=disk,
1214                                                 container_format=container)
1215             image_id = image.id
1216             with open(file_path) as image_data:
1217                 glance_client.images.upload(image_id, image_data)
1218         return image_id
1219     except Exception as e:
1220         logger.error("Error [create_glance_image(glance_client, '%s', '%s', "
1221                      "'%s')]: %s" % (image_name, file_path, public, e))
1222         return None
1223
1224
1225 def get_or_create_image(name, path, format):
1226     image_exists = False
1227     glance_client = get_glance_client()
1228
1229     image_id = get_image_id(glance_client, name)
1230     if image_id != '':
1231         logger.info("Using existing image '%s'..." % name)
1232         image_exists = True
1233     else:
1234         logger.info("Creating image '%s' from '%s'..." % (name, path))
1235         image_id = create_glance_image(glance_client, name, path, format)
1236         if not image_id:
1237             logger.error("Failed to create a Glance image...")
1238         else:
1239             logger.debug("Image '%s' with ID=%s created successfully."
1240                          % (name, image_id))
1241
1242     return image_exists, image_id
1243
1244
1245 def delete_glance_image(glance_client, image_id):
1246     try:
1247         glance_client.images.delete(image_id)
1248         return True
1249     except Exception as e:
1250         logger.error("Error [delete_glance_image(glance_client, '%s')]: %s"
1251                      % (image_id, e))
1252         return False
1253
1254
1255 # *********************************************
1256 #   CINDER
1257 # *********************************************
1258 def get_volumes(cinder_client):
1259     try:
1260         volumes = cinder_client.volumes.list(search_opts={'all_tenants': 1})
1261         return volumes
1262     except Exception as e:
1263         logger.error("Error [get_volumes(cinder_client)]: %s" % e)
1264         return None
1265
1266
1267 def list_volume_types(cinder_client, public=True, private=True):
1268     try:
1269         volume_types = cinder_client.volume_types.list()
1270         if not public:
1271             volume_types = [vt for vt in volume_types if not vt.is_public]
1272         if not private:
1273             volume_types = [vt for vt in volume_types if vt.is_public]
1274         return volume_types
1275     except Exception as e:
1276         logger.error("Error [list_volume_types(cinder_client)]: %s" % e)
1277         return None
1278
1279
1280 def create_volume_type(cinder_client, name):
1281     try:
1282         volume_type = cinder_client.volume_types.create(name)
1283         return volume_type
1284     except Exception as e:
1285         logger.error("Error [create_volume_type(cinder_client, '%s')]: %s"
1286                      % (name, e))
1287         return None
1288
1289
1290 def update_cinder_quota(cinder_client, tenant_id, vols_quota,
1291                         snapshots_quota, gigabytes_quota):
1292     quotas_values = {"volumes": vols_quota,
1293                      "snapshots": snapshots_quota,
1294                      "gigabytes": gigabytes_quota}
1295
1296     try:
1297         cinder_client.quotas.update(tenant_id, **quotas_values)
1298         return True
1299     except Exception as e:
1300         logger.error("Error [update_cinder_quota(cinder_client, '%s', '%s', "
1301                      "'%s' '%s')]: %s" % (tenant_id, vols_quota,
1302                                           snapshots_quota, gigabytes_quota, e))
1303         return False
1304
1305
1306 def delete_volume(cinder_client, volume_id, forced=False):
1307     try:
1308         if forced:
1309             try:
1310                 cinder_client.volumes.detach(volume_id)
1311             except:
1312                 logger.error(sys.exc_info()[0])
1313             cinder_client.volumes.force_delete(volume_id)
1314         else:
1315             cinder_client.volumes.delete(volume_id)
1316         return True
1317     except Exception as e:
1318         logger.error("Error [delete_volume(cinder_client, '%s', '%s')]: %s"
1319                      % (volume_id, str(forced), e))
1320         return False
1321
1322
1323 def delete_volume_type(cinder_client, volume_type):
1324     try:
1325         cinder_client.volume_types.delete(volume_type)
1326         return True
1327     except Exception as e:
1328         logger.error("Error [delete_volume_type(cinder_client, '%s')]: %s"
1329                      % (volume_type, e))
1330         return False
1331
1332
1333 # *********************************************
1334 #   KEYSTONE
1335 # *********************************************
1336 def get_tenants(keystone_client):
1337     try:
1338         if is_keystone_v3():
1339             tenants = keystone_client.projects.list()
1340         else:
1341             tenants = keystone_client.tenants.list()
1342         return tenants
1343     except Exception as e:
1344         logger.error("Error [get_tenants(keystone_client)]: %s" % e)
1345         return None
1346
1347
1348 def get_users(keystone_client):
1349     try:
1350         users = keystone_client.users.list()
1351         return users
1352     except Exception as e:
1353         logger.error("Error [get_users(keystone_client)]: %s" % e)
1354         return None
1355
1356
1357 def get_tenant_id(keystone_client, tenant_name):
1358     tenants = get_tenants(keystone_client)
1359     id = ''
1360     for t in tenants:
1361         if t.name == tenant_name:
1362             id = t.id
1363             break
1364     return id
1365
1366
1367 def get_user_id(keystone_client, user_name):
1368     users = get_users(keystone_client)
1369     id = ''
1370     for u in users:
1371         if u.name == user_name:
1372             id = u.id
1373             break
1374     return id
1375
1376
1377 def get_role_id(keystone_client, role_name):
1378     roles = keystone_client.roles.list()
1379     id = ''
1380     for r in roles:
1381         if r.name == role_name:
1382             id = r.id
1383             break
1384     return id
1385
1386
1387 def get_domain_id(keystone_client, domain_name):
1388     domains = keystone_client.domains.list()
1389     id = ''
1390     for d in domains:
1391         if d.name == domain_name:
1392             id = d.id
1393             break
1394     return id
1395
1396
1397 def create_tenant(keystone_client, tenant_name, tenant_description):
1398     try:
1399         if is_keystone_v3():
1400             domain_name = CONST.__getattribute__('OS_PROJECT_DOMAIN_NAME')
1401             domain_id = get_domain_id(keystone_client, domain_name)
1402             tenant = keystone_client.projects.create(
1403                 name=tenant_name,
1404                 description=tenant_description,
1405                 domain=domain_id,
1406                 enabled=True)
1407         else:
1408             tenant = keystone_client.tenants.create(tenant_name,
1409                                                     tenant_description,
1410                                                     enabled=True)
1411         return tenant.id
1412     except Exception as e:
1413         logger.error("Error [create_tenant(keystone_client, '%s', '%s')]: %s"
1414                      % (tenant_name, tenant_description, e))
1415         return None
1416
1417
1418 def get_or_create_tenant(keystone_client, tenant_name, tenant_description):
1419     tenant_id = get_tenant_id(keystone_client, tenant_name)
1420     if not tenant_id:
1421         tenant_id = create_tenant(keystone_client, tenant_name,
1422                                   tenant_description)
1423
1424     return tenant_id
1425
1426
1427 def get_or_create_tenant_for_vnf(keystone_client, tenant_name,
1428                                  tenant_description):
1429     """Get or Create a Tenant
1430
1431         Args:
1432             keystone_client: keystone client reference
1433             tenant_name: the name of the tenant
1434             tenant_description: the description of the tenant
1435
1436         return False if tenant retrieved though get
1437         return True if tenant created
1438         raise Exception if error during processing
1439     """
1440     try:
1441         tenant_id = get_tenant_id(keystone_client, tenant_name)
1442         if not tenant_id:
1443             tenant_id = create_tenant(keystone_client, tenant_name,
1444                                       tenant_description)
1445             return True
1446         else:
1447             return False
1448     except:
1449         raise Exception("Impossible to create a Tenant for the VNF {}".format(
1450                             tenant_name))
1451
1452
1453 def create_user(keystone_client, user_name, user_password,
1454                 user_email, tenant_id):
1455     try:
1456         if is_keystone_v3():
1457             user = keystone_client.users.create(name=user_name,
1458                                                 password=user_password,
1459                                                 email=user_email,
1460                                                 project_id=tenant_id,
1461                                                 enabled=True)
1462         else:
1463             user = keystone_client.users.create(user_name,
1464                                                 user_password,
1465                                                 user_email,
1466                                                 tenant_id,
1467                                                 enabled=True)
1468         return user.id
1469     except Exception as e:
1470         logger.error("Error [create_user(keystone_client, '%s', '%s', '%s'"
1471                      "'%s')]: %s" % (user_name, user_password,
1472                                      user_email, tenant_id, e))
1473         return None
1474
1475
1476 def get_or_create_user(keystone_client, user_name, user_password,
1477                        tenant_id, user_email=None):
1478     user_id = get_user_id(keystone_client, user_name)
1479     if not user_id:
1480         user_id = create_user(keystone_client, user_name, user_password,
1481                               user_email, tenant_id)
1482     return user_id
1483
1484
1485 def get_or_create_user_for_vnf(keystone_client, vnf_ref):
1486     """Get or Create user for VNF
1487
1488         Args:
1489             keystone_client: keystone client reference
1490             vnf_ref: VNF reference used as user name & password, tenant name
1491
1492         return False if user retrieved through get
1493         return True if user created
1494         raise Exception if error during processing
1495     """
1496     try:
1497         user_id = get_user_id(keystone_client, vnf_ref)
1498         tenant_id = get_tenant_id(keystone_client, vnf_ref)
1499         created = False
1500         if not user_id:
1501             user_id = create_user(keystone_client, vnf_ref, vnf_ref,
1502                                   "", tenant_id)
1503             created = True
1504         try:
1505             role_id = get_role_id(keystone_client, 'admin')
1506             tenant_id = get_tenant_id(keystone_client, vnf_ref)
1507             add_role_user(keystone_client, user_id, role_id, tenant_id)
1508         except:
1509             logger.warn("Cannot associate user to role admin on tenant")
1510         return created
1511     except:
1512         raise Exception("Impossible to create a user for the VNF {}".format(
1513             vnf_ref))
1514
1515
1516 def add_role_user(keystone_client, user_id, role_id, tenant_id):
1517     try:
1518         if is_keystone_v3():
1519             keystone_client.roles.grant(role=role_id,
1520                                         user=user_id,
1521                                         project=tenant_id)
1522         else:
1523             keystone_client.roles.add_user_role(user_id, role_id, tenant_id)
1524         return True
1525     except Exception as e:
1526         logger.error("Error [add_role_user(keystone_client, '%s', '%s'"
1527                      "'%s')]: %s " % (user_id, role_id, tenant_id, e))
1528         return False
1529
1530
1531 def delete_tenant(keystone_client, tenant_id):
1532     try:
1533         if is_keystone_v3():
1534             keystone_client.projects.delete(tenant_id)
1535         else:
1536             keystone_client.tenants.delete(tenant_id)
1537         return True
1538     except Exception as e:
1539         logger.error("Error [delete_tenant(keystone_client, '%s')]: %s"
1540                      % (tenant_id, e))
1541         return False
1542
1543
1544 def delete_user(keystone_client, user_id):
1545     try:
1546         keystone_client.users.delete(user_id)
1547         return True
1548     except Exception as e:
1549         logger.error("Error [delete_user(keystone_client, '%s')]: %s"
1550                      % (user_id, e))
1551         return False
1552
1553
1554 # *********************************************
1555 #   HEAT
1556 # *********************************************
1557 def get_resource(heat_client, stack_id, resource):
1558     try:
1559         resources = heat_client.resources.get(stack_id, resource)
1560         return resources
1561     except Exception as e:
1562         logger.error("Error [get_resource]: %s" % e)
1563         return None
1564
1565
1566 # *********************************************
1567 #   TEMPEST
1568 # *********************************************
1569 def init_tempest_cleanup(tempest_config_dir=None,
1570                          tempest_config_filename='tempest.conf',
1571                          output_file=None):
1572     """
1573     Initialize the Tempest Cleanup utility.
1574     See  https://docs.openstack.org/tempest/latest/cleanup.html for docs.
1575
1576     :param tempest_config_dir: The directory where the Tempest config file is
1577             located. If not specified, we let Tempest pick both the directory
1578             and the filename (i.e. second parameter is ignored)
1579     :param tempest_config_filename: The filename of the Tempest config file
1580     :param output_file: Optional file where to save output
1581     """
1582     # The Tempest cleanup utility currently offers no cmd argument to specify
1583     # the config file, therefore it has to be configured with env variables
1584     env = None
1585     if tempest_config_dir:
1586         env = os.environ.copy()
1587         env['TEMPEST_CONFIG_DIR'] = tempest_config_dir
1588         env['TEMPEST_CONFIG'] = tempest_config_filename
1589
1590     # If this command fails, an exception must be raised to stop the script
1591     # otherwise the later cleanup would destroy also other resources
1592     cmd_line = "tempest cleanup --init-saved-state"
1593     ft_utils.execute_command_raise(cmd_line, env=env, output_file=output_file,
1594                                    error_msg="Tempest cleanup init failed")
1595
1596
1597 def perform_tempest_cleanup(tempest_config_dir=None,
1598                             tempest_config_filename='tempest.conf',
1599                             output_file=None):
1600     """
1601     Perform cleanup using the Tempest Cleanup utility.
1602     See  https://docs.openstack.org/tempest/latest/cleanup.html for docs.
1603
1604     :param tempest_config_dir: The directory where the Tempest config file is
1605             located. If not specified, we let Tempest pick both the directory
1606             and the filename (i.e. second parameter is ignored)
1607     :param tempest_config_filename: The filename of the Tempest config file
1608     :param output_file: Optional file where to save output
1609     """
1610     # The Tempest cleanup utility currently offers no cmd argument to specify
1611     # the config file, therefore it has to be configured with env variables
1612     env = None
1613     if tempest_config_dir:
1614         env = os.environ.copy()
1615         env['TEMPEST_CONFIG_DIR'] = tempest_config_dir
1616         env['TEMPEST_CONFIG'] = tempest_config_filename
1617
1618     # If this command fails, an exception must be raised to stop the script
1619     # otherwise the later cleanup would destroy also other resources
1620     cmd_line = "tempest cleanup"
1621     ft_utils.execute_command(cmd_line, env=env, output_file=output_file,
1622                              error_msg="Tempest cleanup failed")