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 ##############################################################################
13 from keystoneauth1 import loading
14 from keystoneauth1 import session
18 from cinderclient import client as cinderclient
19 from novaclient import client as novaclient
20 from glanceclient import client as glanceclient
21 from neutronclient.neutron import client as neutronclient
24 log = logging.getLogger(__name__)
26 DEFAULT_HEAT_API_VERSION = '1'
27 DEFAULT_API_VERSION = '2'
30 # *********************************************
32 # *********************************************
33 def get_credentials():
34 """Returns a creds dictionary filled with parsed from env
36 Keystone API version used is 3; v2 was deprecated in 2014 (Icehouse). Along
37 with this deprecation, environment variable 'OS_TENANT_NAME' is replaced by
40 creds = {'username': os.environ.get('OS_USERNAME'),
41 'password': os.environ.get('OS_PASSWORD'),
42 'auth_url': os.environ.get('OS_AUTH_URL'),
43 'project_name': os.environ.get('OS_PROJECT_NAME')
46 if os.getenv('OS_USER_DOMAIN_NAME'):
47 creds['user_domain_name'] = os.getenv('OS_USER_DOMAIN_NAME')
48 if os.getenv('OS_PROJECT_DOMAIN_NAME'):
49 creds['project_domain_name'] = os.getenv('OS_PROJECT_DOMAIN_NAME')
54 def get_session_auth():
55 loader = loading.get_plugin_loader('password')
56 creds = get_credentials()
57 auth = loader.load_from_options(**creds)
62 auth = get_session_auth()
64 cacert = os.environ['OS_CACERT']
66 return session.Session(auth=auth)
68 insecure = os.getenv('OS_INSECURE', '').lower() == 'true'
69 cacert = False if insecure else cacert
70 return session.Session(auth=auth, verify=cacert)
73 def get_endpoint(service_type, endpoint_type='publicURL'):
74 auth = get_session_auth()
75 # for multi-region, we need to specify region
76 # when finding the endpoint
77 return get_session().get_endpoint(auth=auth,
78 service_type=service_type,
79 endpoint_type=endpoint_type,
80 region_name=os.environ.get(
84 # *********************************************
86 # *********************************************
87 def get_heat_api_version(): # pragma: no cover
89 api_version = os.environ['HEAT_API_VERSION']
91 return DEFAULT_HEAT_API_VERSION
93 log.info("HEAT_API_VERSION is set in env as '%s'", api_version)
97 def get_cinder_client_version(): # pragma: no cover
99 api_version = os.environ['OS_VOLUME_API_VERSION']
101 return DEFAULT_API_VERSION
103 log.info("OS_VOLUME_API_VERSION is set in env as '%s'", api_version)
107 def get_cinder_client(): # pragma: no cover
109 return cinderclient.Client(get_cinder_client_version(), session=sess)
112 def get_nova_client_version(): # pragma: no cover
114 api_version = os.environ['OS_COMPUTE_API_VERSION']
116 return DEFAULT_API_VERSION
118 log.info("OS_COMPUTE_API_VERSION is set in env as '%s'", api_version)
122 def get_nova_client(): # pragma: no cover
124 return novaclient.Client(get_nova_client_version(), session=sess)
127 def get_neutron_client_version(): # pragma: no cover
129 api_version = os.environ['OS_NETWORK_API_VERSION']
131 return DEFAULT_API_VERSION
133 log.info("OS_NETWORK_API_VERSION is set in env as '%s'", api_version)
137 def get_neutron_client(): # pragma: no cover
139 return neutronclient.Client(get_neutron_client_version(), session=sess)
142 def get_glance_client_version(): # pragma: no cover
144 api_version = os.environ['OS_IMAGE_API_VERSION']
146 return DEFAULT_API_VERSION
148 log.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version)
152 def get_glance_client(): # pragma: no cover
154 return glanceclient.Client(get_glance_client_version(), session=sess)
157 def get_shade_client():
158 return shade.openstack_cloud()
161 # *********************************************
163 # *********************************************
164 def create_keypair(shade_client, name, public_key=None):
165 """Create a new keypair.
167 :param name: Name of the keypair being created.
168 :param public_key: Public key for the new keypair.
170 :return: Created keypair.
173 return shade_client.create_keypair(name, public_key=public_key)
174 except exc.OpenStackCloudException as o_exc:
175 log.error("Error [create_keypair(shade_client)]. "
176 "Exception message, '%s'", o_exc.orig_message)
179 def create_instance_and_wait_for_active(shade_client, name, image,
180 flavor, auto_ip=True, ips=None,
181 ip_pool=None, root_volume=None,
182 terminate_volume=False, wait=True,
183 timeout=180, reuse_ips=True,
184 network=None, boot_from_volume=False,
185 volume_size='20', boot_volume=None,
186 volumes=None, nat_destination=None,
188 """Create a virtual server instance.
190 :param name:(string) Name of the server.
191 :param image:(dict) Image dict, name or ID to boot with. Image is required
192 unless boot_volume is given.
193 :param flavor:(dict) Flavor dict, name or ID to boot onto.
194 :param auto_ip: Whether to take actions to find a routable IP for
196 :param ips: List of IPs to attach to the server.
197 :param ip_pool:(string) Name of the network or floating IP pool to get an
199 :param root_volume:(string) Name or ID of a volume to boot from.
200 (defaults to None - deprecated, use boot_volume)
201 :param boot_volume:(string) Name or ID of a volume to boot from.
202 :param terminate_volume:(bool) If booting from a volume, whether it should
203 be deleted when the server is destroyed.
204 :param volumes:(optional) A list of volumes to attach to the server.
205 :param wait:(optional) Wait for the address to appear as assigned to the server.
206 :param timeout: Seconds to wait, defaults to 60.
207 :param reuse_ips:(bool)Whether to attempt to reuse pre-existing
208 floating ips should a floating IP be needed.
209 :param network:(dict) Network dict or name or ID to attach the server to.
210 Mutually exclusive with the nics parameter. Can also be be
211 a list of network names or IDs or network dicts.
212 :param boot_from_volume:(bool) Whether to boot from volume. 'boot_volume'
213 implies True, but boot_from_volume=True with
214 no boot_volume is valid and will create a
215 volume from the image and use that.
216 :param volume_size: When booting an image from volume, how big should
217 the created volume be?
218 :param nat_destination: Which network should a created floating IP
219 be attached to, if it's not possible to infer from
220 the cloud's configuration.
221 :param meta:(optional) A dict of arbitrary key/value metadata to store for
222 this server. Both keys and values must be <=255 characters.
223 :param reservation_id: A UUID for the set of servers being requested.
224 :param min_count:(optional extension) The minimum number of servers to
226 :param max_count:(optional extension) The maximum number of servers to
228 :param security_groups: A list of security group names.
229 :param userdata: User data to pass to be exposed by the metadata server
230 this can be a file type object as well or a string.
231 :param key_name:(optional extension) Name of previously created keypair to
232 inject into the instance.
233 :param availability_zone: Name of the availability zone for instance
235 :param block_device_mapping:(optional) A dict of block device mappings for
237 :param block_device_mapping_v2:(optional) A dict of block device mappings
239 :param nics:(optional extension) An ordered list of nics to be added to
240 this server, with information about connected networks, fixed
242 :param scheduler_hints:(optional extension) Arbitrary key-value pairs
243 specified by the client to help boot an instance.
244 :param config_drive:(optional extension) Value for config drive either
245 boolean, or volume-id.
246 :param disk_config:(optional extension) Control how the disk is partitioned
247 when the server is created. Possible values are 'AUTO'
249 :param admin_pass:(optional extension) Add a user supplied admin password.
251 :returns: The created server.
254 return shade_client.create_server(
255 name, image, flavor, auto_ip=auto_ip, ips=ips, ip_pool=ip_pool,
256 root_volume=root_volume, terminate_volume=terminate_volume,
257 wait=wait, timeout=timeout, reuse_ips=reuse_ips, network=network,
258 boot_from_volume=boot_from_volume, volume_size=volume_size,
259 boot_volume=boot_volume, volumes=volumes,
260 nat_destination=nat_destination, **kwargs)
261 except exc.OpenStackCloudException as o_exc:
262 log.error("Error [create_instance(shade_client)]. "
263 "Exception message, '%s'", o_exc.orig_message)
266 def attach_volume_to_server(shade_client, server_name_or_id, volume_name_or_id,
267 device=None, wait=True, timeout=None):
268 """Attach a volume to a server.
270 This will attach a volume, described by the passed in volume
271 dict, to the server described by the passed in server dict on the named
272 device on the server.
274 If the volume is already attached to the server, or generally not
275 available, then an exception is raised. To re-attach to a server,
276 but under a different device, the user must detach it first.
278 :param server_name_or_id:(string) The server name or id to attach to.
279 :param volume_name_or_id:(string) The volume name or id to attach.
280 :param device:(string) The device name where the volume will attach.
281 :param wait:(bool) If true, waits for volume to be attached.
282 :param timeout: Seconds to wait for volume attachment. None is forever.
284 :returns: True if attached successful, False otherwise.
287 server = shade_client.get_server(name_or_id=server_name_or_id)
288 volume = shade_client.get_volume(volume_name_or_id)
289 shade_client.attach_volume(
290 server, volume, device=device, wait=wait, timeout=timeout)
292 except exc.OpenStackCloudException as o_exc:
293 log.error("Error [attach_volume_to_server(shade_client)]. "
294 "Exception message: %s", o_exc.orig_message)
298 def delete_instance(shade_client, name_or_id, wait=False, timeout=180,
299 delete_ips=False, delete_ip_retry=1):
300 """Delete a server instance.
302 :param name_or_id: name or ID of the server to delete
303 :param wait:(bool) If true, waits for server to be deleted.
304 :param timeout:(int) Seconds to wait for server deletion.
305 :param delete_ips:(bool) If true, deletes any floating IPs associated with
307 :param delete_ip_retry:(int) Number of times to retry deleting
308 any floating ips, should the first try be
310 :returns: True if delete succeeded, False otherwise.
313 return shade_client.delete_server(
314 name_or_id, wait=wait, timeout=timeout, delete_ips=delete_ips,
315 delete_ip_retry=delete_ip_retry)
316 except exc.OpenStackCloudException as o_exc:
317 log.error("Error [delete_instance(shade_client, '%s')]. "
318 "Exception message: %s", name_or_id,
323 def get_server(shade_client, name_or_id=None, filters=None, detailed=False,
325 """Get a server by name or ID.
327 :param name_or_id: Name or ID of the server.
328 :param filters:(dict) A dictionary of meta data to use for further
330 :param detailed:(bool) Whether or not to add detailed additional
332 :param bare:(bool) Whether to skip adding any additional information to the
335 :returns: A server ``munch.Munch`` or None if no matching server is found.
338 return shade_client.get_server(name_or_id=name_or_id, filters=filters,
339 detailed=detailed, bare=bare)
340 except exc.OpenStackCloudException as o_exc:
341 log.error("Error [get_server(shade_client, '%s')]. "
342 "Exception message: %s", name_or_id, o_exc.orig_message)
345 def create_flavor(name, ram, vcpus, disk, **kwargs): # pragma: no cover
347 return get_nova_client().flavors.create(name, ram, vcpus,
349 except Exception: # pylint: disable=broad-except
350 log.exception("Error [create_flavor(nova_client, %s, %s, %s, %s, %s)]",
351 name, ram, disk, vcpus, kwargs['is_public'])
355 def get_flavor_id(nova_client, flavor_name): # pragma: no cover
356 flavors = nova_client.flavors.list(detailed=True)
359 if f.name == flavor_name:
365 def get_flavor(shade_client, name_or_id, filters=None, get_extra=True):
366 """Get a flavor by name or ID.
368 :param name_or_id: Name or ID of the flavor.
369 :param filters: A dictionary of meta data to use for further filtering.
370 :param get_extra: Whether or not the list_flavors call should get the extra
373 :returns: A flavor ``munch.Munch`` or None if no matching flavor is found.
376 return shade_client.get_flavor(name_or_id, filters=filters,
378 except exc.OpenStackCloudException as o_exc:
379 log.error("Error [get_flavor(shade_client, '%s')]. "
380 "Exception message: %s", name_or_id, o_exc.orig_message)
383 def delete_flavor(flavor_id): # pragma: no cover
385 get_nova_client().flavors.delete(flavor_id)
386 except Exception: # pylint: disable=broad-except
387 log.exception("Error [delete_flavor(nova_client, %s)]", flavor_id)
393 def delete_keypair(shade_client, name):
396 :param name: Name of the keypair to delete.
398 :returns: True if delete succeeded, False otherwise.
401 return shade_client.delete_keypair(name)
402 except exc.OpenStackCloudException as o_exc:
403 log.error("Error [delete_neutron_router(shade_client, '%s')]. "
404 "Exception message: %s", name, o_exc.orig_message)
408 # *********************************************
410 # *********************************************
411 def create_neutron_net(shade_client, network_name, shared=False,
412 admin_state_up=True, external=False, provider=None,
414 """Create a neutron network.
416 :param network_name:(string) name of the network being created.
417 :param shared:(bool) whether the network is shared.
418 :param admin_state_up:(bool) set the network administrative state.
419 :param external:(bool) whether this network is externally accessible.
420 :param provider:(dict) a dict of network provider options.
421 :param project_id:(string) specify the project ID this network
422 will be created on (admin-only).
423 :returns:(string) the network id.
426 networks = shade_client.create_network(
427 name=network_name, shared=shared, admin_state_up=admin_state_up,
428 external=external, provider=provider, project_id=project_id)
429 return networks['id']
430 except exc.OpenStackCloudException as o_exc:
431 log.error("Error [create_neutron_net(shade_client)]."
432 "Exception message, '%s'", o_exc.orig_message)
436 def delete_neutron_net(shade_client, network_id):
438 return shade_client.delete_network(network_id)
439 except exc.OpenStackCloudException:
440 log.error("Error [delete_neutron_net(shade_client, '%s')]", network_id)
444 def create_neutron_subnet(shade_client, network_name_or_id, cidr=None,
445 ip_version=4, enable_dhcp=False, subnet_name=None,
446 tenant_id=None, allocation_pools=None,
447 gateway_ip=None, disable_gateway_ip=False,
448 dns_nameservers=None, host_routes=None,
449 ipv6_ra_mode=None, ipv6_address_mode=None,
450 use_default_subnetpool=False):
451 """Create a subnet on a specified network.
453 :param network_name_or_id:(string) the unique name or ID of the
454 attached network. If a non-unique name is
455 supplied, an exception is raised.
456 :param cidr:(string) the CIDR.
457 :param ip_version:(int) the IP version.
458 :param enable_dhcp:(bool) whether DHCP is enable.
459 :param subnet_name:(string) the name of the subnet.
460 :param tenant_id:(string) the ID of the tenant who owns the network.
461 :param allocation_pools: A list of dictionaries of the start and end
462 addresses for the allocation pools.
463 :param gateway_ip:(string) the gateway IP address.
464 :param disable_gateway_ip:(bool) whether gateway IP address is enabled.
465 :param dns_nameservers: A list of DNS name servers for the subnet.
466 :param host_routes: A list of host route dictionaries for the subnet.
467 :param ipv6_ra_mode:(string) IPv6 Router Advertisement mode.
468 Valid values are: 'dhcpv6-stateful',
469 'dhcpv6-stateless', or 'slaac'.
470 :param ipv6_address_mode:(string) IPv6 address mode.
471 Valid values are: 'dhcpv6-stateful',
472 'dhcpv6-stateless', or 'slaac'.
473 :param use_default_subnetpool:(bool) use the default subnetpool for
474 ``ip_version`` to obtain a CIDR. It is
475 required to pass ``None`` to the ``cidr``
476 argument when enabling this option.
477 :returns:(string) the subnet id.
480 subnet = shade_client.create_subnet(
481 network_name_or_id, cidr=cidr, ip_version=ip_version,
482 enable_dhcp=enable_dhcp, subnet_name=subnet_name,
483 tenant_id=tenant_id, allocation_pools=allocation_pools,
484 gateway_ip=gateway_ip, disable_gateway_ip=disable_gateway_ip,
485 dns_nameservers=dns_nameservers, host_routes=host_routes,
486 ipv6_ra_mode=ipv6_ra_mode, ipv6_address_mode=ipv6_address_mode,
487 use_default_subnetpool=use_default_subnetpool)
489 except exc.OpenStackCloudException as o_exc:
490 log.error("Error [create_neutron_subnet(shade_client)]. "
491 "Exception message: %s", o_exc.orig_message)
495 def create_neutron_router(shade_client, name=None, admin_state_up=True,
496 ext_gateway_net_id=None, enable_snat=None,
497 ext_fixed_ips=None, project_id=None):
498 """Create a logical router.
500 :param name:(string) the router name.
501 :param admin_state_up:(bool) the administrative state of the router.
502 :param ext_gateway_net_id:(string) network ID for the external gateway.
503 :param enable_snat:(bool) enable Source NAT (SNAT) attribute.
504 :param ext_fixed_ips: List of dictionaries of desired IP and/or subnet
505 on the external network.
506 :param project_id:(string) project ID for the router.
508 :returns:(string) the router id.
511 router = shade_client.create_router(
512 name, admin_state_up, ext_gateway_net_id, enable_snat,
513 ext_fixed_ips, project_id)
515 except exc.OpenStackCloudException as o_exc:
516 log.error("Error [create_neutron_router(shade_client)]. "
517 "Exception message: %s", o_exc.orig_message)
520 def delete_neutron_router(shade_client, router_id):
522 return shade_client.delete_router(router_id)
523 except exc.OpenStackCloudException as o_exc:
524 log.error("Error [delete_neutron_router(shade_client, '%s')]. "
525 "Exception message: %s", router_id, o_exc.orig_message)
529 def remove_gateway_router(neutron_client, router_id): # pragma: no cover
531 neutron_client.remove_gateway_router(router_id)
533 except Exception: # pylint: disable=broad-except
534 log.error("Error [remove_gateway_router(neutron_client, '%s')]",
539 def remove_router_interface(shade_client, router, subnet_id=None,
541 """Detach a subnet from an internal router interface.
543 At least one of subnet_id or port_id must be supplied. If you specify both
544 subnet and port ID, the subnet ID must correspond to the subnet ID of the
545 first IP address on the port specified by the port ID.
546 Otherwise an error occurs.
548 :param router: The dict object of the router being changed
549 :param subnet_id:(string) The ID of the subnet to use for the interface
550 :param port_id:(string) The ID of the port to use for the interface
551 :returns: True on success
554 shade_client.remove_router_interface(
555 router, subnet_id=subnet_id, port_id=port_id)
557 except exc.OpenStackCloudException as o_exc:
558 log.error("Error [remove_interface_router(shade_client)]. "
559 "Exception message: %s", o_exc.orig_message)
563 def create_floating_ip(shade_client, network_name_or_id=None, server=None,
564 fixed_address=None, nat_destination=None,
565 port=None, wait=False, timeout=60):
566 """Allocate a new floating IP from a network or a pool.
568 :param network_name_or_id: Name or ID of the network
569 that the floating IP should come from.
570 :param server: Server dict for the server to create
571 the IP for and to which it should be attached.
572 :param fixed_address: Fixed IP to attach the floating ip to.
573 :param nat_destination: Name or ID of the network
574 that the fixed IP to attach the floating
576 :param port: The port ID that the floating IP should be
577 attached to. Specifying a port conflicts with specifying a
578 server,fixed_address or nat_destination.
579 :param wait: Whether to wait for the IP to be active.Only applies
580 if a server is provided.
581 :param timeout: How long to wait for the IP to be active.Only
582 applies if a server is provided.
584 :returns:Floating IP id and address
587 fip = shade_client.create_floating_ip(
588 network=network_name_or_id, server=server,
589 fixed_address=fixed_address, nat_destination=nat_destination,
590 port=port, wait=wait, timeout=timeout)
591 return {'fip_addr': fip['floating_ip_address'], 'fip_id': fip['id']}
592 except exc.OpenStackCloudException as o_exc:
593 log.error("Error [create_floating_ip(shade_client)]. "
594 "Exception message: %s", o_exc.orig_message)
597 def delete_floating_ip(shade_client, floating_ip_id, retry=1):
599 return shade_client.delete_floating_ip(floating_ip_id=floating_ip_id,
601 except exc.OpenStackCloudException as o_exc:
602 log.error("Error [delete_floating_ip(shade_client,'%s')]. "
603 "Exception message: %s", floating_ip_id, o_exc.orig_message)
607 def create_security_group_rule(shade_client, secgroup_name_or_id,
608 port_range_min=None, port_range_max=None,
609 protocol=None, remote_ip_prefix=None,
610 remote_group_id=None, direction='ingress',
611 ethertype='IPv4', project_id=None):
612 """Create a new security group rule
614 :param secgroup_name_or_id:(string) The security group name or ID to
615 associate with this security group rule. If a
616 non-unique group name is given, an exception is
618 :param port_range_min:(int) The minimum port number in the range that is
619 matched by the security group rule. If the protocol
620 is TCP or UDP, this value must be less than or equal
621 to the port_range_max attribute value. If nova is
622 used by the cloud provider for security groups, then
623 a value of None will be transformed to -1.
624 :param port_range_max:(int) The maximum port number in the range that is
625 matched by the security group rule. The
626 port_range_min attribute constrains the
627 port_range_max attribute. If nova is used by the
628 cloud provider for security groups, then a value of
629 None will be transformed to -1.
630 :param protocol:(string) The protocol that is matched by the security group
631 rule. Valid values are None, tcp, udp, and icmp.
632 :param remote_ip_prefix:(string) The remote IP prefix to be associated with
633 this security group rule. This attribute matches
634 the specified IP prefix as the source IP address of
636 :param remote_group_id:(string) The remote group ID to be associated with
637 this security group rule.
638 :param direction:(string) Ingress or egress: The direction in which the
639 security group rule is applied.
640 :param ethertype:(string) Must be IPv4 or IPv6, and addresses represented
641 in CIDR must match the ingress or egress rules.
642 :param project_id:(string) Specify the project ID this security group will
643 be created on (admin-only).
645 :returns: True on success.
649 shade_client.create_security_group_rule(
650 secgroup_name_or_id, port_range_min=port_range_min,
651 port_range_max=port_range_max, protocol=protocol,
652 remote_ip_prefix=remote_ip_prefix, remote_group_id=remote_group_id,
653 direction=direction, ethertype=ethertype, project_id=project_id)
655 except exc.OpenStackCloudException as op_exc:
656 log.error("Failed to create_security_group_rule(shade_client). "
657 "Exception message: %s", op_exc.orig_message)
661 def create_security_group_full(shade_client, sg_name,
662 sg_description, project_id=None):
663 security_group = shade_client.get_security_group(sg_name)
666 log.info("Using existing security group '%s'...", sg_name)
667 return security_group['id']
669 log.info("Creating security group '%s'...", sg_name)
671 security_group = shade_client.create_security_group(
672 sg_name, sg_description, project_id=project_id)
673 except (exc.OpenStackCloudException,
674 exc.OpenStackCloudUnavailableFeature) as op_exc:
675 log.error("Error [create_security_group(shade_client, %s, %s)]. "
676 "Exception message: %s", sg_name, sg_description,
680 log.debug("Security group '%s' with ID=%s created successfully.",
681 security_group['name'], security_group['id'])
683 log.debug("Adding ICMP rules in security group '%s'...", sg_name)
684 if not create_security_group_rule(shade_client, security_group['id'],
685 direction='ingress', protocol='icmp'):
686 log.error("Failed to create the security group rule...")
687 shade_client.delete_security_group(sg_name)
690 log.debug("Adding SSH rules in security group '%s'...", sg_name)
691 if not create_security_group_rule(shade_client, security_group['id'],
692 direction='ingress', protocol='tcp',
694 port_range_max='22'):
695 log.error("Failed to create the security group rule...")
696 shade_client.delete_security_group(sg_name)
699 if not create_security_group_rule(shade_client, security_group['id'],
700 direction='egress', protocol='tcp',
702 port_range_max='22'):
703 log.error("Failed to create the security group rule...")
704 shade_client.delete_security_group(sg_name)
706 return security_group['id']
709 # *********************************************
711 # *********************************************
712 def get_image_id(glance_client, image_name): # pragma: no cover
713 images = glance_client.images.list()
714 return next((i.id for i in images if i.name == image_name), None)
717 def create_image(shade_client, name, filename=None, container='images',
718 md5=None, sha256=None, disk_format=None,
719 container_format=None, disable_vendor_agent=True,
720 wait=False, timeout=3600, allow_duplicates=False, meta=None,
721 volume=None, **kwargs):
724 :param name:(str) Name of the image to create. If it is a pathname of an
725 image, the name will be constructed from the extensionless
726 basename of the path.
727 :param filename:(str) The path to the file to upload, if needed.
728 :param container:(str) Name of the container in swift where images should
729 be uploaded for import if the cloud requires such a thing.
730 :param md5:(str) md5 sum of the image file. If not given, an md5 will
732 :param sha256:(str) sha256 sum of the image file. If not given, an md5
734 :param disk_format:(str) The disk format the image is in.
735 :param container_format:(str) The container format the image is in.
736 :param disable_vendor_agent:(bool) Whether or not to append metadata
737 flags to the image to inform the cloud in
738 question to not expect a vendor agent to be running.
739 :param wait:(bool) If true, waits for image to be created.
740 :param timeout:(str) Seconds to wait for image creation.
741 :param allow_duplicates:(bool) If true, skips checks that enforce unique
743 :param meta:(dict) A dict of key/value pairs to use for metadata that
744 bypasses automatic type conversion.
745 :param volume:(str) Name or ID or volume object of a volume to create an
747 Additional kwargs will be passed to the image creation as additional
748 metadata for the image and will have all values converted to string
749 except for min_disk, min_ram, size and virtual_size which will be
751 If you are sure you have all of your data types correct or have an
752 advanced need to be explicit, use meta. If you are just a normal
753 consumer, using kwargs is likely the right choice.
754 If a value is in meta and kwargs, meta wins.
758 image_id = shade_client.get_image_id(name)
759 if image_id is not None:
760 log.info("Image %s already exists.", name)
762 log.info("Creating image '%s'", name)
763 image = shade_client.create_image(
764 name, filename=filename, container=container, md5=md5, sha256=sha256,
765 disk_format=disk_format, container_format=container_format,
766 disable_vendor_agent=disable_vendor_agent, wait=wait, timeout=timeout,
767 allow_duplicates=allow_duplicates, meta=meta, volume=volume, **kwargs)
768 image_id = image["id"]
770 except exc.OpenStackCloudException as op_exc:
771 log.error("Failed to create_image(shade_client). "
772 "Exception message: %s", op_exc.orig_message)
775 def delete_image(glance_client, image_id): # pragma: no cover
777 glance_client.images.delete(image_id)
779 except Exception: # pylint: disable=broad-except
780 log.exception("Error [delete_flavor(glance_client, %s)]", image_id)
786 def list_images(shade_client=None):
787 if shade_client is None:
788 shade_client = get_shade_client()
791 return shade_client.list_images()
792 except exc.OpenStackCloudException as o_exc:
793 log.error("Error [list_images(shade_client)]."
794 "Exception message, '%s'", o_exc.orig_message)
798 # *********************************************
800 # *********************************************
801 def get_volume_id(shade_client, volume_name):
802 return shade_client.get_volume_id(volume_name)
805 def get_volume(shade_client, name_or_id, filters=None):
806 """Get a volume by name or ID.
808 :param name_or_id: Name or ID of the volume.
809 :param filters: A dictionary of meta data to use for further filtering.
811 :returns: A volume ``munch.Munch`` or None if no matching volume is found.
813 return shade_client.get_volume(name_or_id, filters=filters)
816 def create_volume(shade_client, size, wait=True, timeout=None,
817 image=None, **kwargs):
820 :param size: Size, in GB of the volume to create.
821 :param name: (optional) Name for the volume.
822 :param description: (optional) Name for the volume.
823 :param wait: If true, waits for volume to be created.
824 :param timeout: Seconds to wait for volume creation. None is forever.
825 :param image: (optional) Image name, ID or object from which to create
828 :returns: The created volume object.
832 return shade_client.create_volume(size, wait=wait, timeout=timeout,
833 image=image, **kwargs)
834 except (exc.OpenStackCloudException, exc.OpenStackCloudTimeout) as op_exc:
835 log.error("Failed to create_volume(shade_client). "
836 "Exception message: %s", op_exc.orig_message)
839 def delete_volume(shade_client, name_or_id=None, wait=True, timeout=None):
842 :param name_or_id:(string) Name or unique ID of the volume.
843 :param wait:(bool) If true, waits for volume to be deleted.
844 :param timeout:(string) Seconds to wait for volume deletion. None is forever.
846 :return: True on success, False otherwise.
849 return shade_client.delete_volume(name_or_id=name_or_id,
850 wait=wait, timeout=timeout)
851 except (exc.OpenStackCloudException, exc.OpenStackCloudTimeout) as o_exc:
852 log.error("Error [delete_volume(shade_client,'%s')]. "
853 "Exception message: %s", name_or_id, o_exc.orig_message)
857 def detach_volume(shade_client, server_name_or_id, volume_name_or_id,
858 wait=True, timeout=None):
859 """Detach a volume from a server.
861 :param server_name_or_id: The server name or id to detach from.
862 :param volume_name_or_id: The volume name or id to detach.
863 :param wait: If true, waits for volume to be detached.
864 :param timeout: Seconds to wait for volume detachment. None is forever.
866 :return: True on success.
869 volume = shade_client.get_volume(volume_name_or_id)
870 server = get_server(shade_client, name_or_id=server_name_or_id)
871 shade_client.detach_volume(server, volume, wait=wait, timeout=timeout)
873 except (exc.OpenStackCloudException, exc.OpenStackCloudTimeout) as o_exc:
874 log.error("Error [detach_volume(shade_client)]. "
875 "Exception message: %s", o_exc.orig_message)