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