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