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