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