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 cinderclient import client as cinderclient
16 from novaclient import client as novaclient
17 from glanceclient import client as glanceclient
18 from keystoneauth1 import loading
19 from keystoneauth1 import session
20 from neutronclient.neutron import client as neutronclient
24 from yardstick.common import constants
27 log = logging.getLogger(__name__)
29 DEFAULT_HEAT_API_VERSION = '1'
30 DEFAULT_API_VERSION = '2'
33 # *********************************************
35 # *********************************************
36 def get_credentials():
37 """Returns a creds dictionary filled with parsed from env
39 Keystone API version used is 3; v2 was deprecated in 2014 (Icehouse). Along
40 with this deprecation, environment variable 'OS_TENANT_NAME' is replaced by
43 creds = {'username': os.environ.get('OS_USERNAME'),
44 'password': os.environ.get('OS_PASSWORD'),
45 'auth_url': os.environ.get('OS_AUTH_URL'),
46 'project_name': os.environ.get('OS_PROJECT_NAME')
49 if os.getenv('OS_USER_DOMAIN_NAME'):
50 creds['user_domain_name'] = os.getenv('OS_USER_DOMAIN_NAME')
51 if os.getenv('OS_PROJECT_DOMAIN_NAME'):
52 creds['project_domain_name'] = os.getenv('OS_PROJECT_DOMAIN_NAME')
57 def get_session_auth():
58 loader = loading.get_plugin_loader('password')
59 creds = get_credentials()
60 auth = loader.load_from_options(**creds)
65 auth = get_session_auth()
67 cacert = os.environ['OS_CACERT']
69 return session.Session(auth=auth)
71 insecure = os.getenv('OS_INSECURE', '').lower() == 'true'
72 cacert = False if insecure else cacert
73 return session.Session(auth=auth, verify=cacert)
76 def get_endpoint(service_type, endpoint_type='publicURL'):
77 auth = get_session_auth()
78 # for multi-region, we need to specify region
79 # when finding the endpoint
80 return get_session().get_endpoint(auth=auth,
81 service_type=service_type,
82 endpoint_type=endpoint_type,
83 region_name=os.environ.get(
87 # *********************************************
89 # *********************************************
90 def get_heat_api_version(): # pragma: no cover
92 api_version = os.environ['HEAT_API_VERSION']
94 return DEFAULT_HEAT_API_VERSION
96 log.info("HEAT_API_VERSION is set in env as '%s'", api_version)
100 def get_cinder_client_version(): # pragma: no cover
102 api_version = os.environ['OS_VOLUME_API_VERSION']
104 return DEFAULT_API_VERSION
106 log.info("OS_VOLUME_API_VERSION is set in env as '%s'", api_version)
110 def get_cinder_client(): # pragma: no cover
112 return cinderclient.Client(get_cinder_client_version(), session=sess)
115 def get_nova_client_version(): # pragma: no cover
117 api_version = os.environ['OS_COMPUTE_API_VERSION']
119 return DEFAULT_API_VERSION
121 log.info("OS_COMPUTE_API_VERSION is set in env as '%s'", api_version)
125 def get_nova_client(): # pragma: no cover
127 return novaclient.Client(get_nova_client_version(), session=sess)
130 def get_neutron_client_version(): # pragma: no cover
132 api_version = os.environ['OS_NETWORK_API_VERSION']
134 return DEFAULT_API_VERSION
136 log.info("OS_NETWORK_API_VERSION is set in env as '%s'", api_version)
140 def get_neutron_client(): # pragma: no cover
142 return neutronclient.Client(get_neutron_client_version(), session=sess)
145 def get_glance_client_version(): # pragma: no cover
147 api_version = os.environ['OS_IMAGE_API_VERSION']
149 return DEFAULT_API_VERSION
151 log.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version)
155 def get_glance_client(): # pragma: no cover
157 return glanceclient.Client(get_glance_client_version(), session=sess)
160 def get_shade_client(**os_cloud_config):
161 """Get Shade OpenStack cloud client
163 By default, the input parameters given to "shade.openstack_cloud" method
164 are stored in "constants.OS_CLOUD_DEFAULT_CONFIG". The input parameters
165 passed in this function, "os_cloud_config", will overwrite the default
168 :param os_cloud_config: (kwargs) input arguments for
169 "shade.openstack_cloud" method.
170 :return: ``shade.OpenStackCloud`` object.
172 params = copy.deepcopy(constants.OS_CLOUD_DEFAULT_CONFIG)
173 params.update(os_cloud_config)
174 return shade.openstack_cloud(**params)
177 # *********************************************
179 # *********************************************
180 def create_keypair(shade_client, name, public_key=None):
181 """Create a new keypair.
183 :param name: Name of the keypair being created.
184 :param public_key: Public key for the new keypair.
186 :return: Created keypair.
189 return shade_client.create_keypair(name, public_key=public_key)
190 except exc.OpenStackCloudException as o_exc:
191 log.error("Error [create_keypair(shade_client)]. "
192 "Exception message, '%s'", o_exc.orig_message)
195 def create_instance_and_wait_for_active(shade_client, name, image,
196 flavor, auto_ip=True, ips=None,
197 ip_pool=None, root_volume=None,
198 terminate_volume=False, wait=True,
199 timeout=180, reuse_ips=True,
200 network=None, boot_from_volume=False,
201 volume_size='20', boot_volume=None,
202 volumes=None, nat_destination=None,
204 """Create a virtual server instance.
206 :param name:(string) Name of the server.
207 :param image:(dict) Image dict, name or ID to boot with. Image is required
208 unless boot_volume is given.
209 :param flavor:(dict) Flavor dict, name or ID to boot onto.
210 :param auto_ip: Whether to take actions to find a routable IP for
212 :param ips: List of IPs to attach to the server.
213 :param ip_pool:(string) Name of the network or floating IP pool to get an
215 :param root_volume:(string) Name or ID of a volume to boot from.
216 (defaults to None - deprecated, use boot_volume)
217 :param boot_volume:(string) Name or ID of a volume to boot from.
218 :param terminate_volume:(bool) If booting from a volume, whether it should
219 be deleted when the server is destroyed.
220 :param volumes:(optional) A list of volumes to attach to the server.
221 :param wait:(optional) Wait for the address to appear as assigned to the server.
222 :param timeout: Seconds to wait, defaults to 60.
223 :param reuse_ips:(bool)Whether to attempt to reuse pre-existing
224 floating ips should a floating IP be needed.
225 :param network:(dict) Network dict or name or ID to attach the server to.
226 Mutually exclusive with the nics parameter. Can also be be
227 a list of network names or IDs or network dicts.
228 :param boot_from_volume:(bool) Whether to boot from volume. 'boot_volume'
229 implies True, but boot_from_volume=True with
230 no boot_volume is valid and will create a
231 volume from the image and use that.
232 :param volume_size: When booting an image from volume, how big should
233 the created volume be?
234 :param nat_destination: Which network should a created floating IP
235 be attached to, if it's not possible to infer from
236 the cloud's configuration.
237 :param meta:(optional) A dict of arbitrary key/value metadata to store for
238 this server. Both keys and values must be <=255 characters.
239 :param reservation_id: A UUID for the set of servers being requested.
240 :param min_count:(optional extension) The minimum number of servers to
242 :param max_count:(optional extension) The maximum number of servers to
244 :param security_groups: A list of security group names.
245 :param userdata: User data to pass to be exposed by the metadata server
246 this can be a file type object as well or a string.
247 :param key_name:(optional extension) Name of previously created keypair to
248 inject into the instance.
249 :param availability_zone: Name of the availability zone for instance
251 :param block_device_mapping:(optional) A dict of block device mappings for
253 :param block_device_mapping_v2:(optional) A dict of block device mappings
255 :param nics:(optional extension) An ordered list of nics to be added to
256 this server, with information about connected networks, fixed
258 :param scheduler_hints:(optional extension) Arbitrary key-value pairs
259 specified by the client to help boot an instance.
260 :param config_drive:(optional extension) Value for config drive either
261 boolean, or volume-id.
262 :param disk_config:(optional extension) Control how the disk is partitioned
263 when the server is created. Possible values are 'AUTO'
265 :param admin_pass:(optional extension) Add a user supplied admin password.
267 :returns: The created server.
270 return shade_client.create_server(
271 name, image, flavor, auto_ip=auto_ip, ips=ips, ip_pool=ip_pool,
272 root_volume=root_volume, terminate_volume=terminate_volume,
273 wait=wait, timeout=timeout, reuse_ips=reuse_ips, network=network,
274 boot_from_volume=boot_from_volume, volume_size=volume_size,
275 boot_volume=boot_volume, volumes=volumes,
276 nat_destination=nat_destination, **kwargs)
277 except exc.OpenStackCloudException as o_exc:
278 log.error("Error [create_instance(shade_client)]. "
279 "Exception message, '%s'", o_exc.orig_message)
282 def attach_volume_to_server(shade_client, server_name_or_id, volume_name_or_id,
283 device=None, wait=True, timeout=None):
284 """Attach a volume to a server.
286 This will attach a volume, described by the passed in volume
287 dict, to the server described by the passed in server dict on the named
288 device on the server.
290 If the volume is already attached to the server, or generally not
291 available, then an exception is raised. To re-attach to a server,
292 but under a different device, the user must detach it first.
294 :param server_name_or_id:(string) The server name or id to attach to.
295 :param volume_name_or_id:(string) The volume name or id to attach.
296 :param device:(string) The device name where the volume will attach.
297 :param wait:(bool) If true, waits for volume to be attached.
298 :param timeout: Seconds to wait for volume attachment. None is forever.
300 :returns: True if attached successful, False otherwise.
303 server = shade_client.get_server(name_or_id=server_name_or_id)
304 volume = shade_client.get_volume(volume_name_or_id)
305 shade_client.attach_volume(
306 server, volume, device=device, wait=wait, timeout=timeout)
308 except exc.OpenStackCloudException as o_exc:
309 log.error("Error [attach_volume_to_server(shade_client)]. "
310 "Exception message: %s", o_exc.orig_message)
314 def delete_instance(shade_client, name_or_id, wait=False, timeout=180,
315 delete_ips=False, delete_ip_retry=1):
316 """Delete a server instance.
318 :param name_or_id: name or ID of the server to delete
319 :param wait:(bool) If true, waits for server to be deleted.
320 :param timeout:(int) Seconds to wait for server deletion.
321 :param delete_ips:(bool) If true, deletes any floating IPs associated with
323 :param delete_ip_retry:(int) Number of times to retry deleting
324 any floating ips, should the first try be
326 :returns: True if delete succeeded, False otherwise.
329 return shade_client.delete_server(
330 name_or_id, wait=wait, timeout=timeout, delete_ips=delete_ips,
331 delete_ip_retry=delete_ip_retry)
332 except exc.OpenStackCloudException as o_exc:
333 log.error("Error [delete_instance(shade_client, '%s')]. "
334 "Exception message: %s", name_or_id,
339 def get_server(shade_client, name_or_id=None, filters=None, detailed=False,
341 """Get a server by name or ID.
343 :param name_or_id: Name or ID of the server.
344 :param filters:(dict) A dictionary of meta data to use for further
346 :param detailed:(bool) Whether or not to add detailed additional
348 :param bare:(bool) Whether to skip adding any additional information to the
351 :returns: A server ``munch.Munch`` or None if no matching server is found.
354 return shade_client.get_server(name_or_id=name_or_id, filters=filters,
355 detailed=detailed, bare=bare)
356 except exc.OpenStackCloudException as o_exc:
357 log.error("Error [get_server(shade_client, '%s')]. "
358 "Exception message: %s", name_or_id, o_exc.orig_message)
361 def create_flavor(name, ram, vcpus, disk, **kwargs): # pragma: no cover
363 return get_nova_client().flavors.create(name, ram, vcpus,
365 except Exception: # pylint: disable=broad-except
366 log.exception("Error [create_flavor(nova_client, %s, %s, %s, %s, %s)]",
367 name, ram, disk, vcpus, kwargs['is_public'])
371 def get_flavor_id(nova_client, flavor_name): # pragma: no cover
372 flavors = nova_client.flavors.list(detailed=True)
375 if f.name == flavor_name:
381 def get_flavor(shade_client, name_or_id, filters=None, get_extra=True):
382 """Get a flavor by name or ID.
384 :param name_or_id: Name or ID of the flavor.
385 :param filters: A dictionary of meta data to use for further filtering.
386 :param get_extra: Whether or not the list_flavors call should get the extra
389 :returns: A flavor ``munch.Munch`` or None if no matching flavor is found.
392 return shade_client.get_flavor(name_or_id, filters=filters,
394 except exc.OpenStackCloudException as o_exc:
395 log.error("Error [get_flavor(shade_client, '%s')]. "
396 "Exception message: %s", name_or_id, o_exc.orig_message)
399 def delete_flavor(flavor_id): # pragma: no cover
401 get_nova_client().flavors.delete(flavor_id)
402 except Exception: # pylint: disable=broad-except
403 log.exception("Error [delete_flavor(nova_client, %s)]", flavor_id)
409 def delete_keypair(shade_client, name):
412 :param name: Name of the keypair to delete.
414 :returns: True if delete succeeded, False otherwise.
417 return shade_client.delete_keypair(name)
418 except exc.OpenStackCloudException as o_exc:
419 log.error("Error [delete_neutron_router(shade_client, '%s')]. "
420 "Exception message: %s", name, o_exc.orig_message)
424 # *********************************************
426 # *********************************************
427 def create_neutron_net(shade_client, network_name, shared=False,
428 admin_state_up=True, external=False, provider=None,
430 """Create a neutron network.
432 :param network_name:(string) name of the network being created.
433 :param shared:(bool) whether the network is shared.
434 :param admin_state_up:(bool) set the network administrative state.
435 :param external:(bool) whether this network is externally accessible.
436 :param provider:(dict) a dict of network provider options.
437 :param project_id:(string) specify the project ID this network
438 will be created on (admin-only).
439 :returns:(string) the network id.
442 networks = shade_client.create_network(
443 name=network_name, shared=shared, admin_state_up=admin_state_up,
444 external=external, provider=provider, project_id=project_id)
445 return networks['id']
446 except exc.OpenStackCloudException as o_exc:
447 log.error("Error [create_neutron_net(shade_client)]."
448 "Exception message, '%s'", o_exc.orig_message)
452 def delete_neutron_net(shade_client, network_id):
454 return shade_client.delete_network(network_id)
455 except exc.OpenStackCloudException:
456 log.error("Error [delete_neutron_net(shade_client, '%s')]", network_id)
460 def create_neutron_subnet(shade_client, network_name_or_id, cidr=None,
461 ip_version=4, enable_dhcp=False, subnet_name=None,
462 tenant_id=None, allocation_pools=None,
463 gateway_ip=None, disable_gateway_ip=False,
464 dns_nameservers=None, host_routes=None,
465 ipv6_ra_mode=None, ipv6_address_mode=None,
466 use_default_subnetpool=False):
467 """Create a subnet on a specified network.
469 :param network_name_or_id:(string) the unique name or ID of the
470 attached network. If a non-unique name is
471 supplied, an exception is raised.
472 :param cidr:(string) the CIDR.
473 :param ip_version:(int) the IP version.
474 :param enable_dhcp:(bool) whether DHCP is enable.
475 :param subnet_name:(string) the name of the subnet.
476 :param tenant_id:(string) the ID of the tenant who owns the network.
477 :param allocation_pools: A list of dictionaries of the start and end
478 addresses for the allocation pools.
479 :param gateway_ip:(string) the gateway IP address.
480 :param disable_gateway_ip:(bool) whether gateway IP address is enabled.
481 :param dns_nameservers: A list of DNS name servers for the subnet.
482 :param host_routes: A list of host route dictionaries for the subnet.
483 :param ipv6_ra_mode:(string) IPv6 Router Advertisement mode.
484 Valid values are: 'dhcpv6-stateful',
485 'dhcpv6-stateless', or 'slaac'.
486 :param ipv6_address_mode:(string) IPv6 address mode.
487 Valid values are: 'dhcpv6-stateful',
488 'dhcpv6-stateless', or 'slaac'.
489 :param use_default_subnetpool:(bool) use the default subnetpool for
490 ``ip_version`` to obtain a CIDR. It is
491 required to pass ``None`` to the ``cidr``
492 argument when enabling this option.
493 :returns:(string) the subnet id.
496 subnet = shade_client.create_subnet(
497 network_name_or_id, cidr=cidr, ip_version=ip_version,
498 enable_dhcp=enable_dhcp, subnet_name=subnet_name,
499 tenant_id=tenant_id, allocation_pools=allocation_pools,
500 gateway_ip=gateway_ip, disable_gateway_ip=disable_gateway_ip,
501 dns_nameservers=dns_nameservers, host_routes=host_routes,
502 ipv6_ra_mode=ipv6_ra_mode, ipv6_address_mode=ipv6_address_mode,
503 use_default_subnetpool=use_default_subnetpool)
505 except exc.OpenStackCloudException as o_exc:
506 log.error("Error [create_neutron_subnet(shade_client)]. "
507 "Exception message: %s", o_exc.orig_message)
511 def create_neutron_router(shade_client, name=None, admin_state_up=True,
512 ext_gateway_net_id=None, enable_snat=None,
513 ext_fixed_ips=None, project_id=None):
514 """Create a logical router.
516 :param name:(string) the router name.
517 :param admin_state_up:(bool) the administrative state of the router.
518 :param ext_gateway_net_id:(string) network ID for the external gateway.
519 :param enable_snat:(bool) enable Source NAT (SNAT) attribute.
520 :param ext_fixed_ips: List of dictionaries of desired IP and/or subnet
521 on the external network.
522 :param project_id:(string) project ID for the router.
524 :returns:(string) the router id.
527 router = shade_client.create_router(
528 name, admin_state_up, ext_gateway_net_id, enable_snat,
529 ext_fixed_ips, project_id)
531 except exc.OpenStackCloudException as o_exc:
532 log.error("Error [create_neutron_router(shade_client)]. "
533 "Exception message: %s", o_exc.orig_message)
536 def delete_neutron_router(shade_client, router_id):
538 return shade_client.delete_router(router_id)
539 except exc.OpenStackCloudException as o_exc:
540 log.error("Error [delete_neutron_router(shade_client, '%s')]. "
541 "Exception message: %s", router_id, o_exc.orig_message)
545 def remove_gateway_router(neutron_client, router_id): # pragma: no cover
547 neutron_client.remove_gateway_router(router_id)
549 except Exception: # pylint: disable=broad-except
550 log.error("Error [remove_gateway_router(neutron_client, '%s')]",
555 def remove_router_interface(shade_client, router, subnet_id=None,
557 """Detach a subnet from an internal router interface.
559 At least one of subnet_id or port_id must be supplied. If you specify both
560 subnet and port ID, the subnet ID must correspond to the subnet ID of the
561 first IP address on the port specified by the port ID.
562 Otherwise an error occurs.
564 :param router: The dict object of the router being changed
565 :param subnet_id:(string) The ID of the subnet to use for the interface
566 :param port_id:(string) The ID of the port to use for the interface
567 :returns: True on success
570 shade_client.remove_router_interface(
571 router, subnet_id=subnet_id, port_id=port_id)
573 except exc.OpenStackCloudException as o_exc:
574 log.error("Error [remove_interface_router(shade_client)]. "
575 "Exception message: %s", o_exc.orig_message)
579 def create_floating_ip(shade_client, network_name_or_id=None, server=None,
580 fixed_address=None, nat_destination=None,
581 port=None, wait=False, timeout=60):
582 """Allocate a new floating IP from a network or a pool.
584 :param network_name_or_id: Name or ID of the network
585 that the floating IP should come from.
586 :param server: Server dict for the server to create
587 the IP for and to which it should be attached.
588 :param fixed_address: Fixed IP to attach the floating ip to.
589 :param nat_destination: Name or ID of the network
590 that the fixed IP to attach the floating
592 :param port: The port ID that the floating IP should be
593 attached to. Specifying a port conflicts with specifying a
594 server,fixed_address or nat_destination.
595 :param wait: Whether to wait for the IP to be active.Only applies
596 if a server is provided.
597 :param timeout: How long to wait for the IP to be active.Only
598 applies if a server is provided.
600 :returns:Floating IP id and address
603 fip = shade_client.create_floating_ip(
604 network=network_name_or_id, server=server,
605 fixed_address=fixed_address, nat_destination=nat_destination,
606 port=port, wait=wait, timeout=timeout)
607 return {'fip_addr': fip['floating_ip_address'], 'fip_id': fip['id']}
608 except exc.OpenStackCloudException as o_exc:
609 log.error("Error [create_floating_ip(shade_client)]. "
610 "Exception message: %s", o_exc.orig_message)
613 def delete_floating_ip(shade_client, floating_ip_id, retry=1):
615 return shade_client.delete_floating_ip(floating_ip_id=floating_ip_id,
617 except exc.OpenStackCloudException as o_exc:
618 log.error("Error [delete_floating_ip(shade_client,'%s')]. "
619 "Exception message: %s", floating_ip_id, o_exc.orig_message)
623 def create_security_group_rule(shade_client, secgroup_name_or_id,
624 port_range_min=None, port_range_max=None,
625 protocol=None, remote_ip_prefix=None,
626 remote_group_id=None, direction='ingress',
627 ethertype='IPv4', project_id=None):
628 """Create a new security group rule
630 :param secgroup_name_or_id:(string) The security group name or ID to
631 associate with this security group rule. If a
632 non-unique group name is given, an exception is
634 :param port_range_min:(int) The minimum port number in the range that is
635 matched by the security group rule. If the protocol
636 is TCP or UDP, this value must be less than or equal
637 to the port_range_max attribute value. If nova is
638 used by the cloud provider for security groups, then
639 a value of None will be transformed to -1.
640 :param port_range_max:(int) The maximum port number in the range that is
641 matched by the security group rule. The
642 port_range_min attribute constrains the
643 port_range_max attribute. If nova is used by the
644 cloud provider for security groups, then a value of
645 None will be transformed to -1.
646 :param protocol:(string) The protocol that is matched by the security group
647 rule. Valid values are None, tcp, udp, and icmp.
648 :param remote_ip_prefix:(string) The remote IP prefix to be associated with
649 this security group rule. This attribute matches
650 the specified IP prefix as the source IP address of
652 :param remote_group_id:(string) The remote group ID to be associated with
653 this security group rule.
654 :param direction:(string) Ingress or egress: The direction in which the
655 security group rule is applied.
656 :param ethertype:(string) Must be IPv4 or IPv6, and addresses represented
657 in CIDR must match the ingress or egress rules.
658 :param project_id:(string) Specify the project ID this security group will
659 be created on (admin-only).
661 :returns: True on success.
665 shade_client.create_security_group_rule(
666 secgroup_name_or_id, port_range_min=port_range_min,
667 port_range_max=port_range_max, protocol=protocol,
668 remote_ip_prefix=remote_ip_prefix, remote_group_id=remote_group_id,
669 direction=direction, ethertype=ethertype, project_id=project_id)
671 except exc.OpenStackCloudException as op_exc:
672 log.error("Failed to create_security_group_rule(shade_client). "
673 "Exception message: %s", op_exc.orig_message)
677 def create_security_group_full(shade_client, sg_name,
678 sg_description, project_id=None):
679 security_group = shade_client.get_security_group(sg_name)
682 log.info("Using existing security group '%s'...", sg_name)
683 return security_group['id']
685 log.info("Creating security group '%s'...", sg_name)
687 security_group = shade_client.create_security_group(
688 sg_name, sg_description, project_id=project_id)
689 except (exc.OpenStackCloudException,
690 exc.OpenStackCloudUnavailableFeature) as op_exc:
691 log.error("Error [create_security_group(shade_client, %s, %s)]. "
692 "Exception message: %s", sg_name, sg_description,
696 log.debug("Security group '%s' with ID=%s created successfully.",
697 security_group['name'], security_group['id'])
699 log.debug("Adding ICMP rules in security group '%s'...", sg_name)
700 if not create_security_group_rule(shade_client, security_group['id'],
701 direction='ingress', protocol='icmp'):
702 log.error("Failed to create the security group rule...")
703 shade_client.delete_security_group(sg_name)
706 log.debug("Adding SSH rules in security group '%s'...", sg_name)
707 if not create_security_group_rule(shade_client, security_group['id'],
708 direction='ingress', protocol='tcp',
710 port_range_max='22'):
711 log.error("Failed to create the security group rule...")
712 shade_client.delete_security_group(sg_name)
715 if not create_security_group_rule(shade_client, security_group['id'],
716 direction='egress', protocol='tcp',
718 port_range_max='22'):
719 log.error("Failed to create the security group rule...")
720 shade_client.delete_security_group(sg_name)
722 return security_group['id']
725 # *********************************************
727 # *********************************************
728 def get_image_id(glance_client, image_name): # pragma: no cover
729 images = glance_client.images.list()
730 return next((i.id for i in images if i.name == image_name), None)
733 def create_image(glance_client, image_name, file_path, disk_format,
734 container_format, min_disk, min_ram, protected, tag,
735 public, **kwargs): # pragma: no cover
736 if not os.path.isfile(file_path):
737 log.error("Error: file %s does not exist.", file_path)
740 image_id = get_image_id(glance_client, image_name)
741 if image_id is not None:
742 log.info("Image %s already exists.", image_name)
744 log.info("Creating image '%s' from '%s'...", image_name, file_path)
746 image = glance_client.images.create(
747 name=image_name, visibility=public, disk_format=disk_format,
748 container_format=container_format, min_disk=min_disk,
749 min_ram=min_ram, tags=tag, protected=protected, **kwargs)
751 with open(file_path) as image_data:
752 glance_client.images.upload(image_id, image_data)
754 except Exception: # pylint: disable=broad-except
756 "Error [create_glance_image(glance_client, '%s', '%s', '%s')]",
757 image_name, file_path, public)
761 def delete_image(glance_client, image_id): # pragma: no cover
763 glance_client.images.delete(image_id)
765 except Exception: # pylint: disable=broad-except
766 log.exception("Error [delete_flavor(glance_client, %s)]", image_id)
772 def list_images(shade_client=None):
773 if shade_client is None:
774 shade_client = get_shade_client()
777 return shade_client.list_images()
778 except exc.OpenStackCloudException as o_exc:
779 log.error("Error [list_images(shade_client)]."
780 "Exception message, '%s'", o_exc.orig_message)
784 # *********************************************
786 # *********************************************
787 def get_volume_id(shade_client, volume_name):
788 return shade_client.get_volume_id(volume_name)
791 def get_volume(shade_client, name_or_id, filters=None):
792 """Get a volume by name or ID.
794 :param name_or_id: Name or ID of the volume.
795 :param filters: A dictionary of meta data to use for further filtering.
797 :returns: A volume ``munch.Munch`` or None if no matching volume is found.
799 return shade_client.get_volume(name_or_id, filters=filters)
802 def create_volume(shade_client, size, wait=True, timeout=None,
803 image=None, **kwargs):
806 :param size: Size, in GB of the volume to create.
807 :param name: (optional) Name for the volume.
808 :param description: (optional) Name for the volume.
809 :param wait: If true, waits for volume to be created.
810 :param timeout: Seconds to wait for volume creation. None is forever.
811 :param image: (optional) Image name, ID or object from which to create
814 :returns: The created volume object.
818 return shade_client.create_volume(size, wait=wait, timeout=timeout,
819 image=image, **kwargs)
820 except (exc.OpenStackCloudException, exc.OpenStackCloudTimeout) as op_exc:
821 log.error("Failed to create_volume(shade_client). "
822 "Exception message: %s", op_exc.orig_message)
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)