Merge "DPDK: detect dpdk_make_arch based on image arch"
[yardstick.git] / yardstick / common / openstack_utils.py
1 ##############################################################################
2 # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 import os
11 import time
12 import sys
13 import logging
14
15 from keystoneauth1 import loading
16 from keystoneauth1 import session
17 import shade
18 from shade import exc
19
20 from cinderclient import client as cinderclient
21 from novaclient import client as novaclient
22 from glanceclient import client as glanceclient
23 from neutronclient.neutron import client as neutronclient
24
25
26 log = logging.getLogger(__name__)
27
28 DEFAULT_HEAT_API_VERSION = '1'
29 DEFAULT_API_VERSION = '2'
30
31
32 # *********************************************
33 #   CREDENTIALS
34 # *********************************************
35 def get_credentials():
36     """Returns a creds dictionary filled with parsed from env
37
38     Keystone API version used is 3; v2 was deprecated in 2014 (Icehouse). Along
39     with this deprecation, environment variable 'OS_TENANT_NAME' is replaced by
40     'OS_PROJECT_NAME'.
41     """
42     creds = {'username': os.environ.get('OS_USERNAME'),
43              'password': os.environ.get('OS_PASSWORD'),
44              'auth_url': os.environ.get('OS_AUTH_URL'),
45              'project_name': os.environ.get('OS_PROJECT_NAME')
46              }
47
48     if os.getenv('OS_USER_DOMAIN_NAME'):
49         creds['user_domain_name'] = os.getenv('OS_USER_DOMAIN_NAME')
50     if os.getenv('OS_PROJECT_DOMAIN_NAME'):
51         creds['project_domain_name'] = os.getenv('OS_PROJECT_DOMAIN_NAME')
52
53     return creds
54
55
56 def get_session_auth():
57     loader = loading.get_plugin_loader('password')
58     creds = get_credentials()
59     auth = loader.load_from_options(**creds)
60     return auth
61
62
63 def get_session():
64     auth = get_session_auth()
65     try:
66         cacert = os.environ['OS_CACERT']
67     except KeyError:
68         return session.Session(auth=auth)
69     else:
70         insecure = os.getenv('OS_INSECURE', '').lower() == 'true'
71         cacert = False if insecure else cacert
72         return session.Session(auth=auth, verify=cacert)
73
74
75 def get_endpoint(service_type, endpoint_type='publicURL'):
76     auth = get_session_auth()
77     # for multi-region, we need to specify region
78     # when finding the endpoint
79     return get_session().get_endpoint(auth=auth,
80                                       service_type=service_type,
81                                       endpoint_type=endpoint_type,
82                                       region_name=os.environ.get(
83                                           "OS_REGION_NAME"))
84
85
86 # *********************************************
87 #   CLIENTS
88 # *********************************************
89 def get_heat_api_version():     # pragma: no cover
90     try:
91         api_version = os.environ['HEAT_API_VERSION']
92     except KeyError:
93         return DEFAULT_HEAT_API_VERSION
94     else:
95         log.info("HEAT_API_VERSION is set in env as '%s'", api_version)
96         return api_version
97
98
99 def get_cinder_client_version():      # pragma: no cover
100     try:
101         api_version = os.environ['OS_VOLUME_API_VERSION']
102     except KeyError:
103         return DEFAULT_API_VERSION
104     else:
105         log.info("OS_VOLUME_API_VERSION is set in env as '%s'", api_version)
106         return api_version
107
108
109 def get_cinder_client():      # pragma: no cover
110     sess = get_session()
111     return cinderclient.Client(get_cinder_client_version(), session=sess)
112
113
114 def get_nova_client_version():      # pragma: no cover
115     try:
116         api_version = os.environ['OS_COMPUTE_API_VERSION']
117     except KeyError:
118         return DEFAULT_API_VERSION
119     else:
120         log.info("OS_COMPUTE_API_VERSION is set in env as '%s'", api_version)
121         return api_version
122
123
124 def get_nova_client():      # pragma: no cover
125     sess = get_session()
126     return novaclient.Client(get_nova_client_version(), session=sess)
127
128
129 def get_neutron_client_version():   # pragma: no cover
130     try:
131         api_version = os.environ['OS_NETWORK_API_VERSION']
132     except KeyError:
133         return DEFAULT_API_VERSION
134     else:
135         log.info("OS_NETWORK_API_VERSION is set in env as '%s'", api_version)
136         return api_version
137
138
139 def get_neutron_client():   # pragma: no cover
140     sess = get_session()
141     return neutronclient.Client(get_neutron_client_version(), session=sess)
142
143
144 def get_glance_client_version():    # pragma: no cover
145     try:
146         api_version = os.environ['OS_IMAGE_API_VERSION']
147     except KeyError:
148         return DEFAULT_API_VERSION
149     else:
150         log.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version)
151         return api_version
152
153
154 def get_glance_client():    # pragma: no cover
155     sess = get_session()
156     return glanceclient.Client(get_glance_client_version(), session=sess)
157
158
159 def get_shade_client():
160     return shade.openstack_cloud()
161
162
163 # *********************************************
164 #   NOVA
165 # *********************************************
166 def get_instances(nova_client):
167     try:
168         return nova_client.servers.list(search_opts={'all_tenants': 1})
169     except Exception:  # pylint: disable=broad-except
170         log.exception("Error [get_instances(nova_client)]")
171
172
173 def get_instance_status(nova_client, instance):     # pragma: no cover
174     try:
175         return nova_client.servers.get(instance.id).status
176     except Exception:  # pylint: disable=broad-except
177         log.exception("Error [get_instance_status(nova_client)]")
178
179
180 def get_instance_by_name(nova_client, instance_name):   # pragma: no cover
181     try:
182         return nova_client.servers.find(name=instance_name)
183     except Exception:  # pylint: disable=broad-except
184         log.exception("Error [get_instance_by_name(nova_client, '%s')]",
185                       instance_name)
186
187
188 def get_aggregates(nova_client):    # pragma: no cover
189     try:
190         return nova_client.aggregates.list()
191     except Exception:  # pylint: disable=broad-except
192         log.exception("Error [get_aggregates(nova_client)]")
193
194
195 def get_availability_zones(nova_client):    # pragma: no cover
196     try:
197         return nova_client.availability_zones.list()
198     except Exception:  # pylint: disable=broad-except
199         log.exception("Error [get_availability_zones(nova_client)]")
200
201
202 def get_availability_zone_names(nova_client):   # pragma: no cover
203     try:
204         return [az.zoneName for az in get_availability_zones(nova_client)]
205     except Exception:  # pylint: disable=broad-except
206         log.exception("Error [get_availability_zone_names(nova_client)]")
207
208
209 def create_aggregate(nova_client, aggregate_name, av_zone):  # pragma: no cover
210     try:
211         nova_client.aggregates.create(aggregate_name, av_zone)
212     except Exception:  # pylint: disable=broad-except
213         log.exception("Error [create_aggregate(nova_client, %s, %s)]",
214                       aggregate_name, av_zone)
215         return False
216     else:
217         return True
218
219
220 def get_aggregate_id(nova_client, aggregate_name):      # pragma: no cover
221     try:
222         aggregates = get_aggregates(nova_client)
223         _id = next((ag.id for ag in aggregates if ag.name == aggregate_name))
224     except Exception:  # pylint: disable=broad-except
225         log.exception("Error [get_aggregate_id(nova_client, %s)]",
226                       aggregate_name)
227     else:
228         return _id
229
230
231 def add_host_to_aggregate(nova_client, aggregate_name,
232                           compute_host):    # pragma: no cover
233     try:
234         aggregate_id = get_aggregate_id(nova_client, aggregate_name)
235         nova_client.aggregates.add_host(aggregate_id, compute_host)
236     except Exception:  # pylint: disable=broad-except
237         log.exception("Error [add_host_to_aggregate(nova_client, %s, %s)]",
238                       aggregate_name, compute_host)
239         return False
240     else:
241         return True
242
243
244 def create_aggregate_with_host(nova_client, aggregate_name, av_zone,
245                                compute_host):    # pragma: no cover
246     try:
247         create_aggregate(nova_client, aggregate_name, av_zone)
248         add_host_to_aggregate(nova_client, aggregate_name, compute_host)
249     except Exception:  # pylint: disable=broad-except
250         log.exception("Error [create_aggregate_with_host("
251                       "nova_client, %s, %s, %s)]",
252                       aggregate_name, av_zone, compute_host)
253         return False
254     else:
255         return True
256
257
258 def create_keypair(name, key_path=None):    # pragma: no cover
259     try:
260         with open(key_path) as fpubkey:
261             keypair = get_nova_client().keypairs.create(
262                 name=name, public_key=fpubkey.read())
263             return keypair
264     except Exception:  # pylint: disable=broad-except
265         log.exception("Error [create_keypair(nova_client)]")
266
267
268 def create_instance(json_body):    # pragma: no cover
269     try:
270         return get_nova_client().servers.create(**json_body)
271     except Exception:  # pylint: disable=broad-except
272         log.exception("Error create instance failed")
273         return None
274
275
276 def create_instance_and_wait_for_active(json_body):    # pragma: no cover
277     SLEEP = 3
278     VM_BOOT_TIMEOUT = 180
279     nova_client = get_nova_client()
280     instance = create_instance(json_body)
281     for _ in range(int(VM_BOOT_TIMEOUT / SLEEP)):
282         status = get_instance_status(nova_client, instance)
283         if status.lower() == "active":
284             return instance
285         elif status.lower() == "error":
286             log.error("The instance went to ERROR status.")
287             return None
288         time.sleep(SLEEP)
289     log.error("Timeout booting the instance.")
290     return None
291
292
293 def attach_server_volume(server_id, volume_id,
294                          device=None):    # pragma: no cover
295     try:
296         get_nova_client().volumes.create_server_volume(server_id,
297                                                        volume_id, device)
298     except Exception:  # pylint: disable=broad-except
299         log.exception("Error [attach_server_volume(nova_client, '%s', '%s')]",
300                       server_id, volume_id)
301         return False
302     else:
303         return True
304
305
306 def delete_instance(nova_client, instance_id):      # pragma: no cover
307     try:
308         nova_client.servers.force_delete(instance_id)
309     except Exception:  # pylint: disable=broad-except
310         log.exception("Error [delete_instance(nova_client, '%s')]",
311                       instance_id)
312         return False
313     else:
314         return True
315
316
317 def remove_host_from_aggregate(nova_client, aggregate_name,
318                                compute_host):  # pragma: no cover
319     try:
320         aggregate_id = get_aggregate_id(nova_client, aggregate_name)
321         nova_client.aggregates.remove_host(aggregate_id, compute_host)
322     except Exception:  # pylint: disable=broad-except
323         log.exception("Error remove_host_from_aggregate(nova_client, %s, %s)",
324                       aggregate_name, compute_host)
325         return False
326     else:
327         return True
328
329
330 def remove_hosts_from_aggregate(nova_client,
331                                 aggregate_name):   # pragma: no cover
332     aggregate_id = get_aggregate_id(nova_client, aggregate_name)
333     hosts = nova_client.aggregates.get(aggregate_id).hosts
334     assert(
335         all(remove_host_from_aggregate(nova_client, aggregate_name, host)
336             for host in hosts))
337
338
339 def delete_aggregate(nova_client, aggregate_name):  # pragma: no cover
340     try:
341         remove_hosts_from_aggregate(nova_client, aggregate_name)
342         nova_client.aggregates.delete(aggregate_name)
343     except Exception:  # pylint: disable=broad-except
344         log.exception("Error [delete_aggregate(nova_client, %s)]",
345                       aggregate_name)
346         return False
347     else:
348         return True
349
350
351 def get_server_by_name(name):   # pragma: no cover
352     try:
353         return get_nova_client().servers.list(search_opts={'name': name})[0]
354     except IndexError:
355         log.exception('Failed to get nova client')
356         raise
357
358
359 def create_flavor(name, ram, vcpus, disk, **kwargs):   # pragma: no cover
360     try:
361         return get_nova_client().flavors.create(name, ram, vcpus,
362                                                 disk, **kwargs)
363     except Exception:  # pylint: disable=broad-except
364         log.exception("Error [create_flavor(nova_client, %s, %s, %s, %s, %s)]",
365                       name, ram, disk, vcpus, kwargs['is_public'])
366         return None
367
368
369 def get_image_by_name(name):    # pragma: no cover
370     images = get_nova_client().images.list()
371     try:
372         return next((a for a in images if a.name == name))
373     except StopIteration:
374         log.exception('No image matched')
375
376
377 def get_flavor_id(nova_client, flavor_name):    # pragma: no cover
378     flavors = nova_client.flavors.list(detailed=True)
379     flavor_id = ''
380     for f in flavors:
381         if f.name == flavor_name:
382             flavor_id = f.id
383             break
384     return flavor_id
385
386
387 def get_flavor_by_name(name):   # pragma: no cover
388     flavors = get_nova_client().flavors.list()
389     try:
390         return next((a for a in flavors if a.name == name))
391     except StopIteration:
392         log.exception('No flavor matched')
393
394
395 def check_status(status, name, iterations, interval):   # pragma: no cover
396     for _ in range(iterations):
397         try:
398             server = get_server_by_name(name)
399         except IndexError:
400             log.error('Cannot found %s server', name)
401             raise
402
403         if server.status == status:
404             return True
405
406         time.sleep(interval)
407     return False
408
409
410 def delete_flavor(flavor_id):    # pragma: no cover
411     try:
412         get_nova_client().flavors.delete(flavor_id)
413     except Exception:  # pylint: disable=broad-except
414         log.exception("Error [delete_flavor(nova_client, %s)]", flavor_id)
415         return False
416     else:
417         return True
418
419
420 def delete_keypair(nova_client, key):     # pragma: no cover
421     try:
422         nova_client.keypairs.delete(key=key)
423         return True
424     except Exception:  # pylint: disable=broad-except
425         log.exception("Error [delete_keypair(nova_client)]")
426         return False
427
428
429 # *********************************************
430 #   NEUTRON
431 # *********************************************
432 def create_neutron_net(shade_client, network_name, shared=False,
433                        admin_state_up=True, external=False, provider=None,
434                        project_id=None):
435     """Create a neutron network.
436
437     :param network_name:(string) name of the network being created.
438     :param shared:(bool) whether the network is shared.
439     :param admin_state_up:(bool) set the network administrative state.
440     :param external:(bool) whether this network is externally accessible.
441     :param provider:(dict) a dict of network provider options.
442     :param project_id:(string) specify the project ID this network
443                       will be created on (admin-only).
444     :returns:(string) the network id.
445     """
446     try:
447         networks = shade_client.create_network(
448             name=network_name, shared=shared, admin_state_up=admin_state_up,
449             external=external, provider=provider, project_id=project_id)
450         return networks['id']
451     except exc.OpenStackCloudException as o_exc:
452         log.error("Error [create_neutron_net(shade_client)]."
453                   "Exception message, '%s'", o_exc.orig_message)
454         return None
455
456
457 def delete_neutron_net(shade_client, network_id):
458     try:
459         return shade_client.delete_network(network_id)
460     except exc.OpenStackCloudException:
461         log.error("Error [delete_neutron_net(shade_client, '%s')]", network_id)
462         return False
463
464
465 def create_neutron_subnet(shade_client, network_name_or_id, cidr=None,
466                           ip_version=4, enable_dhcp=False, subnet_name=None,
467                           tenant_id=None, allocation_pools=None,
468                           gateway_ip=None, disable_gateway_ip=False,
469                           dns_nameservers=None, host_routes=None,
470                           ipv6_ra_mode=None, ipv6_address_mode=None,
471                           use_default_subnetpool=False):
472     """Create a subnet on a specified network.
473
474     :param network_name_or_id:(string) the unique name or ID of the
475                               attached network. If a non-unique name is
476                               supplied, an exception is raised.
477     :param cidr:(string) the CIDR.
478     :param ip_version:(int) the IP version.
479     :param enable_dhcp:(bool) whether DHCP is enable.
480     :param subnet_name:(string) the name of the subnet.
481     :param tenant_id:(string) the ID of the tenant who owns the network.
482     :param allocation_pools: A list of dictionaries of the start and end
483                             addresses for the allocation pools.
484     :param gateway_ip:(string) the gateway IP address.
485     :param disable_gateway_ip:(bool) whether gateway IP address is enabled.
486     :param dns_nameservers: A list of DNS name servers for the subnet.
487     :param host_routes: A list of host route dictionaries for the subnet.
488     :param ipv6_ra_mode:(string) IPv6 Router Advertisement mode.
489                         Valid values are: 'dhcpv6-stateful',
490                         'dhcpv6-stateless', or 'slaac'.
491     :param ipv6_address_mode:(string) IPv6 address mode.
492                              Valid values are: 'dhcpv6-stateful',
493                              'dhcpv6-stateless', or 'slaac'.
494     :param use_default_subnetpool:(bool) use the default subnetpool for
495                                   ``ip_version`` to obtain a CIDR. It is
496                                   required to pass ``None`` to the ``cidr``
497                                   argument when enabling this option.
498     :returns:(string) the subnet id.
499     """
500     try:
501         subnet = shade_client.create_subnet(
502             network_name_or_id, cidr=cidr, ip_version=ip_version,
503             enable_dhcp=enable_dhcp, subnet_name=subnet_name,
504             tenant_id=tenant_id, allocation_pools=allocation_pools,
505             gateway_ip=gateway_ip, disable_gateway_ip=disable_gateway_ip,
506             dns_nameservers=dns_nameservers, host_routes=host_routes,
507             ipv6_ra_mode=ipv6_ra_mode, ipv6_address_mode=ipv6_address_mode,
508             use_default_subnetpool=use_default_subnetpool)
509         return subnet['id']
510     except exc.OpenStackCloudException as o_exc:
511         log.error("Error [create_neutron_subnet(shade_client)]. "
512                   "Exception message: %s", o_exc.orig_message)
513         return None
514
515
516 def create_neutron_router(shade_client, name=None, admin_state_up=True,
517                           ext_gateway_net_id=None, enable_snat=None,
518                           ext_fixed_ips=None, project_id=None):
519     """Create a logical router.
520
521     :param name:(string) the router name.
522     :param admin_state_up:(bool) the administrative state of the router.
523     :param ext_gateway_net_id:(string) network ID for the external gateway.
524     :param enable_snat:(bool) enable Source NAT (SNAT) attribute.
525     :param ext_fixed_ips: List of dictionaries of desired IP and/or subnet
526                           on the external network.
527     :param project_id:(string) project ID for the router.
528
529     :returns:(string) the router id.
530     """
531     try:
532         router = shade_client.create_router(
533             name, admin_state_up, ext_gateway_net_id, enable_snat,
534             ext_fixed_ips, project_id)
535         return router['id']
536     except exc.OpenStackCloudException as o_exc:
537         log.error("Error [create_neutron_router(shade_client)]. "
538                   "Exception message: %s", o_exc.orig_message)
539
540
541 def delete_neutron_router(shade_client, router_id):
542     try:
543         return shade_client.delete_router(router_id)
544     except exc.OpenStackCloudException as o_exc:
545         log.error("Error [delete_neutron_router(shade_client, '%s')]. "
546                   "Exception message: %s", router_id, o_exc.orig_message)
547         return False
548
549
550 def remove_gateway_router(neutron_client, router_id):      # pragma: no cover
551     try:
552         neutron_client.remove_gateway_router(router_id)
553         return True
554     except Exception:  # pylint: disable=broad-except
555         log.error("Error [remove_gateway_router(neutron_client, '%s')]",
556                   router_id)
557         return False
558
559
560 def remove_router_interface(shade_client, router, subnet_id=None,
561                             port_id=None):
562     """Detach a subnet from an internal router interface.
563
564     At least one of subnet_id or port_id must be supplied. If you specify both
565     subnet and port ID, the subnet ID must correspond to the subnet ID of the
566     first IP address on the port specified by the port ID.
567     Otherwise an error occurs.
568
569     :param router: The dict object of the router being changed
570     :param subnet_id:(string) The ID of the subnet to use for the interface
571     :param port_id:(string) The ID of the port to use for the interface
572     :returns: True on success
573     """
574     try:
575         shade_client.remove_router_interface(
576             router, subnet_id=subnet_id, port_id=port_id)
577         return True
578     except exc.OpenStackCloudException as o_exc:
579         log.error("Error [remove_interface_router(shade_client)]. "
580                   "Exception message: %s", o_exc.orig_message)
581         return False
582
583
584 def create_floating_ip(shade_client, network_name_or_id=None, server=None,
585                        fixed_address=None, nat_destination=None,
586                        port=None, wait=False, timeout=60):
587     """Allocate a new floating IP from a network or a pool.
588
589     :param network_name_or_id: Name or ID of the network
590                                that the floating IP should come from.
591     :param server: Server dict for the server to create
592                   the IP for and to which it should be attached.
593     :param fixed_address: Fixed IP to attach the floating ip to.
594     :param nat_destination: Name or ID of the network
595                            that the fixed IP to attach the floating
596                            IP to should be on.
597     :param port: The port ID that the floating IP should be
598                 attached to. Specifying a port conflicts with specifying a
599                 server,fixed_address or nat_destination.
600     :param wait: Whether to wait for the IP to be active.Only applies
601                 if a server is provided.
602     :param timeout: How long to wait for the IP to be active.Only
603                    applies if a server is provided.
604
605     :returns:Floating IP id and address
606     """
607     try:
608         fip = shade_client.create_floating_ip(
609             network=network_name_or_id, server=server,
610             fixed_address=fixed_address, nat_destination=nat_destination,
611             port=port, wait=wait, timeout=timeout)
612         return {'fip_addr': fip['floating_ip_address'], 'fip_id': fip['id']}
613     except exc.OpenStackCloudException as o_exc:
614         log.error("Error [create_floating_ip(shade_client)]. "
615                   "Exception message: %s", o_exc.orig_message)
616
617
618 def delete_floating_ip(shade_client, floating_ip_id, retry=1):
619     try:
620         return shade_client.delete_floating_ip(floating_ip_id=floating_ip_id,
621                                                retry=retry)
622     except exc.OpenStackCloudException as o_exc:
623         log.error("Error [delete_floating_ip(shade_client,'%s')]. "
624                   "Exception message: %s", floating_ip_id, o_exc.orig_message)
625         return False
626
627
628 def create_security_group_rule(shade_client, secgroup_name_or_id,
629                                port_range_min=None, port_range_max=None,
630                                protocol=None, remote_ip_prefix=None,
631                                remote_group_id=None, direction='ingress',
632                                ethertype='IPv4', project_id=None):
633     """Create a new security group rule
634
635     :param secgroup_name_or_id:(string) The security group name or ID to
636                                associate with this security group rule. If a
637                                non-unique group name is given, an exception is
638                                raised.
639     :param port_range_min:(int) The minimum port number in the range that is
640                           matched by the security group rule. If the protocol
641                           is TCP or UDP, this value must be less than or equal
642                           to the port_range_max attribute value. If nova is
643                           used by the cloud provider for security groups, then
644                           a value of None will be transformed to -1.
645     :param port_range_max:(int) The maximum port number in the range that is
646                           matched by the security group rule. The
647                           port_range_min attribute constrains the
648                           port_range_max attribute. If nova is used by the
649                           cloud provider for security groups, then a value of
650                           None will be transformed to -1.
651     :param protocol:(string) The protocol that is matched by the security group
652                     rule. Valid values are None, tcp, udp, and icmp.
653     :param remote_ip_prefix:(string) The remote IP prefix to be associated with
654                             this security group rule. This attribute matches
655                             the specified IP prefix as the source IP address of
656                             the IP packet.
657     :param remote_group_id:(string) The remote group ID to be associated with
658                            this security group rule.
659     :param direction:(string) Ingress or egress: The direction in which the
660                      security group rule is applied.
661     :param ethertype:(string) Must be IPv4 or IPv6, and addresses represented
662                      in CIDR must match the ingress or egress rules.
663     :param project_id:(string) Specify the project ID this security group will
664                       be created on (admin-only).
665
666     :returns: True on success.
667     """
668
669     try:
670         shade_client.create_security_group_rule(
671             secgroup_name_or_id, port_range_min=port_range_min,
672             port_range_max=port_range_max, protocol=protocol,
673             remote_ip_prefix=remote_ip_prefix, remote_group_id=remote_group_id,
674             direction=direction, ethertype=ethertype, project_id=project_id)
675         return True
676     except exc.OpenStackCloudException as op_exc:
677         log.error("Failed to create_security_group_rule(shade_client). "
678                   "Exception message: %s", op_exc.orig_message)
679         return False
680
681
682 def create_security_group_full(shade_client, sg_name,
683                                sg_description, project_id=None):
684     security_group = shade_client.get_security_group(sg_name)
685
686     if security_group:
687         log.info("Using existing security group '%s'...", sg_name)
688         return security_group['id']
689
690     log.info("Creating security group  '%s'...", sg_name)
691     try:
692         security_group = shade_client.create_security_group(
693             sg_name, sg_description, project_id=project_id)
694     except (exc.OpenStackCloudException,
695             exc.OpenStackCloudUnavailableFeature) as op_exc:
696         log.error("Error [create_security_group(shade_client, %s, %s)]. "
697                   "Exception message: %s", sg_name, sg_description,
698                   op_exc.orig_message)
699         return
700
701     log.debug("Security group '%s' with ID=%s created successfully.",
702               security_group['name'], security_group['id'])
703
704     log.debug("Adding ICMP rules in security group '%s'...", sg_name)
705     if not create_security_group_rule(shade_client, security_group['id'],
706                                       direction='ingress', protocol='icmp'):
707         log.error("Failed to create the security group rule...")
708         shade_client.delete_security_group(sg_name)
709         return
710
711     log.debug("Adding SSH rules in security group '%s'...", sg_name)
712     if not create_security_group_rule(shade_client, security_group['id'],
713                                       direction='ingress', protocol='tcp',
714                                       port_range_min='22',
715                                       port_range_max='22'):
716         log.error("Failed to create the security group rule...")
717         shade_client.delete_security_group(sg_name)
718         return
719
720     if not create_security_group_rule(shade_client, security_group['id'],
721                                       direction='egress', protocol='tcp',
722                                       port_range_min='22',
723                                       port_range_max='22'):
724         log.error("Failed to create the security group rule...")
725         shade_client.delete_security_group(sg_name)
726         return
727     return security_group['id']
728
729
730 # *********************************************
731 #   GLANCE
732 # *********************************************
733 def get_image_id(glance_client, image_name):    # pragma: no cover
734     images = glance_client.images.list()
735     return next((i.id for i in images if i.name == image_name), None)
736
737
738 def create_image(glance_client, image_name, file_path, disk_format,
739                  container_format, min_disk, min_ram, protected, tag,
740                  public, **kwargs):    # pragma: no cover
741     if not os.path.isfile(file_path):
742         log.error("Error: file %s does not exist.", file_path)
743         return None
744     try:
745         image_id = get_image_id(glance_client, image_name)
746         if image_id is not None:
747             log.info("Image %s already exists.", image_name)
748         else:
749             log.info("Creating image '%s' from '%s'...", image_name, file_path)
750
751             image = glance_client.images.create(
752                 name=image_name, visibility=public, disk_format=disk_format,
753                 container_format=container_format, min_disk=min_disk,
754                 min_ram=min_ram, tags=tag, protected=protected, **kwargs)
755             image_id = image.id
756             with open(file_path) as image_data:
757                 glance_client.images.upload(image_id, image_data)
758         return image_id
759     except Exception:  # pylint: disable=broad-except
760         log.error(
761             "Error [create_glance_image(glance_client, '%s', '%s', '%s')]",
762             image_name, file_path, public)
763         return None
764
765
766 def delete_image(glance_client, image_id):    # pragma: no cover
767     try:
768         glance_client.images.delete(image_id)
769
770     except Exception:  # pylint: disable=broad-except
771         log.exception("Error [delete_flavor(glance_client, %s)]", image_id)
772         return False
773     else:
774         return True
775
776
777 def list_images(shade_client=None):
778     if shade_client is None:
779         shade_client = get_shade_client()
780
781     try:
782         return shade_client.list_images()
783     except exc.OpenStackCloudException as o_exc:
784         log.error("Error [list_images(shade_client)]."
785                   "Exception message, '%s'", o_exc.orig_message)
786         return False
787
788
789 # *********************************************
790 #   CINDER
791 # *********************************************
792 def get_volume_id(volume_name):    # pragma: no cover
793     volumes = get_cinder_client().volumes.list()
794     return next((v.id for v in volumes if v.name == volume_name), None)
795
796
797 def create_volume(cinder_client, volume_name, volume_size,
798                   volume_image=False):    # pragma: no cover
799     try:
800         if volume_image:
801             volume = cinder_client.volumes.create(name=volume_name,
802                                                   size=volume_size,
803                                                   imageRef=volume_image)
804         else:
805             volume = cinder_client.volumes.create(name=volume_name,
806                                                   size=volume_size)
807         return volume
808     except Exception:  # pylint: disable=broad-except
809         log.exception("Error [create_volume(cinder_client, %s)]",
810                       (volume_name, volume_size))
811         return None
812
813
814 def delete_volume(cinder_client, volume_id,
815                   forced=False):      # pragma: no cover
816     try:
817         if forced:
818             try:
819                 cinder_client.volumes.detach(volume_id)
820             except Exception:  # pylint: disable=broad-except
821                 log.error(sys.exc_info()[0])
822             cinder_client.volumes.force_delete(volume_id)
823         else:
824             while True:
825                 volume = get_cinder_client().volumes.get(volume_id)
826                 if volume.status.lower() == 'available':
827                     break
828             cinder_client.volumes.delete(volume_id)
829         return True
830     except Exception:  # pylint: disable=broad-except
831         log.exception("Error [delete_volume(cinder_client, '%s')]", volume_id)
832         return False
833
834
835 def detach_volume(server_id, volume_id):      # pragma: no cover
836     try:
837         get_nova_client().volumes.delete_server_volume(server_id, volume_id)
838         return True
839     except Exception:  # pylint: disable=broad-except
840         log.exception("Error [detach_server_volume(nova_client, '%s', '%s')]",
841                       server_id, volume_id)
842         return False