1 ##############################################################################
2 # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
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 ##############################################################################
15 from keystoneauth1 import loading
16 from keystoneauth1 import session
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
26 log = logging.getLogger(__name__)
28 DEFAULT_HEAT_API_VERSION = '1'
29 DEFAULT_API_VERSION = '2'
32 # *********************************************
34 # *********************************************
35 def get_credentials():
36 """Returns a creds dictionary filled with parsed from env
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
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')
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')
56 def get_session_auth():
57 loader = loading.get_plugin_loader('password')
58 creds = get_credentials()
59 auth = loader.load_from_options(**creds)
64 auth = get_session_auth()
66 cacert = os.environ['OS_CACERT']
68 return session.Session(auth=auth)
70 insecure = os.getenv('OS_INSECURE', '').lower() == 'true'
71 cacert = False if insecure else cacert
72 return session.Session(auth=auth, verify=cacert)
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(
86 # *********************************************
88 # *********************************************
89 def get_heat_api_version(): # pragma: no cover
91 api_version = os.environ['HEAT_API_VERSION']
93 return DEFAULT_HEAT_API_VERSION
95 log.info("HEAT_API_VERSION is set in env as '%s'", api_version)
99 def get_cinder_client_version(): # pragma: no cover
101 api_version = os.environ['OS_VOLUME_API_VERSION']
103 return DEFAULT_API_VERSION
105 log.info("OS_VOLUME_API_VERSION is set in env as '%s'", api_version)
109 def get_cinder_client(): # pragma: no cover
111 return cinderclient.Client(get_cinder_client_version(), session=sess)
114 def get_nova_client_version(): # pragma: no cover
116 api_version = os.environ['OS_COMPUTE_API_VERSION']
118 return DEFAULT_API_VERSION
120 log.info("OS_COMPUTE_API_VERSION is set in env as '%s'", api_version)
124 def get_nova_client(): # pragma: no cover
126 return novaclient.Client(get_nova_client_version(), session=sess)
129 def get_neutron_client_version(): # pragma: no cover
131 api_version = os.environ['OS_NETWORK_API_VERSION']
133 return DEFAULT_API_VERSION
135 log.info("OS_NETWORK_API_VERSION is set in env as '%s'", api_version)
139 def get_neutron_client(): # pragma: no cover
141 return neutronclient.Client(get_neutron_client_version(), session=sess)
144 def get_glance_client_version(): # pragma: no cover
146 api_version = os.environ['OS_IMAGE_API_VERSION']
148 return DEFAULT_API_VERSION
150 log.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version)
154 def get_glance_client(): # pragma: no cover
156 return glanceclient.Client(get_glance_client_version(), session=sess)
159 def get_shade_client():
160 return shade.openstack_cloud()
163 # *********************************************
165 # *********************************************
166 def get_instances(nova_client):
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)]")
173 def get_instance_status(nova_client, instance): # pragma: no cover
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)]")
180 def get_instance_by_name(nova_client, instance_name): # pragma: no cover
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')]",
188 def get_aggregates(nova_client): # pragma: no cover
190 return nova_client.aggregates.list()
191 except Exception: # pylint: disable=broad-except
192 log.exception("Error [get_aggregates(nova_client)]")
195 def get_availability_zones(nova_client): # pragma: no cover
197 return nova_client.availability_zones.list()
198 except Exception: # pylint: disable=broad-except
199 log.exception("Error [get_availability_zones(nova_client)]")
202 def get_availability_zone_names(nova_client): # pragma: no cover
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)]")
209 def create_aggregate(nova_client, aggregate_name, av_zone): # pragma: no cover
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)
220 def get_aggregate_id(nova_client, aggregate_name): # pragma: no cover
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)]",
231 def add_host_to_aggregate(nova_client, aggregate_name,
232 compute_host): # pragma: no cover
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)
244 def create_aggregate_with_host(nova_client, aggregate_name, av_zone,
245 compute_host): # pragma: no cover
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)
258 def create_keypair(name, key_path=None): # pragma: no cover
260 with open(key_path) as fpubkey:
261 keypair = get_nova_client().keypairs.create(
262 name=name, public_key=fpubkey.read())
264 except Exception: # pylint: disable=broad-except
265 log.exception("Error [create_keypair(nova_client)]")
268 def create_instance(json_body): # pragma: no cover
270 return get_nova_client().servers.create(**json_body)
271 except Exception: # pylint: disable=broad-except
272 log.exception("Error create instance failed")
276 def create_instance_and_wait_for_active(json_body): # pragma: no cover
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":
285 elif status.lower() == "error":
286 log.error("The instance went to ERROR status.")
289 log.error("Timeout booting the instance.")
293 def attach_server_volume(server_id, volume_id,
294 device=None): # pragma: no cover
296 get_nova_client().volumes.create_server_volume(server_id,
298 except Exception: # pylint: disable=broad-except
299 log.exception("Error [attach_server_volume(nova_client, '%s', '%s')]",
300 server_id, volume_id)
306 def delete_instance(nova_client, instance_id): # pragma: no cover
308 nova_client.servers.force_delete(instance_id)
309 except Exception: # pylint: disable=broad-except
310 log.exception("Error [delete_instance(nova_client, '%s')]",
317 def remove_host_from_aggregate(nova_client, aggregate_name,
318 compute_host): # pragma: no cover
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)
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
335 all(remove_host_from_aggregate(nova_client, aggregate_name, host)
339 def delete_aggregate(nova_client, aggregate_name): # pragma: no cover
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)]",
351 def get_server_by_name(name): # pragma: no cover
353 return get_nova_client().servers.list(search_opts={'name': name})[0]
355 log.exception('Failed to get nova client')
359 def create_flavor(name, ram, vcpus, disk, **kwargs): # pragma: no cover
361 return get_nova_client().flavors.create(name, ram, vcpus,
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'])
369 def get_image_by_name(name): # pragma: no cover
370 images = get_nova_client().images.list()
372 return next((a for a in images if a.name == name))
373 except StopIteration:
374 log.exception('No image matched')
377 def get_flavor_id(nova_client, flavor_name): # pragma: no cover
378 flavors = nova_client.flavors.list(detailed=True)
381 if f.name == flavor_name:
387 def get_flavor_by_name(name): # pragma: no cover
388 flavors = get_nova_client().flavors.list()
390 return next((a for a in flavors if a.name == name))
391 except StopIteration:
392 log.exception('No flavor matched')
395 def check_status(status, name, iterations, interval): # pragma: no cover
396 for _ in range(iterations):
398 server = get_server_by_name(name)
400 log.error('Cannot found %s server', name)
403 if server.status == status:
410 def delete_flavor(flavor_id): # pragma: no cover
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)
420 def delete_keypair(nova_client, key): # pragma: no cover
422 nova_client.keypairs.delete(key=key)
424 except Exception: # pylint: disable=broad-except
425 log.exception("Error [delete_keypair(nova_client)]")
429 # *********************************************
431 # *********************************************
432 def create_neutron_net(shade_client, network_name, shared=False,
433 admin_state_up=True, external=False, provider=None,
435 """Create a neutron network.
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.
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)
457 def delete_neutron_net(shade_client, network_id):
459 return shade_client.delete_network(network_id)
460 except exc.OpenStackCloudException:
461 log.error("Error [delete_neutron_net(shade_client, '%s')]", network_id)
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.
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.
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)
510 except exc.OpenStackCloudException as o_exc:
511 log.error("Error [create_neutron_subnet(shade_client)]. "
512 "Exception message: %s", o_exc.orig_message)
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.
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.
529 :returns:(string) the router id.
532 router = shade_client.create_router(
533 name, admin_state_up, ext_gateway_net_id, enable_snat,
534 ext_fixed_ips, project_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)
541 def delete_neutron_router(shade_client, router_id):
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)
550 def remove_gateway_router(neutron_client, router_id): # pragma: no cover
552 neutron_client.remove_gateway_router(router_id)
554 except Exception: # pylint: disable=broad-except
555 log.error("Error [remove_gateway_router(neutron_client, '%s')]",
560 def remove_router_interface(shade_client, router, subnet_id=None,
562 """Detach a subnet from an internal router interface.
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.
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
575 shade_client.remove_router_interface(
576 router, subnet_id=subnet_id, port_id=port_id)
578 except exc.OpenStackCloudException as o_exc:
579 log.error("Error [remove_interface_router(shade_client)]. "
580 "Exception message: %s", o_exc.orig_message)
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.
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
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.
605 :returns:Floating IP id and address
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)
618 def delete_floating_ip(shade_client, floating_ip_id, retry=1):
620 return shade_client.delete_floating_ip(floating_ip_id=floating_ip_id,
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)
628 def get_security_groups(neutron_client): # pragma: no cover
630 security_groups = neutron_client.list_security_groups()[
632 return security_groups
633 except Exception: # pylint: disable=broad-except
634 log.error("Error [get_security_groups(neutron_client)]")
638 def get_security_group_id(neutron_client, sg_name): # pragma: no cover
639 security_groups = get_security_groups(neutron_client)
641 for sg in security_groups:
642 if sg['name'] == sg_name:
648 def create_security_group(neutron_client, sg_name,
649 sg_description): # pragma: no cover
650 json_body = {'security_group': {'name': sg_name,
651 'description': sg_description}}
653 secgroup = neutron_client.create_security_group(json_body)
654 return secgroup['security_group']
655 except Exception: # pylint: disable=broad-except
656 log.error("Error [create_security_group(neutron_client, '%s', "
657 "'%s')]", sg_name, sg_description)
661 def create_security_group_rule(shade_client, secgroup_name_or_id,
662 port_range_min=None, port_range_max=None,
663 protocol=None, remote_ip_prefix=None,
664 remote_group_id=None, direction='ingress',
665 ethertype='IPv4', project_id=None):
666 """Create a new security group rule
668 :param secgroup_name_or_id:(string) The security group name or ID to
669 associate with this security group rule. If a
670 non-unique group name is given, an exception is
672 :param port_range_min:(int) The minimum port number in the range that is
673 matched by the security group rule. If the protocol
674 is TCP or UDP, this value must be less than or equal
675 to the port_range_max attribute value. If nova is
676 used by the cloud provider for security groups, then
677 a value of None will be transformed to -1.
678 :param port_range_max:(int) The maximum port number in the range that is
679 matched by the security group rule. The
680 port_range_min attribute constrains the
681 port_range_max attribute. If nova is used by the
682 cloud provider for security groups, then a value of
683 None will be transformed to -1.
684 :param protocol:(string) The protocol that is matched by the security group
685 rule. Valid values are None, tcp, udp, and icmp.
686 :param remote_ip_prefix:(string) The remote IP prefix to be associated with
687 this security group rule. This attribute matches
688 the specified IP prefix as the source IP address of
690 :param remote_group_id:(string) The remote group ID to be associated with
691 this security group rule.
692 :param direction:(string) Ingress or egress: The direction in which the
693 security group rule is applied.
694 :param ethertype:(string) Must be IPv4 or IPv6, and addresses represented
695 in CIDR must match the ingress or egress rules.
696 :param project_id:(string) Specify the project ID this security group will
697 be created on (admin-only).
699 :returns: True on success.
703 shade_client.create_security_group_rule(
704 secgroup_name_or_id, port_range_min=port_range_min,
705 port_range_max=port_range_max, protocol=protocol,
706 remote_ip_prefix=remote_ip_prefix, remote_group_id=remote_group_id,
707 direction=direction, ethertype=ethertype, project_id=project_id)
709 except exc.OpenStackCloudException as op_exc:
710 log.error("Failed to create_security_group_rule(shade_client). "
711 "Exception message: %s", op_exc.orig_message)
715 def create_security_group_full(neutron_client, sg_name,
716 sg_description): # pragma: no cover
717 sg_id = get_security_group_id(neutron_client, sg_name)
719 log.info("Using existing security group '%s'...", sg_name)
721 log.info("Creating security group '%s'...", sg_name)
722 SECGROUP = create_security_group(neutron_client,
726 log.error("Failed to create the security group...")
729 sg_id = SECGROUP['id']
731 log.debug("Security group '%s' with ID=%s created successfully.",
732 SECGROUP['name'], sg_id)
734 log.debug("Adding ICMP rules in security group '%s'...", sg_name)
735 if not create_security_group_rule(neutron_client, sg_id,
737 log.error("Failed to create the security group rule...")
740 log.debug("Adding SSH rules in security group '%s'...", sg_name)
741 if not create_security_group_rule(
742 neutron_client, sg_id, 'ingress', 'tcp', '22', '22'):
743 log.error("Failed to create the security group rule...")
746 if not create_security_group_rule(
747 neutron_client, sg_id, 'egress', 'tcp', '22', '22'):
748 log.error("Failed to create the security group rule...")
753 # *********************************************
755 # *********************************************
756 def get_image_id(glance_client, image_name): # pragma: no cover
757 images = glance_client.images.list()
758 return next((i.id for i in images if i.name == image_name), None)
761 def create_image(glance_client, image_name, file_path, disk_format,
762 container_format, min_disk, min_ram, protected, tag,
763 public, **kwargs): # pragma: no cover
764 if not os.path.isfile(file_path):
765 log.error("Error: file %s does not exist.", file_path)
768 image_id = get_image_id(glance_client, image_name)
769 if image_id is not None:
770 log.info("Image %s already exists.", image_name)
772 log.info("Creating image '%s' from '%s'...", image_name, file_path)
774 image = glance_client.images.create(
775 name=image_name, visibility=public, disk_format=disk_format,
776 container_format=container_format, min_disk=min_disk,
777 min_ram=min_ram, tags=tag, protected=protected, **kwargs)
779 with open(file_path) as image_data:
780 glance_client.images.upload(image_id, image_data)
782 except Exception: # pylint: disable=broad-except
784 "Error [create_glance_image(glance_client, '%s', '%s', '%s')]",
785 image_name, file_path, public)
789 def delete_image(glance_client, image_id): # pragma: no cover
791 glance_client.images.delete(image_id)
793 except Exception: # pylint: disable=broad-except
794 log.exception("Error [delete_flavor(glance_client, %s)]", image_id)
800 # *********************************************
802 # *********************************************
803 def get_volume_id(volume_name): # pragma: no cover
804 volumes = get_cinder_client().volumes.list()
805 return next((v.id for v in volumes if v.name == volume_name), None)
808 def create_volume(cinder_client, volume_name, volume_size,
809 volume_image=False): # pragma: no cover
812 volume = cinder_client.volumes.create(name=volume_name,
814 imageRef=volume_image)
816 volume = cinder_client.volumes.create(name=volume_name,
819 except Exception: # pylint: disable=broad-except
820 log.exception("Error [create_volume(cinder_client, %s)]",
821 (volume_name, volume_size))
825 def delete_volume(cinder_client, volume_id,
826 forced=False): # pragma: no cover
830 cinder_client.volumes.detach(volume_id)
831 except Exception: # pylint: disable=broad-except
832 log.error(sys.exc_info()[0])
833 cinder_client.volumes.force_delete(volume_id)
836 volume = get_cinder_client().volumes.get(volume_id)
837 if volume.status.lower() == 'available':
839 cinder_client.volumes.delete(volume_id)
841 except Exception: # pylint: disable=broad-except
842 log.exception("Error [delete_volume(cinder_client, '%s')]", volume_id)
846 def detach_volume(server_id, volume_id): # pragma: no cover
848 get_nova_client().volumes.delete_server_volume(server_id, volume_id)
850 except Exception: # pylint: disable=broad-except
851 log.exception("Error [detach_server_volume(nova_client, '%s', '%s')]",
852 server_id, volume_id)