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