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