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