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